diff options
205 files changed, 2466 insertions, 1761 deletions
| @@ -61,12 +61,7 @@ op_mode_definitions: $(op_xml_obj)  	# XXX: test if there are empty node.def files - this is not allowed as these  	# could mask help strings or mandatory priority statements -	#find $(OP_TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1' - -.PHONY: component_versions -.ONESHELL: -component_versions: interface_definitions -	$(CURDIR)/scripts/build-component-versions $(BUILD_DIR)/interface-definitions $(DATA_DIR) +	find $(OP_TMPL_DIR) -name node.def -type f -empty -exec false {} + || sh -c 'echo "There are empty node.def files! Check your interface definitions." && exit 1'  .PHONY: vyshim  vyshim: @@ -77,7 +72,7 @@ vyxdp:  	$(MAKE) -C $(XDP_DIR)  .PHONY: all -all: clean interface_definitions op_mode_definitions component_versions vyshim +all: clean interface_definitions op_mode_definitions vyshim  .PHONY: clean  clean: @@ -89,7 +84,7 @@ clean:  .PHONY: test  test: -	set -e; python3 -m compileall -q -x '/vmware-tools/scripts/' . +	set -e; python3 -m compileall -q -x '/vmware-tools/scripts/, /ppp/' .  	PYTHONPATH=python/ python3 -m "nose" --with-xunit src --with-coverage --cover-erase --cover-xml --cover-package src/conf_mode,src/op_mode,src/completion,src/helpers,src/validators,src/tests --verbose  .PHONY: sonar diff --git a/data/configd-include.json b/data/configd-include.json index 3b4e2925b..6893aaa86 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -6,7 +6,6 @@  "dhcpv6_relay.py",  "dns_forwarding.py",  "dynamic_dns.py", -"firewall_options.py",  "host_name.py",  "https.py",  "igmp_proxy.py", @@ -69,5 +68,6 @@  "vpn_pptp.py",  "vpn_sstp.py",  "vrf.py", +"vrf_vni.py",  "vrrp.py"  ] diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.tmpl index 070a966b7..44c96b935 100644 --- a/data/templates/accel-ppp/l2tp.config.tmpl +++ b/data/templates/accel-ppp/l2tp.config.tmpl @@ -150,3 +150,4 @@ vendor={{ radius_shaper_vendor }}  [cli]  tcp=127.0.0.1:2004  sessions-columns=ifname,username,calling-sid,ip,{{ ip6_column | join(',') }}{{ ',' if ip6_column }}rate-limit,type,comp,state,rx-bytes,tx-bytes,uptime + diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl index aa297876b..96815836b 100644 --- a/data/templates/frr/bgpd.frr.tmpl +++ b/data/templates/frr/bgpd.frr.tmpl @@ -257,6 +257,9 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none   address-family ipv6 flowspec  {%     elif afi == 'l2vpn_evpn' %}   address-family l2vpn evpn +{%     if afi_config.rd is defined and afi_config.rd is not none %} +  rd {{ afi_config.rd }} +{%     endif %}  {%     endif %}  {%     if afi_config.aggregate_address is defined and afi_config.aggregate_address is not none %}  {%       for ip in afi_config.aggregate_address %} @@ -294,23 +297,39 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none  {%     if afi_config.advertise is defined and afi_config.advertise is not none %}  {%       for adv_afi, adv_afi_config in afi_config.advertise.items() %}  {%         if adv_afi_config.unicast is defined and adv_afi_config.unicast is not none %} - advertise {{ adv_afi }} unicast {{ 'route-map ' ~ adv_afi_config.unicast.route_map if adv_afi_config.unicast.route_map is defined }} +  advertise {{ adv_afi }} unicast {{ 'route-map ' ~ adv_afi_config.unicast.route_map if adv_afi_config.unicast.route_map is defined }}  {%         endif %}  {%       endfor %}  {%     endif %}  {%     if afi_config.distance is defined and afi_config.distance is not none %}  {%       if afi_config.distance is defined and afi_config.distance.external is defined and afi_config.distance.internal is defined and afi_config.distance.local is defined %} - distance bgp {{ afi_config.distance.external }} {{ afi_config.distance.internal }} {{ afi_config.distance.local }} +  distance bgp {{ afi_config.distance.external }} {{ afi_config.distance.internal }} {{ afi_config.distance.local }}  {%       endif %}  {%       if afi_config.distance.prefix is defined and afi_config.distance.prefix is not none %}  {%         for prefix in afi_config.distance.prefix %} - distance {{ afi_config.distance.prefix[prefix].distance }} {{ prefix }} +  distance {{ afi_config.distance.prefix[prefix].distance }} {{ prefix }} +{%         endfor %} +{%       endif %} +{%     endif %} +{%     if afi_config.export is defined and afi_config.export.vpn is defined %} +  export vpn +{%     endif %} +{%     if afi_config.import is defined and afi_config.import is not none %} +{%       if afi_config.import.vpn is defined %} +  import vpn +{%       endif %} +{%       if afi_config.import.vrf is defined and afi_config.import.vrf is not none %} +{%         for vrf in afi_config.import.vrf %} +  import vrf {{ vrf }}  {%         endfor %}  {%       endif %}  {%     endif %} +{%     if afi_config.label is defined and afi_config.label.vpn is defined and afi_config.label.vpn.export is defined and afi_config.label.vpn.export is not none %} +  label vpn export {{ afi_config.label.vpn.export }} +{%     endif %}  {%     if afi_config.local_install is defined and afi_config.local_install is not none %}  {%       for interface in afi_config.local_install.interface %} - local-install {{ interface }} +  local-install {{ interface }}  {%       endfor %}  {%     endif %}  {%     if afi_config.advertise_all_vni is defined %} @@ -326,26 +345,47 @@ router bgp {{ local_as }} {{ 'vrf ' ~ vrf if vrf is defined and vrf is not none    advertise-svi-ip  {%     endif %}  {%     if afi_config.rt_auto_derive is defined %} - autort rfc8365-compatible +  autort rfc8365-compatible  {%     endif %}  {%     if afi_config.flooding is defined and afi_config.flooding.disable is defined %} - flooding disable +  flooding disable  {%     endif %}  {%     if afi_config.flooding is defined and afi_config.flooding.head_end_replication is defined %} - flooding head-end-replication +  flooding head-end-replication  {%     endif %} -{%     if afi_config.rd is defined and afi_config.rd is not none %} - rd {{ afi_config.rd }} +{%     if afi_config.rd is defined and afi_config.rd.vpn is defined and afi_config.rd.vpn.export is defined %} +  rd vpn export {{ afi_config.rd.vpn.export }}  {%     endif %}  {%     if afi_config.route_target is defined and afi_config.route_target is not none %} +{%       if afi_config.route_target.vpn is defined and afi_config.route_target.vpn is not none %} +{%         if afi_config.route_target.vpn.both is defined and afi_config.route_target.vpn.both is not none %} +  route-target vpn both {{ afi_config.route_target.vpn.both }} +{%         else %} +{%           if afi_config.route_target.vpn.export is defined and afi_config.route_target.vpn.export is not none %} +  route-target vpn export {{ afi_config.route_target.vpn.export }} +{%           endif %} +{%           if afi_config.route_target.vpn.import is defined and afi_config.route_target.vpn.import is not none %} +  route-target vpn import {{ afi_config.route_target.vpn.import }} +{%           endif %} +{%         endif %} +{%       endif %}  {%       if afi_config.route_target.both is defined and afi_config.route_target.both is not none %} - route-target both {{ afi_config.route_target.both }} +  route-target both {{ afi_config.route_target.both }} +{%       else %} +{%         if afi_config.route_target.export is defined and afi_config.route_target.export is not none %} +  route-target export {{ afi_config.route_target.export }} +{%         endif %} +{%         if afi_config.route_target.import is defined and afi_config.route_target.import is not none %} +  route-target import {{ afi_config.route_target.import }} +{%         endif %}  {%       endif %} -{%       if afi_config.route_target.export is defined and afi_config.route_target.export is not none %} - route-target export {{ afi_config.route_target.export }} +{%     endif %} +{%     if afi_config.route_map is defined and afi_config.route_map.vpn is defined and afi_config.route_map.vpn is not none %} +{%       if afi_config.route_map.vpn.export is defined and afi_config.route_map.vpn.export is not none %} +  route-map vpn export {{ afi_config.route_map.vpn.export }}  {%       endif %} -{%       if afi_config.route_target.import is defined and afi_config.route_target.import is not none %} - route-target import {{ afi_config.route_target.import }} +{%       if afi_config.route_map.vpn.import is defined and afi_config.route_map.vpn.import is not none %} +  route-map vpn import {{ afi_config.route_map.vpn.import }}  {%       endif %}  {%     endif %}  {%     if afi_config.vni is defined and afi_config.vni is not none %} diff --git a/data/templates/frr/ospfv3.frr.tmpl b/data/templates/frr/ospf6d.frr.tmpl index 0026c0d2c..0026c0d2c 100644 --- a/data/templates/frr/ospfv3.frr.tmpl +++ b/data/templates/frr/ospf6d.frr.tmpl diff --git a/data/templates/frr/ospf.frr.tmpl b/data/templates/frr/ospfd.frr.tmpl index 36aa699a9..be39519c3 100644 --- a/data/templates/frr/ospf.frr.tmpl +++ b/data/templates/frr/ospfd.frr.tmpl @@ -14,6 +14,12 @@ interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}  {%         endif %}  {%       endif %}  {%     endif %} +{%     if iface_config.area is defined and iface_config.area is not none %} + ip ospf area {{ iface_config.area }} +{%     endif %} +{%     if iface_config.bandwidth is defined and iface_config.bandwidth is not none %} + bandwidth {{ iface_config.bandwidth }} +{%     endif %}  {%     if iface_config.cost is defined and iface_config.cost is not none %}   ip ospf cost {{ iface_config.cost }}  {%     endif %} @@ -43,9 +49,6 @@ interface {{ iface }} {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}  {%     if iface_config.network is defined and iface_config.network is not none %}   ip ospf network {{ iface_config.network }}  {%     endif %} -{%     if iface_config.bandwidth is defined and iface_config.bandwidth is not none %} - bandwidth {{ iface_config.bandwidth }} -{%     endif %}  !  {%   endfor %}  {% endif %} @@ -155,15 +158,19 @@ router ospf {{ 'vrf ' + vrf if vrf is defined and vrf is not none }}   ospf router-id {{ parameters.router_id }}  {%   endif %}  {% endif %} -{% for interface in passive_interface if passive_interface is defined %} +{% if passive_interface is defined and passive_interface is not none %} +{%   for interface in passive_interface %}   passive-interface {{ interface }} -{% endfor %} -{% for interface in passive_interface_exclude if passive_interface_exclude is defined %} -{%   if interface.startswith('vlink') %} +{%   endfor %} +{% endif %} +{% if passive_interface_exclude is defined and passive_interface_exclude is not none %} +{%   for interface in passive_interface_exclude if passive_interface_exclude is defined %} +{%     if interface.startswith('vlink') %}  {%     set interface = interface.upper() %} -{%   endif %} +{%     endif %}   no passive-interface {{ interface }} -{% endfor %} +{%   endfor %} +{% endif %}  {% 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 }} diff --git a/data/templates/frr/policy.frr.tmpl b/data/templates/frr/policy.frr.tmpl index 89bd558dc..51adc1902 100644 --- a/data/templates/frr/policy.frr.tmpl +++ b/data/templates/frr/policy.frr.tmpl @@ -283,6 +283,9 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }}  {%           if rule_config.set.large_community is defined and rule_config.set.large_community is not none %}   set large-community {{ rule_config.set.large_community }}  {%           endif %} +{%           if rule_config.set.large_comm_list_delete is defined and rule_config.set.large_comm_list_delete is not none %} + set large-comm-list {{ rule_config.set.large_comm_list_delete }} delete +{%           endif %}  {%           if rule_config.set.local_preference is defined and rule_config.set.local_preference is not none %}   set local-preference {{ rule_config.set.local_preference }}  {%           endif %} diff --git a/data/templates/frr/static_routes_macro.j2 b/data/templates/frr/static_routes_macro.j2 index f10b58047..3b432b49b 100644 --- a/data/templates/frr/static_routes_macro.j2 +++ b/data/templates/frr/static_routes_macro.j2 @@ -5,7 +5,7 @@  {%   if prefix_config.dhcp_interface is defined and prefix_config.dhcp_interface is not none %}  {%     set next_hop = prefix_config.dhcp_interface | get_dhcp_router %}  {%     if next_hop is defined and next_hop is not none %} -{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} +{{ ip_ipv6 }} route {{ prefix }} {{ next_hop }} {{ prefix_config.dhcp_interface }}  {%     endif %}  {%   endif %}  {%   if prefix_config.interface is defined and prefix_config.interface is not none %} diff --git a/data/templates/frr/vrf-vni.frr.tmpl b/data/templates/frr/vrf-vni.frr.tmpl new file mode 100644 index 000000000..51d4ede1b --- /dev/null +++ b/data/templates/frr/vrf-vni.frr.tmpl @@ -0,0 +1,7 @@ +{% if vrf is defined and vrf is not none %} +vrf {{ vrf }} +{%   if vni is defined and vni is not none %} + vni {{ vni }} +{%   endif %} + exit-vrf +{% endif %} diff --git a/data/templates/frr/vrf.frr.tmpl b/data/templates/frr/vrf.frr.tmpl deleted file mode 100644 index 299c9719e..000000000 --- a/data/templates/frr/vrf.frr.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -{% if name is defined and name is not none %} -{%   for vrf, vrf_config in name.items() %} -vrf {{ vrf }} -{%     if vrf_config.vni is defined and vrf_config.vni is not none %} - vni {{ vrf_config.vni }} -{%     endif %} - exit-vrf -{%   endfor %} -{% endif %} diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl index b40ddcc74..2f8aa06c2 100644 --- a/data/templates/https/nginx.default.tmpl +++ b/data/templates/https/nginx.default.tmpl @@ -17,7 +17,7 @@ server {          listen {{ server.port }} ssl;          listen [::]:{{ server.port }} ssl;  {% else %} -        listen {{ server.address }}:{{ server.port }} ssl; +        listen {{ server.address | bracketize_ipv6 }}:{{ server.port }} ssl;  {% endif %}  {% for name in server.name %} diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl index dd29ea7d4..019f9e0d7 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.tmpl @@ -17,10 +17,10 @@  {%   if ike.key_exchange is defined and ike.key_exchange == "ikev1" and ike.mode is defined and ike.mode == "aggressive" %}          aggressive = yes  {%   endif %} +        rekey_time = {{ ike.lifetime }}s          mobike = {{ "yes" if ike.mobike is not defined or ike.mobike == "enable" else "no" }}  {%   if peer[0:1] == '@' %}          keyingtries = 0 -        rekey_time = 0          reauth_time = 0  {%   elif peer_conf.connection_type is not defined or peer_conf.connection_type == 'initiate' %}          keyingtries = 0 @@ -57,6 +57,7 @@  {%     set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is defined else esp_group[ peer_conf.default_esp_group ] %}              peer_{{ name }}_vti {                  esp_proposals = {{ vti_esp | get_esp_ike_cipher | join(',') }} +                life_time = {{ vti_esp.lifetime }}s                  local_ts = 0.0.0.0/0,::/0                  remote_ts = 0.0.0.0/0,::/0                  updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }} {{ peer_conf.dhcp_interface if peer_conf.dhcp_interface is defined else 'no' }}" @@ -87,6 +88,7 @@  {%       set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %}              peer_{{ name }}_tunnel_{{ tunnel_id }} {                  esp_proposals = {{ tunnel_esp | get_esp_ike_cipher | join(',') }} +                life_time = {{ tunnel_esp.lifetime }}s  {%       if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %}  {%         if tunnel_conf.local is defined and tunnel_conf.local.prefix is defined %}  {%           set local_prefix = tunnel_conf.local.prefix if 'any' not in tunnel_conf.local.prefix else ['0.0.0.0/0', '::/0'] %} diff --git a/data/templates/ipsec/swanctl/profile.tmpl b/data/templates/ipsec/swanctl/profile.tmpl index 0a7268405..66bcaa776 100644 --- a/data/templates/ipsec/swanctl/profile.tmpl +++ b/data/templates/ipsec/swanctl/profile.tmpl @@ -7,7 +7,7 @@      dmvpn-{{ name }}-{{ interface }} {          proposals = {{ ike_group[profile_conf.ike_group] | get_esp_ike_cipher | join(',') }}          version = {{ ike.key_exchange[4:] if ike is defined and ike.key_exchange is defined else "0" }} -        rekey_time = {{ ike.lifetime }}s +        life_time = {{ ike.lifetime }}s          keyingtries = 0  {%       if profile_conf.authentication is defined and profile_conf.authentication.mode is defined and profile_conf.authentication.mode == 'pre-shared-secret' %}          local { diff --git a/data/templates/ipsec/swanctl/remote_access.tmpl b/data/templates/ipsec/swanctl/remote_access.tmpl index 456842488..f906836c6 100644 --- a/data/templates/ipsec/swanctl/remote_access.tmpl +++ b/data/templates/ipsec/swanctl/remote_access.tmpl @@ -10,7 +10,9 @@          send_certreq = no          rekey_time = {{ ike.lifetime }}s          keyingtries = 0 +{%   if rw_conf.unique is defined and rw_conf.unique is not none %}                  unique = {{ rw_conf.unique }} +{%   endif %}  {%   if rw_conf.pool is defined and rw_conf.pool is not none %}          pools = {{ rw_conf.pool | join(',') }}  {%   endif %} diff --git a/data/templates/proxy-ndp/ndppd.conf.tmpl b/data/templates/ndppd/ndppd.conf.tmpl index ccd1d37ad..502dab5b8 100644 --- a/data/templates/proxy-ndp/ndppd.conf.tmpl +++ b/data/templates/ndppd/ndppd.conf.tmpl @@ -6,10 +6,10 @@  #   interface.  #  #   For some services, such as nat66, because it runs -#    stateless, it needs to rely on NDP Proxy to respond  +#    stateless, it needs to rely on NDP Proxy to respond  #   to NDP requests.  # -#   When using nat66 source rules, NDP Proxy needs  +#   When using nat66 source rules, NDP Proxy needs  #   to be enabled  #  ######################################################## @@ -21,7 +21,7 @@  {%         if config.outbound_interface not in global.ndppd_interfaces %}  {%             set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %}  {%         endif   %} -{%         if config.translation.address is defined and config.translation.address | is_ip_network  %} +{%         if config.translation is defined and config.translation.address is defined and config.translation.address | is_ip_network %}  {%             set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.address}] %}  {%         endif    %}  {%     endif    %} @@ -41,4 +41,4 @@ proxy {{ interface }} {  {%     endif  %}  {%  endfor   %}  } -{% endfor %}  +{% endfor %} diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl index d9f01310e..9b07a9ba2 100644 --- a/data/templates/openvpn/server.conf.tmpl +++ b/data/templates/openvpn/server.conf.tmpl @@ -74,6 +74,16 @@ topology {{ server.topology }}  {%       for subnet in server.subnet %}  {%         if subnet | is_ipv4 %}  server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool +{# First ip address is used as gateway. It's allows to use metrics #} +{%     if server.push_route is defined and server.push_route is not none %} +{%       for route, route_config in server.push_route.items() %} +{%         if route | is_ipv4 %} +push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }} {{ subnet | first_host_address }} {{ route_config.metric if route_config.metric is defined else "0" }}" +{%         elif route | is_ipv6 %} +push "route-ipv6 {{ route }}" +{%         endif %} +{%       endfor %} +{%     endif %}  {# OpenVPN assigns the first IP address to its local interface so the pool used #}  {# in net30 topology - where each client receives a /30 must start from the second subnet #}  {%           if server.topology is defined and server.topology == 'net30' %} @@ -106,15 +116,6 @@ management /run/openvpn/openvpn-mgmt-intf unix  ccd-exclusive  {%     endif %} -{%     if server.push_route is defined and server.push_route is not none %} -{%       for route in server.push_route %} -{%         if route | is_ipv4 %} -push "route {{ route | address_from_cidr }} {{ route | netmask_from_cidr }}" -{%         elif route | is_ipv6 %} -push "route-ipv6 {{ route }}" -{%         endif %} -{%       endfor %} -{%     endif %}  {%     if server.name_server is defined and server.name_server is not none %}  {%       for nameserver in server.name_server %}  {%         if nameserver | is_ipv4 %} diff --git a/data/templates/pppoe/ipv6-up.script.tmpl b/data/templates/pppoe/ipv6-up.script.tmpl index 7e1bc33b4..da73cb4d5 100644 --- a/data/templates/pppoe/ipv6-up.script.tmpl +++ b/data/templates/pppoe/ipv6-up.script.tmpl @@ -7,43 +7,6 @@ if [ "$6" != "{{ ifname }}" ]; then      exit  fi -{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined %} -# add some info to syslog -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "executing $0" -logger -t pppd[$DIALER_PID] "configuring interface {{ ifname }} via {{ source_interface }}" - -# Configure interface-specific Host/Router behaviour. -# Note: It is recommended to have the same setting on all interfaces; mixed -# router/host scenarios are rather uncommon. Possible values are: -# -# 0  Forwarding disabled -# 1  Forwarding enabled -# -echo 1 > /proc/sys/net/ipv6/conf/{{ ifname }}/forwarding - -# Accept Router Advertisements; autoconfigure using them. -# -# It also determines whether or not to transmit Router -# Solicitations. If and only if the functional setting is to -# accept Router Advertisements, Router Solicitations will be -# transmitted. Possible values are: -# -# 0  Do not accept Router Advertisements. -# 1  Accept Router Advertisements if forwarding is disabled. -# 2  Overrule forwarding behaviour. Accept Router Advertisements -#    even if forwarding is enabled. -# -echo 2 > /proc/sys/net/ipv6/conf/{{ ifname }}/accept_ra - -# Autoconfigure addresses using Prefix Information in Router Advertisements. -echo 1 > /proc/sys/net/ipv6/conf/{{ ifname }}/autoconf -{% endif %} - -{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %} -# Start wide dhcpv6 client -systemctl restart dhcp6c@{{ ifname }}.service -{% endif %}  {% if default_route != 'none' %}  # See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl index 0f78f9384..928ed1238 100644 --- a/data/templates/pppoe/peer.tmpl +++ b/data/templates/pppoe/peer.tmpl @@ -1,8 +1,5 @@  ### Autogenerated by interfaces-pppoe.py ### - -{% if description %} -# {{ description }} -{% endif %} +{{ '# ' ~ description if description is defined else '' }}  # Require peer to provide the local IP address if it is not  # specified explicitly in the config file. @@ -30,15 +27,21 @@ connect /bin/true  noauth  # Don't try to proxy ARP for the remote endpoint. User can set proxy -# arp entries up manually if they wish.  More importantly, having +# arp entries up manually if they wish. More importantly, having  # the "proxyarp" parameter set disables the "defaultroute" option.  noproxyarp  # Unlimited connection attempts  maxfail 0 -plugin rp-pppoe.so -{{ source_interface }} +plugin rp-pppoe.so {{ source_interface }} +{% if access_concentrator is defined and access_concentrator is not none %} +rp_pppoe_ac '{{ access_concentrator }}' +{% endif %} +{% if service_name is defined and service_name is not none %} +rp_pppoe_service '{{ service_name }}' +{% endif %} +  persist  ifname {{ ifname }}  ipparam {{ ifname }} @@ -54,14 +57,9 @@ mru {{ mtu }}  {{ "usepeerdns" if no_peer_dns is not defined }}  {% if ipv6 is defined %} -+ipv6 -{%   if ipv6.address is defined and ipv6.address.autoconf is defined %} -ipv6cp-use-ipaddr -{%   endif %} -{% endif %} - -{% if service_name is defined %} -rp_pppoe_service "{{ service_name }}" ++ipv6 {{ 'ipv6cp-use-ipaddr' if ipv6.address is defined and ipv6.address.autoconf is defined }} +{% else %} +noipv6  {% endif %}  {% if connect_on_demand is defined %} @@ -71,8 +69,14 @@ demand  # passed to the ip-up.d/ip-down.s scripts which is required for VRF support.  {%   if 'auto' in default_route %}  defaultroute +{{ 'defaultroute6' if ipv6 is defined }}  {%   elif 'force' in default_route %}  defaultroute  replacedefaultroute +{{ 'defaultroute6' if ipv6 is defined }}  {%   endif %} +{% else %} +nodefaultroute +noreplacedefaultroute +{{ 'nodefaultroute6' if ipv6 is defined }}  {% endif %} diff --git a/debian/control b/debian/control index c5cbeb7d4..f3a26e73e 100644 --- a/debian/control +++ b/debian/control @@ -156,6 +156,7 @@ Depends:    traceroute,    tuned,    udp-broadcast-relay, +  uidmap,    usb-modeswitch,    usbutils,    vyatta-bash, diff --git a/debian/rules b/debian/rules index 70d39c481..c7a7138e1 100755 --- a/debian/rules +++ b/debian/rules @@ -18,6 +18,10 @@ DEB_TARGET_ARCH := $(shell dpkg-architecture -qDEB_TARGET_ARCH)  %:  	dh $@ --with python3, --with quilt +# Skip dh_strip_nondeterminism - this is very time consuming +# and we have no non deterministic output (yet) +override_dh_strip_nondeterminism: +  override_dh_gencontrol:  	dh_gencontrol -- -v$(shell (git describe --tags --long --match 'vyos/*' --dirty 2>/dev/null || echo 0.0-no.git.tag) | sed -E 's%vyos/%%' | sed -E 's%-dirty%+dirty%') diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install index 2ed25755f..d332e0d36 100644 --- a/debian/vyos-1x.install +++ b/debian/vyos-1x.install @@ -3,6 +3,7 @@ etc/dhcp  etc/ipsec.d  etc/netplug  etc/opennhrp +etc/ppp  etc/rsyslog.d  etc/securetty  etc/security @@ -10,6 +11,7 @@ etc/sudoers.d  etc/systemd  etc/sysctl.d  etc/udev +etc/update-motd.d  etc/vyos  lib/  opt/ diff --git a/interface-definitions/bcast-relay.xml.in b/interface-definitions/bcast-relay.xml.in index 1b354d885..c7948ded1 100644 --- a/interface-definitions/bcast-relay.xml.in +++ b/interface-definitions/bcast-relay.xml.in @@ -49,18 +49,7 @@                    <multi/>                  </properties>                </leafNode> -              <leafNode name="port"> -                <properties> -                  <help>Destination or source port to listen and retransmit on [REQUIRED]</help> -                  <valueHelp> -                    <format>u32:1-65535</format> -                    <description>UDP port to listen on</description> -                  </valueHelp> -                  <constraint> -                    <validator name="numeric" argument="--range 1-65535"/> -                  </constraint> -                </properties> -              </leafNode> +              #include <include/port-number.xml.i>              </children>            </tagNode>          </children> diff --git a/interface-definitions/containers.xml.in b/interface-definitions/containers.xml.in index 124b1f65e..d990e41a3 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/containers.xml.in @@ -9,6 +9,10 @@        <tagNode name="name">          <properties>            <help>Container name</help> +          <constraint> +            <regex>^[-a-zA-Z0-9]+$</regex> +          </constraint> +          <constraintErrorMessage>Container name must be alphanumeric and can contain hyphens</constraintErrorMessage>          </properties>          <children>            <leafNode name="allow-host-networks"> @@ -17,14 +21,15 @@                <valueless/>              </properties>            </leafNode> -          <leafNode name="description"> -            <properties> -              <help>Container description</help> -            </properties> -          </leafNode> +          #include <include/generic-description.xml.i> +          #include <include/generic-disable-node.xml.i>            <tagNode name="environment">              <properties>                <help>Add custom environment variables</help> +              <constraint> +                <regex>^[-_a-zA-Z0-9]+$</regex> +              </constraint> +              <constraintErrorMessage>Environment variable name must be alphanumeric and can contain hyphen and underscores</constraintErrorMessage>              </properties>              <children>                <leafNode name="value"> @@ -43,6 +48,24 @@                <help>Image name in the hub-registry</help>              </properties>            </leafNode> +          <leafNode name="memory"> +            <properties> +              <help>Constrain the memory available to a container (default: 512MB)</help> +              <valueHelp> +                <format>u32:0</format> +                <description>Unlimited</description> +              </valueHelp> +              <valueHelp> +                <format>u32:1-16384</format> +                <description>Container memory in megabytes (MB)</description> +              </valueHelp> +              <constraint> +                <validator name="numeric" argument="--range 0-16384"/> +              </constraint> +              <constraintErrorMessage>Container memory must be in range 0 to 16384 MB</constraintErrorMessage> +            </properties> +            <defaultValue>512</defaultValue> +          </leafNode>            <tagNode name="network">              <properties>                <help>Attach user defined network to container</help> @@ -56,7 +79,7 @@                    <help>Set IPv4 static address to container (optional)</help>                    <valueHelp>                      <format>ipv4</format> -                    <description>IPv4 address (x.x.x.1 reserved)</description> +                    <description>IPv4 address</description>                    </valueHelp>                    <constraint>                      <validator name="ipv4-address"/> @@ -115,6 +138,30 @@                </leafNode>              </children>            </tagNode> +          <leafNode name="restart"> +            <properties> +              <help>Mount a volume into the container</help> +              <completionHelp> +                <list>no on-failure always</list> +              </completionHelp> +              <valueHelp> +                <format>no</format> +                <description>Do not restart containers on exit</description> +              </valueHelp> +              <valueHelp> +                <format>on-failure</format> +                <description>Restart containers when they exit with a non-zero exit code, retrying indefinitely (default)</description> +              </valueHelp> +              <valueHelp> +                <format>always</format> +                <description>Restart containers when they exit, regardless of status, retrying indefinitely</description> +              </valueHelp> +              <constraint> +                <regex>^(no|on-failure|always)$</regex> +              </constraint> +            </properties> +            <defaultValue>on-failure</defaultValue> +          </leafNode>            <tagNode name="volume">              <properties>                <help>Mount a volume into the container</help> diff --git a/interface-definitions/firewall-options.xml.in b/interface-definitions/firewall-options.xml.in deleted file mode 100644 index 8d9225a9a..000000000 --- a/interface-definitions/firewall-options.xml.in +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> -  <node name="firewall"> -    <children> -      <node name="options"> -        <properties> -          <help>Firewall options/Packet manipulation</help> -          <priority>990</priority> -        </properties> -        <children> -          <tagNode name="interface" owner="${vyos_conf_scripts_dir}/firewall_options.py"> -            <properties> -              <help>Interface clamping options</help> -              <completionHelp> -                <script>${vyos_completion_dir}/list_interfaces.py</script> -              </completionHelp> -            </properties> -            <children> -              #include <include/generic-disable-node.xml.i> -              <leafNode name="adjust-mss"> -                <properties> -                  <help>Adjust MSS for IPv4 transit packets</help> -                  <valueHelp> -                    <format>500-1460</format> -                    <description>TCP Maximum segment size in bytes</description> -                  </valueHelp> -                  <constraint> -                    <validator name="numeric" argument="--range 500-1460"/> -                  </constraint> -                </properties> -              </leafNode> -              <leafNode name="adjust-mss6"> -                <properties> -                  <help>Adjust MSS for IPv6 transit packets</help> -                  <valueHelp> -                    <format>1280-1492</format> -                    <description>TCP Maximum segment size in bytes</description> -                  </valueHelp> -                  <constraint> -                    <validator name="numeric" argument="--range 1280-1492"/> -                  </constraint> -                </properties> -              </leafNode> -            </children> -          </tagNode> -        </children> -      </node> -    </children> -  </node> -</interfaceDefinition> diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index b65a89b56..b0532e249 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -1,7 +1,6 @@  <?xml version="1.0"?>  <!-- HTTPS configuration -->  <interfaceDefinition> -  <syntaxVersion component='https' version='3'></syntaxVersion>    <node name="service">      <children>        <node name="https" owner="${vyos_conf_scripts_dir}/https.py"> diff --git a/interface-definitions/include/bgp/afi-export-import.xml.i b/interface-definitions/include/bgp/afi-export-import.xml.i new file mode 100644 index 000000000..86817cdb3 --- /dev/null +++ b/interface-definitions/include/bgp/afi-export-import.xml.i @@ -0,0 +1,41 @@ +<!-- include start from bgp/afi-export-import.xml.i --> +<node name="export"> +  <properties> +    <help>Export routes from this address-family</help> +  </properties> +  <children> +    <leafNode name="vpn"> +      <properties> +        <help>to/from default instance VPN RIB</help> +        <valueless/> +      </properties> +    </leafNode> +  </children> +</node> +<node name="import"> +  <properties> +    <help>Import routes to this address-family</help> +  </properties> +  <children> +    <leafNode name="vpn"> +      <properties> +        <help>to/from default instance VPN RIB</help> +        <valueless/> +      </properties> +    </leafNode> +    <leafNode name="vrf"> +      <properties> +        <help>VRF to import from</help> +        <valueHelp> +          <format>txt</format> +          <description>VRF instance name</description> +        </valueHelp> +        <completionHelp> +          <path>vrf name</path> +        </completionHelp> +        <multi/> +      </properties> +    </leafNode> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i index aaa69e6c8..8deb189ab 100644 --- a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i +++ b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i @@ -12,5 +12,47 @@    </properties>  </leafNode>  #include <include/bgp/route-distinguisher.xml.i> -#include <include/bgp/route-target.xml.i> +<node name="route-target"> +  <properties> +    <help>Route Target</help> +  </properties> +  <children> +    <leafNode name="both"> +      <properties> +        <help>Route Target both import and export</help> +        <valueHelp> +          <format>txt</format> +          <description>Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)</description> +        </valueHelp> +        <constraint> +          <validator name="bgp-route-target" argument="--single"/> +        </constraint> +      </properties> +    </leafNode> +    <leafNode name="import"> +      <properties> +        <help>Route Target import</help> +        <valueHelp> +          <format>txt</format> +          <description>Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)</description> +        </valueHelp> +        <constraint> +          <validator name="bgp-route-target" argument="--single"/> +        </constraint> +      </properties> +    </leafNode> +    <leafNode name="export"> +      <properties> +        <help>Route Target export</help> +        <valueHelp> +          <format>txt</format> +          <description>Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)</description> +        </valueHelp> +        <constraint> +          <validator name="bgp-route-target" argument="--single"/> +        </constraint> +      </properties> +    </leafNode> +  </children> +</node>  <!-- include end --> diff --git a/interface-definitions/include/bgp/afi-label.xml.i b/interface-definitions/include/bgp/afi-label.xml.i new file mode 100644 index 000000000..f7a1f609f --- /dev/null +++ b/interface-definitions/include/bgp/afi-label.xml.i @@ -0,0 +1,36 @@ +<!-- include start from bgp/afi-label.xml.i --> +<node name="label"> +  <properties> +    <help>Label value for VRF</help> +  </properties> +  <children> +    <node name="vpn"> +      <properties> +        <help>Between current address-family and VPN</help> +      </properties> +      <children> +        <leafNode name="export"> +          <properties> +            <help>For routes leaked from current address-family to VPN</help> +            <completionHelp> +              <list>auto</list> +            </completionHelp> +            <valueHelp> +              <format>auto</format> +              <description>Automatically assign a label</description> +            </valueHelp> +            <valueHelp> +              <format>u32:0-1048575</format> +              <description>Label Value</description> +            </valueHelp> +            <constraint> +              <validator name="numeric" argument="--range 0-1048575"/> +              <regex>^(auto)$</regex> +            </constraint> +          </properties> +        </leafNode> +      </children> +    </node> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/afi-path-limit.xml.i b/interface-definitions/include/bgp/afi-path-limit.xml.i new file mode 100644 index 000000000..e3d630a57 --- /dev/null +++ b/interface-definitions/include/bgp/afi-path-limit.xml.i @@ -0,0 +1,14 @@ +<!-- include start from bgp/afi-path-limit.xml.i --> +<leafNode name="path-limit"> +  <properties> +    <help>AS-path hopcount limit</help> +    <valueHelp> +      <format>u32:0-255</format> +      <description>AS path hop count limit</description> +    </valueHelp> +    <constraint> +      <validator name="numeric" argument="--range 0-255"/> +    </constraint> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/afi-rd.xml.i b/interface-definitions/include/bgp/afi-rd.xml.i new file mode 100644 index 000000000..c4d29268c --- /dev/null +++ b/interface-definitions/include/bgp/afi-rd.xml.i @@ -0,0 +1,28 @@ +<!-- include start from bgp/afi-rd.xml.i --> +<node name="rd"> +  <properties> +    <help>Specify route distinguisher</help> +  </properties> +  <children> +    <node name="vpn"> +      <properties> +        <help>Between current address-family and VPN</help> +      </properties> +      <children> +        <leafNode name="export"> +          <properties> +            <help>For routes leaked from current address-family to VPN</help> +            <valueHelp> +              <format>ASN:NN_OR_IP-ADDRESS:NN</format> +              <description>Route Distinguisher, (x.x.x.x:yyy|xxxx:yyyy)</description> +            </valueHelp> +            <constraint> +              <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> +            </constraint> +          </properties> +        </leafNode> +      </children> +    </node> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/afi-route-map-export-import.xml.i b/interface-definitions/include/bgp/afi-route-map-export-import.xml.i new file mode 100644 index 000000000..eae10d312 --- /dev/null +++ b/interface-definitions/include/bgp/afi-route-map-export-import.xml.i @@ -0,0 +1,34 @@ +<!-- include start from bgp/afi-route-map.xml.i --> +<leafNode name="export"> +  <properties> +    <help>Route-map to filter outgoing route updates</help> +    <completionHelp> +      <path>policy route-map</path> +    </completionHelp> +    <valueHelp> +      <format>txt</format> +      <description>Route map name</description> +    </valueHelp> +    <constraint> +      <regex>^[-_a-zA-Z0-9.]+$</regex> +    </constraint> +    <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> +  </properties> +</leafNode> +<leafNode name="import"> +  <properties> +    <help>Route-map to filter incoming route updates</help> +    <completionHelp> +      <path>policy route-map</path> +    </completionHelp> +    <valueHelp> +      <format>txt</format> +      <description>Route map name</description> +    </valueHelp> +    <constraint> +      <regex>^[-_a-zA-Z0-9.]+$</regex> +    </constraint> +    <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/bgp/afi-route-map-vpn.xml.i b/interface-definitions/include/bgp/afi-route-map-vpn.xml.i new file mode 100644 index 000000000..e6be113c5 --- /dev/null +++ b/interface-definitions/include/bgp/afi-route-map-vpn.xml.i @@ -0,0 +1,17 @@ +<!-- include start from bgp/afi-route-map-vpn.xml.i --> +<node name="route-map"> +  <properties> +    <help>Route-map to filter route updates to/from this peer</help> +  </properties> +  <children> +    <node name="vpn"> +      <properties> +        <help>Between current address-family and VPN</help> +      </properties> +      <children> +        #include <include/bgp/afi-route-map-export-import.xml.i> +      </children> +    </node> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/afi-route-map.xml.i b/interface-definitions/include/bgp/afi-route-map.xml.i index 24a5ddd12..0b6178176 100644 --- a/interface-definitions/include/bgp/afi-route-map.xml.i +++ b/interface-definitions/include/bgp/afi-route-map.xml.i @@ -4,38 +4,7 @@      <help>Route-map to filter route updates to/from this peer</help>    </properties>    <children> -    <leafNode name="export"> -      <properties> -        <help>Route-map to filter outgoing route updates</help> -        <completionHelp> -          <path>policy route-map</path> -        </completionHelp> -        <valueHelp> -          <format>txt</format> -          <description>Route map name</description> -        </valueHelp> -        <constraint> -          <regex>^[-_a-zA-Z0-9.]+$</regex> -        </constraint> -        <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> -      </properties> -    </leafNode> -    <leafNode name="import"> -      <properties> -        <help>Route-map to filter incoming route updates</help> -        <completionHelp> -          <path>policy route-map</path> -        </completionHelp> -        <valueHelp> -          <format>txt</format> -          <description>Route map name</description> -        </valueHelp> -        <constraint> -          <regex>^[-_a-zA-Z0-9.]+$</regex> -        </constraint> -        <constraintErrorMessage>Name of route-map can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> -      </properties> -    </leafNode> +    #include <include/bgp/afi-route-map-export-import.xml.i>    </children>  </node>  <!-- include end --> diff --git a/interface-definitions/include/bgp/afi-route-target-vpn.xml.i b/interface-definitions/include/bgp/afi-route-target-vpn.xml.i new file mode 100644 index 000000000..1dc184a02 --- /dev/null +++ b/interface-definitions/include/bgp/afi-route-target-vpn.xml.i @@ -0,0 +1,52 @@ +<!-- include start from bgp/route-target-both.xml.i --> +<node name="route-target"> +  <properties> +    <help>Specify route distinguisher</help> +  </properties> +  <children> +    <node name="vpn"> +      <properties> +        <help>Between current address-family and VPN</help> +      </properties> +      <children> +        <leafNode name="both"> +          <properties> +            <help>Route Target both import and export</help> +            <valueHelp> +              <format>txt</format> +              <description>Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)</description> +            </valueHelp> +            <constraint> +              <validator name="bgp-route-target" argument="--multi"/> +            </constraint> +          </properties> +        </leafNode> +        <leafNode name="import"> +          <properties> +            <help>Route Target import</help> +            <valueHelp> +              <format>txt</format> +              <description>Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)</description> +            </valueHelp> +            <constraint> +              <validator name="bgp-route-target" argument="--multi"/> +            </constraint> +          </properties> +        </leafNode> +        <leafNode name="export"> +          <properties> +            <help>Route Target export</help> +            <valueHelp> +              <format>txt</format> +              <description>Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)</description> +            </valueHelp> +            <constraint> +              <validator name="bgp-route-target" argument="--multi"/> +            </constraint> +          </properties> +        </leafNode> +      </children> +    </node> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 552e85aa4..2b22bac7d 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -93,6 +93,9 @@              </tagNode>            </children>          </node> +        #include <include/bgp/afi-export-import.xml.i> +        #include <include/bgp/afi-label.xml.i> +        #include <include/bgp/afi-maximum-paths.xml.i>          <tagNode name="network">            <properties>              <help>BGP network</help> @@ -114,7 +117,9 @@              #include <include/route-map.xml.i>            </children>          </tagNode> -        #include <include/bgp/afi-maximum-paths.xml.i> +        #include <include/bgp/afi-rd.xml.i> +        #include <include/bgp/afi-route-map-vpn.xml.i> +        #include <include/bgp/afi-route-target-vpn.xml.i>          <node name="redistribute">            <properties>              <help>Redistribute routes from other protocols into BGP</help> @@ -478,6 +483,9 @@              </tagNode>            </children>          </node> +        #include <include/bgp/afi-export-import.xml.i> +        #include <include/bgp/afi-label.xml.i> +        #include <include/bgp/afi-maximum-paths.xml.i>          <tagNode name="network">            <properties>              <help>BGP network</help> @@ -490,22 +498,13 @@              </constraint>            </properties>            <children> -            <leafNode name="path-limit"> -              <properties> -                <help>AS-path hopcount limit</help> -                <valueHelp> -                  <format>u32:0-255</format> -                  <description>AS path hop count limit</description> -                </valueHelp> -                <constraint> -                  <validator name="numeric" argument="--range 0-255"/> -                </constraint> -              </properties> -            </leafNode> +            #include <include/bgp/afi-path-limit.xml.i>              #include <include/route-map.xml.i>            </children>          </tagNode> -        #include <include/bgp/afi-maximum-paths.xml.i> +        #include <include/bgp/afi-rd.xml.i> +        #include <include/bgp/afi-route-map-vpn.xml.i> +        #include <include/bgp/afi-route-target-vpn.xml.i>          <node name="redistribute">            <properties>              <help>Redistribute routes from other protocols into BGP</help> @@ -661,18 +660,7 @@              </constraint>            </properties>            <children> -            <leafNode name="path-limit"> -              <properties> -                <help>AS-path hopcount limit</help> -                <valueHelp> -                  <format>u32:0-255</format> -                  <description>AS path hop count limit</description> -                </valueHelp> -                <constraint> -                  <validator name="numeric" argument="--range 0-255"/> -                </constraint> -              </properties> -            </leafNode> +            #include <include/bgp/afi-path-limit.xml.i>              #include <include/route-map.xml.i>            </children>          </tagNode> diff --git a/interface-definitions/include/bgp/route-distinguisher.xml.i b/interface-definitions/include/bgp/route-distinguisher.xml.i index fdfbe7076..6d0aa3ef1 100644 --- a/interface-definitions/include/bgp/route-distinguisher.xml.i +++ b/interface-definitions/include/bgp/route-distinguisher.xml.i @@ -3,7 +3,7 @@    <properties>      <help>Route Distinguisher</help>      <valueHelp> -      <format>txt</format> +      <format>ASN:NN_OR_IP-ADDRESS:NN</format>        <description>Route Distinguisher, (x.x.x.x:yyy|xxxx:yyyy)</description>      </valueHelp>      <constraint> diff --git a/interface-definitions/include/bgp/route-target.xml.i b/interface-definitions/include/bgp/route-target.xml.i deleted file mode 100644 index 674b6db15..000000000 --- a/interface-definitions/include/bgp/route-target.xml.i +++ /dev/null @@ -1,45 +0,0 @@ -<!-- include start from bgp/route-target.xml.i --> -<node name="route-target"> -  <properties> -    <help>Route Target</help> -  </properties> -  <children> -    <leafNode name="both"> -      <properties> -        <help>Route Target both import and export</help> -        <valueHelp> -          <format>txt</format> -          <description>Route target (x.x.x.x:yyy|xxxx:yyyy)</description> -        </valueHelp> -        <constraint> -          <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> -        </constraint> -      </properties> -    </leafNode> -    <leafNode name="export"> -      <properties> -        <help>Route Target export</help> -        <valueHelp> -          <format>txt</format> -          <description>Route target (x.x.x.x:yyy|xxxx:yyyy)</description> -        </valueHelp> -        <constraint> -          <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> -        </constraint> -      </properties> -    </leafNode> -    <leafNode name="import"> -      <properties> -        <help>Route Target import</help> -        <valueHelp> -          <format>txt</format> -          <description>Route target (x.x.x.x:yyy|xxxx:yyyy)</description> -        </valueHelp> -        <constraint> -          <regex>^((25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)(\.(25[0-5]|2[0-4][0-9]|[1][0-9][0-9]|[1-9][0-9]|[0-9]?)){3}|[0-9]{1,10}):[0-9]{1,5}$</regex> -        </constraint> -      </properties> -    </leafNode> -  </children> -</node> -<!-- include end --> diff --git a/interface-definitions/include/conntrack-module-disable.xml.i b/interface-definitions/include/conntrack-module-disable.xml.i deleted file mode 100644 index f891225e0..000000000 --- a/interface-definitions/include/conntrack-module-disable.xml.i +++ /dev/null @@ -1,8 +0,0 @@ -<!-- include start from conntrack-module-disable.xml.i --> -<leafNode name="disable"> -  <properties> -    <help>Disable connection tracking helper</help> -    <valueless/> -  </properties> -</leafNode> -<!-- include end --> diff --git a/interface-definitions/include/interface/adjust-mss.xml.i b/interface-definitions/include/interface/adjust-mss.xml.i new file mode 100644 index 000000000..57019f02c --- /dev/null +++ b/interface-definitions/include/interface/adjust-mss.xml.i @@ -0,0 +1,23 @@ +<!-- include start from interface/adjust-mss.xml.i --> +<!-- https://datatracker.ietf.org/doc/html/rfc6691 --> +<leafNode name="adjust-mss"> +  <properties> +    <help>Adjust TCP MSS value</help> +    <completionHelp> +      <list>clamp-mss-to-pmtu</list> +    </completionHelp> +    <valueHelp> +      <format>clamp-mss-to-pmtu</format> +      <description>Automatically sets the MSS to the proper value</description> +    </valueHelp> +    <valueHelp> +      <format>u32:500-65535</format> +      <description>TCP Maximum segment size in bytes</description> +    </valueHelp> +    <constraint> +      <validator name="numeric" argument="--range 500-65535"/> +      <regex>^(clamp-mss-to-pmtu)$</regex> +    </constraint> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i b/interface-definitions/include/interface/arp-cache-timeout.xml.i index b269fecd8..3fb64f1ff 100644 --- a/interface-definitions/include/interface/interface-arp-cache-timeout.xml.i +++ b/interface-definitions/include/interface/arp-cache-timeout.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-arp-cache-timeout.xml.i --> +<!-- include start from interface/arp-cache-timeout.xml.i -->  <leafNode name="arp-cache-timeout">    <properties>      <help>ARP cache entry timeout in seconds</help> diff --git a/interface-definitions/include/interface/interface-description.xml.i b/interface-definitions/include/interface/description.xml.i index d618b50d2..8579cf7d1 100644 --- a/interface-definitions/include/interface/interface-description.xml.i +++ b/interface-definitions/include/interface/description.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-description.xml.i --> +<!-- include start from interface/description.xml.i -->  <leafNode name="description">    <properties>      <help>Interface specific description</help> diff --git a/interface-definitions/include/interface/interface-dial-on-demand.xml.i b/interface-definitions/include/interface/dial-on-demand.xml.i index 66edd9678..30e8c7e97 100644 --- a/interface-definitions/include/interface/interface-dial-on-demand.xml.i +++ b/interface-definitions/include/interface/dial-on-demand.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-dial-on-demand.xml.i --> +<!-- include start from interface/dial-on-demand.xml.i -->  <leafNode name="connect-on-demand">    <properties>      <help>Establishment connection automatically when traffic is sent</help> diff --git a/interface-definitions/include/interface/interface-disable-arp-filter.xml.i b/interface-definitions/include/interface/disable-arp-filter.xml.i index 49cddaf76..a69455d58 100644 --- a/interface-definitions/include/interface/interface-disable-arp-filter.xml.i +++ b/interface-definitions/include/interface/disable-arp-filter.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-disable-arp-filter.xml.i --> +<!-- include start from interface/disable-arp-filter.xml.i -->  <leafNode name="disable-arp-filter">    <properties>      <help>Disable ARP filter on this interface</help> diff --git a/interface-definitions/include/interface/disable-forwarding.xml.i b/interface-definitions/include/interface/disable-forwarding.xml.i new file mode 100644 index 000000000..45382ec95 --- /dev/null +++ b/interface-definitions/include/interface/disable-forwarding.xml.i @@ -0,0 +1,8 @@ +<!-- include start from interface/disable-forwarding.xml.i --> +<leafNode name="disable-forwarding"> +  <properties> +    <help>Disable IP forwarding on this interface</help> +    <valueless/> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/interface-disable-link-detect.xml.i b/interface-definitions/include/interface/disable-link-detect.xml.i index c528885b2..b101ec292 100644 --- a/interface-definitions/include/interface/interface-disable-link-detect.xml.i +++ b/interface-definitions/include/interface/disable-link-detect.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-disable-link-detect.xml.i --> +<!-- include start from interface/disable-link-detect.xml.i -->  <leafNode name="disable-link-detect">    <properties>      <help>Ignore link state changes</help> diff --git a/interface-definitions/include/interface/interface-disable.xml.i b/interface-definitions/include/interface/disable.xml.i index d90e6395b..b76bd3f53 100644 --- a/interface-definitions/include/interface/interface-disable.xml.i +++ b/interface-definitions/include/interface/disable.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-disable.xml.i --> +<!-- include start from interface/disable.xml.i -->  <leafNode name="disable">    <properties>      <help>Administratively disable interface</help> diff --git a/interface-definitions/include/interface/interface-eapol.xml.i b/interface-definitions/include/interface/eapol.xml.i index 270ec5b13..c4cdeae0c 100644 --- a/interface-definitions/include/interface/interface-eapol.xml.i +++ b/interface-definitions/include/interface/eapol.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-eapol.xml.i --> +<!-- include start from interface/eapol.xml.i -->  <node name="eapol">    <properties>       <help>Extensible Authentication Protocol over Local Area Network</help> diff --git a/interface-definitions/include/interface/interface-enable-arp-accept.xml.i b/interface-definitions/include/interface/enable-arp-accept.xml.i index 7c5d51857..90f6bc3db 100644 --- a/interface-definitions/include/interface/interface-enable-arp-accept.xml.i +++ b/interface-definitions/include/interface/enable-arp-accept.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-enable-arp-accept.xml.i --> +<!-- include start from interface/enable-arp-accept.xml.i -->  <leafNode name="enable-arp-accept">    <properties>      <help>Enable ARP accept on this interface</help> diff --git a/interface-definitions/include/interface/interface-enable-arp-announce.xml.i b/interface-definitions/include/interface/enable-arp-announce.xml.i index f44599c54..cf02fce0b 100644 --- a/interface-definitions/include/interface/interface-enable-arp-announce.xml.i +++ b/interface-definitions/include/interface/enable-arp-announce.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-enable-arp-announce.xml.i --> +<!-- include start from interface/enable-arp-announce.xml.i -->  <leafNode name="enable-arp-announce">    <properties>      <help>Enable ARP announce on this interface</help> diff --git a/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i b/interface-definitions/include/interface/enable-arp-ignore.xml.i index 3ea39613c..5bb444f35 100644 --- a/interface-definitions/include/interface/interface-enable-arp-ignore.xml.i +++ b/interface-definitions/include/interface/enable-arp-ignore.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-enable-arp-ignore.xml.i --> +<!-- include start from interface/enable-arp-ignore.xml.i -->  <leafNode name="enable-arp-ignore">    <properties>      <help>Enable ARP ignore on this interface</help> diff --git a/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i b/interface-definitions/include/interface/enable-proxy-arp.xml.i index dbdeeb7a7..27e497f84 100644 --- a/interface-definitions/include/interface/interface-enable-proxy-arp.xml.i +++ b/interface-definitions/include/interface/enable-proxy-arp.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-enable-proxy-arp.xml.i --> +<!-- include start from interface/enable-proxy-arp.xml.i -->  <leafNode name="enable-proxy-arp">    <properties>      <help>Enable proxy-arp on this interface</help> diff --git a/interface-definitions/include/interface/interface-hw-id.xml.i b/interface-definitions/include/interface/hw-id.xml.i index 989cd9cb7..a3a1eec7c 100644 --- a/interface-definitions/include/interface/interface-hw-id.xml.i +++ b/interface-definitions/include/interface/hw-id.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-hw-id.xml.i --> +<!-- include start from interface/hw-id.xml.i -->  <leafNode name="hw-id">    <properties>      <help>Associate Ethernet Interface with given Media Access Control (MAC) address</help> diff --git a/interface-definitions/include/interface/interface-disable-forwarding.xml.i b/interface-definitions/include/interface/interface-disable-forwarding.xml.i deleted file mode 100644 index cb6ef0475..000000000 --- a/interface-definitions/include/interface/interface-disable-forwarding.xml.i +++ /dev/null @@ -1,8 +0,0 @@ -<!-- include start from interface/interface-disable-forwarding.xml.i --> -<leafNode name="disable-forwarding"> -  <properties> -    <help>Disable IPv4 forwarding on this interface</help> -    <valueless/> -  </properties> -</leafNode> -<!-- include end --> diff --git a/interface-definitions/include/interface/interface-ipv4-options.xml.i b/interface-definitions/include/interface/interface-ipv4-options.xml.i deleted file mode 100644 index c2d0677b7..000000000 --- a/interface-definitions/include/interface/interface-ipv4-options.xml.i +++ /dev/null @@ -1,18 +0,0 @@ -<!-- include start from interface/interface-ipv4-options.xml.i --> -<node name="ip"> -  <properties> -    <help>IPv4 routing parameters</help> -  </properties> -  <children> -    #include <include/interface/interface-arp-cache-timeout.xml.i> -    #include <include/interface/interface-disable-arp-filter.xml.i> -    #include <include/interface/interface-disable-forwarding.xml.i> -    #include <include/interface/interface-enable-arp-accept.xml.i> -    #include <include/interface/interface-enable-arp-announce.xml.i> -    #include <include/interface/interface-enable-arp-ignore.xml.i> -    #include <include/interface/interface-enable-proxy-arp.xml.i> -    #include <include/interface/interface-proxy-arp-pvlan.xml.i> -    #include <include/interface/interface-source-validation.xml.i> -  </children> -</node> -<!-- include end --> diff --git a/interface-definitions/include/interface/ipv4-options.xml.i b/interface-definitions/include/interface/ipv4-options.xml.i new file mode 100644 index 000000000..bca1229c6 --- /dev/null +++ b/interface-definitions/include/interface/ipv4-options.xml.i @@ -0,0 +1,19 @@ +<!-- include start from interface/ipv4-options.xml.i --> +<node name="ip"> +  <properties> +    <help>IPv4 routing parameters</help> +  </properties> +  <children> +    #include <include/interface/adjust-mss.xml.i> +    #include <include/interface/arp-cache-timeout.xml.i> +    #include <include/interface/disable-arp-filter.xml.i> +    #include <include/interface/disable-forwarding.xml.i> +    #include <include/interface/enable-arp-accept.xml.i> +    #include <include/interface/enable-arp-announce.xml.i> +    #include <include/interface/enable-arp-ignore.xml.i> +    #include <include/interface/enable-proxy-arp.xml.i> +    #include <include/interface/proxy-arp-pvlan.xml.i> +    #include <include/interface/source-validation.xml.i> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i b/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i deleted file mode 100644 index 4adb77d1b..000000000 --- a/interface-definitions/include/interface/ipv6-disable-forwarding.xml.i +++ /dev/null @@ -1,8 +0,0 @@ -<!-- include start from interface/ipv6-disable-forwarding.xml.i --> -<leafNode name="disable-forwarding"> -  <properties> -    <help>Disable IPv6 forwarding on this interface</help> -    <valueless/> -  </properties> -</leafNode> -<!-- include end --> diff --git a/interface-definitions/include/interface/interface-ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i index dcd5a8710..f740ce0c2 100644 --- a/interface-definitions/include/interface/interface-ipv6-options.xml.i +++ b/interface-definitions/include/interface/ipv6-options.xml.i @@ -1,11 +1,12 @@ -<!-- include start from interface/interface-ipv6-options.xml.i --> +<!-- include start from interface/ipv6-options.xml.i -->  <node name="ipv6">    <properties>      <help>IPv6 routing parameters</help>    </properties>    <children> +    #include <include/interface/adjust-mss.xml.i> +    #include <include/interface/disable-forwarding.xml.i>      #include <include/interface/ipv6-address.xml.i> -    #include <include/interface/ipv6-disable-forwarding.xml.i>      #include <include/interface/ipv6-dup-addr-detect-transmits.xml.i>    </children>  </node> diff --git a/interface-definitions/include/interface/interface-mac.xml.i b/interface-definitions/include/interface/mac.xml.i index d7107ad23..705330dce 100644 --- a/interface-definitions/include/interface/interface-mac.xml.i +++ b/interface-definitions/include/interface/mac.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-mac.xml.i --> +<!-- include start from interface/mac.xml.i -->  <leafNode name="mac">    <properties>      <help>Media Access Control (MAC) address</help> diff --git a/interface-definitions/include/interface/interface-mirror.xml.i b/interface-definitions/include/interface/mirror.xml.i index b3b45fb43..2959551f0 100644 --- a/interface-definitions/include/interface/interface-mirror.xml.i +++ b/interface-definitions/include/interface/mirror.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-mirror.xml.i --> +<!-- include start from interface/mirror.xml.i -->  <node name="mirror">    <properties>      <help>Incoming/outgoing packet mirroring destination</help> diff --git a/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i b/interface-definitions/include/interface/mtu-1200-16000.xml.i index 3241ba912..ccd986d55 100644 --- a/interface-definitions/include/interface/interface-mtu-1200-16000.xml.i +++ b/interface-definitions/include/interface/mtu-1200-16000.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-mtu-1200-16000.xml.i --> +<!-- include start from interface/mtu-1200-16000.xml.i -->  <leafNode name="mtu">    <properties>      <help>Maximum Transmission Unit (MTU)</help> diff --git a/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i b/interface-definitions/include/interface/mtu-1450-16000.xml.i index 0a35bbbaa..2dc3a2029 100644 --- a/interface-definitions/include/interface/interface-mtu-1450-16000.xml.i +++ b/interface-definitions/include/interface/mtu-1450-16000.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-mtu-1450-16000.xml.i --> +<!-- include start from interface/mtu-1450-16000.xml.i -->  <leafNode name="mtu">    <properties>      <help>Maximum Transmission Unit (MTU)</help> diff --git a/interface-definitions/include/interface/interface-mtu-64-8024.xml.i b/interface-definitions/include/interface/mtu-64-8024.xml.i index f75de02ba..9b8bc4697 100644 --- a/interface-definitions/include/interface/interface-mtu-64-8024.xml.i +++ b/interface-definitions/include/interface/mtu-64-8024.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-mtu-68-8024.xml.i --> +<!-- include start from interface/mtu-68-8024.xml.i -->  <leafNode name="mtu">    <properties>      <help>Maximum Transmission Unit (MTU)</help> diff --git a/interface-definitions/include/interface/interface-mtu-68-1500.xml.i b/interface-definitions/include/interface/mtu-68-1500.xml.i index 9e6fe8760..e3b70302f 100644 --- a/interface-definitions/include/interface/interface-mtu-68-1500.xml.i +++ b/interface-definitions/include/interface/mtu-68-1500.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-mtu-68-1500.xml.i --> +<!-- include start from interface/mtu-68-1500.xml.i -->  <leafNode name="mtu">    <properties>      <help>Maximum Transmission Unit (MTU)</help> diff --git a/interface-definitions/include/interface/interface-mtu-68-16000.xml.i b/interface-definitions/include/interface/mtu-68-16000.xml.i index 83af7bbd4..b610ab3e2 100644 --- a/interface-definitions/include/interface/interface-mtu-68-16000.xml.i +++ b/interface-definitions/include/interface/mtu-68-16000.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-mtu-68-16000.xml.i --> +<!-- include start from interface/mtu-68-16000.xml.i -->  <leafNode name="mtu">    <properties>      <help>Maximum Transmission Unit (MTU)</help> diff --git a/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i b/interface-definitions/include/interface/parameters-dont-fragment.xml.i index 166c31115..d34f0a97b 100644 --- a/interface-definitions/include/interface/interface-parameters-dont-fragment.xml.i +++ b/interface-definitions/include/interface/parameters-dont-fragment.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-parameters-df.xml.i --> +<!-- include start from interface/parameters-df.xml.i -->  <leafNode name="dont-fragment">    <properties>      <help>Specifies the usage of the dont fragment (DF) bit</help> diff --git a/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i b/interface-definitions/include/interface/parameters-flowlabel.xml.i index ed075e40d..7fa571634 100644 --- a/interface-definitions/include/interface/interface-parameters-flowlabel.xml.i +++ b/interface-definitions/include/interface/parameters-flowlabel.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-parameters-flowlabel.xml.i --> +<!-- include start from interface/parameters-flowlabel.xml.i -->  <leafNode name="flowlabel">    <properties>      <help>Specifies the flow label to use in outgoing packets</help> diff --git a/interface-definitions/include/interface/interface-parameters-key.xml.i b/interface-definitions/include/interface/parameters-key.xml.i index 6c59f7879..25a6c0303 100644 --- a/interface-definitions/include/interface/interface-parameters-key.xml.i +++ b/interface-definitions/include/interface/parameters-key.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-parameters-key.xml.i --> +<!-- include start from interface/parameters-key.xml.i -->  <leafNode name="key">    <properties>      <help>Tunnel key (only GRE tunnels)</help> diff --git a/interface-definitions/include/interface/interface-parameters-tos.xml.i b/interface-definitions/include/interface/parameters-tos.xml.i index 83b4e0671..83b4e0671 100644 --- a/interface-definitions/include/interface/interface-parameters-tos.xml.i +++ b/interface-definitions/include/interface/parameters-tos.xml.i diff --git a/interface-definitions/include/interface/interface-parameters-ttl.xml.i b/interface-definitions/include/interface/parameters-ttl.xml.i index df193cf24..da5ce69c2 100644 --- a/interface-definitions/include/interface/interface-parameters-ttl.xml.i +++ b/interface-definitions/include/interface/parameters-ttl.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-parameters-ttl.xml.i --> +<!-- include start from interface/parameters-ttl.xml.i -->  <leafNode name="ttl">    <properties>      <help>Specifies TTL value to use in outgoing packets</help> diff --git a/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i b/interface-definitions/include/interface/proxy-arp-pvlan.xml.i index 153dfc072..c00b2fe85 100644 --- a/interface-definitions/include/interface/interface-proxy-arp-pvlan.xml.i +++ b/interface-definitions/include/interface/proxy-arp-pvlan.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-proxy-arp-pvlan.xml.i --> +<!-- include start from interface/proxy-arp-pvlan.xml.i -->  <leafNode name="proxy-arp-pvlan">    <properties>      <help>Enable private VLAN proxy ARP on this interface</help> diff --git a/interface-definitions/include/interface/interface-source-validation.xml.i b/interface-definitions/include/interface/source-validation.xml.i index 70914f2e9..f38065f4d 100644 --- a/interface-definitions/include/interface/interface-source-validation.xml.i +++ b/interface-definitions/include/interface/source-validation.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-source-validation.xml.i --> +<!-- include start from interface/source-validation.xml.i -->  <leafNode name="source-validation">    <properties>      <help>Source validation by reversed path (RFC3704)</help> diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i index 17d1746be..3fd69d9d1 100644 --- a/interface-definitions/include/interface/vif-s.xml.i +++ b/interface-definitions/include/interface/vif-s.xml.i @@ -9,11 +9,11 @@    </properties>    <children>      #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> -    #include <include/interface/interface-description.xml.i> +    #include <include/interface/description.xml.i>      #include <include/interface/dhcp-options.xml.i>      #include <include/interface/dhcpv6-options.xml.i> -    #include <include/interface/interface-disable-link-detect.xml.i> -    #include <include/interface/interface-disable.xml.i> +    #include <include/interface/disable-link-detect.xml.i> +    #include <include/interface/disable.xml.i>      <leafNode name="protocol">        <properties>          <help>Protocol used for service VLAN (default: 802.1ad)</help> @@ -35,10 +35,10 @@        </properties>        <defaultValue>802.1ad</defaultValue>      </leafNode> -    #include <include/interface/interface-ipv4-options.xml.i> -    #include <include/interface/interface-ipv6-options.xml.i> -    #include <include/interface/interface-mac.xml.i> -    #include <include/interface/interface-mtu-68-16000.xml.i> +    #include <include/interface/ipv4-options.xml.i> +    #include <include/interface/ipv6-options.xml.i> +    #include <include/interface/mac.xml.i> +    #include <include/interface/mtu-68-16000.xml.i>      <tagNode name="vif-c">        <properties>          <help>QinQ TAG-C Virtual Local Area Network (VLAN) ID</help> @@ -49,19 +49,19 @@        </properties>        <children>          #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> -        #include <include/interface/interface-description.xml.i> +        #include <include/interface/description.xml.i>          #include <include/interface/dhcp-options.xml.i>          #include <include/interface/dhcpv6-options.xml.i> -        #include <include/interface/interface-disable-link-detect.xml.i> -        #include <include/interface/interface-disable.xml.i> -        #include <include/interface/interface-ipv4-options.xml.i> -        #include <include/interface/interface-ipv6-options.xml.i> -        #include <include/interface/interface-mac.xml.i> -        #include <include/interface/interface-mtu-68-16000.xml.i> -        #include <include/interface/interface-vrf.xml.i> +        #include <include/interface/disable-link-detect.xml.i> +        #include <include/interface/disable.xml.i> +        #include <include/interface/ipv4-options.xml.i> +        #include <include/interface/ipv6-options.xml.i> +        #include <include/interface/mac.xml.i> +        #include <include/interface/mtu-68-16000.xml.i> +        #include <include/interface/vrf.xml.i>        </children>      </tagNode> -    #include <include/interface/interface-vrf.xml.i> +    #include <include/interface/vrf.xml.i>    </children>  </tagNode>  <!-- include end --> diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i index 9e89cbbf6..8daafeaf4 100644 --- a/interface-definitions/include/interface/vif.xml.i +++ b/interface-definitions/include/interface/vif.xml.i @@ -13,12 +13,12 @@    </properties>    <children>      #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> -    #include <include/interface/interface-description.xml.i> +    #include <include/interface/description.xml.i>      #include <include/interface/dhcp-options.xml.i>      #include <include/interface/dhcpv6-options.xml.i> -    #include <include/interface/interface-disable-link-detect.xml.i> -    #include <include/interface/interface-disable.xml.i> -    #include <include/interface/interface-vrf.xml.i> +    #include <include/interface/disable-link-detect.xml.i> +    #include <include/interface/disable.xml.i> +    #include <include/interface/vrf.xml.i>      <leafNode name="egress-qos">        <properties>          <help>VLAN egress QoS</help> @@ -45,10 +45,10 @@          <constraintErrorMessage>QoS mapping should be in the format of '0:7 2:3' with numbers 0-9</constraintErrorMessage>        </properties>      </leafNode> -    #include <include/interface/interface-ipv4-options.xml.i> -    #include <include/interface/interface-ipv6-options.xml.i> -    #include <include/interface/interface-mac.xml.i> -    #include <include/interface/interface-mtu-68-16000.xml.i> +    #include <include/interface/ipv4-options.xml.i> +    #include <include/interface/ipv6-options.xml.i> +    #include <include/interface/mac.xml.i> +    #include <include/interface/mtu-68-16000.xml.i>    </children>  </tagNode>  <!-- include end --> diff --git a/interface-definitions/include/interface/interface-vrf.xml.i b/interface-definitions/include/interface/vrf.xml.i index ef6ca1241..5ad978a27 100644 --- a/interface-definitions/include/interface/interface-vrf.xml.i +++ b/interface-definitions/include/interface/vrf.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-vrf.xml.i --> +<!-- include start from interface/vrf.xml.i -->  <leafNode name="vrf">    <properties>      <help>VRF instance name</help> diff --git a/interface-definitions/include/interface/interface-xdp.xml.i b/interface-definitions/include/interface/xdp.xml.i index 0253f6dad..10223e766 100644 --- a/interface-definitions/include/interface/interface-xdp.xml.i +++ b/interface-definitions/include/interface/xdp.xml.i @@ -1,4 +1,4 @@ -<!-- include start from interface/interface-xdp.xml.i --> +<!-- include start from interface/xdp.xml.i -->  <leafNode name="xdp">    <properties>      <help>Enable eXpress Data Path</help> diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i index af5a21f49..84e2f7bb2 100644 --- a/interface-definitions/include/isis/protocol-common-config.xml.i +++ b/interface-definitions/include/isis/protocol-common-config.xml.i @@ -447,7 +447,7 @@              <help>Border Gateway Protocol (BGP)</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv4.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="connected"> @@ -455,7 +455,7 @@              <help>Redistribute connected routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv4.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="kernel"> @@ -463,7 +463,7 @@              <help>Redistribute kernel routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv4.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="ospf"> @@ -471,7 +471,7 @@              <help>Redistribute OSPF routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv4.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="rip"> @@ -479,7 +479,7 @@              <help>Redistribute RIP routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv4.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="static"> @@ -487,7 +487,7 @@              <help>Redistribute static routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv4.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>        </children> @@ -502,7 +502,7 @@              <help>Redistribute BGP routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv6.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="connected"> @@ -510,7 +510,7 @@              <help>Redistribute connected routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv6.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="kernel"> @@ -518,7 +518,7 @@              <help>Redistribute kernel routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv6.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="ospf6"> @@ -526,7 +526,7 @@              <help>Redistribute OSPFv3 routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv6.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="ripng"> @@ -534,7 +534,7 @@              <help>Redistribute RIPng routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv6.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>          <node name="static"> @@ -542,7 +542,7 @@              <help>Redistribute static routes into IS-IS</help>            </properties>            <children> -            #include <include/isis/redistribute-ipv6.xml.i> +            #include <include/isis/redistribute-level-1-2.xml.i>            </children>          </node>        </children> diff --git a/interface-definitions/include/isis/redistribute-ipv4.xml.i b/interface-definitions/include/isis/redistribute-ipv4.xml.i deleted file mode 100644 index fbb6210c7..000000000 --- a/interface-definitions/include/isis/redistribute-ipv4.xml.i +++ /dev/null @@ -1,42 +0,0 @@ -<!-- include start from isis/redistribute-ipv4.xml.i --> -<node name="level-1"> -  <properties> -    <help>Redistribute into level-1</help> -  </properties> -  <children> -    <leafNode name="metric"> -      <properties> -        <help>Metric for redistributed routes</help> -        <valueHelp> -          <format>u32:0-16777215</format> -          <description>ISIS default metric</description> -        </valueHelp> -        <constraint> -          <validator name="numeric" argument="--range 0-16777215"/> -        </constraint> -      </properties> -    </leafNode> -    #include <include/route-map.xml.i> -  </children> -</node> -<node name="level-2"> -  <properties> -    <help>Redistribute into level-2</help> -  </properties> -  <children> -    <leafNode name="metric"> -      <properties> -        <help>Metric for redistributed routes</help> -        <valueHelp> -          <format>u32:0-16777215</format> -          <description>ISIS default metric</description> -        </valueHelp> -        <constraint> -          <validator name="numeric" argument="--range 0-16777215"/> -        </constraint> -      </properties> -    </leafNode> -    #include <include/route-map.xml.i> -  </children> -</node> -<!-- include end --> diff --git a/interface-definitions/include/isis/redistribute-ipv6.xml.i b/interface-definitions/include/isis/redistribute-ipv6.xml.i deleted file mode 100644 index 7e679e38a..000000000 --- a/interface-definitions/include/isis/redistribute-ipv6.xml.i +++ /dev/null @@ -1,42 +0,0 @@ -<!-- include start from isis/redistribute-ipv6.xml.i --> -<node name="level-1"> -  <properties> -    <help>Redistribute into level-1</help> -  </properties> -  <children> -    <leafNode name="metric"> -      <properties> -        <help>Metric for redistributed routes</help> -        <valueHelp> -          <format>u32:0-16777215</format> -          <description>ISIS default metric</description> -        </valueHelp> -        <constraint> -          <validator name="numeric" argument="--range 0-16777215"/> -        </constraint> -      </properties> -    </leafNode> -    #include <include/route-map.xml.i> -  </children> -</node> -<node name="level-2"> -  <properties> -    <help>Redistribute into level-2</help> -  </properties> -  <children> -    <leafNode name="metric"> -      <properties> -        <help>Metric for redistributed routes</help> -        <valueHelp> -          <format>u32:0-16777215</format> -          <description>ISIS default metric</description> -        </valueHelp> -        <constraint> -          <validator name="numeric" argument="--range 0-16777215"/> -        </constraint> -      </properties> -    </leafNode> -    #include <include/route-map.xml.i> -  </children> -</node> -<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/isis/redistribute-level-1-2.xml.i b/interface-definitions/include/isis/redistribute-level-1-2.xml.i new file mode 100644 index 000000000..abb85274f --- /dev/null +++ b/interface-definitions/include/isis/redistribute-level-1-2.xml.i @@ -0,0 +1,20 @@ +<!-- include start from isis/redistribute-level-1-2.xml.i --> +<node name="level-1"> +  <properties> +    <help>Redistribute into level-1</help> +  </properties> +  <children> +    #include <include/isis/metric.xml.i> +    #include <include/route-map.xml.i> +  </children> +</node> +<node name="level-2"> +  <properties> +    <help>Redistribute into level-2</help> +  </properties> +  <children> +    #include <include/isis/metric.xml.i> +    #include <include/route-map.xml.i> +  </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/ospf/protocol-common-config.xml.i b/interface-definitions/include/ospf/protocol-common-config.xml.i index db39b1a86..c4ca613a4 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -361,6 +361,23 @@      </constraint>    </properties>    <children> +    <leafNode name="area"> +      <properties> +        <help>Enable OSPF on this interface</help> +        <valueHelp> +          <format>u32</format> +          <description>OSPF area ID as decimal notation</description> +        </valueHelp> +        <valueHelp> +          <format>ipv4</format> +          <description>OSPF area ID in IP address notation</description> +        </valueHelp> +        <constraint> +          <validator name="numeric" argument="--range 0-4294967295"/> +          <validator name="ip-address"/> +        </constraint> +      </properties> +    </leafNode>      #include <include/ospf/authentication.xml.i>      #include <include/ospf/intervals.xml.i>      #include <include/ospf/interface-common.xml.i> diff --git a/interface-definitions/include/port-number.xml.i b/interface-definitions/include/port-number.xml.i index b62aef32b..6820df0c4 100644 --- a/interface-definitions/include/port-number.xml.i +++ b/interface-definitions/include/port-number.xml.i @@ -9,6 +9,7 @@      <constraint>        <validator name="numeric" argument="--range 1-65535"/>      </constraint> +    <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>    </properties>  </leafNode>  <!-- include end --> diff --git a/interface-definitions/include/pppoe-access-concentrator.xml.i b/interface-definitions/include/pppoe-access-concentrator.xml.i new file mode 100644 index 000000000..ccfcc1c49 --- /dev/null +++ b/interface-definitions/include/pppoe-access-concentrator.xml.i @@ -0,0 +1,11 @@ +<!-- include start from pppoe-access-concentrator.xml.i --> +<leafNode name="access-concentrator"> +  <properties> +    <help>Access concentrator name</help> +    <constraint> +      <regex>[a-zA-Z0-9]{1,100}</regex> +    </constraint> +    <constraintErrorMessage>Access-concentrator name must be alphanumerical only (max. 100 characters)</constraintErrorMessage> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in index 4bfc6e730..05e0d8461 100644 --- a/interface-definitions/interfaces-bonding.xml.in +++ b/interface-definitions/interfaces-bonding.xml.in @@ -49,13 +49,13 @@                </leafNode>              </children>            </node> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            #include <include/interface/dhcp-options.xml.i>            #include <include/interface/dhcpv6-options.xml.i> -          #include <include/interface/interface-disable-link-detect.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-vrf.xml.i> -          #include <include/interface/interface-mirror.xml.i> +          #include <include/interface/disable-link-detect.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/vrf.xml.i> +          #include <include/interface/mirror.xml.i>            <leafNode name="hash-policy">              <properties>                <help>Bonding transmit hash policy</help> @@ -89,9 +89,9 @@              </properties>              <defaultValue>layer2</defaultValue>            </leafNode> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> -          #include <include/interface/interface-mac.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/mac.xml.i>            <leafNode name="min-links">              <properties>                <help>Minimum number of member interfaces required up before enabling bond</help> @@ -182,7 +182,7 @@                </leafNode>              </children>            </node> -          #include <include/interface/interface-mtu-68-16000.xml.i> +          #include <include/interface/mtu-68-16000.xml.i>            <leafNode name="primary">              <properties>                <help>Primary device interface</help> @@ -193,7 +193,7 @@            </leafNode>            #include <include/interface/vif-s.xml.i>            #include <include/interface/vif.xml.i> -          #include <include/interface/interface-xdp.xml.i> +          #include <include/interface/xdp.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in index 1af002142..ddfc5ade4 100644 --- a/interface-definitions/interfaces-bridge.xml.in +++ b/interface-definitions/interfaces-bridge.xml.in @@ -34,13 +34,13 @@              </properties>              <defaultValue>300</defaultValue>            </leafNode> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            #include <include/interface/dhcp-options.xml.i>            #include <include/interface/dhcpv6-options.xml.i> -          #include <include/interface/interface-disable-link-detect.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-vrf.xml.i> -          #include <include/interface/interface-mtu-68-16000.xml.i> +          #include <include/interface/disable-link-detect.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/vrf.xml.i> +          #include <include/interface/mtu-68-16000.xml.i>            <leafNode name="forwarding-delay">              <properties>                <help>Forwarding delay</help> @@ -82,10 +82,10 @@                </leafNode>              </children>            </node> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> -          #include <include/interface/interface-mac.xml.i> -          #include <include/interface/interface-mirror.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/mac.xml.i> +          #include <include/interface/mirror.xml.i>            <leafNode name="enable-vlan">              <properties>                <help>Enable VLAN aware bridge</help> diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in index 84c6903c7..2bc88c1a7 100644 --- a/interface-definitions/interfaces-dummy.xml.in +++ b/interface-definitions/interfaces-dummy.xml.in @@ -17,17 +17,17 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i>            <node name="ip">              <properties>                <help>IPv4 routing parameters</help>              </properties>              <children> -              #include <include/interface/interface-source-validation.xml.i> +              #include <include/interface/source-validation.xml.i>              </children>            </node> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in index cb451f5be..ca076e3fa 100644 --- a/interface-definitions/interfaces-ethernet.xml.in +++ b/interface-definitions/interfaces-ethernet.xml.in @@ -20,7 +20,7 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            #include <include/interface/dhcp-options.xml.i>            #include <include/interface/dhcpv6-options.xml.i>            <leafNode name="disable-flow-control"> @@ -29,8 +29,8 @@                <valueless/>              </properties>            </leafNode> -          #include <include/interface/interface-disable-link-detect.xml.i> -          #include <include/interface/interface-disable.xml.i> +          #include <include/interface/disable-link-detect.xml.i> +          #include <include/interface/disable.xml.i>            <leafNode name="duplex">              <properties>                <help>Duplex mode</help> @@ -56,13 +56,13 @@              </properties>              <defaultValue>auto</defaultValue>            </leafNode> -          #include <include/interface/interface-eapol.xml.i> -          #include <include/interface/interface-hw-id.xml.i> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> -          #include <include/interface/interface-mac.xml.i> -          #include <include/interface/interface-mtu-68-16000.xml.i> -          #include <include/interface/interface-mirror.xml.i> +          #include <include/interface/eapol.xml.i> +          #include <include/interface/hw-id.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/mac.xml.i> +          #include <include/interface/mtu-68-16000.xml.i> +          #include <include/interface/mirror.xml.i>            <node name="offload">              <properties>                <help>Configurable offload options</help> @@ -202,8 +202,8 @@            </node>            #include <include/interface/vif-s.xml.i>            #include <include/interface/vif.xml.i> -          #include <include/interface/interface-vrf.xml.i> -          #include <include/interface/interface-xdp.xml.i> +          #include <include/interface/vrf.xml.i> +          #include <include/interface/xdp.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in index bdcbc3f5e..2ca7dd9f6 100644 --- a/interface-definitions/interfaces-geneve.xml.in +++ b/interface-definitions/interfaces-geneve.xml.in @@ -17,12 +17,12 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> -          #include <include/interface/interface-mac.xml.i> -          #include <include/interface/interface-mtu-1450-16000.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/mac.xml.i> +          #include <include/interface/mtu-1450-16000.xml.i>            <node name="parameters">              <properties>                <help>GENEVE tunnel parameters</help> @@ -33,9 +33,9 @@                    <help>IPv4 specific tunnel parameters</help>                  </properties>                  <children> -                  #include <include/interface/interface-parameters-dont-fragment.xml.i> -                  #include <include/interface/interface-parameters-tos.xml.i> -                  #include <include/interface/interface-parameters-ttl.xml.i> +                  #include <include/interface/parameters-dont-fragment.xml.i> +                  #include <include/interface/parameters-tos.xml.i> +                  #include <include/interface/parameters-ttl.xml.i>                  </children>                </node>                <node name="ipv6"> @@ -43,7 +43,7 @@                    <help>IPv6 specific tunnel parameters</help>                  </properties>                  <children> -                  #include <include/interface/interface-parameters-flowlabel.xml.i> +                  #include <include/interface/parameters-flowlabel.xml.i>                  </children>                </node>              </children> diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in index 8835a6b1d..9edc98ef6 100644 --- a/interface-definitions/interfaces-l2tpv3.xml.in +++ b/interface-definitions/interfaces-l2tpv3.xml.in @@ -17,7 +17,7 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            <leafNode name="destination-port">              <properties>                <help>UDP destination port for L2TPv3 tunnel (default: 5000)</help> @@ -31,7 +31,7 @@              </properties>              <defaultValue>5000</defaultValue>            </leafNode> -          #include <include/interface/interface-disable.xml.i> +          #include <include/interface/disable.xml.i>            <leafNode name="encapsulation">              <properties>                <help>Encapsulation type (default: UDP)</help> @@ -53,10 +53,10 @@              </properties>              <defaultValue>udp</defaultValue>            </leafNode> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i>            #include <include/source-address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-mtu-68-16000.xml.i> +          #include <include/interface/mtu-68-16000.xml.i>            <leafNode name="mtu">              <defaultValue>1488</defaultValue>            </leafNode> @@ -84,7 +84,7 @@                </constraint>              </properties>            </leafNode> -          #include <include/interface/interface-mtu-68-16000.xml.i> +          #include <include/interface/mtu-68-16000.xml.i>            #include <include/interface/tunnel-remote.xml.i>            <leafNode name="session-id">              <properties> @@ -123,7 +123,7 @@                </constraint>              </properties>            </leafNode> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/interfaces-loopback.xml.in b/interface-definitions/interfaces-loopback.xml.in index 5d0ca5b0a..7be15ab89 100644 --- a/interface-definitions/interfaces-loopback.xml.in +++ b/interface-definitions/interfaces-loopback.xml.in @@ -17,13 +17,13 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            <node name="ip">              <properties>                <help>IPv4 routing parameters</help>              </properties>              <children> -              #include <include/interface/interface-source-validation.xml.i> +              #include <include/interface/source-validation.xml.i>              </children>            </node>          </children> diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in index fce88b21c..e88cb4794 100644 --- a/interface-definitions/interfaces-macsec.xml.in +++ b/interface-definitions/interfaces-macsec.xml.in @@ -17,8 +17,8 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i>            <node name="security">              <properties>                <help>Security/Encryption Settings</help> @@ -111,14 +111,14 @@                </leafNode>              </children>            </node> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-mtu-68-16000.xml.i> +          #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/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 7ff08ac86..01e6bf2fb 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -33,7 +33,7 @@                </leafNode>              </children>            </node> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            <leafNode name="device-type">              <properties>                <help>OpenVPN interface device-type (default: tun)</help> @@ -54,7 +54,7 @@              </properties>              <defaultValue>tun</defaultValue>            </leafNode> -          #include <include/interface/interface-disable.xml.i> +          #include <include/interface/disable.xml.i>            <node name="encryption">              <properties>                <help>Data Encryption settings</help> @@ -165,7 +165,7 @@                </leafNode>              </children>            </node> -          #include <include/interface/interface-ipv6-options.xml.i> +          #include <include/interface/ipv6-options.xml.i>            <leafNode name="hash">              <properties>                <help>Hashing Algorithm</help> @@ -571,7 +571,7 @@                    <multi/>                  </properties>                </leafNode> -              <leafNode name="push-route"> +              <tagNode name="push-route">                  <properties>                    <help>Route to be pushed to all clients</help>                    <valueHelp> @@ -585,9 +585,23 @@                    <constraint>                      <validator name="ip-prefix"/>                    </constraint> -                  <multi/>                  </properties> -              </leafNode> +                <children> +                  <leafNode name="metric"> +                    <properties> +                      <help>Set metric for this route</help> +                      <valueHelp> +                        <format>0-4294967295</format> +                        <description>Metric for this route</description> +                      </valueHelp> +                      <constraint> +                        <validator name="numeric" argument="--range 0-4294967295"/> +                      </constraint> +                    </properties> +                    <defaultValue>0</defaultValue> +                  </leafNode> +                </children> +              </tagNode>                <leafNode name="reject-unconfigured-clients">                  <properties>                    <help>Reject connections from clients that are not explicitly configured</help> @@ -726,7 +740,7 @@                <valueless/>              </properties>            </leafNode> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 96479e057..57bb01258 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -5,7 +5,7 @@        <tagNode name="pppoe" owner="${vyos_conf_scripts_dir}/interfaces-pppoe.py">          <properties>            <help>Point-to-Point Protocol over Ethernet (PPPoE)</help> -          <priority>321</priority> +          <priority>322</priority>            <constraint>              <regex>^pppoe[0-9]+$</regex>            </constraint> @@ -16,17 +16,9 @@            </valueHelp>          </properties>          <children> -          <leafNode name="access-concentrator"> -            <properties> -              <help>Access concentrator name (only connect to this concentrator)</help> -              <constraint> -                <regex>[a-zA-Z0-9]+$</regex> -              </constraint> -              <constraintErrorMessage>Access concentrator name must be composed of uppper and lower case letters or numbers only</constraintErrorMessage> -            </properties> -          </leafNode> +          #include <include/pppoe-access-concentrator.xml.i>            #include <include/interface/authentication.xml.i> -          #include <include/interface/interface-dial-on-demand.xml.i> +          #include <include/interface/dial-on-demand.xml.i>            <leafNode name="default-route">              <properties>                <help>Default route insertion behaviour (default: auto)</help> @@ -53,16 +45,20 @@              <defaultValue>auto</defaultValue>            </leafNode>            #include <include/interface/dhcpv6-options.xml.i> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/vrf.xml.i>            <leafNode name="idle-timeout">              <properties>                <help>Delay before disconnecting idle session (in seconds)</help>                <valueHelp> -                <format>n</format> +                <format>u32:0-86400</format>                  <description>Idle timeout in seconds</description>                </valueHelp> +              <constraint> +                <validator name="numeric" argument="--range 0-86400"/> +              </constraint> +              <constraintErrorMessage>Timeout must be in range 0 to 86400</constraintErrorMessage>              </properties>            </leafNode>            <node name="ip"> @@ -70,7 +66,9 @@                <help>IPv4 routing parameters</help>              </properties>              <children> -              #include <include/interface/interface-source-validation.xml.i> +              #include <include/interface/adjust-mss.xml.i> +              #include <include/interface/disable-forwarding.xml.i> +              #include <include/interface/source-validation.xml.i>              </children>            </node>            <node name="ipv6"> @@ -86,16 +84,11 @@                    #include <include/interface/ipv6-address-autoconf.xml.i>                  </children>                </node> +              #include <include/interface/adjust-mss.xml.i> +              #include <include/interface/disable-forwarding.xml.i>              </children>            </node> -          <leafNode name="source-interface"> -            <properties> -              <help>Physical Interface used for this PPPoE session</help> -              <completionHelp> -                <script>${vyos_completion_dir}/list_interfaces.py --broadcast</script> -              </completionHelp> -            </properties> -          </leafNode> +          #include <include/source-interface.xml.i>            <leafNode name="local-address">              <properties>                <help>IPv4 address of local end of the PPPoE link</help> @@ -108,7 +101,7 @@                </constraint>              </properties>            </leafNode> -          #include <include/interface/interface-mtu-68-1500.xml.i> +          #include <include/interface/mtu-68-1500.xml.i>            <leafNode name="mtu">              <defaultValue>1492</defaultValue>            </leafNode> @@ -136,7 +129,7 @@                <constraint>                  <regex>[a-zA-Z0-9]+$</regex>                </constraint> -              <constraintErrorMessage>Service name must be composed of uppper and lower case letters or numbers only</constraintErrorMessage> +              <constraintErrorMessage>Service name must be alphanumeric only</constraintErrorMessage>              </properties>            </leafNode>          </children> diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in index 136841290..366892032 100644 --- a/interface-definitions/interfaces-pseudo-ethernet.xml.in +++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in @@ -17,16 +17,16 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6-dhcp.xml.i> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            #include <include/interface/dhcp-options.xml.i>            #include <include/interface/dhcpv6-options.xml.i> -          #include <include/interface/interface-disable-link-detect.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-vrf.xml.i> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> +          #include <include/interface/disable-link-detect.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/vrf.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i>            #include <include/source-interface-ethernet.xml.i> -          #include <include/interface/interface-mac.xml.i> +          #include <include/interface/mac.xml.i>            <leafNode name="mode">              <properties>                <help>Receive mode (default: private)</help> @@ -56,7 +56,7 @@              </properties>              <defaultValue>private</defaultValue>            </leafNode> -          #include <include/interface/interface-mtu-68-16000.xml.i> +          #include <include/interface/mtu-68-16000.xml.i>            #include <include/interface/vif-s.xml.i>            #include <include/interface/vif.xml.i>          </children> diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in index b994bdafc..c059ef624 100644 --- a/interface-definitions/interfaces-tunnel.xml.in +++ b/interface-definitions/interfaces-tunnel.xml.in @@ -16,17 +16,17 @@            </valueHelp>          </properties>          <children> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-disable-link-detect.xml.i> -          #include <include/interface/interface-vrf.xml.i> -          #include <include/interface/interface-mtu-64-8024.xml.i> +          #include <include/interface/disable.xml.i> +          #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/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i>            #include <include/source-address-ipv4-ipv6.xml.i>            #include <include/interface/tunnel-remote.xml.i>            #include <include/source-interface.xml.i> @@ -216,9 +216,9 @@                        <valueless/>                      </properties>                    </leafNode> -                  #include <include/interface/interface-parameters-key.xml.i> -                  #include <include/interface/interface-parameters-tos.xml.i> -                  #include <include/interface/interface-parameters-ttl.xml.i> +                  #include <include/interface/parameters-key.xml.i> +                  #include <include/interface/parameters-tos.xml.i> +                  #include <include/interface/parameters-ttl.xml.i>                    <leafNode name="ttl">                      <defaultValue>64</defaultValue>                    </leafNode> @@ -251,7 +251,7 @@                      </properties>                      <defaultValue>4</defaultValue>                    </leafNode> -                  #include <include/interface/interface-parameters-flowlabel.xml.i> +                  #include <include/interface/parameters-flowlabel.xml.i>                    <leafNode name="hoplimit">                      <properties>                        <help>Hoplimit</help> diff --git a/interface-definitions/interfaces-vti.xml.in b/interface-definitions/interfaces-vti.xml.in index 10e1feb6b..b12434ae7 100644 --- a/interface-definitions/interfaces-vti.xml.in +++ b/interface-definitions/interfaces-vti.xml.in @@ -29,10 +29,12 @@                <multi/>              </properties>            </leafNode> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-mtu-68-16000.xml.i> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/mtu-68-16000.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in index 56d01dfb6..43b73a2e9 100644 --- a/interface-definitions/interfaces-vxlan.xml.in +++ b/interface-definitions/interfaces-vxlan.xml.in @@ -17,8 +17,8 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i>            <leafNode name="group">              <properties>                <help>Multicast group address for VXLAN interface</help> @@ -35,10 +35,10 @@                </constraint>              </properties>            </leafNode> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> -          #include <include/interface/interface-mac.xml.i> -          #include <include/interface/interface-mtu-1200-16000.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/mac.xml.i> +          #include <include/interface/mtu-1200-16000.xml.i>            <leafNode name="mtu">              <defaultValue>1450</defaultValue>            </leafNode> @@ -52,9 +52,9 @@                    <help>IPv4 specific tunnel parameters</help>                  </properties>                  <children> -                  #include <include/interface/interface-parameters-dont-fragment.xml.i> -                  #include <include/interface/interface-parameters-tos.xml.i> -                  #include <include/interface/interface-parameters-ttl.xml.i> +                  #include <include/interface/parameters-dont-fragment.xml.i> +                  #include <include/interface/parameters-tos.xml.i> +                  #include <include/interface/parameters-ttl.xml.i>                    <leafNode name="ttl">                      <defaultValue>16</defaultValue>                    </leafNode> @@ -65,7 +65,7 @@                    <help>IPv6 specific tunnel parameters</help>                  </properties>                  <children> -                  #include <include/interface/interface-parameters-flowlabel.xml.i> +                  #include <include/interface/parameters-flowlabel.xml.i>                  </children>                </node>                <leafNode name="nolearning"> @@ -76,23 +76,14 @@                </leafNode>              </children>            </node> +          #include <include/port-number.xml.i>            <leafNode name="port"> -            <properties> -              <help>Destination port of VXLAN tunnel (default: 8472)</help> -              <valueHelp> -                <format>u32:1-65535</format> -                <description>Numeric IP port</description> -              </valueHelp> -              <constraint> -                <validator name="numeric" argument="--range 1-65535"/> -              </constraint> -            </properties>              <defaultValue>8472</defaultValue>            </leafNode>            #include <include/source-address-ipv4-ipv6.xml.i>            #include <include/source-interface.xml.i>            #include <include/interface/tunnel-remote.xml.i> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>            #include <include/vni.xml.i>          </children>        </tagNode> diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index 773bde09c..ecb4cf331 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -17,16 +17,16 @@          </properties>          <children>            #include <include/interface/address-ipv4-ipv6.xml.i> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/vrf.xml.i>            #include <include/port-number.xml.i> -          #include <include/interface/interface-mtu-68-16000.xml.i> +          #include <include/interface/mtu-68-16000.xml.i>            <leafNode name="mtu">              <defaultValue>1420</defaultValue>            </leafNode> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i>            <leafNode name="fwmark">              <properties>                <help>A 32-bit fwmark value set on all outgoing packets</help> @@ -102,18 +102,7 @@                    </constraint>                  </properties>                </leafNode> -              <leafNode name="port"> -                <properties> -                  <help>Port number used for tunnel endpoint</help> -                  <valueHelp> -                    <format>u32:1-65535</format> -                    <description>Numeric IP port</description> -                  </valueHelp> -                  <constraint> -                    <validator name="numeric" argument="--range 1-65535"/> -                  </constraint> -                </properties> -              </leafNode> +              #include <include/port-number.xml.i>                <leafNode name="persistent-keepalive">                  <properties>                    <help>Interval to send keepalive messages</help> diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in index aaeb285f1..c96d9b78d 100644 --- a/interface-definitions/interfaces-wireless.xml.in +++ b/interface-definitions/interfaces-wireless.xml.in @@ -464,7 +464,7 @@                <constraintErrorMessage>Invalid ISO/IEC 3166-1 Country Code</constraintErrorMessage>              </properties>            </leafNode> -          #include <include/interface/interface-description.xml.i> +          #include <include/interface/description.xml.i>            #include <include/interface/dhcp-options.xml.i>            #include <include/interface/dhcpv6-options.xml.i>            <leafNode name="disable-broadcast-ssid"> @@ -473,25 +473,25 @@                <valueless/>              </properties>            </leafNode> -          #include <include/interface/interface-disable-link-detect.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/disable-link-detect.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/vrf.xml.i>            <leafNode name="expunge-failing-stations">              <properties>                <help>Disassociate stations based on excessive transmission failures</help>                <valueless/>              </properties>            </leafNode> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> -          #include <include/interface/interface-hw-id.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/hw-id.xml.i>            <leafNode name="isolate-stations">              <properties>                <help>Isolate stations on the AP so they cannot see each other</help>                <valueless/>              </properties>            </leafNode> -          #include <include/interface/interface-mac.xml.i> +          #include <include/interface/mac.xml.i>            <leafNode name="max-stations">              <properties>                <help>Maximum number of wireless radio stations. Excess stations will be rejected upon authentication request.</help> diff --git a/interface-definitions/interfaces-wwan.xml.in b/interface-definitions/interfaces-wwan.xml.in index ea3184a11..6b6fa1a66 100644 --- a/interface-definitions/interfaces-wwan.xml.in +++ b/interface-definitions/interfaces-wwan.xml.in @@ -28,17 +28,17 @@            #include <include/interface/dhcp-options.xml.i>            #include <include/interface/dhcpv6-options.xml.i>            #include <include/interface/authentication.xml.i> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> -          #include <include/interface/interface-vrf.xml.i> -          #include <include/interface/interface-disable-link-detect.xml.i> -          #include <include/interface/interface-mtu-68-1500.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i> +          #include <include/interface/vrf.xml.i> +          #include <include/interface/disable-link-detect.xml.i> +          #include <include/interface/mtu-68-1500.xml.i>            <leafNode name="mtu">              <defaultValue>1430</defaultValue>            </leafNode> -          #include <include/interface/interface-ipv4-options.xml.i> -          #include <include/interface/interface-ipv6-options.xml.i> -          #include <include/interface/interface-dial-on-demand.xml.i> +          #include <include/interface/ipv4-options.xml.i> +          #include <include/interface/ipv6-options.xml.i> +          #include <include/interface/dial-on-demand.xml.i>          </children>        </tagNode>      </children> diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in index 2bfac900b..a518a9def 100644 --- a/interface-definitions/ntp.xml.in +++ b/interface-definitions/ntp.xml.in @@ -82,7 +82,7 @@              </children>            </node>            #include <include/listen-address.xml.i> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </node>      </children> diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in index 3769c3748..86445b65d 100644 --- a/interface-definitions/policy-local-route.xml.in +++ b/interface-definitions/policy-local-route.xml.in @@ -40,6 +40,18 @@                    </leafNode>                  </children>                </node> +              <leafNode name="fwmark"> +                <properties> +                  <help>Match fwmark value</help> +                  <valueHelp> +                    <format>u32:1-2147483647</format> +                    <description>Address to match against</description> +                  </valueHelp> +                  <constraint> +                    <validator name="numeric" argument="--range 1-2147483647"/> +                  </constraint> +                </properties> +              </leafNode>                <leafNode name="source">                  <properties>                    <help>Source address or prefix</help> diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index fb62d2f89..cf65daf00 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -139,7 +139,7 @@        </tagNode>        <tagNode name="as-path-list">          <properties> -          <help>Border Gateway Protocol (BGP) autonomous system path filter</help> +          <help>Add a BGP autonomous system path filter</help>            <valueHelp>              <format>txt</format>              <description>AS path list name</description> @@ -176,10 +176,10 @@        </tagNode>        <tagNode name="community-list">          <properties> -          <help>Border Gateway Protocol (BGP) autonomous system path filter</help> +          <help>Add a BGP community list entry</help>            <valueHelp>              <format>txt</format> -            <description>Border Gateway Protocol (BGP) community-list filter</description> +            <description>BGP community-list name</description>            </valueHelp>          </properties>          <children> @@ -236,11 +236,15 @@        </tagNode>        <tagNode name="extcommunity-list">          <properties> -          <help>Border Gateway Protocol (BGP) extended community-list filter</help> +          <help>Add a BGP extended community list entry</help>            <valueHelp>              <format>txt</format> -            <description>Border Gateway Protocol (BGP) extended community-list filter</description> +            <description>BGP extended community-list name</description>            </valueHelp> +          <constraint> +            <regex>^[-_a-zA-Z0-9]+$</regex> +          </constraint> +          <constraintErrorMessage>Should be an alphanumeric name</constraintErrorMessage>          </properties>          <children>            #include <include/generic-description.xml.i> @@ -281,11 +285,15 @@        </tagNode>        <tagNode name="large-community-list">          <properties> -          <help>Border Gateway Protocol (BGP) large-community-list filter</help> +          <help>Add a BGP large community list entry</help>            <valueHelp>              <format>txt</format> -            <description>Border Gateway Protocol (BGP) large-community-list filter</description> +            <description>BGP large-community-list name</description>            </valueHelp> +          <constraint> +            <regex>^[-_a-zA-Z0-9]+$</regex> +          </constraint> +          <constraintErrorMessage>Should be an alphanumeric name</constraintErrorMessage>          </properties>          <children>            #include <include/generic-description.xml.i> @@ -307,9 +315,17 @@                  <properties>                    <help>Regular expression to match against a large community list</help>                    <valueHelp> -                    <format><aa:nn:nn></format> -                    <description>Large Community value</description> +                    <format>ASN:NN:NN</format> +                    <description>BGP large-community-list filter</description>                    </valueHelp> +                  <valueHelp> +                    <format>IP:NN:NN</format> +                    <description>BGP large-community-list filter (IPv4 address format)</description> +                  </valueHelp> +                  <constraint> +                    <validator name="bgp-large-community-list"/> +                  </constraint> +                  <constraintErrorMessage>Malformed large-community-list</constraintErrorMessage>                  </properties>                </leafNode>              </children> @@ -808,7 +824,7 @@                    </leafNode>                    <leafNode name="origin">                      <properties> -                      <help>Border Gateway Protocol (BGP) origin code to match</help> +                      <help>BGP origin code to match</help>                        <completionHelp>                          <list>egp igp incomplete</list>                        </completionHelp> @@ -910,7 +926,7 @@                  <children>                    <node name="aggregator">                      <properties> -                      <help>Border Gateway Protocol (BGP) aggregator attribute</help> +                      <help>BGP aggregator attribute</help>                      </properties>                      <children>                        <leafNode name="as"> @@ -959,13 +975,13 @@                    </leafNode>                    <leafNode name="atomic-aggregate">                      <properties> -                      <help>Border Gateway Protocol (BGP) atomic aggregate attribute</help> +                      <help>BGP atomic aggregate attribute</help>                        <valueless/>                      </properties>                    </leafNode>                    <node name="comm-list">                      <properties> -                      <help>Border Gateway Protocol (BGP) communities matching a community-list</help> +                      <help>BGP communities matching a community-list</help>                      </properties>                      <children>                        <leafNode name="comm-list"> @@ -1162,9 +1178,21 @@                        </completionHelp>                      </properties>                    </leafNode> +                  <leafNode name="large-comm-list-delete"> +                    <properties> +                      <help>Delete BGP communities matching the large community-list</help> +                      <completionHelp> +                        <path>policy large-community-list</path> +                      </completionHelp> +                      <valueHelp> +                        <format>txt</format> +                        <description>BGP large community-list</description> +                      </valueHelp> +                    </properties> +                  </leafNode>                    <leafNode name="local-preference">                      <properties> -                      <help>Border Gateway Protocol (BGP) local preference attribute</help> +                      <help>BGP local preference attribute</help>                        <valueHelp>                          <format>u32:0-4294967295</format>                          <description>Local preference value</description> @@ -1234,7 +1262,7 @@                    </leafNode>                    <leafNode name="originator-id">                      <properties> -                      <help>Border Gateway Protocol (BGP) originator ID attribute</help> +                      <help>BGP originator ID attribute</help>                        <valueHelp>                          <format>ipv4</format>                          <description>Orignator IP address</description> @@ -1287,7 +1315,7 @@                    </leafNode>                    <leafNode name="weight">                      <properties> -                      <help>Border Gateway Protocol (BGP) weight attribute</help> +                      <help>BGP weight attribute</help>                        <valueHelp>                          <format>u32:0-4294967295</format>                          <description>BGP weight</description> diff --git a/interface-definitions/service_console-server.xml.in b/interface-definitions/service_console-server.xml.in index 78eb2d0ba..28aa7ea71 100644 --- a/interface-definitions/service_console-server.xml.in +++ b/interface-definitions/service_console-server.xml.in @@ -27,7 +27,7 @@                </constraint>              </properties>              <children> -              #include <include/interface/interface-description.xml.i> +              #include <include/interface/description.xml.i>                <leafNode name="speed">                  <properties>                    <help>Serial port baud rate</help> diff --git a/interface-definitions/service_pppoe-server.xml.in b/interface-definitions/service_pppoe-server.xml.in index 7b96b5692..79042e0f3 100644 --- a/interface-definitions/service_pppoe-server.xml.in +++ b/interface-definitions/service_pppoe-server.xml.in @@ -8,14 +8,8 @@            <priority>900</priority>          </properties>          <children> +          #include <include/pppoe-access-concentrator.xml.i>            <leafNode name="access-concentrator"> -            <properties> -              <help>Access concentrator name</help> -              <constraint> -                <regex>[a-zA-Z0-9]{1,100}</regex> -              </constraint> -              <constraintErrorMessage>access-concentrator name limited to alphanumerical characters only (max. 100)</constraintErrorMessage> -            </properties>              <defaultValue>vyos-ac</defaultValue>            </leafNode>            <node name="authentication"> @@ -129,7 +123,7 @@                <constraint>                  <regex>[a-zA-Z0-9\-]{1,100}</regex>                </constraint> -              <constraintErrorMessage>servicename can contain aplhanumerical characters and dashes only (max. 100)</constraintErrorMessage> +              <constraintErrorMessage>Service-name can contain aplhanumerical characters and dashes only (max. 100)</constraintErrorMessage>                <multi/>              </properties>            </leafNode> diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in index 7cb0f7ece..64747420b 100644 --- a/interface-definitions/service_webproxy.xml.in +++ b/interface-definitions/service_webproxy.xml.in @@ -83,17 +83,8 @@                        <valueless/>                      </properties>                    </leafNode> +                  #include <include/port-number.xml.i>                    <leafNode name="port"> -                    <properties> -                      <help>LDAP server port to use (default: 389)</help> -                      <valueHelp> -                        <format>u32:1-65535</format> -                        <description>Port number to use</description> -                      </valueHelp> -                      <constraint> -                        <validator name="numeric" argument="--range 1-65535"/> -                      </constraint> -                    </properties>                      <defaultValue>389</defaultValue>                    </leafNode>                    <leafNode name="server"> @@ -214,7 +205,7 @@                  <properties>                    <help>Cache peer options (default: "no-query default")</help>                    <valueHelp> -                    <format>text</format> +                    <format>txt</format>                      <description>Cache peer options</description>                    </valueHelp>                  </properties> diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in index 2654449a1..b0b7768d2 100644 --- a/interface-definitions/snmp.xml.in +++ b/interface-definitions/snmp.xml.in @@ -646,7 +646,7 @@                </tagNode>              </children>            </node> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </node>      </children> diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in index 54742f1d0..c447f144d 100644 --- a/interface-definitions/ssh.xml.in +++ b/interface-definitions/ssh.xml.in @@ -146,7 +146,7 @@                </constraint>              </properties>            </leafNode> -          #include <include/interface/interface-vrf.xml.i> +          #include <include/interface/vrf.xml.i>          </children>        </node>      </children> diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in index fa73df3db..daa4177c9 100644 --- a/interface-definitions/system-conntrack.xml.in +++ b/interface-definitions/system-conntrack.xml.in @@ -37,65 +37,51 @@            </leafNode>            <node name="modules">              <properties> -              <help>Connection tracking modules settings</help> +              <help>Connection tracking modules</help>              </properties>              <children> -              <node name="ftp"> +              <leafNode name="ftp">                  <properties> -                  <help>FTP connection tracking settings</help> +                  <help>FTP connection tracking</help> +                  <valueless/>                  </properties> -                <children> -                  #include <include/conntrack-module-disable.xml.i> -                </children> -              </node> -              <node name="h323"> +              </leafNode> +              <leafNode name="h323">                  <properties> -                  <help>H.323 connection tracking settings</help> +                  <help>H.323 connection tracking</help> +                  <valueless/>                  </properties> -                <children> -                  #include <include/conntrack-module-disable.xml.i> -                </children> -              </node> -              <node name="nfs"> +              </leafNode> +              <leafNode name="nfs">                  <properties> -                  <help>NFS connection tracking settings</help> +                  <help>NFS connection tracking</help> +                  <valueless/>                  </properties> -                <children> -                  #include <include/conntrack-module-disable.xml.i> -                </children> -              </node> -              <node name="pptp"> +              </leafNode> +              <leafNode name="pptp">                  <properties> -                  <help>PPTP connection tracking settings</help> +                  <help>PPTP connection tracking</help> +                  <valueless/>                  </properties> -                <children> -                  #include <include/conntrack-module-disable.xml.i> -                </children> -              </node> -              <node name="sip"> +              </leafNode> +              <leafNode name="sip">                  <properties> -                  <help>SIP connection tracking settings</help> +                  <help>SIP connection tracking</help> +                  <valueless/>                  </properties> -                <children> -                  #include <include/conntrack-module-disable.xml.i> -                </children> -              </node> -              <node name="sqlnet"> +              </leafNode> +              <leafNode name="sqlnet">                  <properties> -                  <help>SQLnet connection tracking settings</help> +                  <help>SQLnet connection tracking</help> +                  <valueless/>                  </properties> -                <children> -                  #include <include/conntrack-module-disable.xml.i> -                </children> -              </node> -              <node name="tftp"> +              </leafNode> +              <leafNode name="tftp">                  <properties> -                  <help>TFTP connection tracking settings</help> +                  <help>TFTP connection tracking</help> +                  <valueless/>                  </properties> -                <children> -                  #include <include/conntrack-module-disable.xml.i> -                </children> -              </node> +              </leafNode>              </children>            </node>            <leafNode name="table-size"> diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 86db3f368..fb34b7199 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -145,7 +145,7 @@                    </leafNode>                  </children>                </tagNode> -              #include <include/interface/interface-vrf.xml.i> +              #include <include/interface/vrf.xml.i>              </children>            </node>          </children> diff --git a/interface-definitions/system-proxy.xml.in b/interface-definitions/system-proxy.xml.in index 791f41f2f..ade168522 100644 --- a/interface-definitions/system-proxy.xml.in +++ b/interface-definitions/system-proxy.xml.in @@ -15,18 +15,7 @@                </constraint>              </properties>            </leafNode> -          <leafNode name="port"> -            <properties> -              <help>Proxy port</help> -              <valueHelp> -                <format>u32:1-65535</format> -                <description>Numeric IP port</description> -              </valueHelp> -              <constraint> -                <validator name="numeric" argument="--range 1-65535"/> -              </constraint> -            </properties> -          </leafNode> +          #include <include/port-number.xml.i>            <leafNode name="username">              <properties>                <help>Proxy username</help> diff --git a/interface-definitions/system-syslog.xml.in b/interface-definitions/system-syslog.xml.in index f3dcae2f3..9280a43c8 100644 --- a/interface-definitions/system-syslog.xml.in +++ b/interface-definitions/system-syslog.xml.in @@ -195,19 +195,7 @@                </valueHelp>              </properties>              <children> -              <leafNode name="port"> -                <properties> -                  <help>Destination port</help> -                  <valueHelp> -                    <format>u32:1-65535</format> -                    <description>Destination port</description> -                  </valueHelp> -                  <constraint> -                    <validator name="numeric" argument="--range 1-65535"/> -                  </constraint> -                  <constraintErrorMessage>Invalid destination port value</constraintErrorMessage> -                </properties> -              </leafNode> +              #include <include/port-number.xml.i>                <tagNode name="facility">                  <properties>                    <help>Facility for logging</help> diff --git a/interface-definitions/tftp-server.xml.in b/interface-definitions/tftp-server.xml.in index e903e8f4e..037c097ca 100644 --- a/interface-definitions/tftp-server.xml.in +++ b/interface-definitions/tftp-server.xml.in @@ -20,17 +20,8 @@                <valueless/>              </properties>            </leafNode> +          #include <include/port-number.xml.i>            <leafNode name="port"> -            <properties> -              <help>Port number used to listen for connections</help> -              <valueHelp> -                <format>u32:1-65535</format> -                <description>Numeric IP port</description> -              </valueHelp> -              <constraint> -                <validator name="numeric" argument="--range 1-65535"/> -              </constraint> -            </properties>              <defaultValue>69</defaultValue>            </leafNode>            #include <include/listen-address.xml.i> diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index b28c86ae6..b0dba4bce 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -771,11 +771,19 @@                        <help>Pool name used for IP address assignments</help>                        <completionHelp>                          <path>vpn ipsec remote-access pool</path> -                        <list>dhcp</list> +                        <list>dhcp radius</list>                        </completionHelp>                        <valueHelp>                          <format>txt</format> -                        <description>Pool name</description> +                        <description>Name of predefined IP pool</description> +                      </valueHelp> +                      <valueHelp> +                        <format>dhcp</format> +                        <description>Forward requests for virtual IP addresses to a DHCP server</description> +                      </valueHelp> +                      <valueHelp> +                        <format>radius</format> +                        <description>Forward requests for virtual IP addresses to a RADIUS server</description>                        </valueHelp>                        <multi/>                      </properties> diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in index cf31af70f..907bcaadb 100644 --- a/interface-definitions/vpn_l2tp.xml.in +++ b/interface-definitions/vpn_l2tp.xml.in @@ -5,6 +5,7 @@        <node name="l2tp" owner="${vyos_conf_scripts_dir}/vpn_l2tp.py">          <properties>            <help>L2TP Virtual Private Network (VPN)</help> +          <priority>902</priority>          </properties>          <children>            <node name="remote-access"> diff --git a/interface-definitions/vpn_sstp.xml.in b/interface-definitions/vpn_sstp.xml.in index 3576bac90..5cd331d7f 100644 --- a/interface-definitions/vpn_sstp.xml.in +++ b/interface-definitions/vpn_sstp.xml.in @@ -25,7 +25,7 @@                </node>              </children>            </node> -          #include <include/interface/interface-mtu-68-1500.xml.i> +          #include <include/interface/mtu-68-1500.xml.i>            #include <include/accel-ppp/gateway-address.xml.i>            #include <include/accel-ppp/name-server.xml.i>            <node name="client-ip-pool"> diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 9d513945c..2ed50ec5c 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -26,8 +26,8 @@            </valueHelp>          </properties>          <children> -          #include <include/interface/interface-description.xml.i> -          #include <include/interface/interface-disable.xml.i> +          #include <include/interface/description.xml.i> +          #include <include/interface/disable.xml.i>            <node name="protocols">              <properties>                <help>Routing protocol parameters</help> @@ -85,7 +85,20 @@                <constraintErrorMessage>VRF routing table must be in range from 100 to 65535</constraintErrorMessage>              </properties>            </leafNode> -          #include <include/vni.xml.i> +          <leafNode name="vni" owner="${vyos_conf_scripts_dir}/vrf_vni.py $VAR(../@)"> +            <properties> +              <help>Virtual Network Identifier</help> +              <!-- priority must be after BGP --> +              <priority>822</priority> +              <valueHelp> +                <format>0-16777214</format> +                <description>VXLAN virtual network identifier</description> +              </valueHelp> +              <constraint> +                <validator name="numeric" argument="--range 0-16777214"/> +              </constraint> +            </properties> +          </leafNode>          </children>        </tagNode>      </children> diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index bb551296f..7bbe25347 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -45,7 +45,7 @@                      <properties>                        <help>VRRP password</help>                        <valueHelp> -                        <format>text</format> +                        <format>txt</format>                          <description>Password string (up to 8 characters)</description>                        </valueHelp>                        <constraint> @@ -285,7 +285,7 @@                    <multi/>                    <help>Sync group member</help>                    <valueHelp> -                    <format>text</format> +                    <format>txt</format>                      <description>VRRP group name</description>                    </valueHelp>                    <completionHelp> diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/containers.xml.in index a22549dd9..b2b318786 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/containers.xml.in @@ -17,6 +17,19 @@        </node>      </children>    </node> +  <node name="connect"> +    <children> +      <tagNode name="container"> +        <properties> +          <help>Attach to a running container</help> +          <completionHelp> +            <path>container name</path> +          </completionHelp> +        </properties> +        <command>sudo podman exec --interactive --tty "$3" /bin/sh</command> +      </tagNode> +    </children> +  </node>    <node name="delete">      <children>        <node name="container"> @@ -27,6 +40,9 @@            <tagNode name="image">              <properties>                <help>Delete container image</help> +              <completionHelp> +                <script>sudo podman image ls -q</script> +              </completionHelp>              </properties>              <command>sudo ${vyos_op_scripts_dir}/containers_op.py --remove "${4}"</command>            </tagNode> @@ -48,6 +64,15 @@              </properties>              <command>sudo ${vyos_op_scripts_dir}/containers_op.py --image</command>            </leafNode> +          <tagNode name="log"> +            <properties> +              <help>Show logs from a given container</help> +              <completionHelp> +                <path>container name</path> +              </completionHelp> +            </properties> +            <command>sudo podman logs --names "$4"</command> +          </tagNode>            <leafNode name="network">              <properties>                <help>Show available container networks</help> @@ -56,6 +81,52 @@            </leafNode>          </children>        </node> +      <node name="log"> +        <children> +          <tagNode name="container"> +            <properties> +              <help>Show logs from a given container</help> +              <completionHelp> +                <path>container name</path> +              </completionHelp> +            </properties> +            <command>sudo podman logs --names "$4"</command> +          </tagNode> +        </children> +      </node> +    </children> +  </node> +  <node name="restart"> +    <children> +      <tagNode name="container"> +        <properties> +          <help>Restart a given container</help> +          <completionHelp> +            <path>container name</path> +          </completionHelp> +        </properties> +        <command>sudo podman restart "$3"</command> +      </tagNode> +    </children> +  </node> +  <node name="update"> +    <children> +      <node name="container"> +        <properties> +          <help>Update a container image</help> +        </properties> +        <children> +          <tagNode name="image"> +            <properties> +              <help>Delete container image</help> +              <completionHelp> +                <path>container name</path> +              </completionHelp> +            </properties> +            <command>sudo ${vyos_op_scripts_dir}/containers_op.py --update "${4}"</command> +          </tagNode> +        </children> +      </node>      </children>    </node>  </interfaceDefinition> diff --git a/op-mode-definitions/dns-forwarding.xml.in b/op-mode-definitions/dns-forwarding.xml.in index 36fe6b5ef..6574f2319 100644 --- a/op-mode-definitions/dns-forwarding.xml.in +++ b/op-mode-definitions/dns-forwarding.xml.in @@ -59,9 +59,6 @@      </children>    </node>    <node name="reset"> -    <properties> -      <help>Reset a service</help> -    </properties>      <children>        <node name="dns">          <properties> diff --git a/op-mode-definitions/generate-ipsec-profile.xml.in b/op-mode-definitions/generate-ipsec-profile.xml.in index be9227971..8d1051b94 100644 --- a/op-mode-definitions/generate-ipsec-profile.xml.in +++ b/op-mode-definitions/generate-ipsec-profile.xml.in @@ -100,37 +100,6 @@                            </completionHelp>                          </properties>                          <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9"</command> -                        <children> -                          <tagNode name="profile"> -                            <properties> -                              <help>Profile name as seen under system profiles</help> -                              <completionHelp> -                                <list><name></list> -                              </completionHelp> -                            </properties> -                            <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9" --profile "${11}"</command> -                          </tagNode> -                        </children> -                      </tagNode> -                      <tagNode name="profile"> -                        <properties> -                          <help>Profile name as seen under system profiles</help> -                          <completionHelp> -                            <list><name></list> -                          </completionHelp> -                        </properties> -                        <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9"</command> -                        <children> -                          <tagNode name="name"> -                            <properties> -                              <help>Connection name as seen in the VPN application</help> -                              <completionHelp> -                                <list><name></list> -                              </completionHelp> -                            </properties> -                            <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9" --name "${11}"</command> -                          </tagNode> -                        </children>                        </tagNode>                      </children>                    </tagNode> diff --git a/op-mode-definitions/include/bgp/afi-common.xml.i b/op-mode-definitions/include/bgp/afi-common.xml.i index 7fc59f3b0..4d5f56656 100644 --- a/op-mode-definitions/include/bgp/afi-common.xml.i +++ b/op-mode-definitions/include/bgp/afi-common.xml.i @@ -7,23 +7,33 @@      </completionHelp>    </properties>    <children> -    <leafNode name="exact-match"> -      <properties> -        <help>Exact match of the communities</help> -      </properties> -      <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> -    </leafNode> +    #include <include/bgp/exact-match.xml.i>    </children>    <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>  </tagNode>  <tagNode name="large-community">    <properties> -    <help>List of large-community numbers</help> +    <help>Display routes matching the large-communities</help>      <completionHelp>        <list>AA:BB:CC</list>      </completionHelp>    </properties>    <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +  <children> +    #include <include/bgp/exact-match.xml.i> +  </children> +</tagNode> +<tagNode name="large-community-list"> +  <properties> +    <help>Display routes matching the large-community-list</help> +    <completionHelp> +      <path>policy large-community-list</path> +    </completionHelp> +  </properties> +  <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +  <children> +    #include <include/bgp/exact-match.xml.i> +  </children>  </tagNode>  <leafNode name="statistics">    <properties> diff --git a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i index f1b699347..a51595b7f 100644 --- a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i +++ b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i @@ -22,12 +22,7 @@        </properties>        <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>      </leafNode> -    <leafNode name="exact-match"> -      <properties> -        <help>Exact match of the communities</help> -      </properties> -      <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> -    </leafNode> +    #include <include/bgp/exact-match.xml.i>      <leafNode name="graceful-shutdown">        <properties>          <help>Graceful shutdown (well-known community)</help> @@ -105,12 +100,7 @@      </completionHelp>    </properties>    <children> -    <leafNode name="exact-match"> -      <properties> -        <help>Show BGP routes exactly matching specified community list</help> -      </properties> -      <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> -    </leafNode> +    #include <include/bgp/exact-match.xml.i>    </children>    <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>  </tagNode> diff --git a/op-mode-definitions/include/bgp/afi-ipv4-ipv6-vpn.xml.i b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-vpn.xml.i new file mode 100644 index 000000000..ba6edb256 --- /dev/null +++ b/op-mode-definitions/include/bgp/afi-ipv4-ipv6-vpn.xml.i @@ -0,0 +1,23 @@ +<!-- included start from bgp/afi-ipv4-ipv6-vpn.xml.i --> +<tagNode name="vpn"> +  <properties> +    <help>Network in the BGP routing table to display</help> +    <completionHelp> +      <list><x.x.x.x> <x.x.x.x/x> <h:h:h:h:h:h:h:h> <h:h:h:h:h:h:h:h/x></list> +    </completionHelp> +  </properties> +  <children> +    #include <include/bgp/prefix-bestpath-multipath.xml.i> +  </children> +  <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<node name="vpn"> +  <properties> +    <help>VPN Address Family modifier</help> +  </properties> +  <children> +    #include <include/bgp/afi-common.xml.i> +    #include <include/bgp/afi-ipv4-ipv6-common.xml.i> +  </children> +</node> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/exact-match.xml.i b/op-mode-definitions/include/bgp/exact-match.xml.i new file mode 100644 index 000000000..49026db9b --- /dev/null +++ b/op-mode-definitions/include/bgp/exact-match.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/exact-match.xml.i --> +<leafNode name="exact-match"> +  <properties> +    <help>Exact match of the communities</help> +  </properties> +  <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/show-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-bgp-common.xml.i index b86b09056..0664b11fc 100644 --- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i +++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i @@ -20,6 +20,7 @@    <children>      #include <include/bgp/afi-common.xml.i>      #include <include/bgp/afi-ipv4-ipv6-common.xml.i> +    #include <include/bgp/afi-ipv4-ipv6-vpn.xml.i>    </children>  </node>  <tagNode name="ipv6"> @@ -41,6 +42,7 @@    <children>      #include <include/bgp/afi-common.xml.i>      #include <include/bgp/afi-ipv4-ipv6-common.xml.i> +    #include <include/bgp/afi-ipv4-ipv6-vpn.xml.i>    </children>  </node>  <node name="l2vpn"> diff --git a/op-mode-definitions/ipv4-route.xml.in b/op-mode-definitions/ipv4-route.xml.in index aab3df0f1..8f001d5bb 100644 --- a/op-mode-definitions/ipv4-route.xml.in +++ b/op-mode-definitions/ipv4-route.xml.in @@ -20,11 +20,7 @@        </node>      </children>    </node> -    <node name="reset"> -    <properties> -      <help>Reset a service</help> -    </properties>      <children>        <node name="ip">          <properties> @@ -56,7 +52,6 @@                </tagNode>              </children>            </node> -            <node name="route">              <properties>                <help>Reset IP route</help> @@ -68,7 +63,6 @@                  </properties>                  <command>sudo ip route flush cache</command>                </leafNode> -                <tagNode name="cache">                  <properties>                    <help>Flush the kernel route cache for a given route</help> diff --git a/op-mode-definitions/ipv6-route.xml.in b/op-mode-definitions/ipv6-route.xml.in index 7f188fdb2..5f20444d4 100644 --- a/op-mode-definitions/ipv6-route.xml.in +++ b/op-mode-definitions/ipv6-route.xml.in @@ -28,11 +28,7 @@        </node>      </children>    </node> -    <node name="reset"> -    <properties> -      <help>Reset a service</help> -    </properties>      <children>        <node name="ipv6">          <properties> @@ -64,7 +60,6 @@                </tagNode>              </children>            </node> -            <node name="route">              <properties>                <help>Reset IPv6 route</help> @@ -76,7 +71,6 @@                  </properties>                  <command>sudo ip -f inet6 route flush cache</command>                </leafNode> -                <tagNode name="cache">                  <properties>                    <help>Flush the kernel IPv6 route cache for a given route</help> diff --git a/op-mode-definitions/monitor-protocol.xml.in b/op-mode-definitions/monitor-protocol.xml.in index 6a6bd50f3..f3af3575c 100644 --- a/op-mode-definitions/monitor-protocol.xml.in +++ b/op-mode-definitions/monitor-protocol.xml.in @@ -263,13 +263,14 @@            </node>            <node name="ospf">              <properties> -              <help>Monitor the Open Shortest Path First (OSPF) protocol</help> +              <help>Monitor Open Shortest Path First (OSPF) protocol</help>              </properties>              <children>                #include <include/monitor-background.xml.i> - -                <node name="disable"> +                <properties> +                  <help>Disable Open Shortest Path First (OSPF) debugging</help> +                </properties>                  <children>                    <node name="event">                      <properties> @@ -458,6 +459,9 @@                  </children>                </node>                <node name="enable"> +                <properties> +                  <help>Enable Open Shortest Path First (OSPF) debugging</help> +                </properties>                  <children>                    <node name="event">                      <properties> diff --git a/op-mode-definitions/nhrp.xml.in b/op-mode-definitions/nhrp.xml.in index 9e746cc35..89508e2be 100644 --- a/op-mode-definitions/nhrp.xml.in +++ b/op-mode-definitions/nhrp.xml.in @@ -50,13 +50,13 @@              <properties>                <help>Show NHRP interface connection information</help>              </properties> -            <command>if [ -f /var/run/opennhrp.pid ]; then sudo opennhrpctl interface show; else echo OpenNHRP is not running.; fi</command> +            <command>if pgrep opennhrp >/dev/null; then sudo opennhrpctl interface show; else echo OpenNHRP is not running; fi</command>            </leafNode>            <leafNode name="tunnel">              <properties>                <help>Show NHRP tunnel connection information</help>              </properties> -            <command>if [ -f /var/run/opennhrp.pid ]; then sudo opennhrpctl show ; else echo OpenNHRP is not running.; fi</command> +            <command>if pgrep opennhrp >/dev/null; then sudo opennhrpctl show ; else echo OpenNHRP is not running; fi</command>            </leafNode>          </children>        </node> diff --git a/op-mode-definitions/openvpn.xml.in b/op-mode-definitions/openvpn.xml.in index 781fbdc9d..73cbbe501 100644 --- a/op-mode-definitions/openvpn.xml.in +++ b/op-mode-definitions/openvpn.xml.in @@ -1,11 +1,11 @@  <?xml version="1.0"?>  <interfaceDefinition>    <node name="reset"> -    <properties> -      <help>Reset a service</help> -    </properties>      <children>        <node name="openvpn"> +        <properties> +          <help>Reset OpenVPN client/server connections</help> +        </properties>          <children>            <tagNode name="client">              <properties> diff --git a/op-mode-definitions/pppoe-server.xml.in b/op-mode-definitions/pppoe-server.xml.in index 6efdc5a48..835e03aab 100644 --- a/op-mode-definitions/pppoe-server.xml.in +++ b/op-mode-definitions/pppoe-server.xml.in @@ -40,9 +40,6 @@      </children>    </node>    <node name="reset"> -    <properties> -      <help>Reset a service</help> -    </properties>      <children>        <node name="pppoe-server">          <properties> diff --git a/op-mode-definitions/reset-conntrack.xml.in b/op-mode-definitions/reset-conntrack.xml.in index 827ba4af4..9c8265f77 100644 --- a/op-mode-definitions/reset-conntrack.xml.in +++ b/op-mode-definitions/reset-conntrack.xml.in @@ -1,9 +1,6 @@  <?xml version="1.0"?>  <interfaceDefinition>    <node name="reset"> -    <properties> -      <help>Reset a service</help> -    </properties>      <children>        <node name="conntrack">          <properties> diff --git a/op-mode-definitions/reset-vpn.xml.in b/op-mode-definitions/reset-vpn.xml.in index 71dbb4ed9..94ee1c7df 100644 --- a/op-mode-definitions/reset-vpn.xml.in +++ b/op-mode-definitions/reset-vpn.xml.in @@ -1,9 +1,6 @@  <?xml version="1.0"?>  <interfaceDefinition>    <node name="reset"> -    <properties> -      <help>Reset a service</help> -    </properties>      <children>        <node name="vpn">          <properties> diff --git a/op-mode-definitions/restart-frr.xml.in b/op-mode-definitions/restart-frr.xml.in index 96ad1a650..475bd1ee8 100644 --- a/op-mode-definitions/restart-frr.xml.in +++ b/op-mode-definitions/restart-frr.xml.in @@ -2,62 +2,66 @@  <interfaceDefinition>    <node name="restart">      <children> -      <node name="frr"> +      <leafNode name="all">          <properties> -          <help>Restart FRRouting daemons</help> +          <help>Restart all routing daemons</help>          </properties>          <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart</command> -        <children> -          <leafNode name="bfdd"> -            <properties> -              <help>Restart Bidirectional Forwarding Detection daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd</command> -          </leafNode> -          <leafNode name="bgpd"> -            <properties> -              <help>Restart Border Gateway Protocol daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd</command> -          </leafNode> -          <leafNode name="ospfd"> -            <properties> -              <help>Restart OSPFv2 daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospfd</command> -          </leafNode> -          <leafNode name="ospf6d"> -            <properties> -              <help>Restart OSPFv3 daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospf6d</command> -          </leafNode> -          <leafNode name="ripd"> -            <properties> -              <help>Restart Routing Information Protocol daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripd</command> -          </leafNode> -          <leafNode name="ripngd"> -            <properties> -              <help>Restart RIPng daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripngd</command> -          </leafNode> -          <leafNode name="staticd"> -            <properties> -              <help>Restart Static Route daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd</command> -          </leafNode> -          <leafNode name="zebra"> -            <properties> -              <help>Restart IP routing manager daemon</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra</command> -          </leafNode> -        </children> -      </node> +      </leafNode> +      <leafNode name="bfd"> +        <properties> +          <help>Restart Bidirectional Forwarding Detection (BFD) daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd</command> +      </leafNode> +      <leafNode name="bgp"> +        <properties> +          <help>Restart Border Gateway Protocol (BGP) routing daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd</command> +      </leafNode> +      <leafNode name="isis"> +        <properties> +          <help>Restart Intermediate System to Intermediate System (IS-IS) routing daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon isisd</command> +      </leafNode> +      <leafNode name="ospf"> +        <properties> +          <help>Restart Open Shortest Path First (OSPF) routing daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospfd</command> +      </leafNode> +      <leafNode name="ospfv3"> +        <properties> +          <help>Restart IPv6 Open Shortest Path First (OSPFv3) routing daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ospf6d</command> +      </leafNode> +      <leafNode name="rip"> +        <properties> +          <help>Restart Routing Information Protocol (RIP) routing daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripd</command> +      </leafNode> +      <leafNode name="ripng"> +        <properties> +          <help>Restart Routing Information Protocol NG (RIPng) routing daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripngd</command> +      </leafNode> +      <leafNode name="static"> +        <properties> +          <help>Restart static routing daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd</command> +      </leafNode> +      <leafNode name="zebra"> +        <properties> +          <help>Restart Routing Information Base (RIB) manager daemon</help> +        </properties> +        <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra</command> +      </leafNode>      </children>    </node>  </interfaceDefinition> diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in index 5e9bf719e..18a28868d 100644 --- a/op-mode-definitions/show-system.xml.in +++ b/op-mode-definitions/show-system.xml.in @@ -55,12 +55,6 @@              </properties>              <command>${vyos_op_scripts_dir}/show_cpu.py</command>            </leafNode> -          <leafNode name= "integrity"> -            <properties> -              <help>Checks overall system integrity</help> -            </properties> -            <command>sudo ${vyos_op_scripts_dir}/show_system_integrity.py</command> -          </leafNode>            <leafNode name="kernel-messages">              <properties>                <help>Show messages in kernel ring buffer</help> diff --git a/op-mode-definitions/show-vpn.xml.in b/op-mode-definitions/show-vpn.xml.in deleted file mode 100644 index 3fbc74ad1..000000000 --- a/op-mode-definitions/show-vpn.xml.in +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> -  <node name="show"> -    <children> -      <node name="vpn"> -       <properties> -          <help>Show active remote access Virtual Private Network (VPN) sessions</help> -       </properties> -       <children> -         <leafNode name="remote-access"> -            <properties> -              <help>Show active VPN server sessions</help> -            </properties> -           <command>${vyos_op_scripts_dir}/show_vpn_ra.py</command> -         </leafNode> -        </children> -      </node> -    </children> -  </node> -</interfaceDefinition> diff --git a/op-mode-definitions/terminal.xml.in b/op-mode-definitions/terminal.xml.in index 9c4e629cb..2a76de146 100644 --- a/op-mode-definitions/terminal.xml.in +++ b/op-mode-definitions/terminal.xml.in @@ -40,7 +40,6 @@          </properties>          <command>builtin $3</command>        </tagNode> -        <node name="console">          <properties>            <help>Control console behaviors</help> @@ -54,13 +53,11 @@            </leafNode>          </children>        </node> -        <node name="terminal">          <properties>            <help>Control terminal behaviors</help>          </properties>          <children> -            <node name="key">              <properties>                <help>Set key behaviors</help> @@ -77,7 +74,6 @@                </tagNode>              </children>            </node> -            <node name="pager">              <properties>                <help>Set terminal pager to default (less)</help> @@ -93,7 +89,6 @@              </properties>              <command>VYATTA_PAGER=$4</command>            </tagNode> -            <tagNode name="length">              <properties>                <help>Set terminal to given number of rows (0 disables paging)</help> @@ -103,7 +98,6 @@              </properties>              <command>if [ "$4" -eq 0 ]; then VYATTA_PAGER=cat; else VYATTA_PAGER=${_vyatta_default_pager}; stty rows $4; fi</command>            </tagNode> -            <tagNode name="width">              <properties>                <help>Set terminal to given number of columns</help> @@ -117,6 +111,4 @@        </node>      </children>    </node> - -  </interfaceDefinition> diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 20f275e9b..3d997c143 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -140,6 +140,12 @@                  </properties>                  <command>sudo ip xfrm policy list</command>                </node> +              <leafNode name="remote-access"> +                 <properties> +                   <help>Show active VPN server sessions</help> +                 </properties> +                <command>${vyos_op_scripts_dir}/show_vpn_ra.py</command> +              </leafNode>                <node name="sa">                  <properties>                    <help>Show all active IPSec Security Associations (SA)</help> @@ -178,7 +184,7 @@                      <command>if pgrep charon >/dev/null ; then sudo /usr/sbin/ipsec statusall ; else echo "IPSec process not running" ; fi</command>                    </node>                  </children> -                <command>if pgrep charon >/dev/null ; then sudo /usr/libexec/vyos/op_mode/show_ipsec_sa.py ; else echo "IPSec process not running" ; fi</command> +                <command>if pgrep charon >/dev/null ; then sudo ${vyos_op_scripts_dir}/show_ipsec_sa.py ; else echo "IPSec process not running" ; fi</command>                </node>                <node name="state">                  <properties> diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 0969a5353..e15579b95 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -108,16 +108,20 @@ def leaf_node_changed(conf, path):      """      Check if a leaf node was altered. If it has been altered - values has been      changed, or it was added/removed, we will return a list containing the old -    value(s). If nothing has been changed, None is returned +    value(s). If nothing has been changed, None is returned. + +    NOTE: path must use the real CLI node name (e.g. with a hyphen!)      """      from vyos.configdiff import get_config_diff      D = get_config_diff(conf, key_mangling=('-', '_'))      D.set_level(conf.get_level())      (new, old) = D.get_value_diff(path)      if new != old: +        if old is None: +            return ['']          if isinstance(old, str):              return [old] -        elif isinstance(old, list): +        if isinstance(old, list):              if isinstance(new, str):                  new = [new]              elif isinstance(new, type(None)): diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py index 4279e6982..7f49aa9af 100644 --- a/python/vyos/configverify.py +++ b/python/vyos/configverify.py @@ -237,8 +237,8 @@ def verify_interface_exists(ifname):      Common helper function used by interface implementations to perform      recurring validation if an interface actually exists.      """ -    from netifaces import interfaces -    if ifname not in interfaces(): +    import os +    if not os.path.exists(f'/sys/class/net/{ifname}'):          raise ConfigError(f'Interface "{ifname}" does not exist!')  def verify_source_interface(config): diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index 03006c383..dacdbdef2 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -13,6 +13,7 @@  # You should have received a copy of the GNU Lesser General Public  # License along with this library.  If not, see <http://www.gnu.org/licenses/>. +import os  directories = {    "data": "/usr/share/vyos/", @@ -34,7 +35,7 @@ cfg_vintage = 'vyos'  commit_lock = '/opt/vyatta/config/.lock' -version_file = '/usr/share/vyos/component-versions.json' +component_version_json = os.path.join(directories['data'], 'component-versions.json')  https_data = {      'listen_addresses' : { '*': ['_'] } diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 14f64a8de..27073b266 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -366,5 +366,4 @@ class BridgeIf(Interface):                          cmd = f'bridge vlan add dev {interface} vid {native_vlan_id} pvid untagged master'                          self._cmd(cmd) -        # call base class first          super().update(config) diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index a1928ba51..45a220e22 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -52,6 +52,10 @@ from vyos.ifconfig.vrrp import VRRP  from vyos.ifconfig.operational import Operational  from vyos.ifconfig import Section +from netaddr import EUI +from netaddr import mac_unix_expanded +from random import getrandbits +  class Interface(Control):      # This is the class which will be used to create      # self.operational, it allows subclasses, such as @@ -389,6 +393,31 @@ class Interface(Control):          """          return self.get_interface('mac') +    def get_mac_synthetic(self): +        """ +        Get a synthetic MAC address. This is a common method which can be called +        from derived classes to overwrite the get_mac() call in a generic way. + +        NOTE: Tunnel interfaces have no "MAC" address by default. The content +              of the 'address' file in /sys/class/net/device contains the +              local-ip thus we generate a random MAC address instead + +        Example: +        >>> from vyos.ifconfig import Interface +        >>> Interface('eth0').get_mac() +        '00:50:ab:cd:ef:00' +        """ +        # we choose 40 random bytes for the MAC address, this gives +        # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') +        tmp = EUI(getrandbits(48)).value +        # set locally administered bit in MAC address +        tmp |= 0xf20000000000 +        # convert integer to "real" MAC address representation +        mac = EUI(hex(tmp).split('x')[-1]) +        # change dialect to use : as delimiter instead of - +        mac.dialect = mac_unix_expanded +        return str(mac) +      def set_mac(self, mac):          """          Set interface MAC (Media Access Contrl) address to given value. @@ -436,6 +465,62 @@ class Interface(Control):          """          return self.set_interface('arp_cache_tmo', tmo) +    def set_tcp_ipv4_mss(self, mss): +        """ +        Set IPv4 TCP MSS value advertised when TCP SYN packets leave this +        interface. Value is in bytes. + +        A value of 0 will disable the MSS adjustment + +        Example: +        >>> from vyos.ifconfig import Interface +        >>> Interface('eth0').set_tcp_ipv4_mss(1340) +        """ +        iptables_bin = 'iptables' +        base_options = f'-A FORWARD -o {self.ifname} -p tcp -m tcp --tcp-flags SYN,RST SYN' +        out = self._cmd(f'{iptables_bin}-save -t mangle') +        for line in out.splitlines(): +            if line.startswith(base_options): +                # remove OLD MSS mangling configuration +                line = line.replace('-A FORWARD', '-D FORWARD') +                self._cmd(f'{iptables_bin} -t mangle {line}') + +        cmd_mss = f'{iptables_bin} -t mangle {base_options} --jump TCPMSS' +        if mss == 'clamp-mss-to-pmtu': +            self._cmd(f'{cmd_mss} --clamp-mss-to-pmtu') +        elif int(mss) > 0: +            # probably add option to clamp only if bigger: +            low_mss = str(int(mss) + 1) +            self._cmd(f'{cmd_mss} -m tcpmss --mss {low_mss}:65535 --set-mss {mss}') + +    def set_tcp_ipv6_mss(self, mss): +        """ +        Set IPv6 TCP MSS value advertised when TCP SYN packets leave this +        interface. Value is in bytes. + +        A value of 0 will disable the MSS adjustment + +        Example: +        >>> from vyos.ifconfig import Interface +        >>> Interface('eth0').set_tcp_mss(1320) +        """ +        iptables_bin = 'ip6tables' +        base_options = f'-A FORWARD -o {self.ifname} -p tcp -m tcp --tcp-flags SYN,RST SYN' +        out = self._cmd(f'{iptables_bin}-save -t mangle') +        for line in out.splitlines(): +            if line.startswith(base_options): +                # remove OLD MSS mangling configuration +                line = line.replace('-A FORWARD', '-D FORWARD') +                self._cmd(f'{iptables_bin} -t mangle {line}') + +        cmd_mss = f'{iptables_bin} -t mangle {base_options} --jump TCPMSS' +        if mss == 'clamp-mss-to-pmtu': +            self._cmd(f'{cmd_mss} --clamp-mss-to-pmtu') +        elif int(mss) > 0: +            # probably add option to clamp only if bigger: +            low_mss = str(int(mss) + 1) +            self._cmd(f'{cmd_mss} -m tcpmss --mss {low_mss}:65535 --set-mss {mss}') +      def set_arp_filter(self, arp_filter):          """          Filter ARP requests @@ -1202,6 +1287,16 @@ class Interface(Control):              # checked before              self.set_vrf(config.get('vrf', '')) +        # Configure MSS value for IPv4 TCP connections +        tmp = dict_search('ip.adjust_mss', config) +        value = tmp if (tmp != None) else '0' +        self.set_tcp_ipv4_mss(value) + +        # Configure MSS value for IPv6 TCP connections +        tmp = dict_search('ipv6.adjust_mss', config) +        value = tmp if (tmp != None) else '0' +        self.set_tcp_ipv6_mss(value) +          # Configure ARP cache timeout in milliseconds - has default value          tmp = dict_search('ip.arp_cache_timeout', config)          value = tmp if (tmp != None) else '30' diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py index 65575cf99..9153863de 100644 --- a/python/vyos/ifconfig/pppoe.py +++ b/python/vyos/ifconfig/pppoe.py @@ -1,4 +1,4 @@ -# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2020-2021 VyOS maintainers and contributors <maintainers@vyos.io>  #  # This library is free software; you can redistribute it and/or  # modify it under the terms of the GNU Lesser General Public @@ -14,12 +14,11 @@  # License along with this library.  If not, see <http://www.gnu.org/licenses/>.  from vyos.ifconfig.interface import Interface +from vyos.util import get_interface_config  @Interface.register  class PPPoEIf(Interface): -    default = { -        'type': 'pppoe', -    } +    iftype = 'pppoe'      definition = {          **Interface.definition,          **{ @@ -28,7 +27,31 @@ class PPPoEIf(Interface):          },      } -    # stub this interface is created in the configure script +    def _remove_routes(self, vrf=''): +        # Always delete default routes when interface is removed +        if vrf: +            vrf = f'-c "vrf {vrf}"' +        self._cmd(f'vtysh -c "conf t" {vrf} -c "no ip route 0.0.0.0/0 {self.ifname} tag 210"') +        self._cmd(f'vtysh -c "conf t" {vrf} -c "no ipv6 route ::/0 {self.ifname} tag 210"') + +    def remove(self): +        """ +        Remove interface from operating system. Removing the interface +        deconfigures all assigned IP addresses and clear possible DHCP(v6) +        client processes. +        Example: +        >>> from vyos.ifconfig import Interface +        >>> i = Interface('pppoe0') +        >>> i.remove() +        """ + +        tmp = get_interface_config(self.ifname) +        vrf = '' +        if 'master' in tmp: +            self._remove_routes(tmp['master']) + +        # remove bond master which places members in disabled state +        super().remove()      def _create(self):          # we can not create this interface as it is managed outside @@ -37,3 +60,84 @@ class PPPoEIf(Interface):      def _delete(self):          # we can not create this interface as it is managed outside          pass + +    def del_addr(self, addr): +        # we can not create this interface as it is managed outside +        pass + +    def get_mac(self): +        """ Get a synthetic MAC address. """ +        return self.get_mac_synthetic() + +    def update(self, config): +        """ General helper function which works on a dictionary retrived by +        get_config_dict(). It's main intention is to consolidate the scattered +        interface setup code and provide a single point of entry when workin +        on any interface. """ + +        # remove old routes from an e.g. old VRF assignment +        vrf = '' +        if 'vrf_old' in config: +            vrf = config['vrf_old'] +        self._remove_routes(vrf) + +        # DHCPv6 PD handling is a bit different on PPPoE interfaces, as we do +        # not require an 'address dhcpv6' CLI option as with other interfaces +        if 'dhcpv6_options' in config and 'pd' in config['dhcpv6_options']: +            self.set_dhcpv6(True) +        else: +            self.set_dhcpv6(False) + +        super().update(config) + +        if 'default_route' not in config or config['default_route'] == 'none': +            return + +        # +        # Set default routes pointing to pppoe interface +        # +        vrf = '' +        sed_opt = '^ip route' + +        install_v4 = True +        install_v6 = True + +        # generate proper configuration string when VRFs are in use +        if 'vrf' in config: +            tmp = config['vrf'] +            vrf = f'-c "vrf {tmp}"' +            sed_opt = f'vrf {tmp}' + +        if config['default_route'] == 'auto': +            # only add route if there is no default route present +            tmp = self._cmd(f'vtysh -c "show running-config staticd no-header" | sed -n "/{sed_opt}/,/!/p"') +            for line in tmp.splitlines(): +                line = line.lstrip() +                if line.startswith('ip route 0.0.0.0/0'): +                    install_v4 = False +                    continue + +                if 'ipv6' in config and line.startswith('ipv6 route ::/0'): +                    install_v6 = False +                    continue + +        elif config['default_route'] == 'force': +            # Force means that all static routes are replaced with the ones from this interface +            tmp = self._cmd(f'vtysh -c "show running-config staticd no-header" | sed -n "/{sed_opt}/,/!/p"') +            for line in tmp.splitlines(): +                if self.ifname in line: +                    # It makes no sense to remove a route with our interface and the later re-add it. +                    # This will only make traffic disappear - which is a no-no! +                    continue + +                line = line.lstrip() +                if line.startswith('ip route 0.0.0.0/0'): +                    self._cmd(f'vtysh -c "conf t" {vrf} -c "no {line}"') + +                if 'ipv6' in config and line.startswith('ipv6 route ::/0'): +                    self._cmd(f'vtysh -c "conf t" {vrf} -c "no {line}"') + +        if install_v4: +            self._cmd(f'vtysh -c "conf t" {vrf} -c "ip route 0.0.0.0/0 {self.ifname} tag 210"') +        if install_v6 and 'ipv6' in config: +            self._cmd(f'vtysh -c "conf t" {vrf} -c "ipv6 route ::/0 {self.ifname} tag 210"') diff --git a/python/vyos/ifconfig/section.py b/python/vyos/ifconfig/section.py index 173a90bb4..0e4447b9e 100644 --- a/python/vyos/ifconfig/section.py +++ b/python/vyos/ifconfig/section.py @@ -46,7 +46,7 @@ class Section:          return klass      @classmethod -    def _basename (cls, name, vlan): +    def _basename(cls, name, vlan, vrrp):          """          remove the number at the end of interface name          name: name of the interface @@ -56,16 +56,18 @@ class Section:          name = name.rstrip('.')          if vlan:              name = name.rstrip('0123456789.') +        if vrrp: +            name = name.rstrip('0123456789v')          return name      @classmethod -    def section(cls, name, vlan=True): +    def section(cls, name, vlan=True, vrrp=True):          """          return the name of a section an interface should be under          name: name of the interface (eth0, dum1, ...)          vlan: should we try try to remove the VLAN from the number          """ -        name = cls._basename(name, vlan) +        name = cls._basename(name, vlan, vrrp)          if name in cls._prefixes:              return cls._prefixes[name].definition['section'] @@ -79,8 +81,8 @@ class Section:          return list(set([cls._prefixes[_].definition['section'] for _ in cls._prefixes]))      @classmethod -    def klass(cls, name, vlan=True): -        name = cls._basename(name, vlan) +    def klass(cls, name, vlan=True, vrrp=True): +        name = cls._basename(name, vlan, vrrp)          if name in cls._prefixes:              return cls._prefixes[name]          raise ValueError(f'No type found for interface name: {name}') diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 64c735824..5258a2cb1 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -16,10 +16,6 @@  # https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/  # https://community.hetzner.com/tutorials/linux-setup-gre-tunnel -from netaddr import EUI -from netaddr import mac_unix_expanded -from random import getrandbits -  from vyos.ifconfig.interface import Interface  from vyos.util import dict_search  from vyos.validate import assert_list @@ -163,28 +159,8 @@ class TunnelIf(Interface):          self._cmd(cmd.format(**self.config))      def get_mac(self): -        """ -        Get current interface MAC (Media Access Contrl) address used. - -        NOTE: Tunnel interfaces have no "MAC" address by default. The content -              of the 'address' file in /sys/class/net/device contains the -              local-ip thus we generate a random MAC address instead - -        Example: -        >>> from vyos.ifconfig import Interface -        >>> Interface('eth0').get_mac() -        '00:50:ab:cd:ef:00' -        """ -        # we choose 40 random bytes for the MAC address, this gives -        # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') -        tmp = EUI(getrandbits(48)).value -        # set locally administered bit in MAC address -        tmp |= 0xf20000000000 -        # convert integer to "real" MAC address representation -        mac = EUI(hex(tmp).split('x')[-1]) -        # change dialect to use : as delimiter instead of - -        mac.dialect = mac_unix_expanded -        return str(mac) +        """ Get a synthetic MAC address. """ +        return self.get_mac_synthetic()      def update(self, config):          """ General helper function which works on a dictionary retrived by diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index c4cf2fbbf..28b5e2991 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -17,9 +17,6 @@ import os  import time  from datetime import timedelta -from netaddr import EUI -from netaddr import mac_unix_expanded -from random import getrandbits  from hurry.filesize import size  from hurry.filesize import alternative @@ -159,28 +156,8 @@ class WireGuardIf(Interface):      }      def get_mac(self): -        """ -        Get current interface MAC (Media Access Contrl) address used. - -        NOTE: Tunnel interfaces have no "MAC" address by default. The content -              of the 'address' file in /sys/class/net/device contains the -              local-ip thus we generate a random MAC address instead - -        Example: -        >>> from vyos.ifconfig import Interface -        >>> Interface('eth0').get_mac() -        '00:50:ab:cd:ef:00' -        """ -        # we choose 40 random bytes for the MAC address, this gives -        # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') -        tmp = EUI(getrandbits(48)).value -        # set locally administered bit in MAC address -        tmp |= 0xf20000000000 -        # convert integer to "real" MAC address representation -        mac = EUI(hex(tmp).split('x')[-1]) -        # change dialect to use : as delimiter instead of - -        mac.dialect = mac_unix_expanded -        return str(mac) +        """ Get a synthetic MAC address. """ +        return self.get_mac_synthetic()      def update(self, config):          """ General helper function which works on a dictionary retrived by diff --git a/python/vyos/migrator.py b/python/vyos/migrator.py index 9a5fdef2f..4574bb6d1 100644 --- a/python/vyos/migrator.py +++ b/python/vyos/migrator.py @@ -15,6 +15,7 @@  import sys  import os +import json  import subprocess  import vyos.version  import vyos.defaults @@ -165,6 +166,20 @@ class Migrator(object):                                                      versions_string,                                                      os_version_string) +    def save_json_record(self, component_versions: dict): +        """ +        Write component versions to a json file +        """ +        mask = os.umask(0o113) +        version_file = vyos.defaults.component_version_json +        try: +            with open(version_file, 'w') as f: +                f.write(json.dumps(component_versions, indent=2, sort_keys=True)) +        except OSError: +            pass +        finally: +            os.umask(mask) +      def run(self):          """          Gather component versions from config file and system. @@ -182,6 +197,9 @@ class Migrator(object):          sys_versions = systemversions.get_system_versions() +        # save system component versions in json file for easy reference +        self.save_json_record(sys_versions) +          rev_versions = self.run_migration_scripts(cfg_versions, sys_versions)          if rev_versions != cfg_versions: diff --git a/python/vyos/systemversions.py b/python/vyos/systemversions.py index 5c4deca29..9b3f4f413 100644 --- a/python/vyos/systemversions.py +++ b/python/vyos/systemversions.py @@ -16,15 +16,12 @@  import os  import re  import sys -import json -  import vyos.defaults  def get_system_versions():      """ -    Get component versions from running system: read vyatta directory -    structure for versions, then read vyos JSON file. It is a critical -    error if either migration directory or JSON file is unreadable. +    Get component versions from running system; critical failure if +    unable to read migration directory.      """      system_versions = {} @@ -39,25 +36,4 @@ def get_system_versions():              pair = info.split('@')              system_versions[pair[0]] = int(pair[1]) -    version_dict = {} -    path = vyos.defaults.version_file - -    if os.path.isfile(path): -        with open(path, 'r') as f: -            try: -                version_dict = json.load(f) -            except ValueError as err: -                print(f"\nValue error in {path}: {err}") -                sys.exit(1) - -        for k, v in version_dict.items(): -            if not isinstance(v, int): -                print(f"\nType error in {path}; expecting Dict[str, int]") -                sys.exit(1) -            existing = system_versions.get(k) -            if existing is None: -                system_versions[k] = v -            elif v > existing: -                system_versions[k] = v -      return system_versions diff --git a/python/vyos/template.py b/python/vyos/template.py index 08a5712af..ee6e52e1d 100644 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -406,7 +406,7 @@ def get_esp_ike_cipher(group_config):          'dh-group18' : 'modp8192',          'dh-group19' : 'ecp256',          'dh-group20' : 'ecp384', -        'dh-group21' : 'ecp512', +        'dh-group21' : 'ecp521',          'dh-group22' : 'modp1024s160',          'dh-group23' : 'modp2048s224',          'dh-group24' : 'modp2048s256', diff --git a/python/vyos/util.py b/python/vyos/util.py index 59f9f1c44..8af46a6ee 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -562,12 +562,13 @@ def commit_in_progress():      # Since this will be used in scripts that modify the config outside of the CLI      # framework, those knowingly have root permissions.      # For everything else, we add a safeguard. -    from psutil import process_iter, NoSuchProcess +    from psutil import process_iter +    from psutil import NoSuchProcess +    from getpass import getuser      from vyos.defaults import commit_lock -    idu = cmd('/usr/bin/id -u') -    if idu != '0': -        raise OSError("This functions needs root permissions to return correct results") +    if getuser() != 'root': +        raise OSError('This functions needs to be run as root to return correct results!')      for proc in process_iter():          try: @@ -805,8 +806,16 @@ def make_incremental_progressbar(increment: float):      while True:          yield +def is_systemd_service_active(service): +    """ Test is a specified systemd service is activated. +    Returns True if service is active, false otherwise. +    Copied from: https://unix.stackexchange.com/a/435317 """ +    tmp = cmd(f'systemctl show --value -p ActiveState {service}') +    return bool((tmp == 'active')) +  def is_systemd_service_running(service):      """ Test is a specified systemd service is actually running. -    Returns True if service is running, false otherwise. """ -    tmp = run(f'systemctl is-active --quiet {service}') -    return bool((tmp == 0)) +    Returns True if service is running, false otherwise. +    Copied from: https://unix.stackexchange.com/a/435317 """ +    tmp = cmd(f'systemctl show --value -p SubState {service}') +    return bool((tmp == 'running')) diff --git a/python/vyos/xml/__init__.py b/python/vyos/xml/__init__.py index 0ef0c85ce..e0eacb2d1 100644 --- a/python/vyos/xml/__init__.py +++ b/python/vyos/xml/__init__.py @@ -46,6 +46,8 @@ def is_tag(lpath):  def is_leaf(lpath, flat=True):      return load_configuration().is_leaf(lpath, flat) +def component_versions(): +    return load_configuration().component_versions()  def defaults(lpath, flat=False):      return load_configuration().defaults(lpath, flat) diff --git a/python/vyos/xml/definition.py b/python/vyos/xml/definition.py index f556c5ced..5e0d5282c 100644 --- a/python/vyos/xml/definition.py +++ b/python/vyos/xml/definition.py @@ -30,6 +30,7 @@ class XML(dict):          self[kw.owners] = {}          self[kw.default] = {}          self[kw.tags] = [] +        self[kw.component_version] = {}          dict.__init__(self) @@ -248,6 +249,11 @@ class XML(dict):      # @lru_cache(maxsize=100)      # XXX: need to use cachetool instead - for later +    def component_versions(self) -> dict: +        sort_component = sorted(self[kw.component_version].items(), +                                key = lambda kv: kv[0]) +        return dict(sort_component) +      def defaults(self, lpath, flat):          d = self[kw.default]          for k in lpath: diff --git a/python/vyos/xml/kw.py b/python/vyos/xml/kw.py index 58d47e751..48226ce96 100644 --- a/python/vyos/xml/kw.py +++ b/python/vyos/xml/kw.py @@ -32,6 +32,7 @@ priorities = '[priorities]'  owners = '[owners]'  tags = '[tags]'  default = '[default]' +component_version = '[component_version]'  # nodes diff --git a/python/vyos/xml/load.py b/python/vyos/xml/load.py index 37479c6e1..c3022f3d6 100644 --- a/python/vyos/xml/load.py +++ b/python/vyos/xml/load.py @@ -115,7 +115,12 @@ def _format_nodes(inside, conf, xml):              nodetype = 'tagNode'              nodename = kw.tagNode          elif 'syntaxVersion' in conf.keys(): -            conf.pop('syntaxVersion') +            sv = conf.pop('syntaxVersion') +            if isinstance(sv, list): +                for v in sv: +                    xml[kw.component_version][v['@component']] = v['@version'] +            else: +                xml[kw.component_version][sv['@component']] = sv['@version']              continue          else:              _fatal(conf.keys()) @@ -125,14 +130,20 @@ def _format_nodes(inside, conf, xml):              for node in nodes:                  name = node.pop('@name')                  into = inside + [name] -                r[name] = _format_node(into, node, xml) +                if name in r: +                    r[name].update(_format_node(into, node, xml)) +                else: +                    r[name] = _format_node(into, node, xml)                  r[name][kw.node] = nodename                  xml[kw.tags].append(' '.join(into))          else:              node = nodes              name = node.pop('@name')              into = inside + [name] -            r[name] = _format_node(inside + [name], node, xml) +            if name in r: +                r[name].update(_format_node(inside + [name], node, xml)) +            else: +                r[name] = _format_node(inside + [name], node, xml)              r[name][kw.node] = nodename              xml[kw.tags].append(' '.join(into))      return r diff --git a/scripts/build-command-op-templates b/scripts/build-command-op-templates index c285ee594..a4d6d1d08 100755 --- a/scripts/build-command-op-templates +++ b/scripts/build-command-op-templates @@ -54,7 +54,7 @@ debug = args.debug  try:      xml = ET.parse(input_file)  except Exception as e: -    print("Failed to load interface definition file {0}".format(input_file)) +    print(f"Failed to load interface definition file {input_file}")      print(e)      sys.exit(1) @@ -64,15 +64,15 @@ try:      if not validator.validate(xml):          print(validator.error_log) -        print("Interface definition file {0} does not match the schema!".format(input_file)) +        print(f"Interface definition file {input_file} does not match the schema!")          sys.exit(1)  except Exception as e: -    print("Failed to load the XML schema {0}".format(schema_file)) +    print(f"Failed to load the XML schema {schema_file}")      print(e)      sys.exit(1)  if not os.access(output_dir, os.W_OK): -    print("The output directory {0} is not writeable".format(output_dir)) +    print(f"The output directory {output_dir} is not writeable")      sys.exit(1)  ## If we got this far, everything must be ok and we can convert the file @@ -160,16 +160,16 @@ def process_node(n, tmpl_dir):      my_tmpl_dir.append(name)      if debug: -        print("Name of the node: {};\n Created directory: ".format(name), end="") +        print(f"Name of the node: {name};\n Created directory: ", end="")      os.makedirs(make_path(my_tmpl_dir), exist_ok=True)      props = get_properties(props_elem) +    nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def")      if node_type == "node":          if debug: -          print("Processing node {}".format(name)) +          print(f"Processing node {name}") -        nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def")          # Only create the "node.def" file if it exists but is empty, or if it          # does not exist at all.          if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: @@ -180,19 +180,18 @@ def process_node(n, tmpl_dir):              inner_nodes = children.iterfind("*")              for inner_n in inner_nodes:                  process_node(inner_n, my_tmpl_dir) +      if node_type == "tagNode":          if debug: -          print("Processing tag node {}".format(name)) +          print(f"Processing tagNode {name}")          os.makedirs(make_path(my_tmpl_dir), exist_ok=True) -        nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") -        if not os.path.exists(nodedef_path): +        # Only create the "node.def" file if it exists but is empty, or if it +        # does not exist at all. +        if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0:              with open(nodedef_path, "w") as f:                  f.write('help: {0}\n'.format(props['help'])) -        else: -            # Something has already generated this file -            pass          # Create the inner node.tag part          my_tmpl_dir.append("node.tag") @@ -201,8 +200,12 @@ def process_node(n, tmpl_dir):              print("Created path for the tagNode: {}".format(make_path(my_tmpl_dir)), end="")          # Not sure if we want partially defined tag nodes, write the file unconditionally -        with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f: -            f.write(make_node_def(props, command)) +        nodedef_path = os.path.join(make_path(my_tmpl_dir), "node.def") +        # Only create the "node.def" file if it exists but is empty, or if it +        # does not exist at all. +        if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: +            with open(nodedef_path, "w") as f: +                f.write(make_node_def(props, command))          if children is not None:              inner_nodes = children.iterfind("*") @@ -211,11 +214,11 @@ def process_node(n, tmpl_dir):      else:          # This is a leaf node          if debug: -            print("Processing leaf node {}".format(name)) - -        with open(os.path.join(make_path(my_tmpl_dir), "node.def"), "w") as f: -            f.write(make_node_def(props, command)) +            print(f"Processing leaf node {name}") +        if not os.path.exists(nodedef_path) or os.path.getsize(nodedef_path) == 0: +            with open(nodedef_path, "w") as f: +                f.write(make_node_def(props, command))  root = xml.getroot() diff --git a/scripts/build-component-versions b/scripts/build-component-versions deleted file mode 100755 index 5362dbdd4..000000000 --- a/scripts/build-component-versions +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import argparse -import json - -from lxml import etree as ET - -parser = argparse.ArgumentParser() -parser.add_argument('INPUT_DIR', type=str, -                    help="Directory containing XML interface definition files") -parser.add_argument('OUTPUT_DIR', type=str, -                    help="Output directory for JSON file") - -args = parser.parse_args() - -input_dir = args.INPUT_DIR -output_dir = args.OUTPUT_DIR - -version_dict = {} - -for filename in os.listdir(input_dir): -    filepath = os.path.join(input_dir, filename) -    print(filepath) -    try: -        xml = ET.parse(filepath) -    except Exception as e: -        print("Failed to load interface definition file {0}".format(filename)) -        print(e) -        sys.exit(1) - -    root = xml.getroot() -    version_data = root.iterfind("syntaxVersion") -    for ver in version_data: -        component = ver.get("component") -        version = int(ver.get("version")) - -        v = version_dict.get(component) -        if v is None: -            version_dict[component] = version -        elif version > v: -            version_dict[component] = version - -out_file = os.path.join(output_dir, 'component-versions.json') -with open(out_file, 'w') as f: -    json.dump(version_dict, f, indent=4, sort_keys=True) diff --git a/scripts/override-default b/scripts/override-default index c8a0ff1da..0c49087c8 100755 --- a/scripts/override-default +++ b/scripts/override-default @@ -27,6 +27,7 @@  import sys  import glob  import logging +from copy import deepcopy  from lxml import etree  debug = False @@ -60,30 +61,55 @@ def override_element(l: list):      for el in parents:          el.getparent().remove(el) +def merge_remaining(l: list, elementtree): +    """ +    Merge (now) single leaf node containing 'defaultValue' with leaf nodes +    of same path and no 'defaultValue'. +    """ +    for p in l: +        p = p.split() +        path_str = f'/interfaceDefinition/*' +        path_list = [] +        for i in range(len(p)): +            path_list.append(f'[@name="{p[i]}"]') +        path_str += '/children/*'.join(path_list) +        rp = elementtree.xpath(path_str) +        if len(rp) > 1: +            for el in rp[1:]: +                # in practice there will only be one child of the path, +                # either defaultValue or Properties, since +                # override_element() has already run +                for child in el: +                    rp[0].append(deepcopy(child)) +                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: str). +    Collect elements with defaultValue tag into dictionary indexed by name +    attributes of ancestor path.      """      for fname in glob.glob(f'{dir_name}/*.xml'):          tree = etree.parse(fname)          root = tree.getroot()          defv = {} -        xpath_str = f'//defaultValue' +        xpath_str = '//defaultValue'          xp = tree.xpath(xpath_str)          for element in xp:              ap = element.xpath('ancestor::*[@name]')              ap_name = [el.get("name") for el in ap] -            ap_path_str = ' '.join(ap_name[:-1]) -            defv.setdefault((ap_name[-1], ap_path_str), []).append(element) +            ap_path_str = ' '.join(ap_name) +            defv.setdefault(ap_path_str, []).append(element)          for k, v in defv.items():              if len(v) > 1: -                logger.info(f"overridding default in {k[0]}, path '{k[1]}'") +                logger.info(f"overridding default in path '{k}'")                  override_element(v) +        to_merge = list(defv) +        merge_remaining(to_merge, tree) +          revised_str = etree.tostring(root, encoding='unicode', pretty_print=True)          with open(f'{fname}', 'w') as f: diff --git a/smoketest/configs/isis-small b/smoketest/configs/isis-small index 2c42ac9c4..247ae32b5 100644 --- a/smoketest/configs/isis-small +++ b/smoketest/configs/isis-small @@ -102,4 +102,3 @@ system {  // 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@7:rpki@1: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.0-rc1 - diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 7f69b8444..edb604dbf 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -556,13 +556,16 @@ class BasicInterfaceTest:              if not self._test_ip:                  self.skipTest('not supported') +            arp_tmo = '300' +            mss = '1420' +              for interface in self._interfaces: -                arp_tmo = '300'                  path = self._base_path + [interface]                  for option in self._options.get(interface, []):                      self.cli_set(path + option.split())                  # Options +                self.cli_set(path + ['ip', 'adjust-mss', mss])                  self.cli_set(path + ['ip', 'arp-cache-timeout', arp_tmo])                  self.cli_set(path + ['ip', 'disable-arp-filter'])                  self.cli_set(path + ['ip', 'disable-forwarding']) @@ -576,54 +579,73 @@ class BasicInterfaceTest:              self.cli_commit()              for interface in self._interfaces: +                base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN' +                out = cmd('sudo iptables-save -t mangle') +                for line in out.splitlines(): +                    if line.startswith(base_options): +                        self.assertIn(f'--set-mss {mss}', line) +                  tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms')                  self.assertEqual(tmp, str((int(arp_tmo) * 1000))) # tmo value is in milli seconds -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_filter') +                proc_base = f'/proc/sys/net/ipv4/conf/{interface}' + +                tmp = read_file(f'{proc_base}/arp_filter')                  self.assertEqual('0', tmp) -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_accept') +                tmp = read_file(f'{proc_base}/arp_accept')                  self.assertEqual('1', tmp) -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_announce') +                tmp = read_file(f'{proc_base}/arp_announce')                  self.assertEqual('1', tmp) -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/arp_ignore') +                tmp = read_file(f'{proc_base}/arp_ignore')                  self.assertEqual('1', tmp) -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/forwarding') +                tmp = read_file(f'{proc_base}/forwarding')                  self.assertEqual('0', tmp) -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/proxy_arp') +                tmp = read_file(f'{proc_base}/proxy_arp')                  self.assertEqual('1', tmp) -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/proxy_arp_pvlan') +                tmp = read_file(f'{proc_base}/proxy_arp_pvlan')                  self.assertEqual('1', tmp) -                tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/rp_filter') +                tmp = read_file(f'{proc_base}/rp_filter')                  self.assertEqual('2', tmp)          def test_interface_ipv6_options(self):              if not self._test_ipv6:                  self.skipTest('not supported') +            mss = '1400' +            dad_transmits = '10' +              for interface in self._interfaces: -                dad_transmits = '10'                  path = self._base_path + [interface]                  for option in self._options.get(interface, []):                      self.cli_set(path + option.split())                  # Options +                self.cli_set(path + ['ipv6', 'adjust-mss', mss])                  self.cli_set(path + ['ipv6', 'disable-forwarding'])                  self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits])              self.cli_commit()              for interface in self._interfaces: -                tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding') +                base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN' +                out = cmd('sudo ip6tables-save -t mangle') +                for line in out.splitlines(): +                    if line.startswith(base_options): +                        self.assertIn(f'--set-mss {mss}', line) + +                proc_base = f'/proc/sys/net/ipv6/conf/{interface}' + +                tmp = read_file(f'{proc_base}/forwarding')                  self.assertEqual('0', tmp) -                tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/dad_transmits') +                tmp = read_file(f'{proc_base}/dad_transmits')                  self.assertEqual(dad_transmits, tmp)          def test_dhcpv6_client_options(self): diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py index 18e49f47f..50f80e7d1 100644 --- a/smoketest/scripts/cli/base_vyostest_shim.py +++ b/smoketest/scripts/cli/base_vyostest_shim.py @@ -20,7 +20,9 @@ from time import sleep  from vyos.configsession import ConfigSession  from vyos.configsession import ConfigSessionError  from vyos import ConfigError +from vyos.defaults import commit_lock  from vyos.util import cmd +from vyos.util import run  save_config = '/tmp/vyos-smoketest-save' @@ -70,21 +72,16 @@ class VyOSUnitTestSHIM:          def cli_commit(self):              self._session.commit() +            # during a commit there is a process opening commit_lock, and run() returns 0 +            while run(f'sudo lsof | grep -q {commit_lock}') == 0: +                sleep(0.250) -        def getFRRconfig(self, string, end='$', endsection='^!'): +        def getFRRconfig(self, string, end='$', endsection='^!', daemon=''):              """ Retrieve current "running configuration" from FRR """ -            command = f'vtysh -c "show run" | sed -n "/^{string}{end}/,/{endsection}/p"' - -            count = 0 -            tmp = '' -            while count < 10 and tmp == '': -                # Let FRR settle after a config change first before harassing it again -                sleep(1) -                tmp = cmd(command) -                count += 1 - -            if self.debug or tmp == '': +            command = f'vtysh -c "show run {daemon} no-header" | sed -n "/^{string}{end}/,/{endsection}/p"' +            out = cmd(command) +            if self.debug:                  import pprint                  print(f'\n\ncommand "{command}" returned:\n') -                pprint.pprint(tmp) -            return tmp +                pprint.pprint(out) +            return out diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py index 3412ebae0..67edce2a0 100755 --- a/smoketest/scripts/cli/test_interfaces_pppoe.py +++ b/smoketest/scripts/cli/test_interfaces_pppoe.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -22,10 +22,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM  from vyos.configsession import ConfigSession  from vyos.configsession import ConfigSessionError -from vyos.util import read_file  config_file = '/etc/ppp/peers/{}' -dhcp6c_config_file = '/run/dhcp6c/dhcp6c.{}.conf'  base_path = ['interfaces', 'pppoe']  def get_config_value(interface, key): @@ -35,25 +33,26 @@ def get_config_value(interface, key):                  return list(line.split())      return [] -def get_dhcp6c_config_value(interface, key): -    tmp = read_file(dhcp6c_config_file.format(interface)) -    tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp) - -    out = [] -    for item in tmp: -        out.append(item.replace(';','')) -    return out - +# add a classmethod to setup a temporaray PPPoE server for "proper" validation  class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):      def setUp(self):          self._interfaces = ['pppoe10', 'pppoe20', 'pppoe30']          self._source_interface = 'eth0'      def tearDown(self): +        # Validate PPPoE client process +        for interface in self._interfaces: +            running = False +            for proc in process_iter(): +                if interface in proc.cmdline(): +                    running = True +                    break +            self.assertTrue(running) +          self.cli_delete(base_path)          self.cli_commit() -    def test_pppoe_client(self): +    def test_01_pppoe_client(self):          # Check if PPPoE dialer can be configured and runs          for interface in self._interfaces:              user = 'VyOS-user-' + interface @@ -71,8 +70,8 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):                  self.cli_commit()              self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) -            # commit changes -            self.cli_commit() +        # commit changes +        self.cli_commit()          # verify configuration file(s)          for interface in self._interfaces: @@ -88,17 +87,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):              tmp = get_config_value(interface, 'ifname')[1]              self.assertEqual(tmp, interface) -            # Check if ppp process is running in the interface in question -            running = False -            for p in process_iter(): -                if "pppd" in p.name(): -                    if interface in p.cmdline(): -                        running = True - -            self.assertTrue(running) - - -    def test_pppoe_clent_disabled_interface(self): +    def test_02_pppoe_client_disabled_interface(self):          # Check if PPPoE Client can be disabled          for interface in self._interfaces:              self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) @@ -106,23 +95,45 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):              self.cli_set(base_path + [interface, 'source-interface', self._source_interface])              self.cli_set(base_path + [interface, 'disable']) -            self.cli_commit() +        self.cli_commit() -        # Validate PPPoE client process -        running = False +        # Validate PPPoE client process - must not run as interfaces are disabled          for interface in self._interfaces: +            running = False              for proc in process_iter():                  if interface in proc.cmdline():                      running = True +                    break +            self.assertFalse(running) + +        # enable PPPoE interfaces +        for interface in self._interfaces: +            self.cli_delete(base_path + [interface, 'disable']) + +        self.cli_commit() + -        self.assertFalse(running) +    def test_03_pppoe_authentication(self): +        # When username or password is set - so must be the other +        for interface in self._interfaces: +            self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) +            self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) +            self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf']) +            # check validate() - if user is set, so must be the password +            with self.assertRaises(ConfigSessionError): +                self.cli_commit() + +            self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) + +        self.cli_commit() -    def test_pppoe_dhcpv6pd(self): +    def test_04_pppoe_dhcpv6pd(self):          # Check if PPPoE dialer can be configured with DHCPv6-PD          address = '1'          sla_id = '0'          sla_len = '8' +          for interface in self._interfaces:              self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos'])              self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) @@ -147,51 +158,8 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):              self.assertEqual(tmp, 'vyos')              tmp = get_config_value(interface, 'password')[1].replace('"', '')              self.assertEqual(tmp, 'vyos') - -            for param in ['+ipv6', 'ipv6cp-use-ipaddr']: -                tmp = get_config_value(interface, param)[0] -                self.assertEqual(tmp, param) - -            # verify DHCPv6 prefix delegation -            # will return: ['delegation', '::/56 infinity;'] -            tmp = get_dhcp6c_config_value(interface, 'prefix')[1].split()[0] # mind the whitespace -            self.assertEqual(tmp, '::/56') -            tmp = get_dhcp6c_config_value(interface, 'prefix-interface')[0].split()[0] -            self.assertEqual(tmp, self._source_interface) -            tmp = get_dhcp6c_config_value(interface, 'ifid')[0] -            self.assertEqual(tmp, address) -            tmp = get_dhcp6c_config_value(interface, 'sla-id')[0] -            self.assertEqual(tmp, sla_id) -            tmp = get_dhcp6c_config_value(interface, 'sla-len')[0] -            self.assertEqual(tmp, sla_len) - -            # Check if ppp process is running in the interface in question -            running = False -            for p in process_iter(): -                if "pppd" in p.name(): -                    running = True -            self.assertTrue(running) - -            # We can not check if wide-dhcpv6 process is running as it is started -            # after the PPP interface gets a link to the ISP - but we can see if -            # it would be started by the scripts -            tmp = read_file(f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{interface}') -            tmp = re.findall(f'systemctl restart dhcp6c@{interface}.service', tmp) -            self.assertTrue(tmp) - -    def test_pppoe_authentication(self): -        # When username or password is set - so must be the other -        interface = 'pppoe0' -        self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) -        self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) -        self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf']) - -        # check validate() - if user is set, so must be the password -        with self.assertRaises(ConfigSessionError): -            self.cli_commit() - -        self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) -        self.cli_commit() +            tmp = get_config_value(interface, '+ipv6 ipv6cp-use-ipaddr') +            self.assertListEqual(tmp, ['+ipv6', 'ipv6cp-use-ipaddr'])  if __name__ == '__main__':      unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py index dca92c97d..7721105e0 100755 --- a/smoketest/scripts/cli/test_nat66.py +++ b/smoketest/scripts/cli/test_nat66.py @@ -31,10 +31,13 @@ src_path = base_path + ['source']  dst_path = base_path + ['destination']  class TestNAT66(VyOSUnitTestSHIM.TestCase): -    def setUp(self): +    @classmethod +    def setUpClass(cls): +        super(cls, cls).setUpClass() +          # ensure we can also run this test on a live system - so lets clean          # out the current configuration :) -        self.cli_delete(base_path) +        cls.cli_delete(cls, base_path)      def tearDown(self):          self.cli_delete(base_path) @@ -183,4 +186,4 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):          self.cli_commit()  if __name__ == '__main__': -    unittest.main(verbosity=2) +    unittest.main(verbosity=2, failfast=True) diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index 66d3f3812..c2288a86a 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1149,5 +1149,58 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):          self.assertEqual(tmp, original) +    # Test set table for fwmark +    def test_fwmark_table_id(self): +        path = base_path + ['local-route'] + +        fwmk = '24' +        rule = '101' +        table = '154' + +        self.cli_set(path + ['rule', rule, 'set', 'table', table]) +        self.cli_set(path + ['rule', rule, 'fwmark', fwmk]) + +        self.cli_commit() + +        # Check generated configuration + +        # Expected values +        original = """ +        101:    from all fwmark 0x18 lookup 154 +        """ +        tmp = cmd('ip rule show prio 101') +        original = original.split() +        tmp = tmp.split() + +        self.assertEqual(tmp, original) + +    # Test set table for sources with fwmark +    def test_fwmark_sources_table_id(self): +        path = base_path + ['local-route'] + +        sources = ['203.0.113.11', '203.0.113.12'] +        fwmk = '23' +        rule = '100' +        table = '150' +        for src in sources: +            self.cli_set(path + ['rule', rule, 'set', 'table', table]) +            self.cli_set(path + ['rule', rule, 'source', src]) +            self.cli_set(path + ['rule', rule, 'fwmark', fwmk]) + +        self.cli_commit() + +        # Check generated configuration + +        # Expected values +        original = """ +        100:	from 203.0.113.11 fwmark 0x17 lookup 150 +        100:	from 203.0.113.12 fwmark 0x17 lookup 150 +        """ +        tmp = cmd('ip rule show prio 100') +        original = original.split() +        tmp = tmp.split() + +        self.assertEqual(tmp, original) +  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 c3a2ffbf9..df9dc342b 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -78,6 +78,7 @@ neighbor_config = {          'cap_over'     : '',          'ttl_security' : '5',          'local_as'     : '300', +        'solo'         : '',          'route_map_in' : route_map_in,          'route_map_out': route_map_out,          'no_send_comm_std' : '', @@ -173,6 +174,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):              self.assertIn(f' neighbor {peer} password {peer_config["password"]}', frrconfig)          if 'remote_as' in peer_config:              self.assertIn(f' neighbor {peer} remote-as {peer_config["remote_as"]}', frrconfig) +        if 'solo' in peer_config: +            self.assertIn(f' neighbor {peer} solo', frrconfig)          if 'shutdown' in peer_config:              self.assertIn(f' neighbor {peer} shutdown', frrconfig)          if 'ttl_security' in peer_config: @@ -296,6 +299,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):                  self.cli_set(base_path + ['neighbor', peer, 'strict-capability-match'])              if 'shutdown' in peer_config:                  self.cli_set(base_path + ['neighbor', peer, 'shutdown']) +            if 'solo' in peer_config: +                self.cli_set(base_path + ['neighbor', peer, 'solo'])              if 'ttl_security' in peer_config:                  self.cli_set(base_path + ['neighbor', peer, 'ttl-security', 'hops', peer_config["ttl_security"]])              if 'update_src' in peer_config: @@ -628,6 +633,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):          # templates and Jinja2 FRR template.          table = '1000' +        self.cli_set(base_path + ['local-as', ASN]) +        # testing only one AFI is sufficient as it's generic code +          for vrf in vrfs:              vrf_base = ['vrf', 'name', vrf]              self.cli_set(vrf_base + ['table', table]) @@ -636,15 +644,26 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):              self.cli_set(vrf_base + ['protocols', 'bgp', 'route-map', route_map_in])              table = str(int(table) + 1000) +            # import VRF routes do main RIB +            self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'import', 'vrf', vrf]) +          self.cli_commit() +        # Verify FRR bgpd configuration +        frrconfig = self.getFRRconfig(f'router bgp {ASN}') +        self.assertIn(f'router bgp {ASN}', frrconfig) +        self.assertIn(f' address-family ipv6 unicast', frrconfig) + +          for vrf in vrfs: +            self.assertIn(f'  import vrf {vrf}', frrconfig) +              # Verify FRR bgpd configuration -            frrconfig = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}') -            self.assertIn(f'router bgp {ASN} vrf {vrf}', frrconfig) -            self.assertIn(f' bgp router-id {router_id}', frrconfig) +            frr_vrf_config = self.getFRRconfig(f'router bgp {ASN} vrf {vrf}') +            self.assertIn(f'router bgp {ASN} vrf {vrf}', frr_vrf_config) +            self.assertIn(f' bgp router-id {router_id}', frr_vrf_config) -            # CCC: Currently this is not working as FRR() class does not support +            # XXX: Currently this is not working as FRR() class does not support              # route-maps for multiple vrfs because the modify_section() only works              # on lines and not text blocks.              # @@ -694,13 +713,27 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):          self.assertIn(f'  neighbor {interface} activate', frrconfig)          self.assertIn(f' exit-address-family', frrconfig) -    def test_bgp_13_solo(self): + +    def test_bgp_13_vpn(self):          remote_asn = str(int(ASN) + 150)          neighbor = '192.0.2.55' +        vrf_name = 'red' +        label = 'auto' +        rd = f'{neighbor}:{ASN}' +        rt_export = f'{neighbor}:1002 1.2.3.4:567' +        rt_import = f'{neighbor}:1003 500:100'          self.cli_set(base_path + ['local-as', ASN]) -        self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn]) -        self.cli_set(base_path + ['neighbor', neighbor, 'solo']) +        # testing only one AFI is sufficient as it's generic code +        for afi in ['ipv4-unicast', 'ipv6-unicast']: +            self.cli_set(base_path + ['address-family', afi, 'export', 'vpn']) +            self.cli_set(base_path + ['address-family', afi, 'import', 'vpn']) +            self.cli_set(base_path + ['address-family', afi, 'label', 'vpn', 'export', label]) +            self.cli_set(base_path + ['address-family', afi, 'rd', 'vpn', 'export', rd]) +            self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'export', route_map_out]) +            self.cli_set(base_path + ['address-family', afi, 'route-map', 'vpn', 'import', route_map_in]) +            self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'export', rt_export]) +            self.cli_set(base_path + ['address-family', afi, 'route-target', 'vpn', 'import', rt_import])          # commit changes          self.cli_commit() @@ -708,7 +741,19 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):          # Verify FRR bgpd configuration          frrconfig = self.getFRRconfig(f'router bgp {ASN}')          self.assertIn(f'router bgp {ASN}', frrconfig) -        self.assertIn(f' neighbor {neighbor} solo', frrconfig) + +        for afi in ['ipv4', 'ipv6']: +            afi_config = self.getFRRconfig(f' address-family {afi} unicast', endsection='exit-address-family', daemon='bgpd') +            self.assertIn(f'address-family {afi} unicast', afi_config) +            self.assertIn(f'  export vpn', afi_config) +            self.assertIn(f'  import vpn', afi_config) +            self.assertIn(f'  label vpn export {label}', afi_config) +            self.assertIn(f'  rd vpn export {rd}', afi_config) +            self.assertIn(f'  route-map vpn export {route_map_out}', afi_config) +            self.assertIn(f'  route-map vpn import {route_map_in}', afi_config) +            self.assertIn(f'  rt vpn export {rt_export}', afi_config) +            self.assertIn(f'  rt vpn import {rt_import}', afi_config) +            self.assertIn(f' exit-address-family', afi_config)  if __name__ == '__main__':      unittest.main(verbosity=2)
\ No newline at end of file diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 59862ca3d..3f13eec80 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -20,6 +20,7 @@ import unittest  from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError  from vyos.ifconfig import Section  from vyos.util import process_named_running  from vyos.util import cmd @@ -220,21 +221,23 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):          for protocol in redistribute:              self.cli_set(base_path + ['redistribute', protocol, 'metric', metric])              self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map]) -            if protocol not in ['kernel', 'static']: -                self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type]) +            self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type])          # commit changes          self.cli_commit()          # Verify FRR ospfd configuration          frrconfig = self.getFRRconfig('router ospf') -        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: +        try: +            self.assertIn(f'router ospf', frrconfig) +            for protocol in redistribute:                  self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig) - +        except: +            log.debug(frrconfig) +            log.debug(cmd('sudo dmesg')) +            log.debug(cmd('sudo cat /var/log/messages')) +            log.debug(cmd('vtysh -c "show run"')) +            self.fail('Now we can hopefully see why OSPF fails!')      def test_ospf_09_virtual_link(self):          networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] @@ -266,7 +269,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):              self.assertIn(f' network {network} area {area}', frrconfig) -    def test_ospf_10_interface_configureation(self): +    def test_ospf_10_interface_configuration(self):          interfaces = Section.interfaces('ethernet')          password = 'vyos1234'          bandwidth = '10000' @@ -349,6 +352,30 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):          frrconfig = self.getFRRconfig(zebra_route_map)          self.assertNotIn(zebra_route_map, frrconfig) +    def test_ospf_13_interface_area(self): +        area = '0' +        interfaces = Section.interfaces('ethernet') + +        self.cli_set(base_path + ['area', area, 'network', '10.0.0.0/8']) +        for interface in interfaces: +            self.cli_set(base_path + ['interface', interface, 'area', area]) + +        # we can not have bot area network and interface area set +        with self.assertRaises(ConfigSessionError): +            self.cli_commit() +        self.cli_delete(base_path + ['area', area, 'network']) + +        self.cli_commit() + +        # Verify FRR ospfd configuration +        frrconfig = self.getFRRconfig('router ospf') +        self.assertIn(f'router ospf', frrconfig) + +        for interface in interfaces: +            config = self.getFRRconfig(f'interface {interface}') +            self.assertIn(f'interface {interface}', config) +            self.assertIn(f' ip ospf area {area}', config) +  if __name__ == '__main__':      logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)      unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py index c76f709b1..ded4d8301 100755 --- a/smoketest/scripts/cli/test_service_ssh.py +++ b/smoketest/scripts/cli/test_service_ssh.py @@ -41,10 +41,13 @@ def get_config_value(key):      return tmp  class TestServiceSSH(VyOSUnitTestSHIM.TestCase): -    def setUp(self): +    @classmethod +    def setUpClass(cls): +        super(cls, cls).setUpClass() +          # ensure we can also run this test on a live system - so lets clean          # out the current configuration :) -        self.cli_delete(base_path) +        cls.cli_delete(cls, base_path)      def tearDown(self):          # delete testing SSH config diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py index 21d626d2f..a2380981b 100755 --- a/smoketest/scripts/cli/test_system_conntrack.py +++ b/smoketest/scripts/cli/test_system_conntrack.py @@ -147,8 +147,8 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):              self.assertEqual(get_sysctl(f'{parameter}'), parameter_config['default_value']) -    def test_conntrack_module_disable(self): -        # Some features are disabled by onloading the kernel helper module(s) +    def test_conntrack_module_enable(self): +        # conntrack helper modules are disabled by default          modules = {              'ftp' : {                  'driver' : ['nf_nat_ftp', 'nf_conntrack_ftp'], @@ -176,38 +176,39 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):               },          } +        # load modules          for module in modules: -            self.cli_set(base_path + ['modules', module, 'disable']) +            self.cli_set(base_path + ['modules', module])          # commit changes          self.cli_commit() -        # verify modules are no longer loaded on the system +        # verify modules are loaded on the system          for module, module_options in modules.items():              if 'driver' in module_options:                  for driver in module_options['driver']: -                    self.assertFalse(os.path.isdir(f'/sys/module/{driver}')) +                    self.assertTrue(os.path.isdir(f'/sys/module/{driver}'))              if 'iptables' in module_options:                  rules = cmd('sudo iptables-save -t raw')                  for ruleset in module_options['iptables']: -                    self.assertNotIn(ruleset, rules) +                    self.assertIn(ruleset, rules) -        # reload modules +        # unload modules          for module in modules: -            self.cli_delete(base_path + ['modules', module, 'disable']) +            self.cli_delete(base_path + ['modules', module])          # commit changes          self.cli_commit() -        # verify modules are again loaded on the system +        # verify modules are not loaded on the system          for module, module_options in modules.items():              if 'driver' in module_options:                  for driver in module_options['driver']: -                    self.assertTrue(os.path.isdir(f'/sys/module/{driver}')) +                    self.assertFalse(os.path.isdir(f'/sys/module/{driver}'))              if 'iptables' in module_options:                  rules = cmd('sudo iptables-save -t raw')                  for ruleset in module_options['iptables']: -                    self.assertIn(ruleset, rules) +                    self.assertNotIn(ruleset, rules)      def test_conntrack_hash_size(self):          hash_size = '65536' diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py index a34387dc9..f33268083 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -182,8 +182,10 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          swanctl_conf_lines = [              f'version = 2',              f'auth = psk', +            f'rekey_time = 28800s', # default value              f'proposals = aes128-sha1-modp1024',              f'esp_proposals = aes128-sha1-modp1024', +            f'life_time = 3600s', # default value              f'local_addrs = {local_address} # dhcp:no',              f'remote_addrs = {peer_ip}',              f'mode = tunnel', @@ -255,6 +257,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):      def test_04_dmvpn(self):          tunnel_if = 'tun100'          nhrp_secret = 'secret' +        ike_lifetime = '3600' +        esp_lifetime = '1800'          # Tunnel          self.cli_set(tunnel_path + [tunnel_if, 'address', '172.16.253.134/29']) @@ -272,7 +276,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          # IKE/ESP Groups          self.cli_set(base_path + ['esp-group', esp_group, 'compression', 'disable']) -        self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', '1800']) +        self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', esp_lifetime])          self.cli_set(base_path + ['esp-group', esp_group, 'mode', 'transport'])          self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'dh-group2'])          self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'encryption', 'aes256']) @@ -282,7 +286,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no'])          self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev1']) -        self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', '3600']) +        self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])          self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '2'])          self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256'])          self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha1']) @@ -300,7 +304,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):          swanctl_lines = [              f'proposals = aes128-sha1-modp1024,aes256-sha1-modp1024',              f'version = 1', -            f'rekey_time = 3600s', +            f'life_time = {ike_lifetime}s', +            f'rekey_time = {esp_lifetime}s',              f'esp_proposals = aes128-sha1-modp1024,aes256-sha1-modp1024,3des-md5-modp1024',              f'local_ts = dynamic[gre]',              f'remote_ts = dynamic[gre]', diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py index 4e6e39c0f..68877f794 100755 --- a/src/conf_mode/conntrack.py +++ b/src/conf_mode/conntrack.py @@ -97,7 +97,7 @@ def apply(conntrack):      # Depending on the enable/disable state of the ALG (Application Layer Gateway)      # modules we need to either insmod or rmmod the helpers.      for module, module_config in module_map.items(): -        if dict_search(f'modules.{module}.disable', conntrack) != None: +        if dict_search(f'modules.{module}', conntrack) is None:              if 'ko' in module_config:                  for mod in module_config['ko']:                      # Only remove the module if it's loaded @@ -105,8 +105,9 @@ def apply(conntrack):                          cmd(f'rmmod {mod}')              if 'iptables' in module_config:                  for rule in module_config['iptables']: -                    print(f'iptables --delete {rule}') -                    cmd(f'iptables --delete {rule}') +                    # Only install iptables rule if it does not exist +                    tmp = run(f'iptables --check {rule}') +                    if tmp == 0: cmd(f'iptables --delete {rule}')          else:              if 'ko' in module_config:                  for mod in module_config['ko']: @@ -115,9 +116,7 @@ def apply(conntrack):                  for rule in module_config['iptables']:                      # Only install iptables rule if it does not exist                      tmp = run(f'iptables --check {rule}') -                    if tmp > 0: -                        cmd(f'iptables --insert {rule}') - +                    if tmp > 0: cmd(f'iptables --insert {rule}')      if process_named_running('conntrackd'):          # Reload conntrack-sync daemon to fetch new sysctl values diff --git a/src/conf_mode/containers.py b/src/conf_mode/containers.py index 21b47f42a..32320a4b2 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/containers.py @@ -23,8 +23,11 @@ from ipaddress import ip_network  from vyos.config import Config  from vyos.configdict import dict_merge  from vyos.configdict import node_changed +from vyos.util import call  from vyos.util import cmd -from vyos.util import popen +from vyos.util import run +from vyos.util import read_file +from vyos.util import write_file  from vyos.template import render  from vyos.template import is_ipv4  from vyos.template import is_ipv6 @@ -41,27 +44,7 @@ def _cmd(command):          print(command)      return cmd(command) -# Container management functions -def container_exists(name): -    ''' -    https://docs.podman.io/en/latest/_static/api.html#operation/ContainerExistsLibpod -    Check if container exists. Response codes. -    204 - container exists -    404 - no such container -    ''' -    tmp = _cmd(f"curl --unix-socket /run/podman/podman.sock 'http://d/v3.0.0/libpod/containers/{name}/exists'") -    # If container exists it return status code "0" - code can not be displayed -    return (tmp == "") - -def container_status(name): -    ''' -    https://docs.podman.io/en/latest/_static/api.html#operation/ContainerInspectLibpod -    ''' -    tmp = _cmd(f"curl --unix-socket /run/podman/podman.sock 'http://d/v3.0.0/libpod/containers/{name}/json'") -    data = json.loads(tmp) -    return data['State']['Status'] - -def ctnr_network_exists(name): +def network_exists(name):      # Check explicit name for network, returns True if network exists      c = _cmd(f'podman network ls --quiet --filter name=^{name}$')      return bool(c) @@ -79,11 +62,20 @@ def get_config(config=None):      # 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) +    # container base default values can not be merged here - remove and add them later +    if 'name' in default_values: +        del default_values['name']      container = dict_merge(default_values, container) +    # Merge per-container default values +    if 'name' in container: +        default_values = defaults(base + ['name']) +        for name in container['name']: +            container['name'][name] = dict_merge(default_values, container['name'][name]) +      # Delete container network, delete containers      tmp = node_changed(conf, ['container', 'network']) -    if tmp: container.update({'net_remove' : tmp}) +    if tmp: container.update({'network_remove' : tmp})      tmp = node_changed(conf, ['container', 'name'])      if tmp: container.update({'container_remove' : tmp}) @@ -102,7 +94,6 @@ def verify(container):                  if len(container_config['network']) > 1:                      raise ConfigError(f'Only one network can be specified for container "{name}"!') -                  # Check if the specified container network exists                  network_name = list(container_config['network'])[0]                  if network_name not in container['network']: @@ -125,8 +116,25 @@ def verify(container):                      # We can not use the first IP address of a network prefix as this is used by podman                      if ip_address(address) == ip_network(network)[1]: -                        raise ConfigError(f'Address "{address}" reserved for the container engine!') +                        raise ConfigError(f'IP address "{address}" can not be used for a container, '\ +                                          'reserved for the container engine!') + +            if 'environment' in container_config: +                for var, cfg in container_config['environment'].items(): +                    if 'value' not in cfg: +                        raise ConfigError(f'Environment variable {var} has no value assigned!') +            if 'volume' in container_config: +                for volume, volume_config in container_config['volume'].items(): +                    if 'source' not in volume_config: +                        raise ConfigError(f'Volume "{volume}" has no source path configured!') + +                    if 'destination' not in volume_config: +                        raise ConfigError(f'Volume "{volume}" has no destination path configured!') + +                    source = volume_config['source'] +                    if not os.path.exists(source): +                        raise ConfigError(f'Volume "{volume}" source path "{source}" does not exist!')              # Container image is a mandatory option              if 'image' not in container_config: @@ -142,9 +150,9 @@ def verify(container):      # Add new network      if 'network' in container: -        v4_prefix = 0 -        v6_prefix = 0          for network, network_config in container['network'].items(): +            v4_prefix = 0 +            v6_prefix = 0              # If ipv4-prefix not defined for user-defined network              if 'prefix' not in network_config:                  raise ConfigError(f'prefix for network "{net}" must be defined!') @@ -160,8 +168,8 @@ def verify(container):      # A network attached to a container can not be deleted -    if {'net_remove', 'name'} <= set(container): -        for network in container['net_remove']: +    if {'network_remove', 'name'} <= set(container): +        for network in container['network_remove']:              for container, container_config in container['name'].items():                  if 'network' in container_config and network in container_config['network']:                      raise ConfigError(f'Can not remove network "{network}", used by container "{container}"!') @@ -183,20 +191,19 @@ def apply(container):      # Option "--force" allows to delete containers with any status      if 'container_remove' in container:          for name in container['container_remove']: -            if container_status(name) == 'running': -                _cmd(f'podman stop {name}') -            _cmd(f'podman rm --force {name}') +            call(f'podman stop {name}') +            call(f'podman rm --force {name}')      # Delete old networks if needed -    if 'net_remove' in container: -        for network in container['net_remove']: -            _cmd(f'podman network rm {network}') +    if 'network_remove' in container: +        for network in container['network_remove']: +            call(f'podman network rm --force {network}')      # Add network      if 'network' in container:          for network, network_config in container['network'].items():              # Check if the network has already been created -            if not ctnr_network_exists(network) and 'prefix' in network_config: +            if not network_exists(network) and 'prefix' in network_config:                  tmp = f'podman network create {network}'                  # we can not use list comprehension here as the --ipv6 option                  # must immediately follow the specified subnet!!! @@ -206,56 +213,82 @@ def apply(container):                        tmp += ' --ipv6'                  _cmd(tmp) +                # Disable masquerading and use traditional bridging so VyOS +                # can control firewalling/NAT by the real VyOS CLI +                cni_network_config = f'/etc/cni/net.d/{network}.conflist' +                tmp = read_file(cni_network_config) +                config = json.loads(tmp) +                if 'plugins' in config: +                    for count in range(0, len(config['plugins'])): +                        if 'ipMasq' in config['plugins'][count]: +                            config['plugins'][count]['ipMasq'] = False +                        if 'hairpinMode' in config['plugins'][count]: +                            config['plugins'][count]['hairpinMode'] = False + +                write_file(cni_network_config, json.dumps(config, indent=4)) +      # Add container      if 'name' in container:          for name, container_config in container['name'].items(): -            # Check if the container has already been created -            if not container_exists(name): -                image = container_config['image'] -                # Currently the best way to run a command and immediately print stdout -                print(os.system(f'podman pull {image}')) - -                # Check/set environment options "-e foo=bar" -                env_opt = '' -                if 'environment' in container_config: -                    env_opt = '-e ' -                    env_opt += " -e ".join(f"{k}={v['value']}" for k, v in container_config['environment'].items()) - -                # Publish ports -                port = '' -                if 'port' in container_config: -                    protocol = '' -                    for portmap in container_config['port']: -                        if 'protocol' in container_config['port'][portmap]: -                            protocol = container_config['port'][portmap]['protocol'] -                            protocol = f'/{protocol}' -                        else: -                            protocol = '/tcp' -                        sport = container_config['port'][portmap]['source'] -                        dport = container_config['port'][portmap]['destination'] -                        port += f' -p {sport}:{dport}{protocol}' - -                # Bind volume -                volume = '' -                if 'volume' in container_config: -                    for vol in container_config['volume']: -                        svol = container_config['volume'][vol]['source'] -                        dvol = container_config['volume'][vol]['destination'] -                        volume += f' -v {svol}:{dvol}' - -                if 'allow_host_networks' in container_config: -                    _cmd(f'podman run -dit --name {name} --net host {port} {volume} {env_opt} {image}') -                else: -                    for network in container_config['network']: -                        ipparam = '' -                        if 'address' in container_config['network'][network]: -                            ipparam = '--ip ' + container_config['network'][network]['address'] -                        _cmd(f'podman run --name {name} -dit --net {network} {ipparam} {port} {volume} {env_opt} {image}') - -            # Else container is already created. Just start it. -            # It's needed after reboot. -            elif container_status(name) != 'running': -                _cmd(f'podman start {name}') +            image = container_config['image'] + +            if 'disable' in container_config: +                # check if there is a container by that name running +                tmp = _cmd('podman ps -a --format "{{.Names}}"') +                if name in tmp: +                    _cmd(f'podman stop {name}') +                    _cmd(f'podman rm --force {name}') +                continue + +            memory = container_config['memory'] +            restart = container_config['restart'] + +            # Check if requested container image exists locally. If it does not, we +            # pull it. print() is the best way to have a good response from the +            # polling process to the user to display progress. If the image exists +            # locally, a user can update it running `update container image <name>` +            tmp = run(f'podman image exists {image}') +            if tmp != 0: print(os.system(f'podman pull {image}')) + +            # Check/set environment options "-e foo=bar" +            env_opt = '' +            if 'environment' in container_config: +                for k, v in container_config['environment'].items(): +                    env_opt += f" -e \"{k}={v['value']}\"" + +            # Publish ports +            port = '' +            if 'port' in container_config: +                protocol = '' +                for portmap in container_config['port']: +                    if 'protocol' in container_config['port'][portmap]: +                        protocol = container_config['port'][portmap]['protocol'] +                        protocol = f'/{protocol}' +                    else: +                        protocol = '/tcp' +                    sport = container_config['port'][portmap]['source'] +                    dport = container_config['port'][portmap]['destination'] +                    port += f' -p {sport}:{dport}{protocol}' + +            # Bind volume +            volume = '' +            if 'volume' in container_config: +                for vol, vol_config in container_config['volume'].items(): +                    svol = vol_config['source'] +                    dvol = vol_config['destination'] +                    volume += f' -v {svol}:{dvol}' + +            container_base_cmd = f'podman run --detach --interactive --tty --replace ' \ +                                 f'--memory {memory}m --memory-swap 0 --restart {restart} ' \ +                                 f'--name {name} {port} {volume} {env_opt}' +            if 'allow_host_networks' in container_config: +                _cmd(f'{container_base_cmd} --net host {image}') +            else: +                for network in container_config['network']: +                    ipparam = '' +                    if 'address' in container_config['network'][network]: +                        ipparam = '--ip ' + container_config['network'][network]['address'] +                    _cmd(f'{container_base_cmd} --net {network} {ipparam} {image}')      return None diff --git a/src/conf_mode/firewall_options.py b/src/conf_mode/firewall_options.py deleted file mode 100755 index 67bf5d0e2..000000000 --- a/src/conf_mode/firewall_options.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018 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 sys -import os -import copy - -from vyos.config import Config -from vyos import ConfigError -from vyos.util import call - -from vyos import airbag -airbag.enable() - -default_config_data = { -    'intf_opts': [], -    'new_chain4': False, -    'new_chain6': False -} - -def get_config(config=None): -    opts = copy.deepcopy(default_config_data) -    if config: -        conf = config -    else: -        conf = Config() -    if not conf.exists('firewall options'): -        # bail out early -        return opts -    else: -        conf.set_level('firewall options') - -        # Parse configuration of each individual instance -        if conf.exists('interface'): -            for intf in conf.list_nodes('interface'): -                conf.set_level('firewall options interface {0}'.format(intf)) -                config = { -                    'intf': intf, -                    'disabled': False, -                    'mss4': '', -                    'mss6': '' -                } - -                # Check if individual option is disabled -                if conf.exists('disable'): -                    config['disabled'] = True - -                # -                # Get MSS value IPv4 -                # -                if conf.exists('adjust-mss'): -                    config['mss4'] = conf.return_value('adjust-mss') - -                    # We need a marker that a new iptables chain needs to be generated -                    if not opts['new_chain4']: -                        opts['new_chain4'] = True - -                # -                # Get MSS value IPv6 -                # -                if conf.exists('adjust-mss6'): -                    config['mss6'] = conf.return_value('adjust-mss6') - -                    # We need a marker that a new ip6tables chain needs to be generated -                    if not opts['new_chain6']: -                        opts['new_chain6'] = True - -                # Append interface options to global list -                opts['intf_opts'].append(config) - -    return opts - -def verify(tcp): -    # syntax verification is done via cli -    return None - -def apply(tcp): -    target = 'VYOS_FW_OPTIONS' - -    # always cleanup iptables -    call('iptables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) -    call('iptables --table mangle --flush {} >&/dev/null'.format(target)) -    call('iptables --table mangle --delete-chain {} >&/dev/null'.format(target)) - -    # always cleanup ip6tables -    call('ip6tables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) -    call('ip6tables --table mangle --flush {} >&/dev/null'.format(target)) -    call('ip6tables --table mangle --delete-chain {} >&/dev/null'.format(target)) - -    # Setup new iptables rules -    if tcp['new_chain4']: -        call('iptables --table mangle --new-chain {} >&/dev/null'.format(target)) -        call('iptables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) - -        for opts in tcp['intf_opts']: -            intf = opts['intf'] -            mss = opts['mss4'] - -            # Check if this rule iis disabled -            if opts['disabled']: -                continue - -            # adjust TCP MSS per interface -            if mss: -                call('iptables --table mangle --append {} --out-interface {} --protocol tcp ' -                          '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) - -    # Setup new ip6tables rules -    if tcp['new_chain6']: -        call('ip6tables --table mangle --new-chain {} >&/dev/null'.format(target)) -        call('ip6tables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) - -        for opts in tcp['intf_opts']: -            intf = opts['intf'] -            mss = opts['mss6'] - -            # Check if this rule iis disabled -            if opts['disabled']: -                continue - -            # adjust TCP MSS per interface -            if mss: -                call('ip6tables --table mangle --append {} --out-interface {} --protocol tcp ' -                    '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) - -    return None - -if __name__ == '__main__': - -    try: -        c = get_config() -        verify(c) -        apply(c) -    except ConfigError as e: -        print(e) -        sys.exit(1) diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 74e29ed82..6be4e918b 100755 --- a/src/conf_mode/interfaces-openvpn.py +++ b/src/conf_mode/interfaces-openvpn.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -273,6 +273,9 @@ def verify(openvpn):          if openvpn['protocol'] == 'tcp-active':              raise ConfigError('Protocol "tcp-active" is not valid in server mode') +        if dict_search('authentication.username', openvpn) or dict_search('authentication.password', openvpn): +            raise ConfigError('Cannot specify "authentication" in server mode') +          if 'remote_port' in openvpn:              raise ConfigError('Cannot specify "remote-port" in server mode') diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 6c4c6c95b..584adc75e 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-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 @@ -22,12 +22,16 @@ from netifaces import interfaces  from vyos.config import Config  from vyos.configdict import get_interface_dict +from vyos.configdict import leaf_node_changed  from vyos.configverify import verify_authentication  from vyos.configverify import verify_source_interface +from vyos.configverify import verify_interface_exists  from vyos.configverify import verify_vrf  from vyos.configverify import verify_mtu_ipv6 +from vyos.ifconfig import PPPoEIf  from vyos.template import render  from vyos.util import call +from vyos.util import is_systemd_service_running  from vyos import ConfigError  from vyos import airbag  airbag.enable() @@ -44,6 +48,32 @@ def get_config(config=None):      base = ['interfaces', 'pppoe']      pppoe = get_interface_dict(conf, base) +    # We should only terminate the PPPoE session if critical parameters change. +    # All parameters that can be changed on-the-fly (like interface description) +    # should not lead to a reconnect! +    tmp = leaf_node_changed(conf, ['access-concentrator']) +    if tmp: pppoe.update({'shutdown_required': {}}) + +    tmp = leaf_node_changed(conf, ['connect-on-demand']) +    if tmp: pppoe.update({'shutdown_required': {}}) + +    tmp = leaf_node_changed(conf, ['service-name']) +    if tmp: pppoe.update({'shutdown_required': {}}) + +    tmp = leaf_node_changed(conf, ['source-interface']) +    if tmp: pppoe.update({'shutdown_required': {}}) + +    tmp = leaf_node_changed(conf, ['vrf']) +    # leaf_node_changed() returns a list, as VRF is a non-multi node, there +    # will be only one list element +    if tmp: pppoe.update({'vrf_old': tmp[0]}) + +    tmp = leaf_node_changed(conf, ['authentication', 'user']) +    if tmp: pppoe.update({'shutdown_required': {}}) + +    tmp = leaf_node_changed(conf, ['authentication', 'password']) +    if tmp: pppoe.update({'shutdown_required': {}}) +      return pppoe  def verify(pppoe): @@ -66,57 +96,42 @@ def generate(pppoe):      # rendered into      ifname = pppoe['ifname']      config_pppoe = f'/etc/ppp/peers/{ifname}' -    script_pppoe_pre_up = f'/etc/ppp/ip-pre-up.d/1000-vyos-pppoe-{ifname}' -    script_pppoe_ip_up = f'/etc/ppp/ip-up.d/1000-vyos-pppoe-{ifname}' -    script_pppoe_ip_down = f'/etc/ppp/ip-down.d/1000-vyos-pppoe-{ifname}' -    script_pppoe_ipv6_up = f'/etc/ppp/ipv6-up.d/1000-vyos-pppoe-{ifname}' -    config_wide_dhcp6c = f'/run/dhcp6c/dhcp6c.{ifname}.conf' - -    config_files = [config_pppoe, script_pppoe_pre_up, script_pppoe_ip_up, -                    script_pppoe_ip_down, script_pppoe_ipv6_up, config_wide_dhcp6c]      if 'deleted' in pppoe or 'disable' in pppoe: -        # stop DHCPv6-PD client -        call(f'systemctl stop dhcp6c@{ifname}.service') -        # Hang-up PPPoE connection -        call(f'systemctl stop ppp@{ifname}.service') - -        # Delete PPP configuration files -        for file in config_files: -            if os.path.exists(file): -                os.unlink(file) +        if os.path.exists(config_pppoe): +            os.unlink(config_pppoe)          return None      # Create PPP configuration files -    render(config_pppoe, 'pppoe/peer.tmpl', pppoe, permission=0o755) - -    # Create script for ip-pre-up.d -    render(script_pppoe_pre_up, 'pppoe/ip-pre-up.script.tmpl', pppoe, -           permission=0o755) -    # Create script for ip-up.d -    render(script_pppoe_ip_up, 'pppoe/ip-up.script.tmpl', pppoe, -           permission=0o755) -    # Create script for ip-down.d -    render(script_pppoe_ip_down, 'pppoe/ip-down.script.tmpl', pppoe, -           permission=0o755) -    # Create script for ipv6-up.d -    render(script_pppoe_ipv6_up, 'pppoe/ipv6-up.script.tmpl', pppoe, -           permission=0o755) - -    if 'dhcpv6_options' in pppoe and 'pd' in pppoe['dhcpv6_options']: -        # ipv6.tmpl relies on ifname - this should be made consitent in the -        # future better then double key-ing the same value -        render(config_wide_dhcp6c, 'dhcp-client/ipv6.tmpl', pppoe) +    render(config_pppoe, 'pppoe/peer.tmpl', pppoe, permission=0o640)      return None  def apply(pppoe): +    ifname = pppoe['ifname']      if 'deleted' in pppoe or 'disable' in pppoe: -        call('systemctl stop ppp@{ifname}.service'.format(**pppoe)) +        if os.path.isdir(f'/sys/class/net/{ifname}'): +            p = PPPoEIf(ifname) +            p.remove() +        call(f'systemctl stop ppp@{ifname}.service')          return None -    call('systemctl restart ppp@{ifname}.service'.format(**pppoe)) +    # reconnect should only be necessary when certain config options change, +    # like ACS name, authentication, no-peer-dns, source-interface +    if ((not is_systemd_service_running(f'ppp@{ifname}.service')) or +        'shutdown_required' in pppoe): + +        # cleanup system (e.g. FRR routes first) +        if os.path.isdir(f'/sys/class/net/{ifname}'): +            p = PPPoEIf(ifname) +            p.remove() + +        call(f'systemctl restart ppp@{ifname}.service') +    else: +        if os.path.isdir(f'/sys/class/net/{ifname}'): +            p = PPPoEIf(ifname) +            p.update(pppoe)      return None diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py index 31c599145..faa5eb628 100755 --- a/src/conf_mode/interfaces-wwan.py +++ b/src/conf_mode/interfaces-wwan.py @@ -26,7 +26,6 @@ from vyos.configverify import verify_vrf  from vyos.ifconfig import WWANIf  from vyos.util import cmd  from vyos.util import dict_search -from vyos.template import render  from vyos import ConfigError  from vyos import airbag  airbag.enable() diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index dae958774..59939d0fb 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -139,12 +139,10 @@ def verify(nat):          for rule, config in dict_search('source.rule', nat).items():              err_msg = f'Source NAT 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 'any' and config['outbound_interface'] not in interfaces(): -                    print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system') +                raise ConfigError(f'{err_msg} outbound-interface not specified') +            if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces(): +                print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')              addr = dict_search('translation.address', config)              if addr != None: diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index e2bd6417d..f8bc073bb 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.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 @@ -55,7 +55,7 @@ def get_config(config=None):          conf = config      else:          conf = Config() -     +      base = ['nat66']      nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) @@ -90,7 +90,7 @@ def get_config(config=None):      # 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_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_IGNORE')          nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_PREROUTING_HOOK') @@ -109,21 +109,22 @@ def verify(nat):      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') +                raise ConfigError(f'{err_msg} outbound-interface not specified') + +            if config['outbound_interface'] not in interfaces(): +                print(f'WARNING: rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')              addr = dict_search('translation.address', config)              if addr != None:                  if addr != 'masquerade' and not is_ipv6(addr):                      raise ConfigError(f'Warning: IPv6 address {addr} is not a valid address') +            else: +                raise ConfigError(f'{err_msg} translation address not specified')              prefix = dict_search('source.prefix', config)              if prefix != None: @@ -145,7 +146,7 @@ def verify(nat):  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) +    render(ndppd_config, 'ndppd/ndppd.conf.tmpl', nat, permission=0o755)      return None  def apply(nat): diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py index 013f22665..539189442 100755 --- a/src/conf_mode/policy-local-route.py +++ b/src/conf_mode/policy-local-route.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 @@ -44,17 +44,26 @@ def get_config(config=None):      if tmp:          for rule in (tmp or []):              src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source']) +            fwmk = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'fwmark'])              if src:                  dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)                  pbr.update(dict) +            if fwmk: +                dict = dict_merge({'rule_remove' : {rule : {'fwmark' : fwmk}}}, dict) +                pbr.update(dict)      # delete policy local-route rule x source x.x.x.x +    # delete policy local-route rule x fwmark x      if 'rule' in pbr:          for rule in pbr['rule']:              src = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'source']) +            fwmk = leaf_node_changed(conf, ['policy', 'local-route', 'rule', rule, 'fwmark'])              if src:                  dict = dict_merge({'rule_remove' : {rule : {'source' : src}}}, dict)                  pbr.update(dict) +            if fwmk: +                dict = dict_merge({'rule_remove' : {rule : {'fwmark' : fwmk}}}, dict) +                pbr.update(dict)      return pbr @@ -65,8 +74,8 @@ def verify(pbr):      if 'rule' in pbr:          for rule in pbr['rule']: -            if 'source' not in pbr['rule'][rule]: -                raise ConfigError('Source address required!') +            if 'source' not in pbr['rule'][rule] and 'fwmark' not in pbr['rule'][rule]: +                raise ConfigError('Source address or fwmark is required!')              else:                  if 'set' not in pbr['rule'][rule] or 'table' not in pbr['rule'][rule]['set']:                      raise ConfigError('Table set is required!') @@ -86,16 +95,34 @@ def apply(pbr):      # Delete old rule if needed      if 'rule_remove' in pbr:          for rule in pbr['rule_remove']: -            for src in pbr['rule_remove'][rule]['source']: -                call(f'ip rule del prio {rule} from {src}') +            if 'source' in pbr['rule_remove'][rule]: +                for src in pbr['rule_remove'][rule]['source']: +                    call(f'ip rule del prio {rule} from {src}') +            if 'fwmark' in  pbr['rule_remove'][rule]: +                for fwmk in pbr['rule_remove'][rule]['fwmark']: +                    call(f'ip rule del prio {rule} from all fwmark {fwmk}')      # Generate new config      if 'rule' in pbr:          for rule in pbr['rule']:              table = pbr['rule'][rule]['set']['table'] -            if pbr['rule'][rule]['source']: +            # Only source in the rule +            # set policy local-route rule 100 source '203.0.113.1' +            if 'source' in pbr['rule'][rule] and not 'fwmark' in pbr['rule'][rule]:                  for src in pbr['rule'][rule]['source']:                      call(f'ip rule add prio {rule} from {src} lookup {table}') +            # Only fwmark in the rule +            # set policy local-route rule 101 fwmark '23' +            if 'fwmark' in pbr['rule'][rule] and not 'source' in pbr['rule'][rule]: +                fwmk = pbr['rule'][rule]['fwmark'] +                call(f'ip rule add prio {rule} from all fwmark {fwmk} lookup {table}') +            # Source and fwmark in the rule +            # set policy local-route rule 100 source '203.0.113.1' +            # set policy local-route rule 100 fwmark '23' +            if 'source' in pbr['rule'][rule] and 'fwmark' in pbr['rule'][rule]: +                fwmk = pbr['rule'][rule]['fwmark'] +                for src in pbr['rule'][rule]['source']: +                    call(f'ip rule add prio {rule} from {src} fwmark {fwmk} lookup {table}')      return None diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index 9ecfd07fe..7d05eed9f 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -23,6 +23,7 @@ from vyos.config import Config  from vyos.configdict import dict_merge  from vyos.configverify import verify_prefix_list  from vyos.configverify import verify_route_map +from vyos.configverify import verify_vrf  from vyos.template import is_ip  from vyos.template import is_interface  from vyos.template import render_to_string @@ -221,27 +222,47 @@ def verify(bgp):              raise ConfigError(f'Peer-group "{peer_group}" requires remote-as to be set!')      # Throw an error if the global administrative distance parameters aren't all filled out. -    if dict_search('parameters.distance', bgp) == None: -        pass -    else: -        if dict_search('parameters.distance.global', bgp): -            for key in ['external', 'internal', 'local']: -                if dict_search(f'parameters.distance.global.{key}', bgp) == None: -                    raise ConfigError('Missing mandatory configuration option for '\ -                                     f'global administrative distance {key}!') - -    # Throw an error if the address family specific administrative distance parameters aren't all filled out. -    if dict_search('address_family', bgp) == None: -        pass -    else: -        for address_family_name in dict_search('address_family', bgp): -            if dict_search(f'address_family.{address_family_name}.distance', bgp) == None: -                pass -            else: +    if dict_search('parameters.distance.global', bgp) != None: +        for key in ['external', 'internal', 'local']: +            if dict_search(f'parameters.distance.global.{key}', bgp) == None: +                raise ConfigError('Missing mandatory configuration option for '\ +                                 f'global administrative distance {key}!') + +    # Address Family specific validation +    if 'address_family' in bgp: +        for afi, afi_config in bgp['address_family'].items(): +            if 'distance' in afi_config: +                # Throw an error if the address family specific administrative +                # distance parameters aren't all filled out.                  for key in ['external', 'internal', 'local']: -                    if dict_search(f'address_family.{address_family_name}.distance.{key}', bgp) == None: +                    if key not in afi_config['distance']:                          raise ConfigError('Missing mandatory configuration option for '\ -                                         f'{address_family_name} administrative distance {key}!') +                                         f'{afi} administrative distance {key}!') + +            if afi in ['ipv4_unicast', 'ipv6_unicast']: +                if 'import' in afi_config and 'vrf' in afi_config['import']: +                    # Check if VRF exists +                    verify_vrf(afi_config['import']['vrf']) + +                    # FRR error: please unconfigure vpn to vrf commands before +                    # using import vrf commands +                    if 'vpn' in afi_config['import'] or dict_search('export.vpn', afi_config) != None: +                        raise ConfigError('Please unconfigure VPN to VRF commands before '\ +                                          'using "import vrf" commands!') + +                # Verify that the export/import route-maps do exist +                for export_import in ['export', 'import']: +                    tmp = dict_search(f'route_map.vpn.{export_import}', afi_config) +                    if tmp: verify_route_map(tmp, bgp) + +            if afi in ['l2vpn_evpn'] and 'vrf' not in bgp: +                # Some L2VPN EVPN AFI options are only supported under VRF +                if 'vni' in afi_config: +                    for vni, vni_config in afi_config['vni'].items(): +                        if 'rd' in vni_config: +                            raise ConfigError('VNI route-distinguisher is only supported under EVPN VRF') +                        if 'route_target' in vni_config: +                            raise ConfigError('VNI route-target is only supported under EVPN VRF')      return None diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index d4c82249b..4cf0312e9 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -113,9 +113,13 @@ def verify(isis):          # Interface MTU must be >= configured lsp-mtu          mtu = Interface(interface).get_mtu()          area_mtu = isis['lsp_mtu'] -        if mtu < int(area_mtu): -            raise ConfigError(f'Interface {interface} has MTU {mtu}, minimum ' \ -                              f'area MTU is {area_mtu}!') +        # Recommended maximum PDU size = interface MTU - 3 bytes +        recom_area_mtu = mtu - 3 +        if mtu < int(area_mtu) or int(area_mtu) > recom_area_mtu: +            raise ConfigError(f'Interface {interface} has MTU {mtu}, ' \ +                              f'current area MTU is {area_mtu}! \n' \ +                              f'Recommended area lsp-mtu {recom_area_mtu} or less ' \ +                              '(calculated on MTU size).')          if 'vrf' in isis:              # If interface specific options are set, we must ensure that the diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index 78c1c82bd..06a29106d 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -149,14 +149,23 @@ def verify(ospf):      if route_map_name: verify_route_map(route_map_name, ospf)      if 'interface' in ospf: -        for interface in ospf['interface']: +        for interface, interface_config in ospf['interface'].items():              verify_interface_exists(interface)              # One can not use dead-interval and hello-multiplier at the same              # time. FRR will only activate the last option set via CLI. -            if {'hello_multiplier', 'dead_interval'} <= set(ospf['interface'][interface]): +            if {'hello_multiplier', 'dead_interval'} <= set(interface_config):                  raise ConfigError(f'Can not use hello-multiplier and dead-interval ' \                                    f'concurrently for {interface}!') +            # One can not use the "network <prefix> area <id>" command and an +            # per interface area assignment at the same time. FRR will error +            # out using: "Please remove all network commands first." +            if 'area' in ospf and 'area' in interface_config: +                for area, area_config in ospf['area'].items(): +                    if 'network' in area_config: +                        raise ConfigError('Can not use OSPF interface area and area ' \ +                                          'network configuration at the same time!') +              if 'vrf' in ospf:              # If interface specific options are set, we must ensure that the              # interface is bound to our requesting VRF. Due to the VyOS @@ -177,7 +186,7 @@ def generate(ospf):      ospf['protocol'] = 'ospf' # required for frr/vrf.route-map.frr.tmpl      ospf['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.tmpl', ospf) -    ospf['frr_ospfd_config']   = render_to_string('frr/ospf.frr.tmpl', ospf) +    ospf['frr_ospfd_config'] = render_to_string('frr/ospfd.frr.tmpl', ospf)      return None  def apply(ospf): diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index fef0f509b..536ffa690 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -65,7 +65,7 @@ def verify(ospfv3):              if 'ifmtu' in if_config:                  mtu = Interface(ifname).get_mtu()                  if int(if_config['ifmtu']) > int(mtu): -                    raise ConfigError(f'OSPFv3 ifmtu cannot go beyond physical MTU of "{mtu}"') +                    raise ConfigError(f'OSPFv3 ifmtu can not exceed physical MTU of "{mtu}"')      return None @@ -74,7 +74,7 @@ def generate(ospfv3):          ospfv3['new_frr_config'] = ''          return None -    ospfv3['new_frr_config'] = render_to_string('frr/ospfv3.frr.tmpl', ospfv3) +    ospfv3['new_frr_config'] = render_to_string('frr/ospf6d.frr.tmpl', ospfv3)      return None  def apply(ospfv3): diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index d3065fc47..ff6090e22 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -286,20 +286,34 @@ def verify(ipsec):                      if 'pre_shared_secret' not in ra_conf['authentication']:                          raise ConfigError(f"Missing pre-shared-key on {name} remote-access config") +                if 'client_mode' not in ra_conf['authentication']: +                    raise ConfigError('Client authentication method is required!') -                if 'client_mode' in ra_conf['authentication']: -                    if ra_conf['authentication']['client_mode'] == 'eap-radius': -                        if 'radius' not in ipsec['remote_access'] or 'server' not in ipsec['remote_access']['radius'] or len(ipsec['remote_access']['radius']['server']) == 0: -                            raise ConfigError('RADIUS authentication requires at least one server') +                if dict_search('authentication.client_mode', ra_conf) == 'eap-radius': +                    if dict_search('remote_access.radius.server', ipsec) == None: +                        raise ConfigError('RADIUS authentication requires at least one server')                  if 'pool' in ra_conf: +                    if {'dhcp', 'radius'} <= set(ra_conf['pool']): +                        raise ConfigError(f'Can not use both DHCP and RADIUS for address allocation '\ +                                          f'at the same time for "{name}"!') +                      if 'dhcp' in ra_conf['pool'] and len(ra_conf['pool']) > 1: -                        raise ConfigError(f'Can not use both DHCP and a predefined address pool for "{name}"!') +                        raise ConfigError(f'Can not use DHCP and a predefined address pool for "{name}"!') + +                    if 'radius' in ra_conf['pool'] and len(ra_conf['pool']) > 1: +                        raise ConfigError(f'Can not use RADIUS and a predefined address pool for "{name}"!')                      for pool in ra_conf['pool']:                          if pool == 'dhcp':                              if dict_search('remote_access.dhcp.server', ipsec) == None:                                  raise ConfigError('IPSec DHCP server is not configured!') +                        elif pool == 'radius': +                            if dict_search('remote_access.radius.server', ipsec) == None: +                                raise ConfigError('IPSec RADIUS server is not configured!') + +                            if dict_search('authentication.client_mode', ra_conf) != 'eap-radius': +                                raise ConfigError('RADIUS IP pool requires eap-radius client authentication!')                          elif 'pool' not in ipsec['remote_access'] or pool not in ipsec['remote_access']['pool']:                              raise ConfigError(f'Requested pool "{pool}" does not exist!') diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index c1cfc1dcb..919083ac4 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -24,7 +24,6 @@ from vyos.config import Config  from vyos.configdict import node_changed  from vyos.ifconfig import Interface  from vyos.template import render -from vyos.template import render_to_string  from vyos.util import call  from vyos.util import cmd  from vyos.util import dict_search @@ -32,12 +31,9 @@ from vyos.util import get_interface_config  from vyos.util import popen  from vyos.util import run  from vyos import ConfigError -from vyos import frr  from vyos import airbag  airbag.enable() -frr_daemon = 'zebra' -  config_file = r'/etc/iproute2/rt_tables.d/vyos-vrf.conf'  def list_rules(): @@ -131,7 +127,6 @@ def verify(vrf):  def generate(vrf):      render(config_file, 'vrf/vrf.conf.tmpl', vrf) -    vrf['new_frr_config'] = render_to_string('frr/vrf.frr.tmpl', vrf)      # Render nftables zones config      vrf['nft_vrf_zones'] = NamedTemporaryFile().name      render(vrf['nft_vrf_zones'], 'firewall/nftables-vrf-zones.tmpl', vrf) @@ -242,21 +237,6 @@ def apply(vrf):          if tmp == 0:              cmd('nft delete table inet vrf_zones') -    #   T3694: Somehow we hit a priority inversion here as we need to remove the -    #   VRF assigned VNI before we can remove a BGP bound VRF instance. Maybe -    #   move this to an individual helper script that set's up the VNI for the -    #   given VRF after any routing protocol. -    # -    #   # add configuration to FRR -    #   frr_cfg = frr.FRRConfig() -    #   frr_cfg.load_configuration(frr_daemon) -    #   frr_cfg.modify_section(f'^vrf [a-zA-Z-]*$', '') -    #   frr_cfg.add_before(r'(interface .*|line vty)', vrf['new_frr_config']) -    #   frr_cfg.commit_configuration(frr_daemon) -    # -    #   # Save configuration to /run/frr/config/frr.conf -    #   frr.save_configuration() -      return None  if __name__ == '__main__': diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py new file mode 100755 index 000000000..87ee8f2d1 --- /dev/null +++ b/src/conf_mode/vrf_vni.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +# +# 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 +# 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 +from sys import exit + +from vyos.config import Config +from vyos.template import render_to_string +from vyos import ConfigError +from vyos import frr +from vyos import airbag +airbag.enable() + +frr_daemon = 'zebra' + +def get_config(config=None): +    if config: +        conf = config +    else: +        conf = Config() + +    # This script only works with a passed VRF name +    if len(argv) < 1: +        raise NotImplementedError +    vrf = argv[1] + +    # "assemble" dict - easier here then use a full blown get_config_dict() +    # on a single leafNode +    vni = { 'vrf' : vrf } +    tmp = conf.return_value(['vrf', 'name', vrf, 'vni']) +    if tmp: vni.update({ 'vni' : tmp }) + +    return vni + +def verify(vni): +    return None + +def generate(vni): +    vni['new_frr_config'] = render_to_string('frr/vrf-vni.frr.tmpl', vni) +    return None + +def apply(vni): +    # add configuration to FRR +    frr_cfg = frr.FRRConfig() +    frr_cfg.load_configuration(frr_daemon) +    frr_cfg.modify_section(f'^vrf [a-zA-Z-]*$', '') +    frr_cfg.add_before(r'(interface .*|line vty)', vni['new_frr_config']) +    frr_cfg.commit_configuration(frr_daemon) + +    # Save configuration to /run/frr/config/frr.conf +    frr.save_configuration() + +    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/etc/ipsec.d/vti-up-down b/src/etc/ipsec.d/vti-up-down index 281c9bf2b..011013a2e 100755 --- a/src/etc/ipsec.d/vti-up-down +++ b/src/etc/ipsec.d/vti-up-down @@ -55,7 +55,7 @@ if __name__ == '__main__':          syslog(f'Interface {interface} not found')          sys.exit(0) -    vti_link_up = (vti_link['operstate'] == 'UP' if 'operstate' in vti_link else False) +    vti_link_up = (vti_link['operstate'] != 'DOWN' if 'operstate' in vti_link else False)      config = ConfigTreeQuery()      vti_dict = config.get_config_dict(['interfaces', 'vti', interface], diff --git a/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback b/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback new file mode 100755 index 000000000..bb918a468 --- /dev/null +++ b/src/etc/ppp/ip-up.d/99-vyos-pppoe-callback @@ -0,0 +1,59 @@ +#!/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/>. + +# This is a Python hook script which is invoked whenever a PPPoE session goes +# "ip-up". It will call into our vyos.ifconfig library and will then execute +# common tasks for the PPPoE interface. The reason we have to "hook" this is +# that we can not create a pppoeX interface in advance in linux and then connect +# pppd to this already existing interface. + +from sys import argv +from sys import exit + +from syslog import syslog +from syslog import openlog +from syslog import LOG_PID +from syslog import LOG_INFO + +from vyos.configquery import ConfigTreeQuery +from vyos.ifconfig import PPPoEIf +from vyos.util import read_file + +# When the ppp link comes up, this script is called with the following +# parameters +#       $1      the interface name used by pppd (e.g. ppp3) +#       $2      the tty device name +#       $3      the tty device speed +#       $4      the local IP address for the interface +#       $5      the remote IP address +#       $6      the parameter specified by the 'ipparam' option to pppd + +if (len(argv) < 7): +    exit(1) + +interface = argv[6] +dialer_pid = read_file(f'/var/run/{interface}.pid') + +openlog(ident=f'pppd[{dialer_pid}]', facility=LOG_INFO) +syslog('executing ' + argv[0]) + +conf = ConfigTreeQuery() +pppoe = conf.get_config_dict(['interfaces', 'pppoe', argv[6]], +                             get_first_key=True, key_mangling=('-', '_')) +pppoe['ifname'] = argv[6] + +p = PPPoEIf(pppoe['ifname']) +p.update(pppoe) diff --git a/src/etc/sysctl.d/32-vyos-podman.conf b/src/etc/sysctl.d/32-vyos-podman.conf new file mode 100644 index 000000000..7068bf88d --- /dev/null +++ b/src/etc/sysctl.d/32-vyos-podman.conf @@ -0,0 +1,5 @@ +# Increase inotify watchers as per https://bugzilla.redhat.com/show_bug.cgi?id=1829596 +fs.inotify.max_queued_events = 1048576 +fs.inotify.max_user_instances = 1048576 +fs.inotify.max_user_watches = 1048576 + diff --git a/src/etc/udev/rules.d/90-vyos-serial.rules b/src/etc/udev/rules.d/90-vyos-serial.rules index 3f10f4924..872fd4fea 100644 --- a/src/etc/udev/rules.d/90-vyos-serial.rules +++ b/src/etc/udev/rules.d/90-vyos-serial.rules @@ -8,7 +8,7 @@ SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"  SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"  # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices -KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end" +KERNEL!="ttyUSB[0-9]*", GOTO="serial_end"  SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" @@ -18,11 +18,11 @@ IMPORT{builtin}="path_id", IMPORT{builtin}="usb_id"  #  # - $env{ID_PATH} usually is a name like: "pci-0000:00:10.0-usb-0:2.3.3.4:1.0-port0" so we strip the "pci-*"  #   portion and only use the usb part -# - Transform the USB "speach" to the tree like structure so we start with "usb0" as root-complex 0. +# - Transform the USB "speech" to the tree like structure so we start with "usb0" as root-complex 0.  #   (tr -d -) does the replacement  # - Replace the first group after ":" to represent the bus relation (sed -e 0,/:/s//b/) indicated by "b"  # - Replace the next group after ":" to represent the port relation (sed -e 0,/:/s//p/) indicated by "p" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="",   PROGRAM="/bin/sh -c 'D=$env{ID_PATH}; echo ${D:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", PROGRAM="/bin/sh -c 'D=$env{ID_PATH}; echo ${D:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="",   PROGRAM="/bin/sh -c 'echo $env{ID_PATH:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", PROGRAM="/bin/sh -c 'echo $env{ID_PATH:17} | tr -d - | sed -e 0,/:/s//b/ | sed -e 0,/:/s//p/'", SYMLINK+="serial/by-bus/$result"  LABEL="serial_end" diff --git a/src/etc/update-motd.d/99-reboot b/src/etc/update-motd.d/99-reboot new file mode 100755 index 000000000..718be1a7a --- /dev/null +++ b/src/etc/update-motd.d/99-reboot @@ -0,0 +1,7 @@ +#!/bin/vbash +source /opt/vyatta/etc/functions/script-template +if [ -f /run/systemd/shutdown/scheduled ]; then +    echo +    run show reboot +fi +exit diff --git a/src/migration-scripts/conntrack/2-to-3 b/src/migration-scripts/conntrack/2-to-3 new file mode 100755 index 000000000..8a8b43279 --- /dev/null +++ b/src/migration-scripts/conntrack/2-to-3 @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# Conntrack syntax version 3 +# Enables all conntrack modules (previous default behaviour) and omits manually disabled modules. + +import sys + +from vyos.configtree import ConfigTree +from vyos.version import get_version + +if len(sys.argv) < 1: +    print('Must specify file name!') +    sys.exit(1) + +filename = sys.argv[1] + +with open(filename, 'r') as f: +    config = ConfigTree(f.read()) + +module_path = ['system', 'conntrack', 'modules'] + +# Go over all conntrack modules available as of v1.3.0. +for module in ['ftp', 'h323', 'nfs', 'pptp', 'sip', 'sqlnet', 'tftp']: +    # 'disable' is being phased out. +    if config.exists(module_path + [module, 'disable']): +        config.delete(module_path + [module]) +    # If it wasn't manually 'disable'd, it was enabled by default. +    else: +        config.set(module_path + [module]) + +try: +    if config.exists(module_path): +        with open(filename, 'w') as f: +            f.write(config.to_string()) +except OSError as e: +    print(f'Failed to save the modified config: {e}') +    sys.exit(1) diff --git a/src/migration-scripts/firewall/5-to-6 b/src/migration-scripts/firewall/5-to-6 new file mode 100755 index 000000000..ccb86830a --- /dev/null +++ b/src/migration-scripts/firewall/5-to-6 @@ -0,0 +1,63 @@ +#!/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/>. + +# T3090: migrate "firewall options interface <name> adjust-mss" to the +#        individual interface. + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.ifconfig import Section + +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() + +base = ['firewall', 'options', 'interface'] +config = ConfigTree(config_file) + +if not config.exists(base): +    # Nothing to do +    exit(0) + +for interface in config.list_nodes(base): +    if config.exists(base + [interface, 'disable']): +        continue + +    if config.exists(base + [interface, 'adjust-mss']): +        section = Section.section(interface) +        tmp = config.return_value(base + [interface, 'adjust-mss']) +        config.set(['interfaces', section, interface, 'ip', 'adjust-mss'], value=tmp) + +    if config.exists(base + [interface, 'adjust-mss6']): +        section = Section.section(interface) +        tmp = config.return_value(base + [interface, 'adjust-mss6']) +        config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp) + +config.delete(['firewall', 'options']) + +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/op_mode/containers_op.py b/src/op_mode/containers_op.py index 1e3fc3a8f..bc317029c 100755 --- a/src/op_mode/containers_op.py +++ b/src/op_mode/containers_op.py @@ -15,10 +15,10 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  import argparse -from vyos.configquery import query_context, ConfigQueryError -from vyos.util import cmd -config, op = query_context() +from getpass import getuser +from vyos.configquery import ConfigTreeQuery +from vyos.util import cmd  parser = argparse.ArgumentParser()  parser.add_argument("-a", "--all", action="store_true", help="Show all containers") @@ -26,34 +26,53 @@ parser.add_argument("-i", "--image", action="store_true", help="Show container i  parser.add_argument("-n", "--networks", action="store_true", help="Show container images")  parser.add_argument("-p", "--pull", action="store", help="Pull image for container")  parser.add_argument("-d", "--remove", action="store", help="Delete container image") +parser.add_argument("-u", "--update", action="store", help="Update given container image") -if not config.exists(['container']): +config = ConfigTreeQuery() +base = ['container'] +if not config.exists(base):      print('Containers not configured')      exit(0) +if getuser() != 'root': +    raise OSError('This functions needs to be run as root to return correct results!') +  if __name__ == '__main__':      args = parser.parse_args()      if args.all:          print(cmd('podman ps --all')) -        exit(0) -    if args.image: + +    elif args.image:          print(cmd('podman image ls')) -        exit(0) -    if args.networks: + +    elif args.networks:          print(cmd('podman network ls')) -        exit(0) -    if args.pull: + +    elif args.pull:          image = args.pull          try: -            print(cmd(f'sudo podman image pull {image}')) +            print(cmd(f'podman image pull {image}'))          except:              print(f'Can\'t find or download image "{image}"') -        exit(0) -    if args.remove: + +    elif args.remove:          image = args.remove          try: -            print(cmd(f'sudo podman image rm {image}')) +            print(cmd(f'podman image rm {image}'))          except:              print(f'Can\'t delete image "{image}"') -        exit(0) + +    elif args.update: +        tmp = config.get_config_dict(base + ['name', args.update], +                                     key_mangling=('-', '_'), get_first_key=True) +        try: +            image = tmp['image'] +            print(cmd(f'podman image pull {image}')) +        except: +            print(f'Can\'t find or download image "{image}"') +    else: +        parser.print_help() +        exit(1) + +    exit(0) diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py index 1fb61d263..d79b6c024 100755 --- a/src/op_mode/dns_forwarding_statistics.py +++ b/src/op_mode/dns_forwarding_statistics.py @@ -11,7 +11,7 @@ PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns'  OUT_TMPL_SRC = """  DNS forwarding statistics: -Cache entries: {{ cache_entries -}} +Cache entries: {{ cache_entries }}  Cache size: {{ cache_size }} kbytes  """ diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py index d45525431..990b06c12 100755 --- a/src/op_mode/ikev2_profile_generator.py +++ b/src/op_mode/ikev2_profile_generator.py @@ -21,7 +21,7 @@ from sys import exit  from socket import getfqdn  from cryptography.x509.oid import NameOID -from vyos.config import Config +from vyos.configquery import ConfigTreeQuery  from vyos.pki import load_certificate  from vyos.template import render_to_string  from vyos.util import ask_input @@ -117,7 +117,7 @@ args = parser.parse_args()  ipsec_base = ['vpn', 'ipsec']  config_base = ipsec_base +  ['remote-access', 'connection']  pki_base = ['pki'] -conf = Config() +conf = ConfigTreeQuery()  if not conf.exists(config_base):      exit('IPSec remote-access is not configured!') @@ -153,7 +153,7 @@ cert = load_certificate(pki['certificate'][cert_name]['certificate'])  data['ca_cn'] = ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value  data['cert_cn'] = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value -data['ca_cert'] = conf.return_value(pki_base + ['ca', ca_name, 'certificate']) +data['ca_cert'] = conf.value(pki_base + ['ca', ca_name, 'certificate'])  esp_proposals = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'],                                       key_mangling=('-', '_'), get_first_key=True) diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 297270cf1..36891d080 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -24,7 +24,7 @@ import tabulate  from cryptography import x509  from cryptography.x509.oid import ExtendedKeyUsageOID -from vyos.config import Config +from vyos.configquery import ConfigTreeQuery  from vyos.configdict import dict_merge  from vyos.pki import encode_certificate, encode_public_key, encode_private_key, encode_dh_parameters  from vyos.pki import create_certificate, create_certificate_request, create_certificate_revocation_list @@ -41,10 +41,10 @@ CERT_REQ_END = '-----END CERTIFICATE REQUEST-----'  auth_dir = '/config/auth'  # Helper Functions - +conf = ConfigTreeQuery()  def get_default_values():      # Fetch default x509 values -    conf = Config() +      base = ['pki', 'x509', 'default']      x509_defaults = conf.get_config_dict(base, key_mangling=('-', '_'),                                       get_first_key=True, no_tag_node_value_mangle=True) @@ -53,9 +53,7 @@ def get_default_values():  def get_config_ca_certificate(name=None):      # Fetch ca certificates from config -    conf = Config()      base = ['pki', 'ca'] -      if not conf.exists(base):          return False @@ -69,9 +67,7 @@ def get_config_ca_certificate(name=None):  def get_config_certificate(name=None):      # Get certificates from config -    conf = Config()      base = ['pki', 'certificate'] -      if not conf.exists(base):          return False @@ -100,7 +96,6 @@ def get_certificate_ca(cert, ca_certs):  def get_config_revoked_certificates():      # Fetch revoked certificates from config -    conf = Config()      ca_base = ['pki', 'ca']      cert_base = ['pki', 'certificate'] @@ -464,7 +459,7 @@ def generate_certificate_sign(name, ca_name, install=False, file=False):      if not cert_req:          print("Invalid certificate request")          return None -         +      cert = generate_certificate(cert_req, ca_cert, ca_private_key, is_ca=False)      passphrase = ask_passphrase() @@ -813,7 +808,7 @@ if __name__ == '__main__':                  elif args.self_sign:                      generate_certificate_selfsign(args.certificate, install=args.install, file=args.file)                  else: -                    generate_certificate_request(name=args.certificate, install=args.install) +                    generate_certificate_request(name=args.certificate, install=args.install, file=args.file)              elif args.crl:                  generate_certificate_revocation_list(args.crl, install=args.install, file=args.file)              elif args.ssh: diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py index d1b66b33f..0b2322478 100755 --- a/src/op_mode/restart_frr.py +++ b/src/op_mode/restart_frr.py @@ -155,7 +155,7 @@ def _check_args_daemon(daemons):  # define program arguments  cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons')  cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons') -cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False,  nargs='*', help='select single or multiple daemons') +cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False,  nargs='*', help='select single or multiple daemons')  # parse arguments  cmd_args = cmd_args_parser.parse_args() diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py index 20d5d9e17..241fba4f4 100755 --- a/src/op_mode/show_interfaces.py +++ b/src/op_mode/show_interfaces.py @@ -120,10 +120,6 @@ def split_text(text, used=0):      yield line[1:] -def get_vrrp_intf(): -    return [intf for intf in Section.interfaces() if intf.is_vrrp()] - -  def get_counter_val(clear, now):      """      attempt to correct a counter if it wrapped, copied from perl diff --git a/src/op_mode/show_ipsec_sa.py b/src/op_mode/show_ipsec_sa.py index e491267fd..c964caaeb 100755 --- a/src/op_mode/show_ipsec_sa.py +++ b/src/op_mode/show_ipsec_sa.py @@ -23,6 +23,12 @@ import hurry.filesize  import vyos.util +def convert(text): +    return int(text) if text.isdigit() else text.lower() + +def alphanum_key(key): +    return [convert(c) for c in re.split('([0-9]+)', str(key))] +  def format_output(conns, sas):      sa_data = [] @@ -111,7 +117,7 @@ if __name__ == '__main__':          headers = ["Connection", "State", "Uptime", "Bytes In/Out", "Packets In/Out", "Remote address", "Remote ID", "Proposal"]          sa_data = format_output(conns, sas) -        sa_data = sorted(sa_data, key=lambda peer: peer[0]) +        sa_data = sorted(sa_data, key=alphanum_key)          output = tabulate.tabulate(sa_data, headers)          print(output)      except PermissionError: diff --git a/src/op_mode/show_nat_rules.py b/src/op_mode/show_nat_rules.py index 0f40ecabe..4a059c848 100755 --- a/src/op_mode/show_nat_rules.py +++ b/src/op_mode/show_nat_rules.py @@ -67,46 +67,54 @@ if args.source or args.destination:              continue          interface = dict_search('match.right', data['expr'][0])          srcdest = '' -        for i in [1, 2]: -            srcdest_json = dict_search('match.right', data['expr'][i]) -            if not srcdest_json: -                continue - -            if isinstance(srcdest_json,str): -                srcdest += srcdest_json + ' ' -            elif 'prefix' in srcdest_json: -                addr_tmp = dict_search('match.right.prefix.addr', data['expr'][i]) -                len_tmp = dict_search('match.right.prefix.len', data['expr'][i]) -                if addr_tmp and len_tmp: -                    srcdest = addr_tmp + '/' + str(len_tmp) + ' ' -            elif 'set' in srcdest_json: -                if isinstance(srcdest_json['set'][0],str): -                    srcdest += 'port ' + str(srcdest_json['set'][0]) + ' ' -                else: -                    port_range = srcdest_json['set'][0]['range'] -                    srcdest += 'port ' + str(port_range[0]) + '-' + str(port_range[1]) + ' ' - +        srcdests = []          tran_addr = '' -        tran_addr_json = dict_search('snat.addr' if args.source else 'dnat.addr', data['expr'][3]) -        if tran_addr_json: -            if isinstance(tran_addr_json,str): -                tran_addr = tran_addr_json -            elif 'prefix' in tran_addr_json: -                addr_tmp = dict_search('snat.addr.prefix.addr' if args.source else 'dnat.addr.prefix.addr', data['expr'][3]) -                len_tmp = dict_search('snat.addr.prefix.len' if args.source else 'dnat.addr.prefix.len', data['expr'][3]) -                if addr_tmp and len_tmp: -                    tran_addr = addr_tmp + '/' + str(len_tmp) -        else: -            if 'masquerade' in data['expr'][3]: -                tran_addr = 'masquerade' -            elif 'log' in data['expr'][3]: -                continue - -        tran_port = dict_search('snat.port' if args.source else 'dnat.port', data['expr'][3]) -        if tran_port: -            tran_addr += ' port ' + str(tran_port) +        for i in range(1,len(data['expr']) + 1): +            srcdest_json = dict_search('match.right', data['expr'][i]) +            if srcdest_json: +                if isinstance(srcdest_json,str): +                    if srcdest != '': +                        srcdests.append(srcdest) +                        srcdest = '' +                    srcdest = srcdest_json + ' ' +                elif 'prefix' in srcdest_json: +                    addr_tmp = dict_search('match.right.prefix.addr', data['expr'][i]) +                    len_tmp = dict_search('match.right.prefix.len', data['expr'][i]) +                    if addr_tmp and len_tmp: +                        srcdest = addr_tmp + '/' + str(len_tmp) + ' ' +                elif 'set' in srcdest_json: +                    if isinstance(srcdest_json['set'][0],int): +                        srcdest += 'port ' + str(srcdest_json['set'][0]) + ' ' +                    else: +                        port_range = srcdest_json['set'][0]['range'] +                        srcdest += 'port ' + str(port_range[0]) + '-' + str(port_range[1]) + ' ' +             +            tran_addr_json = dict_search('snat' if args.source else 'dnat', data['expr'][i]) +            if tran_addr_json: +                if isinstance(tran_addr_json['addr'],str): +                    tran_addr += tran_addr_json['addr'] + ' ' +                elif 'prefix' in tran_addr_json['addr']: +                    addr_tmp = dict_search('snat.addr.prefix.addr' if args.source else 'dnat.addr.prefix.addr', data['expr'][3]) +                    len_tmp = dict_search('snat.addr.prefix.len' if args.source else 'dnat.addr.prefix.len', data['expr'][3]) +                    if addr_tmp and len_tmp: +                        tran_addr += addr_tmp + '/' + str(len_tmp) + ' ' +             +                if isinstance(tran_addr_json['port'],int): +                    tran_addr += 'port ' + tran_addr_json['port'] +             +            else: +                if 'masquerade' in data['expr'][i]: +                    tran_addr = 'masquerade' +                elif 'log' in data['expr'][i]: +                    continue -        print(format_nat_rule.format(rule, srcdest, tran_addr, interface)) +        if srcdest != '': +            srcdests.append(srcdest) +            srcdest = '' +        print(format_nat_rule.format(rule, srcdests[0], tran_addr, interface)) +         +        for i in range(1, list(srcdest)): +            print(format_nat_rule.format(' ', srcdests[i], ' ', ' '))      exit(0)  else: diff --git a/src/op_mode/show_system_integrity.py b/src/op_mode/show_system_integrity.py deleted file mode 100755 index c34d41e80..000000000 --- a/src/op_mode/show_system_integrity.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/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 sys -import os -import re -import json -from datetime import datetime, timedelta - -version_file = r'/usr/share/vyos/version.json' - - -def _get_sys_build_version(): -    if not os.path.exists(version_file): -        return None -    buf = open(version_file, 'r').read() -    j = json.loads(buf) -    if not 'built_on' in j: -        return None -    return datetime.strptime(j['built_on'], '%a %d %b %Y %H:%M %Z') - - -def _check_pkgs(build_stamp): -    pkg_diffs = { -        'buildtime': str(build_stamp), -        'pkg': {} -    } - -    pkg_info = os.listdir('/var/lib/dpkg/info/') -    for file in pkg_info: -        if re.search('\.list$', file): -            fts = os.stat('/var/lib/dpkg/info/' + file).st_mtime -            dt_str = (datetime.utcfromtimestamp( -                fts).strftime('%Y-%m-%d %H:%M:%S')) -            fdt = datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S') -            if fdt > build_stamp: -                pkg_diffs['pkg'].update( -                    {str(re.sub('\.list', '', file)): str(fdt)}) - -    if len(pkg_diffs['pkg']) != 0: -        return pkg_diffs -    else: -        return None - - -if __name__ == '__main__': -    built_date = _get_sys_build_version() -    if not built_date: -        sys.exit(1) -    pkgs = _check_pkgs(built_date) -    if pkgs: -        print ( -            "The following packages don\'t fit the image creation time\nbuild time:\t" + pkgs['buildtime']) -        for k, v in pkgs['pkg'].items(): -            print ("installed: " + v + '\t' + k) diff --git a/src/op_mode/wireguard_client.py b/src/op_mode/wireguard_client.py index 7661254da..76c1ff7d1 100755 --- a/src/op_mode/wireguard_client.py +++ b/src/op_mode/wireguard_client.py @@ -39,10 +39,11 @@ To enable this configuration on a VyOS router you can use the following commands  set interfaces wireguard {{ interface }} peer {{ name }} allowed-ips '{{ addr }}'  {% endfor %}  set interfaces wireguard {{ interface }} peer {{ name }} public-key '{{ pubkey }}' + +=== RoadWarrior (client) configuration ===  """  client_config = """ -=== RoadWarrior (client) configuration ===  [Interface]  PrivateKey = {{ privkey }} diff --git a/src/systemd/opennhrp.service b/src/systemd/opennhrp.service index 70235f89d..c9a44de29 100644 --- a/src/systemd/opennhrp.service +++ b/src/systemd/opennhrp.service @@ -6,8 +6,8 @@ StartLimitIntervalSec=0  [Service]  Type=forking -ExecStart=/usr/sbin/opennhrp -d -v -a /run/opennhrp.socket -c /run/opennhrp/opennhrp.conf -s /etc/opennhrp/opennhrp-script.py -p /run/opennhrp.pid +ExecStart=/usr/sbin/opennhrp -d -v -a /run/opennhrp.socket -c /run/opennhrp/opennhrp.conf -s /etc/opennhrp/opennhrp-script.py -p /run/opennhrp/opennhrp.pid  ExecReload=/usr/bin/kill -HUP $MAINPID -PIDFile=/run/opennhrp.pid +PIDFile=/run/opennhrp/opennhrp.pid  Restart=on-failure  RestartSec=20 diff --git a/src/validators/bgp-large-community-list b/src/validators/bgp-large-community-list new file mode 100755 index 000000000..c07268e81 --- /dev/null +++ b/src/validators/bgp-large-community-list @@ -0,0 +1,36 @@ +#!/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 re +import sys + +from vyos.template import is_ipv4 + +pattern = '(.*):(.*):(.*)' + +if __name__ == '__main__': +    if len(sys.argv) != 2: +        sys.exit(1) + +    value = sys.argv[1].split(':') +    if not len(value) == 3: +        sys.exit(1) + +    if not (re.match(pattern, sys.argv[1]) and +           (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit()): +        sys.exit(1) + +    sys.exit(0) diff --git a/src/validators/bgp-route-target b/src/validators/bgp-route-target new file mode 100755 index 000000000..e7e4d403f --- /dev/null +++ b/src/validators/bgp-route-target @@ -0,0 +1,51 @@ +#!/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/>. + +from argparse import ArgumentParser +from vyos.template import is_ipv4 + +parser = ArgumentParser() +group = parser.add_mutually_exclusive_group() +group.add_argument('--single', action='store', help='Validate and allow only one route-target') +group.add_argument('--multi', action='store', help='Validate multiple, whitespace separated route-targets') +args = parser.parse_args() + +def is_valid_rt(rt): +    # every route target needs to have a colon and must consists of two parts +    value = rt.split(':') +    if len(value) != 2: +        return False +    # A route target must either be only numbers, or the first part must be an +    # IPv4 address +    if (is_ipv4(value[0]) or value[0].isdigit()) and value[1].isdigit(): +        return True +    return False + +if __name__ == '__main__': +    if args.single: +        if not is_valid_rt(args.single): +            exit(1) + +    elif args.multi: +        for rt in args.multi.split(' '): +            if not is_valid_rt(rt): +                exit(1) + +    else: +        parser.print_help() +        exit(1) + +    exit(0) | 
