diff options
71 files changed, 6066 insertions, 218 deletions
@@ -33,6 +33,8 @@ op_xml_obj = $(op_xml_src:.xml.in=.xml) interface_definitions: $(config_xml_obj) mkdir -p $(TMPL_DIR) + $(CURDIR)/scripts/override-default $(BUILD_DIR)/interface-definitions + find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1 # XXX: delete top level node.def's that now live in other packages diff --git a/data/configd-include.json b/data/configd-include.json index eb1dd13f9..751d8e012 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -28,11 +28,15 @@ "ipsec-settings.py", "lldp.py", "nat.py", +"nat66.py", "ntp.py", "policy-local-route.py", +"protocols_bgp.py", "protocols_igmp.py", "protocols_isis.py", "protocols_mpls.py", +"protocols_ospf.py", +"protocols_ospfv3.py", "protocols_pim.py", "protocols_rip.py", "protocols_static_multicast.py", diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl index 770a24a95..499733225 100644 --- a/data/templates/firewall/nftables-nat.tmpl +++ b/data/templates/firewall/nftables-nat.tmpl @@ -21,18 +21,34 @@ {% set comment = 'DST-NAT-' + rule %} {% set base_log = '[NAT-DST-' + rule %} {% set interface = ' iifname "' + config.inbound_interface + '"' if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %} -{% set trns_addr = 'dnat to ' + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{# support 1:1 network translation #} +{% if config.translation.address | is_ip_network %} +{% set trns_addr = 'dnat ip prefix to ip daddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %} +{# we can now clear out the src_addr part as it's already covered in aboves map #} +{% set src_addr = '' %} +{% else %} +{% set trns_addr = 'dnat to ' + config.translation.address %} +{% endif %} +{% endif %} {% elif chain == 'POSTROUTING' %} {% set comment = 'SRC-NAT-' + rule %} {% set base_log = '[NAT-SRC-' + rule %} {% set interface = ' oifname "' + config.outbound_interface + '"' if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %} -{% if config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %} -{% set trns_addr = config.translation.address %} -{% if config.translation.port is defined and config.translation.port is not none %} -{% set trns_addr = trns_addr + ' to ' %} +{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{% if config.translation.address == 'masquerade' %} +{% set trns_addr = config.translation.address %} +{% if config.translation.port is defined and config.translation.port is not none %} +{% set trns_addr = trns_addr + ' to ' %} +{% endif %} +{# support 1:1 network translation #} +{% elif config.translation.address | is_ip_network %} +{% set trns_addr = 'snat ip prefix to ip saddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %} +{# we can now clear out the src_addr part as it's already covered in aboves map #} +{% set src_addr = '' %} +{% else %} +{% set trns_addr = 'snat to ' + config.translation.address %} {% endif %} -{% else %} -{% set trns_addr = 'snat to ' + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} {% endif %} {% endif %} {% set trns_port = ':' + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %} @@ -102,7 +118,7 @@ {% endmacro %} # Start with clean NAT table -flush table nat +flush table ip nat {% if helper_functions == 'remove' %} {# NAT if going to be disabled - remove rules and targets from nftables #} {% set base_command = 'delete rule ip raw' %} @@ -132,7 +148,6 @@ add rule ip raw NAT_CONNTRACK counter accept {{ nat_rule(rule, config, 'PREROUTING') }} {% endfor %} {% endif %} - # # Source NAT rules build up here # diff --git a/data/templates/firewall/nftables-nat66.tmpl b/data/templates/firewall/nftables-nat66.tmpl new file mode 100644 index 000000000..b1a8f7a16 --- /dev/null +++ b/data/templates/firewall/nftables-nat66.tmpl @@ -0,0 +1,85 @@ +#!/usr/sbin/nft -f + +{% macro nptv6_rule(rule,config, chain) %} +{% set src_prefix = "ip6 saddr " + config.source.prefix if config.source is defined and config.source.prefix is defined and config.source.prefix is not none %} +{% set dest_address = "ip6 daddr " + config.destination.address if config.destination is defined and config.destination.address is defined and config.destination.address is not none %} +{% if chain == "PREROUTING" %} +{% set interface = " iifname \"" + config.inbound_interface + "\"" if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %} +{% if config.translation.address | is_ip_network %} +{# support 1:1 network translation #} +{% set dnat_type = "dnat prefix to " %} +{% else %} +{% set dnat_type = "dnat to " %} +{% endif %} +{% set trns_address = dnat_type + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %} +{% elif chain == "POSTROUTING" %} +{% set interface = " oifname \"" + config.outbound_interface + "\"" if config.outbound_interface is defined else '' %} +{% set trns_prefix = "snat prefix to " + config.translation.prefix if config.translation is defined and config.translation.prefix is defined and config.translation.prefix is not none %} +{% endif %} +{% set comment = "NPT-NAT-" + rule %} +{% if rule.log %} +{% set base_log = "[NPT-DST-" + rule %} +{% set log = base_log + "]" %} +{% endif %} +{% set output = "add rule ip6 nat " + chain + interface %} +{# Count packets #} +{% set output = output + " counter" %} +{# Special handling of log option, we must repeat the entire rule before the #} +{# NAT translation options are added, this is essential #} +{% if log %} +{% set log_output = output + " log prefix \"" + log + "\" comment \"" + comment + "\"" %} +{% endif %} +{% if src_prefix %} +{% set output = output + " " + src_prefix %} +{% endif %} +{% if dest_address %} +{% set output = output + " " + dest_address %} +{% endif %} +{% if trns_prefix %} +{% set output = output + " " + trns_prefix %} +{% endif %} +{% if trns_address %} +{% set output = output + " " + trns_address %} +{% endif %} +{% if comment %} +{% set output = output + " comment \"" + comment + "\"" %} +{% endif %} +{{ log_output if log_output }} +{{ output }} +{% endmacro %} + +# Start with clean NAT table +flush table ip6 nat +{% if helper_functions == 'remove' %} +{# NAT if going to be disabled - remove rules and targets from nftables #} +{% set base_command = "delete rule ip6 raw" %} +{{base_command}} PREROUTING handle {{ pre_ct_conntrack }} +{{base_command}} OUTPUT handle {{ out_ct_conntrack }} + +delete chain ip6 raw NAT_CONNTRACK + +{% elif helper_functions == 'add' %} +{# NAT if enabled - add targets to nftables #} +add chain ip6 raw NAT_CONNTRACK +add rule ip6 raw NAT_CONNTRACK counter accept +{% set base_command = "add rule ip6 raw" %} +{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK +{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK +{% endif %} + +# +# Destination NAT66 rules build up here +# +{% if destination is defined and destination.rule is defined and destination.rule is not none %} +{% for rule, config in destination.rule.items() if config.disable is not defined %} +{{ nptv6_rule(rule, config, 'PREROUTING') }} +{% endfor %} +{% endif %} +# +# Source NAT66 rules build up here +# +{% if source is defined and source.rule is defined and source.rule is not none %} +{% for rule, config in source.rule.items() if config.disable is not defined %} +{{ nptv6_rule(rule, config, 'POSTROUTING') }} +{% endfor %} +{% endif %} diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl index 74a9b8c30..523fe75e3 100644 --- a/data/templates/frr/bgp.frr.tmpl +++ b/data/templates/frr/bgp.frr.tmpl @@ -52,6 +52,22 @@ {% if config.update_source is defined and config.update_source is not none %} neighbor {{ neighbor }} update-source {{ config.update_source }} {% endif %} +{% if config.interface is defined and config.interface is not none %} +{% if config.interface.peer_group is defined and config.interface.peer_group is not none %} + neighbor {{ neighbor }} interface peer-group {{ config.interface.peer_group }} +{% endif %} +{% if config.interface.remote_as is defined and config.interface.remote_as is not none %} + neighbor {{ neighbor }} interface remote-as {{ config.interface.remote_as }} +{% endif %} +{% if config.interface.v6only is defined and config.interface.v6only is not none %} +{% if config.interface.v6only.peer_group is defined and config.interface.v6only.peer_group is not none %} + neighbor {{ neighbor }} interface v6only peer-group {{ config.interface.v6only.peer_group }} +{% endif %} +{% if config.interface.v6only.remote_as is defined and config.interface.v6only.remote_as is not none %} + neighbor {{ neighbor }} interface v6only remote-as {{ config.interface.v6only.remote_as }} +{% endif %} +{% endif %} +{% endif %} ! {% if config.address_family is defined and config.address_family is not none %} {% for af in config.address_family %} @@ -84,14 +100,16 @@ {% if config.address_family[af].distribute_list is defined and config.address_family[af].distribute_list is not none %} {% if config.address_family[af].distribute_list.export is defined and config.address_family[af].distribute_list.export is not none %} neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} out -{% elif config.address_family[af].distribute_list.import is defined and config.address_family[af].distribute_list.import is not none %} - neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.export }} in +{% endif %} +{% if config.address_family[af].distribute_list.import is defined and config.address_family[af].distribute_list.import is not none %} + neighbor {{ neighbor }} distribute-list {{ config.address_family[af].distribute_list.import }} in {% endif %} {% endif %} {% if config.address_family[af].filter_list is defined and config.address_family[af].filter_list is not none %} {% if config.address_family[af].filter_list.export is defined and config.address_family[af].filter_list.export is not none %} neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.export }} out -{% elif config.address_family[af].filter_list.import is defined and config.address_family[af].filter_list.import is not none %} +{% endif %} +{% if config.address_family[af].filter_list.import is defined and config.address_family[af].filter_list.import is not none %} neighbor {{ neighbor }} filter-list {{ config.address_family[af].filter_list.import }} in {% endif %} {% endif %} @@ -108,14 +126,16 @@ {% if config.address_family[af].route_map is defined and config.address_family[af].route_map is not none %} {% if config.address_family[af].route_map.export is defined and config.address_family[af].route_map.export is not none %} neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.export }} out -{% elif config.address_family[af].route_map.import is defined and config.address_family[af].route_map.import is not none %} +{% endif %} +{% if config.address_family[af].route_map.import is defined and config.address_family[af].route_map.import is not none %} neighbor {{ neighbor }} route-map {{ config.address_family[af].route_map.import }} in {% endif %} {% endif %} {% if config.address_family[af].prefix_list is defined and config.address_family[af].prefix_list is not none %} {% if config.address_family[af].prefix_list.export is defined and config.address_family[af].prefix_list.export is not none %} neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.export }} out -{% elif config.address_family[af].prefix_list.import is defined and config.address_family[af].prefix_list.import is not none %} +{% endif %} +{% if config.address_family[af].prefix_list.import is defined and config.address_family[af].prefix_list.import is not none %} neighbor {{ neighbor }} prefix-list {{ config.address_family[af].prefix_list.import }} in {% endif %} {% endif %} @@ -198,11 +218,21 @@ router bgp {{ asn }} {% endif %} ! {% if neighbor is defined and neighbor is not none %} -{% for n, config in neighbor.items() %} -{{ bgp_neighbor(n, config) }} +{% for peer, config in neighbor.items() %} +{{ bgp_neighbor(peer, config) }} {% endfor %} {% endif %} ! +{% if listen is defined %} +{% if listen.limit is defined and listen.limit is not none %} + bgp listen limit {{ listen.limit }} +{% endif %} +{% for prefix, options in listen.range.items() %} +{% if options.peer_group is defined and options.peer_group is not none %} + bgp listen range {{ prefix }} peer-group {{ options.peer_group }} +{% endif %} +{% endfor %} +{% endif %} {% if parameters is defined %} {% if parameters.always_compare_med is defined %} bgp always-compare-med @@ -282,8 +312,8 @@ router bgp {{ asn }} {% if timers is defined and timers.keepalive is defined and timers.holdtime is defined %} timers bgp {{ timers.keepalive }} {{ timers.holdtime }} {% endif %} - ! +! {% if route_map is defined and route_map is not none %} - ip protocol bgp route-map {{ route_map }} +ip protocol bgp route-map {{ route_map }} {% endif %} - ! +! diff --git a/data/templates/frr/ospf.frr.tmpl b/data/templates/frr/ospf.frr.tmpl new file mode 100644 index 000000000..07699290c --- /dev/null +++ b/data/templates/frr/ospf.frr.tmpl @@ -0,0 +1,131 @@ +! +router ospf +{% if access_list is defined and access_list is not none %} +{% for acl, acl_config in access_list.items() %} +{% for protocol in acl_config.export if acl_config.export is defined %} + distribute-list {{ acl }} out {{ protocol }} +{% endfor %} +{% endfor %} +{% endif %} +{% if area is defined and area is not none %} +{% for area_id, area_config in area.items() %} +{% if area_config.area_type is defined and area_config.area_type is not none %} +{% for type, type_config in area_config.area_type.items() if type != 'normal' %} + area {{ area_id }} {{ type }} {{ 'no-summary' if type_config.no_summary is defined }} +{% if type_config.default_cost is defined and type_config.default_cost is not none %} + area {{ area_id }} default-cost {{ type_config.default_cost }} +{% endif %} +{% endfor %} +{% endif %} +{% if area_config.authentication is defined and area_config.authentication is not none %} + area {{ area_id }} authentication {{ 'message-digest' if area_config.authentication == 'md5' }} +{% endif %} +{% for network in area_config.network if area_config.network is defined %} + network {{ network }} area {{ area_id }} +{% endfor %} +{% if area_config.range is defined and area_config.range is not none %} +{% for range, range_config in area_config.range.items() %} +{% if range_config.cost is defined and range_config.cost is not none %} + area {{ area_id }} range {{ range }} cost {{ range_config.cost }} +{% endif %} +{% if range_config.not_advertise is defined %} + area {{ area_id }} range {{ range }} not-advertise +{% endif %} +{% if range_config.substitute is defined and range_config.substitute is not none %} + area {{ area_id }} range {{ range }} substitute {{ range_config.substitute }} +{% endif %} +{% endfor %} +{% endif %} +{% if area_config.shortcut is defined and area_config.shortcut is not none %} + area {{ area_id }} shortcut {{ area_config.shortcut }} +{% endif %} +{% if area_config.virtual_link is defined and area_config.virtual_link is not none %} +{% for link, link_config in area_config.virtual_link.items() %} +{% if link_config.authentication is defined and link_config.authentication is not none %} +{% if link_config.authentication.plaintext_password is defined and link_config.authentication.plaintext_password is not none %} + area {{ area_id }} virtual-link {{ link }} authentication-key {{ link_config.authentication.plaintext_password }} +{% elif link_config.authentication.md5 is defined and link_config.authentication.md5.key_id is defined and link_config.authentication.md5.key_id is not none %} +{% for key, key_config in link_config.authentication.md5.key_id.items() %} + area {{ area_id }} virtual-link {{ link }} message-digest-key {{ key }} md5 {{ key_config.md5_key }} +{% endfor %} +{% endif %} +{% endif %} +{% if link_config.dead_interval is defined and link_config.dead_interval is not none %} +{# The following values are default values #} + area {{ area_id }} virtual-link {{ link }} hello-interval {{ link_config.hello_interval }} retransmit-interval {{ link_config.retransmit_interval }} transmit-delay {{ link_config.retransmit_interval }} dead-interval {{ link_config.dead_interval }} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{% endif %} +{% if auto_cost is defined and auto_cost.reference_bandwidth is defined and auto_cost.reference_bandwidth is not none %} + auto-cost reference-bandwidth {{ auto_cost.reference_bandwidth }} +{% endif %} +{% if default_information is defined and default_information.originate is defined and default_information.originate is not none %} + default-information originate {{ 'always' if default_information.originate.always is defined }} {{ 'metric ' + default_information.originate.metric if default_information.originate.metric is defined }} {{ 'metric-type ' + default_information.originate.metric_type if default_information.originate.metric_type is defined }} {{ 'route-map ' + default_information.originate.route_map if default_information.originate.route_map is defined }} +{% endif %} +{% if default_metric is defined and default_metric is not none %} + default-metric {{ default_metric }} +{% endif %} +{% if distance is defined and distance is not none %} +{% if distance.global is defined and distance.global is not none %} + distance {{ distance.global }} +{% endif %} +{% if distance.ospf is defined and distance.ospf is not none %} + distance ospf {{ 'intra-area ' + distance.ospf.intra_area if distance.ospf.intra_area is defined }} {{ 'inter-area ' + distance.ospf.inter_area if distance.ospf.inter_area is defined }} {{ 'external ' + distance.ospf.external if distance.ospf.external is defined }} +{% endif %} +{% endif %} +{% if log_adjacency_changes is defined %} + log-adjacency-changes {{ "detail" if log_adjacency_changes.detail is defined }} +{% endif %} +{% if max_metric is defined and max_metric.router_lsa is defined and max_metric.router_lsa is not none %} +{% if max_metric.router_lsa.administrative is defined %} + max-metric router-lsa administrative +{% endif %} +{% if max_metric.router_lsa.on_shutdown is defined and max_metric.router_lsa.on_shutdown is not none %} + max-metric router-lsa on-shutdown {{ max_metric.router_lsa.on_shutdown }} +{% endif %} +{% if max_metric.router_lsa.on_startup is defined and max_metric.router_lsa.on_startup is not none %} + max-metric router-lsa on-startup {{ max_metric.router_lsa.on_startup }} +{% endif %} +{% endif %} +{% if mpls_te is defined and mpls_te.enable is defined %} + mpls-te on + mpls-te router-address {{ mpls_te.router_address }} +{% endif %} +{% if neighbor is defined and neighbor is not none%} +{% for address, address_config in neighbor.items() %} + neighbor {{ address }} {{ 'priority ' + address_config.priority if address_config.priority is defined }} {{ 'poll-interval ' + address_config.poll_interval if address_config.poll_interval is defined }} +{% endfor %} +{% endif %} +{% if parameters is defined and parameters is not none %} +{% if parameters.abr_type is defined and parameters.abr_type is not none %} + ospf abr-type {{ parameters.abr_type }} +{% endif %} +{% if parameters.router_id is defined and parameters.router_id is not none %} + ospf router-id {{ parameters.router_id }} +{% endif %} +{% endif %} +{% for interface in passive_interface if passive_interface is defined %} + passive-interface {{ interface }} +{% endfor %} +{% for interface in passive_interface_exclude if passive_interface_exclude is defined %} + no passive-interface {{ interface }} +{% endfor %} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, options in redistribute.items() %} + redistribute {{ protocol }} {{ 'metric ' + options.metric if options.metric is defined }} {{ 'metric-type ' + options.metric_type if options.metric_type is defined }} {{ 'route-map ' + options.route_map if options.route_map is defined }} +{% endfor %} +{% endif %} +{% if refresh is defined and refresh.timers is defined and refresh.timers is not none %} + refresh timer {{ refresh.timers }} +{% endif %} +{% if timers is defined and timers.throttle is defined and timers.throttle.spf is defined and timers.throttle.spf is not none %} +{# Timer values have default values #} + timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }} +{% endif %} +! +{% if route_map is defined and route_map is not none %} +ip protocol ospf route-map {{ route_map }} +{% endif %} +! diff --git a/data/templates/frr/ospfv3.frr.tmpl b/data/templates/frr/ospfv3.frr.tmpl new file mode 100644 index 000000000..c63ef80dc --- /dev/null +++ b/data/templates/frr/ospfv3.frr.tmpl @@ -0,0 +1,41 @@ +! +router ospf6 +{% if area is defined and area is not none %} +{% for area_id, area_config in area.items() %} +{% if area_config.interface is defined and area_config.interface is not none %} +{% for interface in area_config.interface %} + interface {{ interface }} area {{ area_id }} +{% endfor %} +{% endif %} +{% if area_config.range is defined and area_config.range is not none %} +{% for prefix, prefix_config in area_config.range.items() %} + area {{ area_id }} range {{ prefix }} {{ 'advertise' if prefix_config.advertise is defined }} {{ 'not-advertise' if prefix_config.not_advertise is defined }} +{% endfor %} +{% endif %} +{% if area_config.export_list is defined and area_config.export_list is not none %} + area {{ area_id }} export-list {{ area_config.export_list }} +{% endif %} +{% if area_config.import_list is defined and area_config.import_list is not none %} + area {{ area_id }} import-list {{ area_config.import_list }} +{% endif %} +{% endfor %} +{% endif %} +{% if distance is defined and distance is not none %} +{% if distance.global is defined and distance.global is not none %} + distance {{ distance.global }} +{% endif %} +{% if distance.ospfv3 is defined and distance.ospfv3 is not none %} + distance ospf6 {{ 'intra-area ' + distance.ospfv3.intra_area if distance.ospfv3.intra_area is defined }} {{ 'inter-area ' + distance.ospfv3.inter_area if distance.ospfv3.inter_area is defined }} {{ 'external ' + distance.ospfv3.external if distance.ospfv3.external is defined }} +{% endif %} +{% endif %} +{% if parameters is defined and parameters is not none %} +{% if parameters.router_id is defined and parameters.router_id is not none %} + ospf6 router-id {{ parameters.router_id }} +{% endif %} +{% endif %} +{% if redistribute is defined and redistribute is not none %} +{% for protocol, options in redistribute.items() %} + redistribute {{ protocol }} {{ 'route-map ' + options.route_map if options.route_map is defined }} +{% endfor %} +{% endif %} +! diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index b3b0c936a..79288e40f 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -281,6 +281,10 @@ compat-names # Custom options added by user (not validated) # {% for option in openvpn_option %} -{{ option }} +{% for argument in option.split('--') %} +{% if argument is defined and argument != '' %} +--{{ argument }} +{% endif %} +{% endfor %} {% endfor %} {% endif %} diff --git a/data/templates/proxy-ndp/ndppd.conf.tmpl b/data/templates/proxy-ndp/ndppd.conf.tmpl new file mode 100644 index 000000000..0137d8135 --- /dev/null +++ b/data/templates/proxy-ndp/ndppd.conf.tmpl @@ -0,0 +1,44 @@ +######################################################## +# +# autogenerated by nat66.py +# +# The configuration file must define one upstream +# interface. +# +# For some services, such as nat66, because it runs +# stateless, it needs to rely on NDP Proxy to respond +# to NDP requests. +# +# When using nat66 source rules, NDP Proxy needs +# to be enabled +# +######################################################## + +{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %} +{% if source is defined and source.rule is defined and source.rule is not none %} +{% for rule, config in source.rule.items() if config.disable is not defined %} +{% if config.outbound_interface is defined %} +{% if config.outbound_interface not in global.ndppd_interfaces %} +{% set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %} +{% endif %} +{% if config.translation.prefix is defined %} +{% set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.prefix}] %} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} + +{% for interface in global.ndppd_interfaces %} +proxy {{ interface }} { + router yes + timeout 500 + ttl 30000 +{% for map in global.ndppd_prefixs %} +{% if map.interface == interface %} + rule {{ map.rule }} { + static + } +{% endif %} +{% endfor %} +} +{% endfor %} diff --git a/data/templates/ssh/override.conf.tmpl b/data/templates/ssh/override.conf.tmpl index 0abde6248..5f8f35e89 100644 --- a/data/templates/ssh/override.conf.tmpl +++ b/data/templates/ssh/override.conf.tmpl @@ -8,5 +8,6 @@ ConditionPathExists={{config_file}} ExecStart= ExecStart={{vrf_command}}/usr/sbin/sshd -f {{config_file}} -D $SSHD_OPTS Restart=always +RestartPreventExitStatus= RestartSec=10 RuntimeDirectoryPreserve=yes diff --git a/debian/control b/debian/control index d0ba72bcf..2c8ee3d43 100644 --- a/debian/control +++ b/debian/control @@ -129,7 +129,8 @@ Depends: wide-dhcpv6-client, wireguard-tools, wireless-regdb, - wpasupplicant (>= 0.6.7) + wpasupplicant (>= 0.6.7), + ndppd Description: VyOS configuration scripts and data VyOS configuration scripts, interface definitions, and everything diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst index 92948de12..5fadddc86 100644 --- a/debian/vyos-1x.postinst +++ b/debian/vyos-1x.postinst @@ -20,6 +20,34 @@ if ! grep -q '^minion' /etc/passwd; then adduser --quiet minion users fi +# OpenVPN should get its own user +if ! grep -q '^openvpn' /etc/passwd; then + adduser --quiet --firstuid 100 --system --group --shell /usr/sbin/nologin openvpn +fi + +# Add RADIUS operator user for RADIUS authenticated users to map to +if ! grep -q '^radius_user' /etc/passwd; then + adduser --quiet --firstuid 1001 --disabled-login --ingroup users --gecos "radius user" --shell /bin/vbash radius_user + adduser --quiet radius_user frrvty + adduser --quiet radius_user vyattaop + adduser --quiet radius_user operator + adduser --quiet radius_user adm + adduser --quiet radius_user dip + adduser --quiet radius_user users +fi + +# Add RADIUS admin user for RADIUS authenticated users to map to +if ! grep -q '^radius_priv_user' /etc/passwd; then + adduser --quiet --firstuid 1001 --disabled-login --ingroup vyattacfg --gecos "radius privileged user" --shell /bin/vbash radius_priv_user + adduser --quiet radius_priv_user frrvty + adduser --quiet radius_priv_user vyattacfg + adduser --quiet radius_priv_user sudo + adduser --quiet radius_priv_user adm + adduser --quiet radius_priv_user dip + adduser --quiet radius_priv_user disk + adduser --quiet radius_priv_user users +fi + # add hostsd group for vyos-hostsd if ! grep -q '^hostsd' /etc/group; then addgroup --quiet --system hostsd diff --git a/interface-definitions/arp.xml.in b/interface-definitions/arp.xml.in index b72f025a8..082afe00f 100644 --- a/interface-definitions/arp.xml.in +++ b/interface-definitions/arp.xml.in @@ -20,7 +20,7 @@ <properties> <help>mac address to translate to</help> <valueHelp> - <format>h:h:h:h:h:h</format> + <format>macaddr</format> <description>Hardware (MAC) address</description> </valueHelp> <constraint> diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 912e4eaf7..28b61e92d 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -335,11 +335,14 @@ </leafNode> <leafNode name="mac-address"> <properties> - <help>MAC address of static mapping [REQUIRED]</help> + <help>Media Access Control (MAC) address</help> <valueHelp> - <format>h:h:h:h:h:h</format> - <description>MAC address used in static mapping [REQUIRED]</description> + <format>macaddr</format> + <description>Hardware (MAC) address</description> </valueHelp> + <constraint> + <validator name="mac-address"/> + </constraint> </properties> </leafNode> <leafNode name="static-mapping-parameters"> diff --git a/interface-definitions/include/interface-hw-id.xml.i b/interface-definitions/include/interface-hw-id.xml.i index af58fae3c..55ef55add 100644 --- a/interface-definitions/include/interface-hw-id.xml.i +++ b/interface-definitions/include/interface-hw-id.xml.i @@ -3,8 +3,8 @@ <properties> <help>Associate Ethernet Interface with given Media Access Control (MAC) address</help> <valueHelp> - <format>h:h:h:h:h:h</format> - <description>Hardware Media Access Control (MAC) address</description> + <format>macaddr</format> + <description>Hardware (MAC) address</description> </valueHelp> <constraint> <validator name="mac-address"/> diff --git a/interface-definitions/include/interface-mac.xml.i b/interface-definitions/include/interface-mac.xml.i index e277de85c..87dc5fb60 100644 --- a/interface-definitions/include/interface-mac.xml.i +++ b/interface-definitions/include/interface-mac.xml.i @@ -3,7 +3,7 @@ <properties> <help>Media Access Control (MAC) address</help> <valueHelp> - <format>h:h:h:h:h:h</format> + <format>macaddr</format> <description>Hardware (MAC) address</description> </valueHelp> <constraint> diff --git a/interface-definitions/include/ospf-metric-type.xml.i b/interface-definitions/include/ospf-metric-type.xml.i new file mode 100644 index 000000000..50f11960c --- /dev/null +++ b/interface-definitions/include/ospf-metric-type.xml.i @@ -0,0 +1,15 @@ +<!-- included start from ospf-metric-type.xml.i --> +<leafNode name="metric-type"> + <properties> + <help>OSPF metric type for default routes (default: 2)</help> + <valueHelp> + <format>u32:1-2</format> + <description>Metric type for default routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-2"/> + </constraint> + </properties> + <defaultValue>2</defaultValue> +</leafNode> +<!-- included end --> diff --git a/interface-definitions/include/ospf-metric.xml.i b/interface-definitions/include/ospf-metric.xml.i new file mode 100644 index 000000000..3ce12e877 --- /dev/null +++ b/interface-definitions/include/ospf-metric.xml.i @@ -0,0 +1,14 @@ +<!-- included start from ospf-metric.xml.i --> +<leafNode name="metric"> + <properties> + <help>OSPF default metric</help> + <valueHelp> + <format>u32:0-16777214</format> + <description>Default metric</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777214"/> + </constraint> + </properties> +</leafNode> +<!-- included end --> diff --git a/interface-definitions/include/ospf-route-map.xml.i b/interface-definitions/include/ospf-route-map.xml.i new file mode 100644 index 000000000..8dc5b37da --- /dev/null +++ b/interface-definitions/include/ospf-route-map.xml.i @@ -0,0 +1,10 @@ +<!-- included start from ospf-route-map.xml.i --> +<leafNode name="route-map"> + <properties> + <help>Route map reference</help> + <completionHelp> + <path>policy route-map</path> + </completionHelp> + </properties> +</leafNode> +<!-- included end --> diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in index a8ddb74fb..3853cd339 100644 --- a/interface-definitions/interfaces-l2tpv3.xml.in +++ b/interface-definitions/interfaces-l2tpv3.xml.in @@ -72,6 +72,9 @@ </properties> </leafNode> #include <include/interface-mtu-68-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1488</defaultValue> + </leafNode> <leafNode name="peer-session-id"> <properties> <help>Peer session identifier</help> diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index 4d2581906..94d78c6dd 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -110,6 +110,9 @@ #include <include/interface-description.xml.i> #include <include/interface-disable.xml.i> #include <include/interface-mtu-68-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1460</defaultValue> + </leafNode> #include <include/source-interface-ethernet.xml.i> #include <include/interface-vrf.xml.i> </children> diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 7dccfbc9c..9bbaae9ea 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -125,6 +125,9 @@ </properties> </leafNode> #include <include/interface-mtu-68-1500.xml.i> + <leafNode name="mtu"> + <defaultValue>1492</defaultValue> + </leafNode> <leafNode name="no-peer-dns"> <properties> <help>Do not use DNS servers provided by the peer</help> diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index 39e274840..7fa847ab0 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -22,6 +22,9 @@ #include <include/interface-disable-link-detect.xml.i> #include <include/interface-vrf.xml.i> #include <include/interface-mtu-64-8024.xml.i> + <leafNode name="mtu"> + <defaultValue>1476</defaultValue> + </leafNode> #include <include/interface-ipv4-options.xml.i> #include <include/interface-ipv6-options.xml.i> <leafNode name="local-ip"> diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index 8c76ab60b..f90a86274 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -41,6 +41,9 @@ #include <include/source-interface.xml.i> #include <include/interface-mac.xml.i> #include <include/interface-mtu-1200-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1450</defaultValue> + </leafNode> <leafNode name="remote"> <properties> <help>Remote address of VXLAN tunnel</help> diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index acf5082d6..3f56046c4 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -22,6 +22,9 @@ #include <include/interface-vrf.xml.i> #include <include/port-number.xml.i> #include <include/interface-mtu-68-16000.xml.i> + <leafNode name="mtu"> + <defaultValue>1420</defaultValue> + </leafNode> #include <include/interface-ipv4-options.xml.i> #include <include/interface-ipv6-options.xml.i> <leafNode name="fwmark"> diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in index d6bed5b27..3cff8abc9 100644 --- a/interface-definitions/nat.xml.in +++ b/interface-definitions/nat.xml.in @@ -56,73 +56,6 @@ </tagNode> </children> </node> - <node name="nptv6"> - <properties> - <help>IPv6-to-IPv6 Network Prefix Translation Settings</help> - </properties> - <children> - <tagNode name="rule"> - <properties> - <help>NPTv6 rule number</help> - <valueHelp> - <format>1-999999</format> - <description>Number for this rule</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1-999999"/> - </constraint> - <constraintErrorMessage>NAT rule number must be between 1 and 999999</constraintErrorMessage> - </properties> - <children> - <leafNode name="description"> - <properties> - <help>Rule description</help> - </properties> - </leafNode> - #include <include/generic-disable-node.xml.i> - #include <include/nat-interface.xml.i> - <node name="source"> - <properties> - <help>IPv6 source prefix options</help> - </properties> - <children> - <leafNode name="prefix"> - <properties> - <help>IPv6 prefix to be translated</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 prefix</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - <node name="translation"> - <properties> - <help>Translated IPv6 prefix options</help> - </properties> - <children> - <leafNode name="prefix"> - <properties> - <help>IPv6 prefix to translate to</help> - <valueHelp> - <format>ipv6net</format> - <description>IPv6 prefix</description> - </valueHelp> - <constraint> - <validator name="ipv6-prefix"/> - </constraint> - </properties> - </leafNode> - </children> - </node> - </children> - </tagNode> - </children> - </node> <node name="source"> <properties> <help>Source NAT settings</help> diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in new file mode 100644 index 000000000..b45ebc0a8 --- /dev/null +++ b/interface-definitions/nat66.xml.in @@ -0,0 +1,192 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="nat66" owner="${vyos_conf_scripts_dir}/nat66.py"> + <properties> + <help>IPv6-to-IPv6 Network Prefix Translation (NAT66/NPT) Settings</help> + <priority>220</priority> + </properties> + <children> + <node name="source"> + <properties> + <help>Prefix mapping of IPv6 source address translation</help> + </properties> + <children> + <tagNode name="rule"> + <properties> + <help>Source NAT66 rule number</help> + <valueHelp> + <format>1-999999</format> + <description>Number for this rule</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-999999"/> + </constraint> + <constraintErrorMessage>NAT66 rule number must be between 1 and 999999</constraintErrorMessage> + </properties> + <children> + <leafNode name="description"> + <properties> + <help>Rule description</help> + </properties> + </leafNode> + <leafNode name="disable"> + <properties> + <help>Disable NAT66 rule</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="log"> + <properties> + <help>NAT66 rule logging</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="outbound-interface"> + <properties> + <help>Outbound interface of NAT66 traffic</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + </leafNode> + <node name="source"> + <properties> + <help>IPv6 source prefix options</help> + </properties> + <children> + <leafNode name="prefix"> + <properties> + <help>IPv6 prefix to be translated</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="translation"> + <properties> + <help>Translated IPv6 prefix options</help> + </properties> + <children> + <leafNode name="prefix"> + <properties> + <help>IPv6 prefix to translate to</help> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </node> + <node name="destination"> + <properties> + <help>Prefix mapping for IPv6 destination address translation</help> + </properties> + <children> + <tagNode name="rule"> + <properties> + <help>Destination NAT66 rule number</help> + <valueHelp> + <format>1-999999</format> + <description>Number for this rule</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-999999"/> + </constraint> + <constraintErrorMessage>NAT66 rule number must be between 1 and 999999</constraintErrorMessage> + </properties> + <children> + <leafNode name="description"> + <properties> + <help>Rule description</help> + </properties> + </leafNode> + <leafNode name="disable"> + <properties> + <help>Disable NAT66 rule</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="log"> + <properties> + <help>NAT66 rule logging</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="inbound-interface"> + <properties> + <help>Inbound interface of NAT66 traffic</help> + <completionHelp> + <list>any</list> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + </properties> + </leafNode> + <node name="destination"> + <properties> + <help>IPv6 destination prefix options</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>IPv6 address or prefix to be translated</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="translation"> + <properties> + <help>Translated IPv6 address options</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>IPv6 address or prefix to translate to</help> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix</description> + </valueHelp> + <constraint> + <validator name="ipv6-address"/> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in index cec73a15b..3edacb0ca 100644 --- a/interface-definitions/protocols-bgp.xml.in +++ b/interface-definitions/protocols-bgp.xml.in @@ -5,7 +5,7 @@ <children> <tagNode name="bgp" owner="${vyos_conf_scripts_dir}/protocols_bgp.py"> <properties> - <help>Border Gateway Protocol (BGP) parameters</help> + <help>Border Gateway Protocol (BGP)</help> <valueHelp> <format>u32:1-4294967294</format> <description>AS number</description> @@ -250,6 +250,45 @@ </leafNode> </children> </node> + <node name="listen"> + <properties> + <help>Listen for and accept BGP dynamic neighbors from range</help> + </properties> + <children> + <leafNode name="limit"> + <properties> + <help>Maximum number of dynamic neighbors that can be created</help> + <valueHelp> + <format>u32:1-5000</format> + <description>BGP neighbor limit</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-5000"/> + </constraint> + </properties> + </leafNode> + <tagNode name="range"> + <properties> + <help>BGP dynamic neighbors listen range</help> + <valueHelp> + <format>ipv4net</format> + <description>IPv4 dynamic neighbors listen range</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 dynamic neighbors listen range</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + #include <include/bgp-peer-group.xml.i> + </children> + </tagNode> + </children> + </node> <tagNode name="neighbor"> <properties> <help>BGP neighbor</help> diff --git a/interface-definitions/protocols-isis.xml.in b/interface-definitions/protocols-isis.xml.in index 8c5691ecf..4ac378977 100644 --- a/interface-definitions/protocols-isis.xml.in +++ b/interface-definitions/protocols-isis.xml.in @@ -257,7 +257,7 @@ <valueless/> </properties> </leafNode> -<!-- +<!-- <node name="inter-as"> <properties> <help>MPLS traffic engineering inter-AS support</help> @@ -394,9 +394,18 @@ <tagNode name="prefix"> <properties> <help>Static IPv4/IPv6 prefix segment/label mapping</help> - <completionHelp> - <list><x.x.x.x/x> <h:h:h:h:h:h:h:h/h></list> - </completionHelp> + <valueHelp> + <format>ipv4net</format> + <description>IPv4 prefix segment</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 prefix segment</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + <validator name="ipv6-prefix"/> + </constraint> </properties> <children> <node name="absolute"> diff --git a/interface-definitions/protocols-ospf.xml.in b/interface-definitions/protocols-ospf.xml.in new file mode 100644 index 000000000..074d0db63 --- /dev/null +++ b/interface-definitions/protocols-ospf.xml.in @@ -0,0 +1,854 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Protocol OSPF configuration --> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="ospf" owner="${vyos_conf_scripts_dir}/protocols_ospf.py"> + <properties> + <help>Open Shortest Path First (OSPF)</help> + <priority>620</priority> + </properties> + <children> + <tagNode name="access-list"> + <properties> + <help>Access list to filter networks in routing updates</help> + <completionHelp> + <path>policy access-list</path> + </completionHelp> + <valueHelp> + <format>u32</format> + <description>Access-list number</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + </constraint> + </properties> + <children> + <leafNode name="export"> + <properties> + <help>Filter for outgoing routing update [REQUIRED]</help> + <completionHelp> + <list>bgp connected kernel rip static</list> + </completionHelp> + <valueHelp> + <format>bgp</format> + <description>Filter BGP routes</description> + </valueHelp> + <valueHelp> + <format>connected</format> + <description>Filter connected routes</description> + </valueHelp> + <valueHelp> + <format>kernel</format> + <description>Filter Kernel routes</description> + </valueHelp> + <valueHelp> + <format>rip</format> + <description>Filter RIP routes</description> + </valueHelp> + <valueHelp> + <format>static</format> + <description>Filter static routes</description> + </valueHelp> + <constraint> + <regex>^(bgp|connected|kernel|rip|static)$</regex> + </constraint> + <constraintErrorMessage>Must be bgp, connected, kernel, rip, or static</constraintErrorMessage> + <multi/> + </properties> + </leafNode> + </children> + </tagNode> + <tagNode name="area"> + <properties> + <help>OSPF Area</help> + <valueHelp> + <format>u32</format> + <description>OSPF area in decimal notation</description> + </valueHelp> + <valueHelp> + <format>ipv4</format> + <description>OSPF area in dotted decimal notation</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + <validator name="ip-address"/> + </constraint> + </properties> + <children> + <node name="area-type"> + <properties> + <help>Area type</help> + </properties> + <children> + <leafNode name="normal"> + <properties> + <help>Normal OSPF area</help> + <valueless/> + </properties> + </leafNode> + <node name="nssa"> + <properties> + <help>Nssa OSPF area</help> + </properties> + <children> + <leafNode name="default-cost"> + <properties> + <help>Summary-default cost of nssa area</help> + <valueHelp> + <format>u32:0-16777215</format> + <description>Summary default cost</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777215"/> + </constraint> + </properties> + </leafNode> + <leafNode name="no-summary"> + <properties> + <help>Do not inject inter-area routes into stub</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="translate"> + <properties> + <help>Configure NSSA-ABR (default: candidate)</help> + <completionHelp> + <list>always candidate never</list> + </completionHelp> + <valueHelp> + <format>always</format> + <description>NSSA-ABR to always translate</description> + </valueHelp> + <valueHelp> + <format>candidate</format> + <description>NSSA-ABR for translate election (default)</description> + </valueHelp> + <valueHelp> + <format>never</format> + <description>NSSA-ABR to never translate</description> + </valueHelp> + <constraint> + <regex>^(always|candidate|never)$</regex> + </constraint> + </properties> + <defaultValue>candidate</defaultValue> + </leafNode> + </children> + </node> + <node name="stub"> + <properties> + <help>Stub OSPF area</help> + </properties> + <children> + <leafNode name="default-cost"> + <properties> + <help>Summary-default cost of nssa area</help> + <valueHelp> + <format>u32:0-16777215</format> + <description>Summary default cost</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777215"/> + </constraint> + </properties> + </leafNode> + <leafNode name="no-summary"> + <properties> + <help>Do not inject inter-area routes into stub</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + <leafNode name="authentication"> + <properties> + <help>OSPF area authentication type</help> + <completionHelp> + <list>plaintext-password md5</list> + </completionHelp> + <valueHelp> + <format>plaintext-password</format> + <description>Use plain-text authentication</description> + </valueHelp> + <valueHelp> + <format>md5</format> + <description>Use md5 authentication</description> + </valueHelp> + <constraint> + <regex>^(plaintext-password|md5)$</regex> + </constraint> + </properties> + </leafNode> + <leafNode name="network"> + <properties> + <help>OSPF network [REQUIRED]</help> + <valueHelp> + <format>ipv4net</format> + <description>OSPF network [REQUIRED]</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + <multi/> + </properties> + </leafNode> + <tagNode name="range"> + <properties> + <help>Summarize routes matching prefix (border routers only)</help> + <valueHelp> + <format>ipv4net</format> + <description>Area range prefix</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + </properties> + <children> + <leafNode name="cost"> + <properties> + <help>Metric for this range</help> + <valueHelp> + <format>u32:0-16777215</format> + <description>Metric for this range</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777215"/> + </constraint> + </properties> + </leafNode> + <leafNode name="not-advertise"> + <properties> + <help>Do not advertise this range</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="substitute"> + <properties> + <help>Announce area range as another prefix</help> + <valueHelp> + <format>ipv4net</format> + <description>Announce area range as another prefix</description> + </valueHelp> + <constraint> + <validator name="ipv4-prefix"/> + </constraint> + </properties> + </leafNode> + </children> + </tagNode> + <leafNode name="shortcut"> + <properties> + <help>Area shortcut mode</help> + <completionHelp> + <list>default disable enable</list> + </completionHelp> + <valueHelp> + <format>default</format> + <description>Set default</description> + </valueHelp> + <valueHelp> + <format>disable</format> + <description>Disable shortcutting mode</description> + </valueHelp> + <valueHelp> + <format>enable</format> + <description>Enable shortcutting mode</description> + </valueHelp> + <constraint> + <regex>^(default|disable|enable)$</regex> + </constraint> + </properties> + </leafNode> + <tagNode name="virtual-link"> + <properties> + <help>Virtual link</help> + <valueHelp> + <format>ipv4</format> + <description>OSPF area in dotted decimal notation</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + <validator name="ip-address"/> + </constraint> + </properties> + <children> + <node name="authentication"> + <properties> + <help>Authentication</help> + </properties> + <children> + <node name="md5"> + <properties> + <help>MD5 key id</help> + </properties> + <children> + <tagNode name="key-id"> + <properties> + <help>MD5 key id</help> + <valueHelp> + <format>u32:1-255</format> + <description>MD5 key id</description> + </valueHelp> + </properties> + <children> + <leafNode name="md5-key"> + <properties> + <help>MD5 authentication type</help> + <valueHelp> + <format>txt</format> + <description>MD5 Key (16 characters or less)</description> + </valueHelp> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </node> + <leafNode name="plaintext-password"> + <properties> + <help>Plain text password</help> + <valueHelp> + <format>txt</format> + <description>Plain text password (8 characters or less)</description> + </valueHelp> + </properties> + </leafNode> + </children> + </node> + <leafNode name="dead-interval"> + <properties> + <help>Interval after which a neighbor is declared dead (default: 40)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Neighbor dead interval (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>40</defaultValue> + </leafNode> + <leafNode name="hello-interval"> + <properties> + <help>Interval between hello packets (default: 10)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Hello interval (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>10</defaultValue> + </leafNode> + <leafNode name="retransmit-interval"> + <properties> + <help>Interval between retransmitting lost link state advertisements (default: 5)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Retransmit interval (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>5</defaultValue> + </leafNode> + <leafNode name="transmit-delay"> + <properties> + <help>Link state transmit delay (default: 1)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Link state transmit delay (seconds)</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>1</defaultValue> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <node name="auto-cost"> + <properties> + <help>Calculate OSPF interface cost according to bandwidth (default: 100)</help> + </properties> + <children> + <leafNode name="reference-bandwidth"> + <properties> + <help>Reference bandwidth method to assign OSPF cost</help> + <valueHelp> + <format>u32:1-4294967</format> + <description>Reference bandwidth cost in Mbits/sec</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-4294967"/> + </constraint> + </properties> + <defaultValue>100</defaultValue> + </leafNode> + </children> + </node> + <node name="default-information"> + <properties> + <help>Control distribution of default information</help> + </properties> + <children> + <node name="originate"> + <properties> + <help>Distribute a default route</help> + </properties> + <children> + <leafNode name="always"> + <properties> + <help>Always advertise default route</help> + <valueless/> + </properties> + </leafNode> + #include <include/ospf-metric.xml.i> + #include <include/ospf-metric-type.xml.i> + #include <include/ospf-route-map.xml.i> + </children> + </node> + </children> + </node> + <leafNode name="default-metric"> + <properties> + <help>Metric of redistributed routes</help> + <valueHelp> + <format>u32:0-16777214</format> + <description>Metric of redistributed routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-16777214"/> + </constraint> + </properties> + </leafNode> + <node name="distance"> + <properties> + <help>Administrative distance</help> + </properties> + <children> + <leafNode name="global"> + <properties> + <help>OSPF administrative distance</help> + <valueHelp> + <format>u32:1-255</format> + <description>Administrative distance</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <node name="ospf"> + <properties> + <help>OSPF administrative distance</help> + </properties> + <children> + <leafNode name="external"> + <properties> + <help>Distance for external routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for external routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <leafNode name="inter-area"> + <properties> + <help>Distance for inter-area routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for inter-area routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <leafNode name="intra-area"> + <properties> + <help>Distance for intra-area routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for intra-area routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + <node name="log-adjacency-changes"> + <properties> + <help>Log changes in adjacency state</help> + </properties> + <children> + <leafNode name="detail"> + <properties> + <help>Log all state changes</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + <node name="max-metric"> + <properties> + <help>OSPF maximum and infinite-distance metric</help> + </properties> + <children> + <node name="router-lsa"> + <properties> + <help>Advertise own Router-LSA with infinite distance (stub router)</help> + </properties> + <children> + <leafNode name="administrative"> + <properties> + <help>Administratively apply, for an indefinite period</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="on-shutdown"> + <properties> + <help>Advertise stub-router prior to full shutdown of OSPF</help> + <valueHelp> + <format>u32:5-100</format> + <description>Time (seconds) to advertise self as stub-router</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 5-100"/> + </constraint> + </properties> + </leafNode> + <leafNode name="on-startup"> + <properties> + <help>Automatically advertise stub Router-LSA on startup of OSPF</help> + <valueHelp> + <format>u32:5-86400</format> + <description>Time (seconds) to advertise self as stub-router</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 5-86400"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + <node name="mpls-te"> + <properties> + <help>MultiProtocol Label Switching-Traffic Engineering (MPLS-TE) parameters</help> + </properties> + <children> + <leafNode name="enable"> + <properties> + <help>Enable MPLS-TE functionality</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="router-address"> + <properties> + <help>Stable IP address of the advertising router</help> + <valueHelp> + <format>ipv4</format> + <description>Stable IP address of the advertising router</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <defaultValue>0.0.0.0</defaultValue> + </leafNode> + </children> + </node> + <tagNode name="neighbor"> + <properties> + <help>Specify neighbor router</help> + <valueHelp> + <format>ipv4</format> + <description>Neighbor IP address</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + <children> + <leafNode name="poll-interval"> + <properties> + <help>Dead neighbor polling interval (default: 60)</help> + <valueHelp> + <format>u32:1-65535</format> + <description>Seconds between dead neighbor polling interval</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65535"/> + </constraint> + </properties> + <defaultValue>60</defaultValue> + </leafNode> + <leafNode name="priority"> + <properties> + <help>Neighbor priority in seconds (default: 0)</help> + <valueHelp> + <format>u32:0-255</format> + <description>Neighbor priority</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-255"/> + </constraint> + </properties> + <defaultValue>0</defaultValue> + </leafNode> + </children> + </tagNode> + <node name="parameters"> + <properties> + <help>OSPF specific parameters</help> + </properties> + <children> + <leafNode name="abr-type"> + <properties> + <help>OSPF ABR type (default: cisco)</help> + <completionHelp> + <list>cisco ibm shortcut standard</list> + </completionHelp> + <valueHelp> + <format>cisco</format> + <description>Cisco ABR type (default)</description> + </valueHelp> + <valueHelp> + <format>ibm</format> + <description>Ibm ABR type</description> + </valueHelp> + <valueHelp> + <format>shortcut</format> + <description>Shortcut ABR type</description> + </valueHelp> + <valueHelp> + <format>standard</format> + <description>Standard ABR type</description> + </valueHelp> + <constraint> + <regex>^(cisco|ibm|shortcut|standard)$</regex> + </constraint> + </properties> + <defaultValue>cisco</defaultValue> + </leafNode> + <leafNode name="opaque-lsa"> + <properties> + <help>Enable the Opaque-LSA capability (rfc2370)</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="rfc1583-compatibility"> + <properties> + <help>Enable rfc1583 criteria for handling AS external routes</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="router-id"> + <properties> + <help>Override the default router identifier</help> + <valueHelp> + <format>ipv4</format> + <description>Override the default router identifier</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <leafNode name="passive-interface"> + <properties> + <help>Suppress routing updates on an interface</help> + <completionHelp> + <list>default</list> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface to be passive (i.e. suppress routing updates)</description> + </valueHelp> + <valueHelp> + <format>default</format> + <description>Default to suppress routing updates on all interfaces</description> + </valueHelp> + <constraint> + <regex>^(br|bond|dum|en|eth|gnv|peth|tun|vti|vxlan|wg|wlan)[0-9]+|lo|default$</regex> + </constraint> + <multi/> + </properties> + </leafNode> + <leafNode name="passive-interface-exclude"> + <properties> + <help>Interface to exclude when using 'passive-interface default'</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface to be passive (i.e. suppress routing updates)</description> + </valueHelp> + <constraint> + <regex>^(br|bond|dum|en|eth|gnv|peth|tun|vti|vxlan|wg|wlan)[0-9]+|lo$</regex> + </constraint> + <multi/> + </properties> + </leafNode> + <node name="redistribute"> + <properties> + <help>Redistribute information from another routing protocol</help> + </properties> + <children> + <node name="bgp"> + <properties> + <help>Redistribute BGP routes</help> + </properties> + <children> + #include <include/ospf-metric.xml.i> + #include <include/ospf-metric-type.xml.i> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="connected"> + <properties> + <help>Redistribute connected routes</help> + </properties> + <children> + #include <include/ospf-metric.xml.i> + #include <include/ospf-metric-type.xml.i> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="kernel"> + <properties> + <help>Redistribute kernel routes</help> + </properties> + <children> + #include <include/ospf-metric.xml.i> + #include <include/ospf-metric-type.xml.i> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="rip"> + <properties> + <help>Redistribute rip routes</help> + </properties> + <children> + #include <include/ospf-metric.xml.i> + #include <include/ospf-metric-type.xml.i> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="static"> + <properties> + <help>Redistribute static routes</help> + </properties> + <children> + #include <include/ospf-metric.xml.i> + #include <include/ospf-metric-type.xml.i> + #include <include/ospf-route-map.xml.i> + </children> + </node> + </children> + </node> + <node name="refresh"> + <properties> + <help>Adjust refresh parameters</help> + </properties> + <children> + <leafNode name="timers"> + <properties> + <help>Refresh timer</help> + <valueHelp> + <format>u32:10-1800</format> + <description>Timer value in seconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 10-1800"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + #include <include/ospf-route-map.xml.i> + <node name="timers"> + <properties> + <help>Adjust routing timers</help> + </properties> + <children> + <node name="throttle"> + <properties> + <help>Throttling adaptive timers</help> + </properties> + <children> + <node name="spf"> + <properties> + <help>OSPF SPF timers</help> + </properties> + <children> + <leafNode name="delay"> + <properties> + <help>Delay from first change received till SPF calculation (default: 200)</help> + <valueHelp> + <format>u32:0-600000</format> + <description>Delay in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-600000"/> + </constraint> + </properties> + <defaultValue>200</defaultValue> + </leafNode> + <leafNode name="initial-holdtime"> + <properties> + <help>Initial hold time between consecutive SPF calculations (default: 1000)</help> + <valueHelp> + <format>u32:0-600000</format> + <description>Initial hold time in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-600000"/> + </constraint> + </properties> + <defaultValue>1000</defaultValue> + </leafNode> + <leafNode name="max-holdtime"> + <properties> + <help>Maximum hold time (default: 10000)</help> + <valueHelp> + <format>u32:0-600000</format> + <description>Max hold time in milliseconds</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-600000"/> + </constraint> + </properties> + <defaultValue>10000</defaultValue> + </leafNode> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/protocols-ospfv3.xml.in b/interface-definitions/protocols-ospfv3.xml.in new file mode 100644 index 000000000..7f80f9f9d --- /dev/null +++ b/interface-definitions/protocols-ospfv3.xml.in @@ -0,0 +1,221 @@ +<?xml version="1.0" encoding="utf-8"?> +<interfaceDefinition> + <node name="protocols"> + <children> + <node name="ospfv3" owner="${vyos_conf_scripts_dir}/protocols_ospfv3.py"> + <properties> + <help>Open Shortest Path First (OSPF) for IPv6</help> + <priority>620</priority> + </properties> + <children> + <tagNode name="area"> + <properties> + <help>OSPFv3 Area</help> + <valueHelp> + <format>u32</format> + <description>Area ID as a decimal value</description> + </valueHelp> + <valueHelp> + <format>ipv4</format> + <description>Area ID in IP address forma</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-4294967295"/> + <validator name="ip-address"/> + </constraint> + </properties> + <children> + <leafNode name="export-list"> + <properties> + <help>Name of export-list</help> + <completionHelp> + <path>policy access-list6</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="import-list"> + <properties> + <help>Name of import-list</help> + <completionHelp> + <path>policy access-list6</path> + </completionHelp> + </properties> + </leafNode> + <leafNode name="interface"> + <properties> + <help>Enable routing on an IPv6 interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces.py</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface used for routing information exchange</description> + </valueHelp> + <constraint> + <regex>^(br|bond|dum|en|eth|gnv|peth|tun|vti|vxlan|wg|wlan)[0-9]+|lo$</regex> + </constraint> + <multi/> + </properties> + </leafNode> + <tagNode name="range"> + <properties> + <help>Specify IPv6 prefix (border routers only)</help> + <valueHelp> + <format>ipv6net</format> + <description>Specify IPv6 prefix (border routers only)</description> + </valueHelp> + <constraint> + <validator name="ipv6-prefix"/> + </constraint> + </properties> + <children> + <leafNode name="advertise"> + <properties> + <help>Advertise this range</help> + <valueless/> + </properties> + </leafNode> + <leafNode name="not-advertise"> + <properties> + <help>Do not advertise this range</help> + <valueless/> + </properties> + </leafNode> + </children> + </tagNode> + </children> + </tagNode> + <node name="distance"> + <properties> + <help>Administrative distance</help> + </properties> + <children> + <leafNode name="global"> + <properties> + <help>OSPFv3 administrative distance</help> + <valueHelp> + <format>u32:1-255</format> + <description>Administrative distance</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <node name="ospfv3"> + <properties> + <help>OSPFv3 administrative distance</help> + </properties> + <children> + <leafNode name="external"> + <properties> + <help>Distance for external routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for external routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <leafNode name="inter-area"> + <properties> + <help>Distance for inter-area routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for inter-area routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + <leafNode name="intra-area"> + <properties> + <help>Distance for intra-area routes</help> + <valueHelp> + <format>u32:1-255</format> + <description>Distance for intra-area routes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-255"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> + </node> + <node name="parameters"> + <properties> + <help>OSPFv3 specific parameters</help> + </properties> + <children> + <leafNode name="router-id"> + <properties> + <help>Override the default router identifier</help> + <valueHelp> + <format>ipv4</format> + <description>Override the default router identifier</description> + </valueHelp> + <constraint> + <validator name="ipv4-address"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + <node name="redistribute"> + <properties> + <help>Redistribute information from another routing protocol</help> + </properties> + <children> + <node name="bgp"> + <properties> + <help>Redistribute BGP routes</help> + </properties> + <children> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="connected"> + <properties> + <help>Redistribute connected routes</help> + </properties> + <children> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="kernel"> + <properties> + <help>Redistribute kernel routes</help> + </properties> + <children> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="ripng"> + <properties> + <help>Redistribute RIPNG routes</help> + </properties> + <children> + #include <include/ospf-route-map.xml.i> + </children> + </node> + <node name="static"> + <properties> + <help>Redistribute static routes</help> + </properties> + <children> + #include <include/ospf-route-map.xml.i> + </children> + </node> + </children> + </node> + #include <include/ospf-route-map.xml.i> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/interface-definitions/service_ipoe-server.xml.in b/interface-definitions/service_ipoe-server.xml.in index 07241fcc2..c54de58f5 100644 --- a/interface-definitions/service_ipoe-server.xml.in +++ b/interface-definitions/service_ipoe-server.xml.in @@ -153,7 +153,7 @@ <properties> <help>Client mac address allowed to receive an IP address</help> <valueHelp> - <format>h:h:h:h:h:h</format> + <format>macaddr</format> <description>Hardware (MAC) address</description> </valueHelp> <constraint> diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index bcaec55be..abd91583d 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -307,3 +307,26 @@ def verify_diffie_hellman_length(file, min_keysize): return False +def verify_route_maps(config): + """ + Common helper function used by routing protocol implementations to perform + recurring validation if the specified route-map for either zebra to kernel + installation exists (this is the top-level route_map key) or when a route + is redistributed with a route-map that it exists! + """ + if 'route_map' in config: + route_map = config['route_map'] + # Check if the specified route-map exists, if not error out + if dict_search(f'policy.route_map.{route_map}', config) == None: + raise ConfigError(f'Specified route-map "{route_map}" does not exist!') + + if 'redistribute' in config: + for protocol, protocol_config in config['redistribute'].items(): + if 'route_map' in protocol_config: + # A hyphen in a route-map name will be converted to _, take care + # about this effect during validation + route_map = protocol_config['route_map'].replace('-','_') + # Check if the specified route-map exists, if not error out + if dict_search(f'policy.route_map.{route_map}', config) == None: + raise ConfigError(f'Redistribution route-map "{route_map}" ' \ + f'for "{protocol}" does not exist!') diff --git a/python/vyos/frr.py b/python/vyos/frr.py index 3bab64301..76e204ab3 100644 --- a/python/vyos/frr.py +++ b/python/vyos/frr.py @@ -459,7 +459,8 @@ class FRRConfig: start = _find_first_element(self.config, before_pattern) if start < 0: return False - + for i, e in enumerate(addition, start=start): + LOG.debug(f'add_before: add {i:3} {e}') self.config[start:start] = addition return True diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 7e3f9565a..4320bf8bc 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -55,6 +55,7 @@ class _Tunnel(Interface): 'ttl' : '', 'tos' : '', 'key' : '', + 'raw' : '', } options = Interface.options + list(default.keys()) diff --git a/python/vyos/template.py b/python/vyos/template.py index bf087c223..527384d0b 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -149,7 +149,9 @@ def netmask_from_ipv4(address): Example: - 172.18.201.10 -> 255.255.255.128 """ - from netifaces import interfaces, ifaddresses, AF_INET + from netifaces import interfaces + from netifaces import ifaddresses + from netifaces import AF_INET for interface in interfaces(): tmp = ifaddresses(interface) if AF_INET in tmp: @@ -160,6 +162,30 @@ def netmask_from_ipv4(address): raise ValueError +@register_filter('is_ip_network') +def is_ip_network(addr): + """ Take IP(v4/v6) address and validate if the passed argument is a network + or a host address. + + Example: + - 192.0.2.0 -> False + - 192.0.2.10/24 -> False + - 192.0.2.0/24 -> True + - 2001:db8:: -> False + - 2001:db8::100 -> False + - 2001:db8::/48 -> True + - 2001:db8:1000::/64 -> True + """ + try: + from ipaddress import ip_network + # input variables must contain a / to indicate its CIDR notation + if len(addr.split('/')) != 2: + raise ValueError() + ip_network(addr) + return True + except: + return False + @register_filter('network_from_ipv4') def network_from_ipv4(address): """ Take IP address and search all attached interface IP addresses for the @@ -248,6 +274,21 @@ def dec_ip(address, decrement): from ipaddress import ip_interface return str(ip_interface(address).ip - int(decrement)) +@register_filter('compare_netmask') +def compare_netmask(netmask1, netmask2): + """ + Compare two IP netmask if they have the exact same size. + + compare_netmask('10.0.0.0/8', '20.0.0.0/8') -> True + compare_netmask('10.0.0.0/8', '20.0.0.0/16') -> False + """ + from ipaddress import ip_network + try: + return ip_network(netmask1).netmask == ip_network(netmask2).netmask + except: + return False + + @register_filter('isc_static_route') def isc_static_route(subnet, router): # https://ercpe.de/blog/pushing-static-routes-with-isc-dhcp-server diff --git a/scripts/override-default b/scripts/override-default new file mode 100755 index 000000000..d91b89426 --- /dev/null +++ b/scripts/override-default @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# override-default: preprocessor for XML interface definitions to interpret +# redundant entries (relative to path) with tag 'defaultValue' as an override +# directive. Must be called before build-command-templates, as the schema +# disallows redundancy. +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# + +# Use lxml xpath capability to find multiple elements with tag defaultValue +# relative to path; replace and remove to override the value. + +import sys +import glob +import logging +from lxml import etree + +debug = False + +logger = logging.getLogger(__name__) +logs_handler = logging.StreamHandler() +logger.addHandler(logs_handler) + +if debug: + logger.setLevel(logging.DEBUG) +else: + logger.setLevel(logging.INFO) + +def override_element(l: list): + """ + Allow multiple override elements; use the final one (in document order). + """ + if len(l) < 2: + logger.debug("passing list of single element to override_element") + return + + # assemble list of leafNodes of overriding defaultValues, for later removal + parents = [] + for el in l[1:]: + parents.append(el.getparent()) + + # replace element with final override + l[0].getparent().replace(l[0], l[-1]) + + # remove all but overridden element + for el in parents: + el.getparent().remove(el) + +def collect_and_override(dir_name): + """ + Collect elements with defaultValue tag into dictionary indexed by tuple + of (name, str(ancestor path)); the second component must be immutable for + tuple to act as key, hence str(). + """ + for fname in glob.glob(f'{dir_name}/*.xml'): + tree = etree.parse(fname) + root = tree.getroot() + defv = {} + + xpath_str = f'//defaultValue' + xp = tree.xpath(xpath_str) + + for element in xp: + ap = element.xpath('ancestor::*[@name]') + defv.setdefault((ap[-1].get("name"), str(ap[:-1])), []).append(element) + + for k, v in defv.items(): + if len(v) > 1: + logger.debug(f'overridding default in {k[0]}') + override_element(v) + + revised_str = etree.tostring(root, encoding='unicode', pretty_print=True) + + with open(f'{fname}', 'w') as f: + f.write(revised_str) + +def main(): + if len(sys.argv) < 2: + logger.critical('Must specify XML directory!') + sys.exit(1) + + dir_name = sys.argv[1] + + collect_and_override(dir_name) + +if __name__ == '__main__': + main() diff --git a/smoketest/configs/bgp-ix-router b/smoketest/configs/bgp-ixp index de6213b50..de6213b50 100644 --- a/smoketest/configs/bgp-ix-router +++ b/smoketest/configs/bgp-ixp diff --git a/smoketest/configs/bgp-rpki b/smoketest/configs/bgp-rpki new file mode 100644 index 000000000..e11ec9e72 --- /dev/null +++ b/smoketest/configs/bgp-rpki @@ -0,0 +1,116 @@ +interfaces { + ethernet eth0 { + address 192.0.2.100/25 + address 2001:db8::ffff/64 + } + ethernet eth1 { + } + loopback lo { + } +} +policy { + route-map ebgp-transit-rpki { + rule 10 { + action deny + match { + rpki invalid + } + } + rule 20 { + action permit + match { + rpki notfound + } + set { + local-preference 20 + } + } + rule 30 { + action permit + match { + rpki valid + } + set { + local-preference 100 + } + } + } +} +protocols { + bgp 64500 { + neighbor 1.2.3.4 { + address-family { + ipv4-unicast { + nexthop-self { + } + route-map { + import ebgp-transit-rpki + } + } + } + remote-as 10 + } + } + rpki { + cache routinator { + address 192.0.2.10 + port 3323 + } + } + static { + route 0.0.0.0/0 { + next-hop 192.0.2.1 { + } + } + route6 ::/0 { + next-hop 2001:db8::1 { + } + } + } +} +service { + ssh { + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@13:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@19:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webgui@1:webproxy@2:zone-policy@1" +// Release version: 1.3-rolling-202010241631 diff --git a/smoketest/configs/dmz-guest-lan-nat-pppoe-router b/smoketest/configs/dmz-guest-lan-nat-pppoe-router new file mode 100644 index 000000000..e671126a6 --- /dev/null +++ b/smoketest/configs/dmz-guest-lan-nat-pppoe-router @@ -0,0 +1,1663 @@ +firewall { + all-ping enable + broadcast-ping disable + config-trap disable + group { + address-group MEDIA-STREAMING-CLIENTS { + address 172.16.35.241 + address 172.16.35.242 + address 172.16.35.243 + } + address-group DMZ-WEBSERVER { + address 172.16.36.10 + address 172.16.36.40 + address 172.16.36.20 + } + address-group DMZ-RDP-SERVER { + address 172.16.33.40 + } + address-group DOMAIN-CONTROLLER { + address 172.16.100.10 + address 172.16.100.20 + } + address-group AUDIO-STREAM { + address 172.16.35.20 + address 172.16.35.21 + address 172.16.35.22 + address 172.16.35.23 + } + ipv6-network-group LOCAL-ADDRESSES { + network ff02::/64 + network fe80::/10 + } + network-group SSH-IN-ALLOW { + network 192.0.2.0/24 + network 10.0.0.0/8 + network 172.16.0.0/12 + network 192.168.0.0/16 + } + port-group SMART-TV-PORTS { + port 5005-5006 + port 80 + port 443 + port 3722 + } + } + ipv6-name ALLOW-ALL-6 { + default-action accept + } + ipv6-name ALLOW-BASIC-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + protocol icmpv6 + } + } + ipv6-name ALLOW-ESTABLISHED-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + destination { + group { + network-group LOCAL-ADDRESSES + } + } + protocol icmpv6 + source { + address fe80::/10 + } + } + rule 20 { + action accept + icmpv6 { + type echo-request + } + protocol icmpv6 + } + rule 21 { + action accept + icmpv6 { + type destination-unreachable + } + protocol icmpv6 + } + rule 22 { + action accept + icmpv6 { + type packet-too-big + } + protocol icmpv6 + } + rule 23 { + action accept + icmpv6 { + type time-exceeded + } + protocol icmpv6 + } + rule 24 { + action accept + icmpv6 { + type parameter-problem + } + protocol icmpv6 + } + } + ipv6-name WAN-LOCAL-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + destination { + address ff02::/64 + } + protocol icmpv6 + source { + address fe80::/10 + } + } + rule 50 { + action accept + description DHCPv6 + destination { + address fe80::/10 + port 546 + } + protocol udp + source { + address fe80::/10 + port 547 + } + } + } + ipv6-receive-redirects disable + ipv6-src-route disable + ip-src-route disable + log-martians enable + name DMZ-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name DMZ-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "NTP and LDAP to AD DC" + destination { + group { + address-group DOMAIN-CONTROLLER + } + port 123,389,636 + } + protocol tcp_udp + } + rule 300 { + action accept + destination { + group { + address-group DMZ-RDP-SERVER + } + port 3389 + } + protocol tcp_udp + source { + address 172.16.36.20 + } + } + } + name DMZ-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 50 { + action accept + destination { + address 172.16.254.30 + port 53 + } + protocol tcp_udp + } + rule 123 { + action accept + destination { + port 123 + } + protocol udp + } + } + name DMZ-WAN { + default-action accept + } + name GUEST-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + destination { + port 80,443 + } + protocol tcp + } + } + name GUEST-IOT { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "MEDIA-STREAMING-CLIENTS Devices to GUEST" + destination { + group { + address-group MEDIA-STREAMING-CLIENTS + } + } + protocol tcp_udp + } + rule 110 { + action accept + description "AUDIO-STREAM Devices to GUEST" + destination { + group { + address-group AUDIO-STREAM + } + } + protocol tcp_udp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900 + } + protocol udp + } + } + name GUEST-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name GUEST-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + description DNS + destination { + address 172.31.0.254 + port 53 + } + protocol tcp_udp + } + rule 11 { + action accept + description DHCP + destination { + port 67 + } + protocol udp + } + rule 15 { + action accept + destination { + address 172.31.0.254 + } + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 210 { + action accept + description "AUDIO-STREAM Broadcast" + destination { + port 1900 + } + protocol udp + } + } + name GUEST-WAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 25 { + action accept + description SMTP + destination { + port 25,587 + } + protocol tcp + } + rule 53 { + action accept + destination { + port 53 + } + protocol tcp_udp + } + rule 60 { + action accept + source { + address 172.31.0.200 + } + } + rule 80 { + action accept + source { + address 172.31.0.200 + } + } + rule 100 { + action accept + protocol icmp + } + rule 110 { + action accept + description POP3 + destination { + port 110,995 + } + protocol tcp + } + rule 123 { + action accept + description "NTP Client" + destination { + port 123 + } + protocol udp + } + rule 143 { + action accept + description IMAP + destination { + port 143,993 + } + protocol tcp + } + rule 200 { + action accept + destination { + port 80,443 + } + protocol tcp + } + rule 500 { + action accept + description "L2TP IPSec" + destination { + port 500,4500 + } + protocol udp + } + rule 600 { + action accept + destination { + port 5222-5224 + } + protocol tcp + } + rule 601 { + action accept + destination { + port 3478-3497,4500,16384-16387,16393-16402 + } + protocol udp + } + rule 1000 { + action accept + source { + address 172.31.0.184 + } + } + } + name IOT-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "MEDIA-STREAMING-CLIENTS Devices to IOT" + protocol tcp_udp + source { + group { + address-group MEDIA-STREAMING-CLIENTS + } + } + } + rule 110 { + action accept + description "AUDIO-STREAM Devices to IOT" + protocol tcp_udp + source { + group { + address-group AUDIO-STREAM + } + } + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900 + } + protocol udp + } + } + name IOT-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + description "AppleTV to LAN" + destination { + group { + port-group SMART-TV-PORTS + } + } + protocol tcp_udp + source { + group { + address-group MEDIA-STREAMING-CLIENTS + } + } + } + rule 110 { + action accept + description "AUDIO-STREAM Devices to LAN" + protocol tcp_udp + source { + group { + address-group AUDIO-STREAM + } + } + } + } + name IOT-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + description DNS + destination { + address 172.16.254.30 + port 53 + } + protocol tcp_udp + } + rule 11 { + action accept + description DHCP + destination { + port 67 + } + protocol udp + } + rule 15 { + action accept + destination { + address 172.16.35.254 + } + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 201 { + action accept + description "MCAST relay" + destination { + address 172.16.35.254 + port 5353 + } + protocol udp + } + rule 210 { + action accept + description "AUDIO-STREAM Broadcast" + destination { + port 1900,1902,6969 + } + protocol udp + } + } + name IOT-WAN { + default-action accept + } + name LAN-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 22 { + action accept + description "SSH into DMZ" + destination { + port 22 + } + protocol tcp + } + rule 100 { + action accept + destination { + group { + address-group DMZ-WEBSERVER + } + port 22,80,443 + } + protocol tcp + } + } + name LAN-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name LAN-IOT { + default-action accept + } + name LAN-LOCAL { + default-action accept + } + name LAN-WAN { + default-action accept + } + name LOCAL-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name LOCAL-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 5 { + action accept + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900 + } + protocol udp + } + } + name LOCAL-IOT { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 5 { + action accept + protocol icmp + } + rule 200 { + action accept + description "MCAST relay" + destination { + address 224.0.0.251 + port 5353 + } + protocol udp + } + rule 300 { + action accept + description "BCAST relay" + destination { + port 1900,6969 + } + protocol udp + } + } + name LOCAL-LAN { + default-action accept + } + name LOCAL-WAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + protocol icmp + } + rule 50 { + action accept + description DNS + destination { + port 53 + } + protocol tcp_udp + } + rule 80 { + action accept + destination { + port 80,443 + } + protocol tcp + } + rule 123 { + action accept + description NTP + destination { + port 123 + } + protocol udp + } + } + name WAN-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + destination { + address 172.16.36.10 + port 80,443 + } + protocol tcp + } + } + name WAN-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 1000 { + action accept + destination { + address 172.31.0.184 + } + } + rule 8000 { + action accept + destination { + address 172.31.0.200 + port 10000 + } + protocol udp + } + } + name WAN-IOT { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name WAN-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 1000 { + action accept + destination { + address 172.16.33.40 + port 3389 + } + protocol tcp + source { + group { + network-group SSH-IN-ALLOW + } + } + } + } + name WAN-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 22 { + action accept + destination { + port 22 + } + protocol tcp + source { + group { + network-group SSH-IN-ALLOW + } + } + } + } + options { + interface pppoe0 { + adjust-mss 1452 + adjust-mss6 1432 + } + } + receive-redirects disable + send-redirects enable + source-validation disable + syn-cookies enable + twa-hazards-protection disable +} +interfaces { + dummy dum0 { + address 172.16.254.30/32 + } + ethernet eth0 { + duplex auto + speed auto + vif 5 { + address 172.16.37.254/24 + } + vif 10 { + address 172.16.33.254/24 + } + vif 20 { + address 172.31.0.254/24 + } + vif 35 { + address 172.16.35.254/24 + } + vif 50 { + address 172.16.36.254/24 + } + vif 100 { + address 172.16.100.254/24 + } + vif 201 { + address 172.18.201.254/24 + } + vif 202 { + address 172.18.202.254/24 + } + vif 203 { + address 172.18.203.254/24 + } + vif 204 { + address 172.18.204.254/24 + } + } + ethernet eth1 { + vif 7 { + description FTTH-PPPoE + } + } + loopback lo { + address 172.16.254.30/32 + } + pppoe pppoe0 { + authentication { + password vyos + user vyos + } + default-route auto + description "FTTH 100/50MBit" + dhcpv6-options { + pd 0 { + interface eth0.10 { + address 1 + sla-id 10 + } + interface eth0.20 { + address 1 + sla-id 20 + } + length 56 + } + } + ipv6 { + address { + autoconf + } + } + mtu 1492 + no-peer-dns + source-interface eth1.7 + } +} +nat { + destination { + rule 100 { + description HTTP(S) + destination { + port 80,443 + } + inbound-interface pppoe0 + log + protocol tcp + translation { + address 172.16.36.10 + } + } + rule 1000 { + destination { + port 3389 + } + disable + inbound-interface pppoe0 + protocol tcp + translation { + address 172.16.33.40 + } + } + rule 8000 { + destination { + port 10000 + } + inbound-interface pppoe0 + log + protocol udp + translation { + address 172.31.0.200 + } + } + } + source { + rule 100 { + log + outbound-interface pppoe0 + source { + address 172.16.32.0/19 + } + translation { + address masquerade + } + } + rule 200 { + outbound-interface pppoe0 + source { + address 172.16.100.0/24 + } + translation { + address masquerade + } + } + rule 300 { + outbound-interface pppoe0 + source { + address 172.31.0.0/24 + } + translation { + address masquerade + } + } + rule 400 { + outbound-interface pppoe0 + source { + address 172.18.200.0/21 + } + translation { + address masquerade + } + } + } +} +protocols { + static { + interface-route6 2000::/3 { + next-hop-interface pppoe0 { + } + } + route 10.0.0.0/8 { + blackhole { + distance 254 + } + } + route 169.254.0.0/16 { + blackhole { + distance 254 + } + } + route 172.16.0.0/12 { + blackhole { + distance 254 + } + } + route 192.168.0.0/16 { + blackhole { + distance 254 + } + } + } +} +service { + dhcp-server { + shared-network-name BACKBONE { + authoritative + subnet 172.16.37.0/24 { + default-router 172.16.37.254 + dns-server 172.16.254.30 + domain-name vyos.net + domain-search vyos.net + lease 86400 + ntp-server 172.16.254.30 + range 0 { + start 172.16.37.120 + stop 172.16.37.149 + } + static-mapping AP1.wue3 { + ip-address 172.16.37.231 + mac-address 18:e8:29:6c:c3:a5 + } + } + } + shared-network-name GUEST { + authoritative + subnet 172.31.0.0/24 { + default-router 172.31.0.254 + dns-server 172.31.0.254 + domain-name vyos.net + domain-search vyos.net + lease 86400 + range 0 { + start 172.31.0.100 + stop 172.31.0.199 + } + static-mapping host01 { + ip-address 172.31.0.200 + mac-address 00:50:00:00:00:01 + } + static-mapping host02 { + ip-address 172.31.0.184 + mac-address 00:50:00:00:00:02 + } + } + } + shared-network-name IOT { + authoritative + subnet 172.16.35.0/24 { + default-router 172.16.35.254 + dns-server 172.16.254.30 + domain-name vyos.net + domain-search vyos.net + lease 86400 + ntp-server 172.16.254.30 + range 0 { + start 172.16.35.101 + stop 172.16.35.149 + } + } + } + shared-network-name LAN { + authoritative + subnet 172.16.33.0/24 { + default-router 172.16.33.254 + dns-server 172.16.254.30 + domain-name vyos.net + domain-search vyos.net + lease 86400 + ntp-server 172.16.254.30 + range 0 { + start 172.16.33.100 + stop 172.16.33.189 + } + } + } + } + dns { + forwarding { + allow-from 172.16.0.0/12 + cache-size 0 + domain 16.172.in-addr.arpa { + addnta + recursion-desired + server 172.16.100.10 + server 172.16.100.20 + server 172.16.110.30 + } + domain 18.172.in-addr.arpa { + addnta + recursion-desired + server 172.16.100.10 + server 172.16.100.20 + server 172.16.110.30 + } + domain vyos.net { + addnta + recursion-desired + server 172.16.100.20 + server 172.16.100.10 + server 172.16.110.30 + } + ignore-hosts-file + listen-address 172.16.254.30 + listen-address 172.31.0.254 + negative-ttl 60 + } + } + lldp { + legacy-protocols { + cdp + } + snmp { + enable + } + } + mdns { + repeater { + interface eth0.35 + interface eth0.10 + } + } + router-advert { + interface eth0.10 { + prefix ::/64 { + preferred-lifetime 2700 + valid-lifetime 5400 + } + } + interface eth0.20 { + prefix ::/64 { + preferred-lifetime 2700 + valid-lifetime 5400 + } + } + } + snmp { + community fooBar { + authorization ro + network 172.16.100.0/24 + } + contact "VyOS maintainers and contributors <maintainers@vyos.io>" + listen-address 172.16.254.30 { + port 161 + } + location "The Internet" + } + ssh { + disable-host-validation + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + conntrack { + expect-table-size 2048 + hash-size 32768 + modules { + sip { + disable + } + } + table-size 262144 + timeout { + icmp 30 + other 600 + udp { + other 300 + stream 300 + } + } + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + name-server 172.16.254.30 + ntp { + allow-clients { + address 172.16.0.0/12 + } + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + option { + ctrl-alt-delete ignore + reboot-on-panic + startup-beep + } + syslog { + global { + facility all { + level debug + } + facility protocols { + level debug + } + } + host 172.16.100.1 { + facility all { + level warning + } + } + } + time-zone Europe/Berlin +} +traffic-policy { + shaper QoS { + bandwidth 50mbit + default { + bandwidth 100% + burst 15k + queue-limit 1000 + queue-type fq-codel + } + } +} +zone-policy { + zone DMZ { + default-action drop + from GUEST { + firewall { + name GUEST-DMZ + } + } + from LAN { + firewall { + name LAN-DMZ + } + } + from LOCAL { + firewall { + name LOCAL-DMZ + } + } + from WAN { + firewall { + name WAN-DMZ + } + } + interface eth0.50 + } + zone GUEST { + default-action drop + from DMZ { + firewall { + name DMZ-GUEST + } + } + from IOT { + firewall { + name IOT-GUEST + } + } + from LAN { + firewall { + name LAN-GUEST + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-GUEST + } + } + from WAN { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name WAN-GUEST + } + } + interface eth0.20 + } + zone IOT { + default-action drop + from GUEST { + firewall { + name GUEST-IOT + } + } + from LAN { + firewall { + name LAN-IOT + } + } + from LOCAL { + firewall { + name LOCAL-IOT + } + } + from WAN { + firewall { + name WAN-IOT + } + } + interface eth0.35 + } + zone LAN { + default-action drop + from DMZ { + firewall { + name DMZ-LAN + } + } + from GUEST { + firewall { + name GUEST-LAN + } + } + from IOT { + firewall { + name IOT-LAN + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-LAN + } + } + from WAN { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name WAN-LAN + } + } + interface eth0.5 + interface eth0.10 + interface eth0.100 + interface eth0.201 + interface eth0.202 + interface eth0.203 + interface eth0.204 + } + zone LOCAL { + default-action drop + from DMZ { + firewall { + name DMZ-LOCAL + } + } + from GUEST { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name GUEST-LOCAL + } + } + from IOT { + firewall { + name IOT-LOCAL + } + } + from LAN { + firewall { + ipv6-name ALLOW-ALL-6 + name LAN-LOCAL + } + } + from WAN { + firewall { + ipv6-name WAN-LOCAL-6 + name WAN-LOCAL + } + } + local-zone + } + zone WAN { + default-action drop + from DMZ { + firewall { + name DMZ-WAN + } + } + from GUEST { + firewall { + ipv6-name ALLOW-ALL-6 + name GUEST-WAN + } + } + from IOT { + firewall { + name IOT-WAN + } + } + from LAN { + firewall { + ipv6-name ALLOW-ALL-6 + name LAN-WAN + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-WAN + } + } + interface pppoe0 + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3-beta-202101091250 + diff --git a/smoketest/configs/ospf-config b/smoketest/configs/ospf-config new file mode 100644 index 000000000..fe313e4b0 --- /dev/null +++ b/smoketest/configs/ospf-config @@ -0,0 +1,120 @@ +interfaces { + dummy dum0 { + address 172.18.254.201/32 + } + ethernet eth0 { + duplex auto + smp-affinity auto + speed auto + vif 201 { + address 172.18.201.10/24 + ip { + ospf { + authentication { + md5 { + key-id 10 { + md5-key OSPFVyOSNET + } + } + } + dead-interval 40 + hello-interval 10 + priority 1 + retransmit-interval 5 + transmit-delay 1 + } + } + } + } + ethernet eth1 { + duplex auto + smp-affinity auto + speed auto + } +} +protocols { + ospf { + area 0 { + network 172.18.201.0/24 + network 172.18.254.201/32 + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 172.18.254.201 + } + passive-interface default + passive-interface-exclude eth0.201 + } + static { + route 0.0.0.0/0 { + next-hop 172.18.201.254 { + distance 10 + } + } + } +} +service { + lldp { + interface all { + } + } + snmp { + community public { + authorization ro + network 172.16.100.0/24 + } + contact "VyOS maintainers and contributors <maintainers@vyos.io>" + location "Jenkins" + } + ssh { + disable-host-validation + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + level admin + } + } + name-server 172.16.254.30 + ntp { + server 0.pool.ntp.org { + } + server 1.pool.ntp.org { + } + server 2.pool.ntp.org { + } + } + syslog { + global { + facility all { + level info + } + facility protocols { + level debug + } + } + } + time-zone Europe/Berlin +} + +/* Warning: Do not remove the following line. */ +/* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ +/* Release version: 1.2.6 */ diff --git a/smoketest/configs/small-as-bgp-vrrp b/smoketest/configs/small-as-bgp-vrrp new file mode 100644 index 000000000..61286c324 --- /dev/null +++ b/smoketest/configs/small-as-bgp-vrrp @@ -0,0 +1,683 @@ +firewall { + all-ping enable + broadcast-ping disable + config-trap disable + group { + address-group NET-VYOS-HTTPS-4 { + address 10.0.150.73 + } + ipv6-network-group NET-VYOS-6 { + network 2001:db8:200::/40 + } + network-group NET-VYOS-4 { + network 10.0.150.0/23 + network 192.168.189.0/24 + } + port-group MY-NAS-PORTS { + port 80 + port 5000 + port 5001 + port 6022 + port 9443 + } + } + ipv6-name WAN-TO-VLAN15-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + source { + group { + network-group NET-VYOS-6 + } + } + } + rule 1010 { + action accept + destination { + address 2001:db8:200:15::a + group { + port-group MY-NAS-PORTS + } + } + protocol tcp + } + } + ipv6-receive-redirects disable + ipv6-src-route disable + ip-src-route disable + log-martians enable + name WAN-TO-VLAN15-4 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + source { + group { + network-group NET-VYOS-4 + } + } + } + rule 1000 { + action accept + destination { + group { + address-group NET-VYOS-HTTPS-4 + } + port 80,443 + } + protocol tcp + } + rule 1010 { + action accept + destination { + address 10.0.150.74 + group { + port-group MY-NAS-PORTS + } + } + protocol tcp + } + } + receive-redirects disable + send-redirects enable + source-validation disable + syn-cookies enable + twa-hazards-protection disable +} +high-availability { + vrrp { + group VLAN5-IPv4 { + interface eth0.5 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.120/28 + vrid 5 + } + group VLAN5-IPv6 { + interface eth0.5 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:f0::ffff/64 + vrid 6 + } + group VLAN10-IPv4 { + interface eth0.10 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.62/26 + vrid 10 + } + group VLAN10-IPv6 { + interface eth0.10 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:10::ffff/64 + virtual-address 2001:db8:200::ffff/64 + vrid 11 + } + group VLAN15-IPv4 { + interface eth0.15 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.78/28 + vrid 15 + } + group VLAN15-IPv6 { + interface eth0.15 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:15::ffff/64 + vrid 16 + } + group VLAN500-IPv4 { + interface eth0.500 + preempt-delay 180 + priority 250 + virtual-address 10.0.151.238/28 + vrid 238 + } + group VLAN500-IPv6 { + interface eth0.500 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:50::ffff/64 + vrid 239 + } + group VLAN520-IPv4 { + interface eth0.520 + preempt-delay 180 + priority 250 + virtual-address 10.0.150.190/28 + vrid 52 + } + group VLAN520-IPv6 { + interface eth0.520 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:520::ffff/64 + vrid 53 + } + group VLAN810-IPv4 { + interface eth0.810 + preempt-delay 180 + priority 250 + virtual-address 10.0.151.30/27 + vrid 80 + } + group VLAN810-IPv6 { + interface eth0.810 + preempt-delay 180 + priority 250 + virtual-address 2001:db8:200:102::ffff/64 + vrid 81 + } + sync-group VYOS { + member VLAN5-IPv4 + member VLAN5-IPv6 + member VLAN10-IPv4 + member VLAN10-IPv6 + member VLAN500-IPv4 + member VLAN500-IPv6 + member VLAN15-IPv4 + member VLAN15-IPv6 + member VLAN810-IPv6 + member VLAN810-IPv4 + member VLAN520-IPv4 + member VLAN520-IPv6 + } + } +} +interfaces { + dummy dum0 { + address 2001:db8:200:ffff::2/128 + address 10.0.151.251/32 + } + ethernet eth0 { + vif 5 { + address 10.0.150.121/28 + address 2001:db8:200:f0::4/64 + ip { + ospf { + authentication { + md5 { + key-id 10 { + md5-key vyosospfkey + } + } + } + cost 10 + dead-interval 40 + hello-interval 10 + network broadcast + priority 200 + retransmit-interval 5 + transmit-delay 5 + } + } + } + vif 10 { + address 2001:db8:200:10::1:ffff/64 + address 2001:db8:200::1:ffff/64 + address 10.0.150.60/26 + } + vif 15 { + address 10.0.150.76/28 + address 2001:db8:200:15::1:ffff/64 + firewall { + out { + ipv6-name WAN-TO-VLAN15-6 + name WAN-TO-VLAN15-4 + } + } + } + vif 50 { + address 192.168.189.2/24 + } + vif 110 { + address 2001:db8:200:101::ffff/64 + address 10.0.151.190/27 + address 10.0.151.158/28 + } + vif 410 { + address 10.0.151.206/28 + address 2001:db8:200:104::ffff/64 + } + vif 450 { + address 2001:db8:200:103::ffff/64 + address 10.0.151.142/29 + disable + } + vif 500 { + address 10.0.151.236/28 + address 2001:db8:200:50::1:ffff/64 + } + vif 520 { + address 10.0.150.188/26 + address 2001:db8:200:520::1:ffff/64 + } + vif 800 { + address 2001:db8:200:ff::104:1/112 + address 10.0.151.212/31 + } + vif 810 { + address 10.0.151.28/27 + address 2001:db8:200:102::1:ffff/64 + } + } + ethernet eth1 { + } + loopback lo { + } +} +policy { + prefix-list as65000-origin-v4 { + rule 10 { + action permit + prefix 10.0.150.0/23 + } + rule 100 { + action permit + prefix 0.0.0.0/0 + } + } + prefix-list6 as65000-origin-v6 { + rule 10 { + action permit + prefix 2001:db8:200::/40 + } + } + route-map as65010-in { + rule 10 { + action permit + set { + local-preference 30 + } + } + } + route-map as65010-out { + rule 10 { + action permit + set { + as-path-prepend "65000 65000" + } + } + } +} +protocols { + bgp 65000 { + address-family { + ipv4-unicast { + network 10.0.150.0/23 { + } + } + ipv6-unicast { + network 2001:db8:200::/40 { + } + } + } + neighbor 10.0.151.222 { + address-family { + ipv4-unicast { + default-originate { + } + prefix-list { + export as65000-origin-v4 + } + route-map { + export as65010-out + import as65010-in + } + soft-reconfiguration { + inbound + } + } + } + capability { + dynamic + } + remote-as 65010 + } + neighbor 10.0.151.252 { + peer-group VYOSv4 + } + neighbor 10.0.151.254 { + peer-group VYOSv4 + } + neighbor 2001:db8:200:ffff::3 { + peer-group VYOSv6 + } + neighbor 2001:db8:200:ffff::a { + peer-group VYOSv6 + } + neighbor 2001:db8:200:ff::101:2 { + address-family { + ipv6-unicast { + capability { + dynamic + } + prefix-list { + export as65000-origin-v6 + } + route-map { + import as65010-in + } + soft-reconfiguration { + inbound + } + } + } + remote-as 65010 + } + parameters { + default { + no-ipv4-unicast + } + log-neighbor-changes + router-id 10.0.151.251 + } + peer-group VYOSv4 { + address-family { + ipv4-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as 65000 + update-source dum0 + } + peer-group VYOSv6 { + address-family { + ipv6-unicast { + nexthop-self { + } + } + } + capability { + dynamic + } + remote-as 65000 + update-source dum0 + } + timers { + holdtime 30 + keepalive 10 + } + } + ospf { + area 0 { + area-type { + normal + } + authentication md5 + network 10.0.151.251/32 + network 10.0.151.208/31 + network 10.0.150.112/28 + } + parameters { + abr-type cisco + router-id 10.0.151.251 + } + passive-interface default + passive-interface-exclude dum0 + passive-interface-exclude eth0.5 + redistribute { + connected { + metric-type 2 + } + static { + metric-type 2 + } + } + } + ospfv3 { + area 0.0.0.0 { + interface dum0 + interface eth0.5 + } + parameters { + router-id 10.0.151.251 + } + redistribute { + connected { + } + static { + } + } + } + static { + route 10.0.0.0/8 { + MY-NAS { + distance 254 + } + } + route 172.16.0.0/12 { + MY-NAS { + distance 254 + } + } + route 192.168.0.0/16 { + MY-NAS { + distance 254 + } + } + route 193.148.249.144/32 { + next-hop 192.168.189.1 { + } + } + route 10.0.150.0/23 { + MY-NAS { + distance 254 + } + } + route 10.0.151.32/27 { + next-hop 10.0.151.5 { + } + } + route6 2001:db8:2fe:ffff::/64 { + next-hop 2001:db8:200:102::4 { + } + } + route6 2001:db8:2ff::/48 { + next-hop 2001:db8:200:101::1 { + } + } + route6 2001:db8:200::/40 { + MY-NAS { + distance 254 + } + } + } +} +service { + dhcp-server { + shared-network-name NET-VYOS-DHCP-1 { + subnet 10.0.151.224/28 { + default-router 10.0.151.238 + dns-server 10.0.150.2 + dns-server 10.0.150.1 + domain-name vyos.net + failover { + local-address 10.0.151.236 + name NET-VYOS-DHCP-1 + peer-address 10.0.151.237 + status primary + } + lease 1800 + range 0 { + start 10.0.151.225 + stop 10.0.151.237 + } + } + } + shared-network-name NET-VYOS-HOSTING-1 { + subnet 10.0.150.128/26 { + default-router 10.0.150.190 + dns-server 10.0.150.2 + dns-server 10.0.150.1 + domain-name vyos.net + failover { + local-address 10.0.150.188 + name NET-VYOS-HOSTING-1 + peer-address 10.0.150.189 + status primary + } + lease 604800 + range 0 { + start 10.0.150.129 + stop 10.0.150.187 + } + } + } + } + lldp { + interface all { + } + management-address 10.0.151.251 + snmp { + enable + } + } + router-advert { + interface eth4.500 { + default-preference high + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + prefix 2001:db8:200:50::/64 { + valid-lifetime infinity + } + } + interface eth4.520 { + default-preference high + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + prefix 2001:db8:200:520::/64 { + valid-lifetime infinity + } + } + } + snmp { + community public { + network 10.0.150.0/26 + network 2001:db8:200:10::/64 + } + contact noc@vyos.net + listen-address 10.0.151.251 { + } + listen-address 2001:db8:200:ffff::2 { + } + location "Jenkins" + } + ssh { + disable-host-validation + listen-address 10.0.151.251 + listen-address 2001:db8:200:ffff::2 + listen-address 192.168.189.2 + loglevel fatal + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name vyos + login { + banner { + pre-login "VyOS - Network\n" + } + radius { + server 192.0.2.1 { + key SuperS3cretRADIUSkey + timeout 1 + } + server 192.0.2.2 { + key SuperS3cretRADIUSkey + timeout 1 + } + source-address 192.0.2.254 + } + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + name-server 192.0.2.1 + name-server 192.0.2.2 + name-server 2001:db8:200::1 + name-server 2001:db8:200::2 + ntp { + allow-clients { + address 10.0.150.0/23 + address 2001:db8:200::/40 + } + listen-address 10.0.151.251 + listen-address 2001:db8:200:ffff::2 + server 0.de.pool.ntp.org { + } + server 1.de.pool.ntp.org { + } + server 2.de.pool.ntp.org { + } + } + syslog { + global { + facility all { + level notice + } + facility protocols { + level debug + } + } + host 10.0.150.26 { + facility all { + level all + } + } + } + time-zone Europe/Berlin +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@18:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@20:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3-beta-202101151942 diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py index f67b813af..8405fc7d0 100755 --- a/smoketest/scripts/cli/test_interfaces_tunnel.py +++ b/smoketest/scripts/cli/test_interfaces_tunnel.py @@ -84,7 +84,6 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest): self.session.delete(['interfaces', 'dummy', source_if]) super().tearDown() - def test_ipv4_encapsulations(self): # When running tests ensure that for certain encapsulation types the # local and remote IP address is actually an IPv4 address @@ -106,7 +105,6 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest): with self.assertRaises(ConfigSessionError): self.session.commit() self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) - self.session.set(self._base_path + [interface, 'source-interface', source_if]) # Source interface can not be used with sit and gre-bridge @@ -130,6 +128,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest): self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local']) self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) + self.assertTrue(conf['linkinfo']['info_data']['pmtudisc']) # cleanup this instance self.session.delete(self._base_path + [interface]) @@ -177,7 +176,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest): self.assertEqual(encapsulation, conf['link_type']) self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local']) - self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote']) + self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote']) # cleanup this instance self.session.delete(self._base_path + [interface]) @@ -203,5 +202,31 @@ class TunnelInterfaceTest(BasicInterfaceTest.BaseTest): # Check if commit is ok self.session.commit() + def test_tunnel_parameters_gre(self): + interface = f'tun1030' + gre_key = '10' + encapsulation = 'gre' + tos = '20' + + self.session.set(self._base_path + [interface, 'encapsulation', encapsulation]) + self.session.set(self._base_path + [interface, 'local-ip', self.local_v4]) + self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4]) + + self.session.set(self._base_path + [interface, 'parameters', 'ip', 'no-pmtu-discovery']) + self.session.set(self._base_path + [interface, 'parameters', 'ip', 'key', gre_key]) + self.session.set(self._base_path + [interface, 'parameters', 'ip', 'tos', tos]) + + # Check if commit is ok + self.session.commit() + + conf = tunnel_conf(interface) + self.assertEqual(mtu, conf['mtu']) + self.assertEqual(interface, conf['ifname']) + self.assertEqual(encapsulation, conf['link_type']) + self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local']) + self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote']) + self.assertEqual(0, conf['linkinfo']['info_data']['ttl']) + self.assertFalse( conf['linkinfo']['info_data']['pmtudisc']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py new file mode 100755 index 000000000..ccc4196e0 --- /dev/null +++ b/smoketest/scripts/cli/test_nat66.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import jmespath +import json +import unittest + +from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError +from vyos.util import cmd +from vyos.util import dict_search + +base_path = ['nat66'] +src_path = base_path + ['source'] +dst_path = base_path + ['destination'] + +class TestNAT66(unittest.TestCase): + def setUp(self): + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + self.session = ConfigSession(os.getpid()) + self.session.delete(base_path) + + def tearDown(self): + self.session.delete(base_path) + self.session.commit() + + def test_source_nat66(self): + source_prefix = 'fc00::/64' + translation_prefix = 'fc01::/64' + self.session.set(src_path + ['rule', '1', 'outbound-interface', 'eth1']) + self.session.set(src_path + ['rule', '1', 'source', 'prefix', source_prefix]) + self.session.set(src_path + ['rule', '1', 'translation', 'prefix', translation_prefix]) + + # check validate() - outbound-interface must be defined + self.session.commit() + + tmp = cmd('sudo nft -j list table ip6 nat') + data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + + for idx in range(0, len(data_json)): + data = data_json[idx] + + self.assertEqual(data['chain'], 'POSTROUTING') + self.assertEqual(data['family'], 'ip6') + self.assertEqual(data['table'], 'nat') + + iface = dict_search('match.right', data['expr'][0]) + address = dict_search('match.right.prefix.addr', data['expr'][2]) + mask = dict_search('match.right.prefix.len', data['expr'][2]) + translation_address = dict_search('snat.addr.prefix.addr', data['expr'][3]) + translation_mask = dict_search('snat.addr.prefix.len', data['expr'][3]) + + self.assertEqual(iface, 'eth1') + # check for translation address + self.assertEqual(f'{translation_address}/{translation_mask}', translation_prefix) + self.assertEqual(f'{address}/{mask}', source_prefix) + + def test_destination_nat66(self): + destination_address = 'fc00::1' + translation_address = 'fc01::1' + self.session.set(dst_path + ['rule', '1', 'inbound-interface', 'eth1']) + self.session.set(dst_path + ['rule', '1', 'destination', 'address', destination_address]) + self.session.set(dst_path + ['rule', '1', 'translation', 'address', translation_address]) + + # check validate() - outbound-interface must be defined + self.session.commit() + + tmp = cmd('sudo nft -j list table ip6 nat') + data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + + for idx in range(0, len(data_json)): + data = data_json[idx] + + self.assertEqual(data['chain'], 'PREROUTING') + self.assertEqual(data['family'], 'ip6') + self.assertEqual(data['table'], 'nat') + + iface = dict_search('match.right', data['expr'][0]) + dnat_addr = dict_search('dnat.addr', data['expr'][3]) + + self.assertEqual(dnat_addr, translation_address) + self.assertEqual(iface, 'eth1') + + def test_destination_nat66_prefix(self): + destination_prefix = 'fc00::/64' + translation_prefix = 'fc01::/64' + self.session.set(dst_path + ['rule', '1', 'inbound-interface', 'eth1']) + self.session.set(dst_path + ['rule', '1', 'destination', 'address', destination_prefix]) + self.session.set(dst_path + ['rule', '1', 'translation', 'address', translation_prefix]) + + # check validate() - outbound-interface must be defined + self.session.commit() + + tmp = cmd('sudo nft -j list table ip6 nat') + data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp)) + + for idx in range(0, len(data_json)): + data = data_json[idx] + + self.assertEqual(data['chain'], 'PREROUTING') + self.assertEqual(data['family'], 'ip6') + self.assertEqual(data['table'], 'nat') + + iface = dict_search('match.right', data['expr'][0]) + translation_address = dict_search('dnat.addr.prefix.addr', data['expr'][3]) + translation_mask = dict_search('dnat.addr.prefix.len', data['expr'][3]) + + self.assertEqual(f'{translation_address}/{translation_mask}', translation_prefix) + self.assertEqual(iface, 'eth1') + + def test_source_nat66_required_translation_prefix(self): + # T2813: Ensure translation address is specified + rule = '5' + source_prefix = 'fc00::/64' + translation_prefix = 'fc00:2::/64' + self.session.set(src_path + ['rule', rule, 'source', 'prefix', source_prefix]) + + # check validate() - outbound-interface must be defined + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.set(src_path + ['rule', rule, 'outbound-interface', 'eth0']) + + # check validate() - translation address not specified + with self.assertRaises(ConfigSessionError): + self.session.commit() + + self.session.set(src_path + ['rule', rule, 'translation', 'prefix', translation_prefix]) + self.session.commit() + + def test_nat66_no_rules(self): + # T3206: deleting all rules but keep the direction 'destination' or + # 'source' resulteds in KeyError: 'rule'. + # + # Test that both 'nat destination' and 'nat source' nodes can exist + # without any rule + self.session.set(src_path) + self.session.set(dst_path) + self.session.commit() + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index ccb9268fe..66ca3cdd7 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -26,59 +26,72 @@ PROCESS_NAME = 'bgpd' ASN = '64512' base_path = ['protocols', 'bgp', ASN] +route_map_in = 'foo-map-in' +route_map_out = 'foo-map-out' +prefix_list_in = 'pfx-foo-in' +prefix_list_out = 'pfx-foo-out' + neighbor_config = { '192.0.2.1' : { - 'cap_dynamic' : '', - 'cap_ext_next': '', - 'remote_as' : '100', - 'adv_interv' : '400', - 'passive' : '', - 'password' : 'VyOS-Secure123', - 'shutdown' : '', - 'cap_over' : '', - 'ttl_security': '5', - 'local_as' : '300', + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '100', + 'adv_interv' : '400', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', + 'ttl_security' : '5', + 'local_as' : '300', + 'route_map_in' : route_map_in, + 'route_map_out': route_map_out, }, '192.0.2.2' : { - 'remote_as' : '200', - 'shutdown' : '', - 'no_cap_nego' : '', - 'port' : '667', - 'cap_strict' : '', + 'remote_as' : '200', + 'shutdown' : '', + 'no_cap_nego' : '', + 'port' : '667', + 'cap_strict' : '', + 'pfx_list_in' : prefix_list_in, + 'pfx_list_out' : prefix_list_out, }, '192.0.2.3' : { - 'description' : 'foo bar baz', - 'remote_as' : '200', - 'passive' : '', - 'multi_hop' : '5', - 'update_src' : 'lo', + 'description' : 'foo bar baz', + 'remote_as' : '200', + 'passive' : '', + 'multi_hop' : '5', + 'update_src' : 'lo', }, } peer_group_config = { 'foo' : { - 'remote_as' : '100', - 'passive' : '', - 'password' : 'VyOS-Secure123', - 'shutdown' : '', - 'cap_over' : '', + 'remote_as' : '100', + 'passive' : '', + 'password' : 'VyOS-Secure123', + 'shutdown' : '', + 'cap_over' : '', # XXX: not available in current Perl backend # 'ttl_security': '5', }, 'bar' : { - 'description' : 'foo peer bar group', - 'remote_as' : '200', - 'shutdown' : '', - 'no_cap_nego' : '', - 'local_as' : '300', + 'description' : 'foo peer bar group', + 'remote_as' : '200', + 'shutdown' : '', + 'no_cap_nego' : '', + 'local_as' : '300', + 'pfx_list_in' : prefix_list_in, + 'pfx_list_out' : prefix_list_out, }, 'baz' : { - 'cap_dynamic' : '', - 'cap_ext_next': '', - 'remote_as' : '200', - 'passive' : '', - 'multi_hop' : '5', - 'update_src' : 'lo', + 'cap_dynamic' : '', + 'cap_ext_next' : '', + 'remote_as' : '200', + 'passive' : '', + 'multi_hop' : '5', + 'update_src' : 'lo', + 'route_map_in' : route_map_in, + 'route_map_out': route_map_out, }, } @@ -89,7 +102,19 @@ class TestProtocolsBGP(unittest.TestCase): def setUp(self): self.session = ConfigSession(os.getpid()) + self.session.set(['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25']) + self.session.set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25']) + def tearDown(self): + self.session.delete(['policy', 'route-map', route_map_in]) + self.session.delete(['policy', 'route-map', route_map_out]) + self.session.delete(['policy', 'prefix-list', prefix_list_in]) + self.session.delete(['policy', 'prefix-list', prefix_list_out]) + self.session.delete(base_path) self.session.commit() del self.session @@ -122,6 +147,15 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' neighbor {peer} ttl-security hops {peer_config["ttl_security"]}', frrconfig) if 'update_src' in peer_config: self.assertIn(f' neighbor {peer} update-source {peer_config["update_src"]}', frrconfig) + if 'route_map_in' in peer_config: + self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_in"]} in', frrconfig) + if 'route_map_out' in peer_config: + self.assertIn(f' neighbor {peer} route-map {peer_config["route_map_out"]} out', frrconfig) + if 'pfx_list_in' in peer_config: + self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_in"]} in', frrconfig) + if 'pfx_list_out' in peer_config: + self.assertIn(f' neighbor {peer} prefix-list {peer_config["pfx_list_out"]} out', frrconfig) + def test_bgp_01_simple(self): router_id = '127.0.0.1' @@ -184,6 +218,14 @@ class TestProtocolsBGP(unittest.TestCase): self.session.set(base_path + ['neighbor', neighbor, 'ttl-security', 'hops', config["ttl_security"]]) if 'update_src' in config: self.session.set(base_path + ['neighbor', neighbor, 'update-source', config["update_src"]]) + if 'route_map_in' in config: + self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'route-map', 'import', config["route_map_in"]]) + if 'route_map_out' in config: + self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'route-map', 'export', config["route_map_out"]]) + if 'pfx_list_in' in config: + self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]]) + if 'pfx_list_out' in config: + self.session.set(base_path + ['neighbor', neighbor, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]]) # commit changes self.session.commit() @@ -231,6 +273,14 @@ class TestProtocolsBGP(unittest.TestCase): self.session.set(base_path + ['peer-group', peer_group, 'ttl-security', 'hops', config["ttl_security"]]) if 'update_src' in config: self.session.set(base_path + ['peer-group', peer_group, 'update-source', config["update_src"]]) + if 'route_map_in' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'import', config["route_map_in"]]) + if 'route_map_out' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'route-map', 'export', config["route_map_out"]]) + if 'pfx_list_in' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'import', config["pfx_list_in"]]) + if 'pfx_list_out' in config: + self.session.set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'prefix-list', 'export', config["pfx_list_out"]]) # commit changes self.session.commit() @@ -336,5 +386,35 @@ class TestProtocolsBGP(unittest.TestCase): self.assertIn(f' aggregate-address {network} summary-only', frrconfig) + def test_bgp_06_listen_range(self): + # Implemented via T1875 + limit = '64' + listen_ranges = ['192.0.2.0/25', '192.0.2.128/25'] + peer_group = 'listenfoobar' + self.session.set(base_path + ['listen', 'limit', limit]) + for prefix in listen_ranges: + self.session.set(base_path + ['listen', 'range', prefix]) + # check validate() - peer-group must be defined for range/prefix + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.set(base_path + ['listen', 'range', prefix, 'peer-group', peer_group]) + + # check validate() - peer-group does yet not exist! + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.set(base_path + ['peer-group', peer_group, 'remote-as', ASN]) + + # commit changes + self.session.commit() + + # Verify FRR bgpd configuration + frrconfig = getFRRBGPconfig() + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f' neighbor {peer_group} peer-group', frrconfig) + self.assertIn(f' neighbor {peer_group} remote-as {ASN}', frrconfig) + self.assertIn(f' bgp listen limit {limit}', frrconfig) + for prefix in listen_ranges: + self.assertIn(f' bgp listen range {prefix} peer-group {peer_group}', frrconfig) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py new file mode 100755 index 000000000..d47838d8c --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from vyos.configsession import ConfigSession +from vyos.ifconfig import Section +from vyos.util import cmd +from vyos.util import process_named_running + +PROCESS_NAME = 'ospfd' +base_path = ['protocols', 'ospf'] + +route_map = 'foo-bar-baz10' + +def getFRROSPFconfig(): + return cmd('vtysh -c "show run" | sed -n "/router ospf/,/^!/p"') + +class TestProtocolsOSPF(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + self.session.set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + self.session.set(['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit']) + + def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + self.session.delete(['policy', 'route-map', route_map]) + self.session.delete(base_path) + self.session.commit() + del self.session + + def test_ospf_01_defaults(self): + # commit changes + self.session.set(base_path) + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + + + def test_ospf_02_simple(self): + router_id = '127.0.0.1' + abr_type = 'ibm' + bandwidth = '1000' + metric = '123' + + self.session.set(base_path + ['auto-cost', 'reference-bandwidth', bandwidth]) + self.session.set(base_path + ['parameters', 'router-id', router_id]) + self.session.set(base_path + ['parameters', 'abr-type', abr_type]) + self.session.set(base_path + ['log-adjacency-changes', 'detail']) + self.session.set(base_path + ['default-metric', metric]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' auto-cost reference-bandwidth {bandwidth}', frrconfig) + self.assertIn(f' ospf router-id {router_id}', frrconfig) + self.assertIn(f' ospf abr-type {abr_type}', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + self.assertIn(f' default-metric {metric}', frrconfig) + + + def test_ospf_03_access_list(self): + acl = '100' + seq = '10' + protocols = ['bgp', 'connected', 'kernel', 'rip', 'static'] + + self.session.set(['policy', 'access-list', acl, 'rule', seq, 'action', 'permit']) + self.session.set(['policy', 'access-list', acl, 'rule', seq, 'source', 'any']) + self.session.set(['policy', 'access-list', acl, 'rule', seq, 'destination', 'any']) + for ptotocol in protocols: + self.session.set(base_path + ['access-list', acl, 'export', ptotocol]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + for ptotocol in protocols: + self.assertIn(f' distribute-list {acl} out {ptotocol}', frrconfig) # defaults + self.session.delete(['policy', 'access-list', acl]) + + + def test_ospf_04_default_originate(self): + seq = '100' + metric = '50' + metric_type = '1' + + self.session.set(base_path + ['default-information', 'originate', 'metric', metric]) + self.session.set(base_path + ['default-information', 'originate', 'metric-type', metric_type]) + self.session.set(base_path + ['default-information', 'originate', 'route-map', route_map]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults + self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + + # Now set 'always' + self.session.set(base_path + ['default-information', 'originate', 'always']) + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + + + def test_ospf_05_options(self): + global_distance = '128' + intra_area = '100' + inter_area = '110' + external = '120' + on_startup = '30' + on_shutdown = '60' + refresh = '50' + + self.session.set(base_path + ['distance', 'global', global_distance]) + self.session.set(base_path + ['distance', 'ospf', 'external', external]) + self.session.set(base_path + ['distance', 'ospf', 'intra-area', intra_area]) + + self.session.set(base_path + ['max-metric', 'router-lsa', 'on-startup', on_startup]) + self.session.set(base_path + ['max-metric', 'router-lsa', 'on-shutdown', on_shutdown]) + + self.session.set(base_path + ['mpls-te', 'enable']) + self.session.set(base_path + ['refresh', 'timers', refresh]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' mpls-te on', frrconfig) + self.assertIn(f' mpls-te router-address 0.0.0.0', frrconfig) # default + self.assertIn(f' distance {global_distance}', frrconfig) + self.assertIn(f' distance ospf intra-area {intra_area} external {external}', frrconfig) + self.assertIn(f' max-metric router-lsa on-startup {on_startup}', frrconfig) + self.assertIn(f' max-metric router-lsa on-shutdown {on_shutdown}', frrconfig) + self.assertIn(f' refresh timer {refresh}', frrconfig) + + + # enable inter-area + self.session.set(base_path + ['distance', 'ospf', 'inter-area', inter_area]) + self.session.commit() + + frrconfig = getFRROSPFconfig() + self.assertIn(f' distance ospf intra-area {intra_area} inter-area {inter_area} external {external}', frrconfig) + + + def test_ospf_06_neighbor(self): + priority = '10' + poll_interval = '20' + neighbors = ['1.1.1.1', '2.2.2.2', '3.3.3.3'] + for neighbor in neighbors: + self.session.set(base_path + ['neighbor', neighbor, 'priority', priority]) + self.session.set(base_path + ['neighbor', neighbor, 'poll-interval', poll_interval]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + for neighbor in neighbors: + self.assertIn(f' neighbor {neighbor} priority {priority} poll-interval {poll_interval}', frrconfig) # default + + + def test_ospf_07_passive_interface(self): + self.session.set(base_path + ['passive-interface', 'default']) + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + self.session.set(base_path + ['passive-interface-exclude', interface]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) # default + for interface in interfaces: + self.assertIn(f' no passive-interface {interface}', frrconfig) # default + + + def test_ospf_08_redistribute(self): + metric = '15' + metric_type = '1' + redistribute = ['bgp', 'connected', 'kernel', 'rip', 'static'] + + for protocol in redistribute: + self.session.set(base_path + ['redistribute', protocol, 'metric', metric]) + self.session.set(base_path + ['redistribute', protocol, 'route-map', route_map]) + if protocol not in ['kernel', 'static']: + self.session.set(base_path + ['redistribute', protocol, 'metric-type', metric_type]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + for protocol in redistribute: + if protocol in ['kernel', 'static']: + self.assertIn(f' redistribute {protocol} metric {metric} route-map {route_map}', frrconfig) + else: + self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) + + + def test_ospf_09_area(self): + area = '0' + networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] + for network in networks: + self.session.set(base_path + ['area', area, 'network', network]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + for network in networks: + self.assertIn(f' network {network} area {area}', frrconfig) + + + def test_ospf_10_virtual_link(self): + area = '10' + shortcut = 'enable' + virtual_link = '192.0.2.1' + hello = '6' + retransmit = '5' + transmit = '5' + dead = '40' + + self.session.set(base_path + ['area', area, 'shortcut', shortcut]) + self.session.set(base_path + ['area', area, 'virtual-link', virtual_link, 'hello-interval', hello]) + self.session.set(base_path + ['area', area, 'virtual-link', virtual_link, 'retransmit-interval', retransmit]) + self.session.set(base_path + ['area', area, 'virtual-link', virtual_link, 'transmit-delay', transmit]) + self.session.set(base_path + ['area', area, 'virtual-link', virtual_link, 'dead-interval', dead]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' area {area} shortcut {shortcut}', frrconfig) + self.assertIn(f' area {area} virtual-link {virtual_link} hello-interval {hello} retransmit-interval {retransmit} transmit-delay {transmit} dead-interval {dead}', frrconfig) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py new file mode 100755 index 000000000..297d5d996 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_ospfv3.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from vyos.configsession import ConfigSession +from vyos.ifconfig import Section +from vyos.util import cmd +from vyos.util import process_named_running + +PROCESS_NAME = 'ospf6d' +base_path = ['protocols', 'ospfv3'] + + +def getFRROSPFconfig(): + return cmd('vtysh -c "show run" | sed -n "/router ospf6/,/^!/p"') + +class TestProtocolsOSPFv3(unittest.TestCase): + def setUp(self): + self.session = ConfigSession(os.getpid()) + + def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + + self.session.delete(base_path) + self.session.commit() + del self.session + + + def test_ospfv3_01_basic(self): + area = '0' + seq = '10' + prefix = '2001:db8::/32' + acl_name = 'foo-acl-100' + router_id = '192.0.2.1' + + self.session.set(['policy', 'access-list6', acl_name, 'rule', seq, 'action', 'permit']) + self.session.set(['policy', 'access-list6', acl_name, 'rule', seq, 'source', 'any']) + + self.session.set(base_path + ['parameters', 'router-id', router_id]) + self.session.set(base_path + ['area', area, 'range', prefix, 'advertise']) + self.session.set(base_path + ['area', area, 'export-list', acl_name]) + self.session.set(base_path + ['area', area, 'import-list', acl_name]) + + interfaces = Section.interfaces('ethernet') + for interface in interfaces: + self.session.set(base_path + ['area', area, 'interface', interface]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf6', frrconfig) + self.assertIn(f' area {area} range {prefix}', frrconfig) + self.assertIn(f' ospf6 router-id {router_id}', frrconfig) + self.assertIn(f' area {area} import-list {acl_name}', frrconfig) + self.assertIn(f' area {area} export-list {acl_name}', frrconfig) + + for interface in interfaces: + self.assertIn(f' interface {interface} area {area}', frrconfig) + + self.session.delete(['policy', 'access-list6', acl_name]) + + + def test_ospfv3_02_distance(self): + dist_global = '200' + dist_external = '110' + dist_inter_area = '120' + dist_intra_area = '130' + + self.session.set(base_path + ['distance', 'global', dist_global]) + self.session.set(base_path + ['distance', 'ospfv3', 'external', dist_external]) + self.session.set(base_path + ['distance', 'ospfv3', 'inter-area', dist_inter_area]) + self.session.set(base_path + ['distance', 'ospfv3', 'intra-area', dist_intra_area]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf6', frrconfig) + self.assertIn(f' distance {dist_global}', frrconfig) + self.assertIn(f' distance ospf6 intra-area {dist_intra_area} inter-area {dist_inter_area} external {dist_external}', frrconfig) + + + def test_ospfv3_03_redistribute(self): + route_map = 'foo-bar' + route_map_seq = '10' + redistribute = ['bgp', 'connected', 'kernel', 'ripng', 'static'] + + self.session.set(['policy', 'route-map', route_map, 'rule', route_map_seq, 'action', 'permit']) + + for protocol in redistribute: + self.session.set(base_path + ['redistribute', protocol, 'route-map', route_map]) + + # commit changes + self.session.commit() + + # Verify FRR ospfd configuration + frrconfig = getFRROSPFconfig() + self.assertIn(f'router ospf6', frrconfig) + for protocol in redistribute: + self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py index 1e099b0a5..68081e56f 100755 --- a/smoketest/scripts/cli/test_service_ssh.py +++ b/smoketest/scripts/cli/test_service_ssh.py @@ -29,6 +29,10 @@ SSHD_CONF = '/run/sshd/sshd_config' base_path = ['service', 'ssh'] vrf = 'mgmt' +key_rsa = '/etc/ssh/ssh_host_rsa_key' +key_dsa = '/etc/ssh/ssh_host_dsa_key' +key_ed25519 = '/etc/ssh/ssh_host_ed25519_key' + def get_config_value(key): tmp = read_file(SSHD_CONF) tmp = re.findall(f'\n?{key}\s+(.*)', tmp) @@ -47,6 +51,10 @@ class TestServiceSSH(unittest.TestCase): self.session.commit() del self.session + self.assertTrue(os.path.isfile(key_rsa)) + self.assertTrue(os.path.isfile(key_dsa)) + self.assertTrue(os.path.isfile(key_ed25519)) + def test_ssh_default(self): # Check if SSH service runs with default settings - used for checking # behavior of <defaultValue> in XML definition diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py index 1118143e4..7b3afa058 100755 --- a/src/conf_mode/interfaces-l2tpv3.py +++ b/src/conf_mode/interfaces-l2tpv3.py @@ -47,12 +47,6 @@ def get_config(config=None): base = ['interfaces', 'l2tpv3'] l2tpv3 = get_interface_dict(conf, base) - # L2TPv3 is "special" the default MTU is 1488 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - l2tpv3['mtu'] = '1488' - # To delete an l2tpv3 interface we need the current tunnel and session-id if 'deleted' in l2tpv3: tmp = leaf_node_changed(conf, ['tunnel-id']) diff --git a/src/conf_mode/interfaces-macsec.py b/src/conf_mode/interfaces-macsec.py index 2c8367ff3..bfebed7e4 100755 --- a/src/conf_mode/interfaces-macsec.py +++ b/src/conf_mode/interfaces-macsec.py @@ -49,14 +49,6 @@ def get_config(config=None): base = ['interfaces', 'macsec'] macsec = get_interface_dict(conf, base) - # MACsec is "special" the default MTU is 1460 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - # base MTU for MACsec is 1468 bytes, but we leave room for 802.1ad and - # 802.1q VLAN tags, thus the limit is 1460 bytes. - macsec['mtu'] = '1460' - # Check if interface has been removed if 'deleted' in macsec: source_interface = conf.return_effective_value(['source-interface']) diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index e4a6a5ec1..ee6f05fcd 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -17,6 +17,7 @@ import os import re +from glob import glob from sys import exit from ipaddress import IPv4Address from ipaddress import IPv4Network @@ -488,14 +489,9 @@ def apply(openvpn): # Do some cleanup when OpenVPN is disabled/deleted if 'deleted' in openvpn or 'disable' in openvpn: - # cleanup old configuration files - cleanup = [] - cleanup.append(cfg_file.format(**openvpn)) - cleanup.append(openvpn['auth_user_pass_file']) - - for file in cleanup: - if os.path.isfile(file): - os.unlink(file) + for cleanup_file in glob(f'/run/openvpn/{interface}.*'): + if os.path.isfile(cleanup_file): + os.unlink(cleanup_file) if interface in interfaces(): VTunIf(interface).remove() diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index c31e49574..f49792a7a 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -43,12 +43,6 @@ def get_config(config=None): base = ['interfaces', 'pppoe'] pppoe = get_interface_dict(conf, base) - # PPPoE is "special" the default MTU is 1492 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - pppoe['mtu'] = '1492' - return pppoe def verify(pppoe): diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py index d2fcf3121..f03bc9d5d 100755 --- a/src/conf_mode/interfaces-tunnel.py +++ b/src/conf_mode/interfaces-tunnel.py @@ -57,12 +57,6 @@ def get_config(config=None): base = ['interfaces', 'tunnel'] tunnel = get_interface_dict(conf, base) - # Wireguard is "special" the default MTU is 1420 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - tunnel['mtu'] = '1476' - tmp = leaf_node_changed(conf, ['encapsulation']) if tmp: tunnel.update({'encapsulation_changed': {}}) diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py index 04e258fcf..9a6d72772 100755 --- a/src/conf_mode/interfaces-vxlan.py +++ b/src/conf_mode/interfaces-vxlan.py @@ -42,12 +42,6 @@ def get_config(config=None): base = ['interfaces', 'vxlan'] vxlan = get_interface_dict(conf, base) - # VXLAN is "special" the default MTU is 1492 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - vxlan['mtu'] = '1450' - return vxlan def verify(vxlan): diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py index 3e6320f02..024ab8f59 100755 --- a/src/conf_mode/interfaces-wireguard.py +++ b/src/conf_mode/interfaces-wireguard.py @@ -46,12 +46,6 @@ def get_config(config=None): base = ['interfaces', 'wireguard'] wireguard = get_interface_dict(conf, base) - # Wireguard is "special" the default MTU is 1420 - update accordingly - # as the config_level is already st in get_interface_dict() - we can use [] - tmp = conf.get_config_dict([], key_mangling=('-', '_'), get_first_key=True) - if 'mtu' not in tmp: - wireguard['mtu'] = '1420' - # Mangle private key - it has a default so its always valid wireguard['private_key'] = '/config/auth/wireguard/{private_key}/private.key'.format(**wireguard) diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index 2d98cb11b..dae958774 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -26,6 +26,7 @@ from netifaces import interfaces from vyos.config import Config from vyos.configdict import dict_merge from vyos.template import render +from vyos.template import is_ip_network from vyos.util import cmd from vyos.util import check_kmod from vyos.util import dict_search @@ -68,9 +69,9 @@ def verify_rule(config, err_msg): 'ports can only be specified when protocol is '\ 'either tcp, udp or tcp_udp!') - if '/' in (dict_search('translation.address', config) or []): + if is_ip_network(dict_search('translation.address', config)): raise ConfigError(f'{err_msg}\n' \ - 'Cannot use ports with an IPv4net type translation address as it\n' \ + 'Cannot use ports with an IPv4 network as translation address as it\n' \ 'statically maps a whole network of addresses onto another\n' \ 'network of addresses') @@ -147,7 +148,7 @@ def verify(nat): addr = dict_search('translation.address', config) if addr != None: - if addr != 'masquerade': + if addr != 'masquerade' and not is_ip_network(addr): for ip in addr.split('-'): if not is_addr_assigned(ip): print(f'WARNING: IP address {ip} does not exist on the system!') diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py new file mode 100755 index 000000000..dc3cd550c --- /dev/null +++ b/src/conf_mode/nat66.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import jmespath +import json +import os + +from sys import exit +from netifaces import interfaces + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.template import render +from vyos.util import cmd +from vyos.util import check_kmod +from vyos.util import dict_search +from vyos.template import is_ipv6 +from vyos.xml import defaults +from vyos import ConfigError +from vyos import airbag +airbag.enable() + +k_mod = ['nft_nat', 'nft_chain_nat'] + +iptables_nat_config = '/tmp/vyos-nat66-rules.nft' +ndppd_config = '/run/ndppd/ndppd.conf' + +def get_handler(json, chain, target): + """ Get nftable rule handler number of given chain/target combination. + Handler is required when adding NAT66/Conntrack helper targets """ + for x in json: + if x['chain'] != chain: + continue + if x['target'] != target: + continue + return x['handle'] + + return None + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + + base = ['nat66'] + nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # T2665: we must add the tagNode defaults individually until this is + # moved to the base class + for direction in ['source', 'destination']: + if direction in nat: + default_values = defaults(base + [direction, 'rule']) + if 'rule' in nat[direction]: + for rule in nat[direction]['rule']: + nat[direction]['rule'][rule] = dict_merge(default_values, + nat[direction]['rule'][rule]) + + # read in current nftable (once) for further processing + tmp = cmd('nft -j list table ip6 raw') + nftable_json = json.loads(tmp) + + # condense the full JSON table into a list with only relevand informations + pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}' + condensed_json = jmespath.search(pattern, nftable_json) + + if not conf.exists(base): + nat['helper_functions'] = 'remove' + nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK') + nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT','NAT_CONNTRACK') + nat['deleted'] = '' + return nat + + # check if NAT66 connection tracking helpers need to be set up - this has to + # be done only once + if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'): + nat['helper_functions'] = 'add' + + # Retrieve current table handler positions + nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_PREROUTING_HOOK') + nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT','VYATTA_CT_OUTPUT_HOOK') + else: + nat['helper_functions'] = 'has' + + return nat + +def verify(nat): + if not nat or 'deleted' in nat: + # no need to verify the CLI as NAT66 is going to be deactivated + return None + + if 'helper_functions' in nat and nat['helper_functions'] != 'has': + if not (nat['pre_ct_conntrack'] or nat['out_ct_conntrack']): + raise Exception('could not determine nftable ruleset handlers') + + if dict_search('source.rule', nat): + for rule, config in dict_search('source.rule', nat).items(): + err_msg = f'Source NAT66 configuration error in rule {rule}:' + if 'outbound_interface' not in config: + raise ConfigError(f'{err_msg}\n' \ + 'outbound-interface not specified') + else: + if config['outbound_interface'] not in interfaces(): + print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') + + prefix = dict_search('translation.prefix', config) + if prefix != None: + if not is_ipv6(prefix): + raise ConfigError(f'Warning: IPv6 prefix {prefix} is not a valid address prefix') + + prefix = dict_search('source.prefix', config) + if prefix != None: + if not is_ipv6(prefix): + raise ConfigError(f'{err_msg} source-prefix not specified') + + if dict_search('destination.rule', nat): + for rule, config in dict_search('destination.rule', nat).items(): + err_msg = f'Destination NAT66 configuration error in rule {rule}:' + + if 'inbound_interface' not in config: + raise ConfigError(f'{err_msg}\n' \ + 'inbound-interface not specified') + else: + if config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces(): + print(f'WARNING: rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system') + + return None + +def generate(nat): + render(iptables_nat_config, 'firewall/nftables-nat66.tmpl', nat, permission=0o755) + render(ndppd_config, 'proxy-ndp/ndppd.conf.tmpl', nat, permission=0o755) + return None + +def apply(nat): + if not nat: + return None + cmd(f'{iptables_nat_config}') + if 'deleted' in nat or not dict_search('source.rule', nat): + cmd('systemctl stop ndppd') + if os.path.isfile(ndppd_config): + os.unlink(ndppd_config) + else: + cmd('systemctl restart ndppd') + if os.path.isfile(iptables_nat_config): + os.unlink(iptables_nat_config) + + return None + +if __name__ == '__main__': + try: + check_kmod(k_mod) + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index de0148b2f..41d89e03b 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -14,6 +14,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import os + from sys import exit from vyos.config import Config @@ -28,12 +30,25 @@ from vyos import airbag airbag.enable() config_file = r'/tmp/bgp.frr' - -def get_config(): - conf = Config() +frr_daemon = 'bgpd' + +DEBUG = os.path.exists('/tmp/bgp.debug') +if DEBUG: + import logging + lg = logging.getLogger("vyos.frr") + lg.setLevel(logging.DEBUG) + ch = logging.StreamHandler() + lg.addHandler(ch) + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() base = ['protocols', 'bgp'] bgp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + # Bail out early if configuration tree does not exist if not conf.exists(base): return bgp @@ -78,8 +93,13 @@ def verify(bgp): if neighbor == 'neighbor': # remote-as must be either set explicitly for the neighbor # or for the entire peer-group - if 'remote_as' not in peer_config: - if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][peer_config['peer_group']]: + if 'interface' in peer_config: + if 'remote_as' not in peer_config['interface']: + if 'peer_group' not in peer_config['interface'] or 'remote_as' not in asn_config['peer_group'][ peer_config['interface']['peer_group'] ]: + raise ConfigError('Remote AS must be set for neighbor or peer-group!') + + elif 'remote_as' not in peer_config: + if 'peer_group' not in peer_config or 'remote_as' not in asn_config['peer_group'][ peer_config['peer_group'] ]: raise ConfigError('Remote AS must be set for neighbor or peer-group!') for afi in ['ipv4_unicast', 'ipv6_unicast']: @@ -94,7 +114,7 @@ def verify(bgp): if tmp not in afi_config['prefix_list']: # bail out early continue - # get_config_dict() mangles all '-' characters to '_' this is legitim, thus all our + # get_config_dict() mangles all '-' characters to '_' this is legitimate, thus all our # compares will run on '_' as also '_' is a valid name for a prefix-list prefix_list = afi_config['prefix_list'][tmp].replace('-', '_') if afi == 'ipv4_unicast': @@ -113,6 +133,16 @@ def verify(bgp): if dict_search(f'policy.route_map.{route_map}', asn_config) == None: raise ConfigError(f'route-map "{route_map}" used for "{tmp}" does not exist!') + # Throw an error if a peer group is not configured for allow range + for prefix in dict_search('listen.range', asn_config) or []: + # we can not use dict_search() here as prefix contains dots ... + if 'peer_group' not in asn_config['listen']['range'][prefix]: + raise ConfigError(f'Listen range for prefix "{prefix}" has no peer group configured.') + else: + peer_group = asn_config['listen']['range'][prefix]['peer_group'] + # the peer group must also exist + if not dict_search(f'peer_group.{peer_group}', asn_config): + raise ConfigError(f'Peer-group "{peer_group}" for listen range "{prefix}" does not exist!') return None @@ -135,25 +165,32 @@ def generate(bgp): def apply(bgp): # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration(daemon='bgpd') + frr_cfg.load_configuration(frr_daemon) frr_cfg.modify_section(f'router bgp \S+', '') frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', bgp['new_frr_config']) - frr_cfg.commit_configuration(daemon='bgpd') + + # Debugging + if DEBUG: + from pprint import pprint + print('') + print('--------- DEBUGGING ----------') + pprint(dir(frr_cfg)) + print('Existing config:\n') + for line in frr_cfg.original_config: + print(line) + print(f'Replacement config:\n') + print(f'{bgp["new_frr_config"]}') + print(f'Modified config:\n') + print(f'{frr_cfg}') + + frr_cfg.commit_configuration(frr_daemon) # If FRR config is blank, rerun the blank commit x times due to frr-reload # behavior/bug not properly clearing out on one commit. if bgp['new_frr_config'] == '': for a in range(5): - frr_cfg.commit_configuration(daemon='bgpd') + frr_cfg.commit_configuration(frr_daemon) - # Debugging - ''' - print('') - print('--------- DEBUGGING ----------') - print(f'Existing config:\n{frr_cfg["original_config"]}\n\n') - print(f'Replacement config:\n{bgp["new_frr_config"]}\n\n') - print(f'Modified config:\n{frr_cfg["modified_config"]}\n\n') - ''' return None diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py new file mode 100755 index 000000000..7c0ffbd27 --- /dev/null +++ b/src/conf_mode/protocols_ospf.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_route_maps +from vyos.template import render +from vyos.template import render_to_string +from vyos.util import call +from vyos.util import dict_search +from vyos.xml import defaults +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +config_file = r'/tmp/ospf.frr' +frr_daemon = 'ospfd' + +DEBUG = os.path.exists('/tmp/ospf.debug') +if DEBUG: + import logging + lg = logging.getLogger("vyos.frr") + lg.setLevel(logging.DEBUG) + ch = logging.StreamHandler() + lg.addHandler(ch) + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'ospf'] + ospf = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return ospf + + # We have gathered the dict representation of the CLI, but there are default + # options which we need to update into the dictionary retrived. + default_values = defaults(base) + + # We have to cleanup the default dict, as default values could enable features + # which are not explicitly enabled on the CLI. Example: default-information + # originate comes with a default metric-type of 2, which will enable the + # entire default-information originate tree, even when not set via CLI so we + # need to check this first and probably drop that key. + if dict_search('default_information.originate', ospf) is None: + del default_values['default_information'] + if dict_search('area.area_type.nssa', ospf) is None: + del default_values['area']['area_type']['nssa'] + if 'mpls_te' not in ospf: + del default_values['mpls_te'] + for protocol in ['bgp', 'connected', 'kernel', 'rip', 'static']: + if dict_search(f'redistribute.{protocol}', ospf) is None: + del default_values['redistribute'][protocol] + # XXX: T2665: we currently have no nice way for defaults under tag nodes, + # clean them out and add them manually :( + del default_values['neighbor'] + del default_values['area']['virtual_link'] + + # merge in remaining default values + ospf = dict_merge(default_values, ospf) + + if 'neighbor' in ospf: + default_values = defaults(base + ['neighbor']) + for neighbor in ospf['neighbor']: + ospf['neighbor'][neighbor] = dict_merge(default_values, ospf['neighbor'][neighbor]) + + if 'area' in ospf: + default_values = defaults(base + ['area', 'virtual-link']) + for area, area_config in ospf['area'].items(): + if 'virtual_link' in area_config: + print(default_values) + for virtual_link in area_config['virtual_link']: + ospf['area'][area]['virtual_link'][virtual_link] = dict_merge( + default_values, ospf['area'][area]['virtual_link'][virtual_link]) + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + ospf = dict_merge(tmp, ospf) + + return ospf + +def verify(ospf): + if not ospf: + return None + + verify_route_maps(ospf) + return None + +def generate(ospf): + if not ospf: + ospf['new_frr_config'] = '' + return None + + # render(config) not needed, its only for debug + render(config_file, 'frr/ospf.frr.tmpl', ospf) + ospf['new_frr_config'] = render_to_string('frr/ospf.frr.tmpl', ospf) + + return None + +def apply(ospf): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section('router ospf', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospf['new_frr_config']) + + # Debugging + if DEBUG: + from pprint import pprint + print('') + print('--------- DEBUGGING ----------') + pprint(dir(frr_cfg)) + print('Existing config:\n') + for line in frr_cfg.original_config: + print(line) + print(f'Replacement config:\n') + print(f'{ospf["new_frr_config"]}') + print(f'Modified config:\n') + print(f'{frr_cfg}') + + frr_cfg.commit_configuration(frr_daemon) + + # If FRR config is blank, rerun the blank commit x times due to frr-reload + # behavior/bug not properly clearing out on one commit. + if ospf['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py new file mode 100755 index 000000000..2fb600056 --- /dev/null +++ b/src/conf_mode/protocols_ospfv3.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_route_maps +from vyos.template import render +from vyos.template import render_to_string +from vyos.util import call +from vyos.util import dict_search +from vyos.xml import defaults +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +config_file = r'/tmp/ospfv3.frr' +frr_daemon = 'ospf6d' + +DEBUG = os.path.exists('/tmp/ospfv3.debug') +if DEBUG: + import logging + lg = logging.getLogger("vyos.frr") + lg.setLevel(logging.DEBUG) + ch = logging.StreamHandler() + lg.addHandler(ch) + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['protocols', 'ospfv3'] + ospfv3 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + + # Bail out early if configuration tree does not exist + if not conf.exists(base): + return ospfv3 + + # We also need some additional information from the config, prefix-lists + # and route-maps for instance. They will be used in verify() + base = ['policy'] + tmp = conf.get_config_dict(base, key_mangling=('-', '_')) + # Merge policy dict into OSPF dict + ospfv3 = dict_merge(tmp, ospfv3) + + return ospfv3 + +def verify(ospfv3): + if not ospfv3: + return None + + verify_route_maps(ospfv3) + return None + +def generate(ospfv3): + if not ospfv3: + ospfv3['new_frr_config'] = '' + return None + + # render(config) not needed, its only for debug + render(config_file, 'frr/ospfv3.frr.tmpl', ospfv3) + ospfv3['new_frr_config'] = render_to_string('frr/ospfv3.frr.tmpl', ospfv3) + + return None + +def apply(ospfv3): + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + frr_cfg.load_configuration(frr_daemon) + frr_cfg.modify_section('router ospf6', '') + frr_cfg.add_before(r'(ip prefix-list .*|route-map .*|line vty)', ospfv3['new_frr_config']) + + # Debugging + if DEBUG: + from pprint import pprint + print('') + print('--------- DEBUGGING ----------') + pprint(dir(frr_cfg)) + print('Existing config:\n') + for line in frr_cfg.original_config: + print(line) + print(f'Replacement config:\n') + print(f'{ospfv3["new_frr_config"]}') + print(f'Modified config:\n') + print(f'{frr_cfg}') + + frr_cfg.commit_configuration(frr_daemon) + + # If FRR config is blank, re-run the blank commit x times due to frr-reload + # behavior/bug not properly clearing out on one commit. + if ospfv3['new_frr_config'] == '': + for a in range(5): + frr_cfg.commit_configuration(frr_daemon) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py index 8eeb0a7c1..67724b043 100755 --- a/src/conf_mode/ssh.py +++ b/src/conf_mode/ssh.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2021 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,6 +17,8 @@ import os from sys import exit +from syslog import syslog +from syslog import LOG_INFO from vyos.config import Config from vyos.configdict import dict_merge @@ -31,6 +33,10 @@ airbag.enable() config_file = r'/run/sshd/sshd_config' systemd_override = r'/etc/systemd/system/ssh.service.d/override.conf' +key_rsa = '/etc/ssh/ssh_host_rsa_key' +key_dsa = '/etc/ssh/ssh_host_dsa_key' +key_ed25519 = '/etc/ssh/ssh_host_ed25519_key' + def get_config(config=None): if config: conf = config @@ -66,6 +72,18 @@ def generate(ssh): return None + # This usually happens only once on a fresh system, SSH keys need to be + # freshly generted, one per every system! + if not os.path.isfile(key_rsa): + syslog(LOG_INFO, 'SSH RSA host key not found, generating new key!') + call(f'ssh-keygen -q -N "" -t rsa -f {key_rsa}') + if not os.path.isfile(key_dsa): + syslog(LOG_INFO, 'SSH DSA host key not found, generating new key!') + call(f'ssh-keygen -q -N "" -t dsa -f {key_dsa}') + if not os.path.isfile(key_ed25519): + syslog(LOG_INFO, 'SSH ed25519 host key not found, generating new key!') + call(f'ssh-keygen -q -N "" -t ed25519 -f {key_ed25519}') + render(config_file, 'ssh/sshd_config.tmpl', ssh) render(systemd_override, 'ssh/override.conf.tmpl', ssh) # Reload systemd manager configuration diff --git a/src/conf_mode/system-option.py b/src/conf_mode/system-option.py index 910c14474..454611c55 100755 --- a/src/conf_mode/system-option.py +++ b/src/conf_mode/system-option.py @@ -87,10 +87,10 @@ def apply(options): # Ctrl-Alt-Delete action if os.path.exists(systemd_action_file): os.unlink(systemd_action_file) - if 'ctrl_alt_del' in options: - if options['ctrl_alt_del'] == 'reboot': + if 'ctrl_alt_delete' in options: + if options['ctrl_alt_delete'] == 'reboot': os.symlink('/lib/systemd/system/reboot.target', systemd_action_file) - elif options['ctrl_alt_del'] == 'poweroff': + elif options['ctrl_alt_delete'] == 'poweroff': os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file) # Configure HTTP client @@ -104,11 +104,11 @@ def apply(options): os.unlink(ssh_config) # Reboot system on kernel panic + timeout = '0' + if 'reboot_on_panic' in options: + timeout = '60' with open('/proc/sys/kernel/panic', 'w') as f: - if 'reboot_on_panic' in options: - f.write('60') - else: - f.write('0') + f.write(timeout) # tuned - performance tuning if 'performance' in options: diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf new file mode 100644 index 000000000..8265e12dc --- /dev/null +++ b/src/etc/sysctl.d/30-vyos-router.conf @@ -0,0 +1,98 @@ +# +# VyOS specific sysctl settings, see sysctl.conf (5) for information. +# + +# Panic on OOPS +kernel.panic_on_oops=1 + +# Timeout before rebooting on panic +kernel.panic=60 + +# Send all core files to /var/core/core.program.pid.time +kernel.core_pattern=/var/core/core-%e-%p-%t + +# ARP configuration +# arp_filter - allow multiple network interfaces on same subnet +# arp_announce - avoid local addresses no on target's subnet +# arp_ignore - reply only if target IP is local_address on the interface + +# arp_filter defaults to 1 so set all to 0 so vrrp interfaces can override it. +net.ipv4.conf.all.arp_filter=0 + +# https://phabricator.vyos.net/T300 +net.ipv4.conf.all.arp_ignore=0 + +net.ipv4.conf.all.arp_announce=2 + +# Enable packet forwarding for IPv4 +net.ipv4.ip_forward=1 + +# if a primary address is removed from an interface promote the +# secondary address if available +net.ipv4.conf.all.promote_secondaries=1 + +# Ignore ICMP broadcasts sent to broadcast/multicast +net.ipv4.icmp_echo_ignore_broadcasts=1 + +# Ignore bogus ICMP errors +net.ipv4.icmp_ignore_bogus_error_responses=1 + +# Send ICMP responses with primary address of exiting interface +net.ipv4.icmp_errors_use_inbound_ifaddr=1 + +# Log packets with impossible addresses to kernel log +net.ipv4.conf.all.log_martians=1 + +# Do not ignore all ICMP ECHO requests by default +net.ipv4.icmp_echo_ignore_all=0 + +# Disable source validation by default +net.ipv4.conf.all.rp_filter=0 +net.ipv4.conf.default.rp_filter=0 + +# Enable tcp syn-cookies by default +net.ipv4.tcp_syncookies=1 + +# Disable accept_redirects by default for any interface +net.ipv4.conf.all.accept_redirects=0 +net.ipv4.conf.default.accept_redirects=0 +net.ipv6.conf.all.accept_redirects=0 +net.ipv6.conf.default.accept_redirects=0 + +# Disable accept_source_route by default +net.ipv4.conf.all.accept_source_route=0 +net.ipv4.conf.default.accept_source_route=0 +net.ipv6.conf.all.accept_source_route=0 +net.ipv6.conf.default.accept_source_route=0 + +# Enable send_redirects by default +net.ipv4.conf.all.send_redirects=1 +net.ipv4.conf.default.send_redirects=1 + +# Increase size of buffer for netlink +net.core.rmem_max=2097152 + +# Enable packet forwarding for IPv6 +net.ipv6.conf.all.forwarding=1 + +# Increase route table limit +net.ipv6.route.max_size = 262144 + +# Do not forget IPv6 addresses when a link goes down +net.ipv6.conf.default.keep_addr_on_down=1 +net.ipv6.conf.all.keep_addr_on_down=1 + +# Default value of 20 seems to interfere with larger OSPF and VRRP setups +net.ipv4.igmp_max_memberships = 512 + +# Enable conntrack helper by default +net.netfilter.nf_conntrack_helper=1 + +# Increase default garbage collection thresholds +net.ipv4.neigh.default.gc_thresh1 = 1024 +net.ipv4.neigh.default.gc_thresh2 = 4096 +net.ipv4.neigh.default.gc_thresh3 = 8192 +# +net.ipv6.neigh.default.gc_thresh1 = 1024 +net.ipv6.neigh.default.gc_thresh2 = 4096 +net.ipv6.neigh.default.gc_thresh3 = 8192 diff --git a/src/etc/udev/rules.d/42-qemu-usb.rules b/src/etc/udev/rules.d/42-qemu-usb.rules new file mode 100644 index 000000000..a79543df7 --- /dev/null +++ b/src/etc/udev/rules.d/42-qemu-usb.rules @@ -0,0 +1,14 @@ +# +# Enable autosuspend for qemu emulated usb hid devices. +# +# Note that there are buggy qemu versions which advertise remote +# wakeup support but don't actually implement it correctly. This +# is the reason why we need a match for the serial number here. +# The serial number "42" is used to tag the implementations where +# remote wakeup is working. +# +# Gerd Hoffmann <kraxel@xxxxxxxxxx> + +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Keyboard", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" diff --git a/src/etc/udev/rules.d/63-hyperv-vf-net.rules b/src/etc/udev/rules.d/63-hyperv-vf-net.rules new file mode 100644 index 000000000..b4dcb5a39 --- /dev/null +++ b/src/etc/udev/rules.d/63-hyperv-vf-net.rules @@ -0,0 +1,5 @@ +ATTR{[dmi/id]sys_vendor}!="Microsoft Corporation", GOTO="end_hyperv_nic" + +ACTION=="add", SUBSYSTEM=="net", DRIVERS=="hv_pci", NAME="vf_%k" + +LABEL="end_hyperv_nic" diff --git a/src/etc/udev/rules.d/64-vyos-vmware-net.rules b/src/etc/udev/rules.d/64-vyos-vmware-net.rules new file mode 100644 index 000000000..66a4a069b --- /dev/null +++ b/src/etc/udev/rules.d/64-vyos-vmware-net.rules @@ -0,0 +1,14 @@ +ATTR{[dmi/id]sys_vendor}!="VMware, Inc.", GOTO="end_vmware_nic" + +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet0", ENV{VYOS_IFNAME}="eth0" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet1", ENV{VYOS_IFNAME}="eth1" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet2", ENV{VYOS_IFNAME}="eth2" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet3", ENV{VYOS_IFNAME}="eth3" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet4", ENV{VYOS_IFNAME}="eth4" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet5", ENV{VYOS_IFNAME}="eth5" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet6", ENV{VYOS_IFNAME}="eth6" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet7", ENV{VYOS_IFNAME}="eth7" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet8", ENV{VYOS_IFNAME}="eth8" +ACTION=="add", SUBSYSTEM=="net", ATTRS{label}=="Ethernet9", ENV{VYOS_IFNAME}="eth9" + +LABEL="end_vmware_nic" diff --git a/src/etc/udev/rules.d/65-vyatta-net.rules b/src/etc/udev/rules.d/65-vyatta-net.rules new file mode 100644 index 000000000..2b48c1213 --- /dev/null +++ b/src/etc/udev/rules.d/65-vyatta-net.rules @@ -0,0 +1,26 @@ +# These rules use vyatta_net_name to persistently name network interfaces +# per "hwid" association in the Vyatta configuration file. + +ACTION!="add", GOTO="vyatta_net_end" +SUBSYSTEM!="net", GOTO="vyatta_net_end" + +# ignore the interface if a name has already been set +NAME=="?*", GOTO="vyatta_net_end" + +# Do name change for ethernet and wireless devices only +KERNEL!="eth*|wlan*", GOTO="vyatta_net_end" + +# ignore "secondary" monitor interfaces of mac80211 drivers +KERNEL=="wlan*", ATTRS{type}=="803", GOTO="vyatta_net_end" + +# If using VyOS predefined names +ENV{VYOS_IFNAME}!="eth*", GOTO="end_vyos_predef_names" + +DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address} $env{VYOS_IFNAME}", NAME="%c", GOTO="vyatta_net_end" + +LABEL="end_vyos_predef_names" + +# ignore interfaces without a driver link like bridges and VLANs +DRIVERS=="?*", PROGRAM="vyatta_net_name %k $attr{address}", NAME="%c" + +LABEL="vyatta_net_end" diff --git a/src/migration-scripts/nat66/0-to-1 b/src/migration-scripts/nat66/0-to-1 new file mode 100755 index 000000000..74d64c07b --- /dev/null +++ b/src/migration-scripts/nat66/0-to-1 @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from sys import argv,exit +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +def merge_npt(config,base,rule): + merge_base = ['nat66','source','rule',rule] + # Configure migration functions + if config.exists(base + ['description']): + tmp = config.return_value(base + ['description']) + config.set(merge_base + ['description'],value=tmp) + + if config.exists(base + ['disable']): + tmp = config.return_value(base + ['disable']) + config.set(merge_base + ['disable'],value=tmp) + + if config.exists(base + ['outbound-interface']): + tmp = config.return_value(base + ['outbound-interface']) + config.set(merge_base + ['outbound-interface'],value=tmp) + + if config.exists(base + ['source','prefix']): + tmp = config.return_value(base + ['source','prefix']) + config.set(merge_base + ['source','prefix'],value=tmp) + + if config.exists(base + ['translation','prefix']): + tmp = config.return_value(base + ['translation','prefix']) + config.set(merge_base + ['translation','prefix'],value=tmp) + +if not config.exists(['nat', 'nptv6']): + # Nothing to do + exit(0) + +for rule in config.list_nodes(['nat', 'nptv6', 'rule']): + base = ['nat', 'nptv6', 'rule', rule] + # Merge 'nat nptv6' to 'nat66 source' + merge_npt(config,base,rule) + +# Delete the original NPT configuration +config.delete(['nat','nptv6']); + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) diff --git a/src/systemd/dropbear@.service b/src/systemd/dropbear@.service index 606a7ea6d..a3fde5708 100644 --- a/src/systemd/dropbear@.service +++ b/src/systemd/dropbear@.service @@ -4,6 +4,7 @@ Requires=dropbearkey.service Wants=conserver-server.service ConditionPathExists=/run/conserver/conserver.cf After=dropbearkey.service vyos-router.service conserver-server.service +StartLimitIntervalSec=0 [Service] Type=forking @@ -11,4 +12,6 @@ ExecStartPre=/usr/bin/bash -c '/usr/bin/systemctl set-environment PORT=$(cli-she ExecStart=-/usr/sbin/dropbear -w -j -k -r /etc/dropbear/dropbear_rsa_host_key -c "/usr/bin/console %I" -P /run/conserver/dropbear.%I.pid -p ${PORT} PIDFile=/run/conserver/dropbear.%I.pid KillMode=process -Restart=on-failure +Restart=always +RestartSec=10 +RuntimeDirectoryPreserve=yes diff --git a/src/systemd/ndppd.service b/src/systemd/ndppd.service new file mode 100644 index 000000000..5790d37f1 --- /dev/null +++ b/src/systemd/ndppd.service @@ -0,0 +1,15 @@ +[Unit] +Description=NDP Proxy Daemon +After=vyos-router.service +ConditionPathExists=/run/ndppd/ndppd.conf +StartLimitIntervalSec=0 + +[Service] +Type=forking +ExecStart=/usr/sbin/ndppd -d -p /run/ndppd/ndppd.pid -c /run/ndppd/ndppd.conf +PIDFile=/run/ndppd/ndppd.pid +Restart=on-failure +RestartSec=20 + +[Install] +WantedBy=multi-user.target diff --git a/src/tests/test_template.py b/src/tests/test_template.py index 544755692..7800d007f 100644 --- a/src/tests/test_template.py +++ b/src/tests/test_template.py @@ -93,3 +93,22 @@ class TestVyOSTemplate(TestCase): self.assertEqual(vyos.template.dec_ip('2001:db8::b/64', '10'), '2001:db8::1') self.assertEqual(vyos.template.dec_ip('2001:db8::f', '5'), '2001:db8::a') + def test_is_network(self): + self.assertFalse(vyos.template.is_ip_network('192.0.2.0')) + self.assertFalse(vyos.template.is_ip_network('192.0.2.1/24')) + self.assertTrue(vyos.template.is_ip_network('192.0.2.0/24')) + + self.assertFalse(vyos.template.is_ip_network('2001:db8::')) + self.assertFalse(vyos.template.is_ip_network('2001:db8::ffff')) + self.assertTrue(vyos.template.is_ip_network('2001:db8::/48')) + self.assertTrue(vyos.template.is_ip_network('2001:db8:1000::/64')) + + def test_is_network(self): + self.assertTrue(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/8')) + self.assertTrue(vyos.template.compare_netmask('10.0.0.0/16', '20.0.0.0/16')) + self.assertFalse(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/16')) + self.assertFalse(vyos.template.compare_netmask('10.0.0.1', '20.0.0.0/16')) + + self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/48')) + self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/64', '2001:db8:2000::/64')) + self.assertFalse(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/64')) |