diff options
168 files changed, 2297 insertions, 1133 deletions
@@ -65,9 +65,9 @@ op_mode_definitions: $(op_xml_obj) rm -f $(OP_TMPL_DIR)/generate/node.def rm -f $(OP_TMPL_DIR)/monitor/node.def rm -f $(OP_TMPL_DIR)/set/node.def - rm -f $(OP_TMPL_DIR)/show/interfaces/node.def rm -f $(OP_TMPL_DIR)/show/node.def rm -f $(OP_TMPL_DIR)/show/system/node.def + rm -f $(OP_TMPL_DIR)/show/tech-support/node.def # XXX: ping and traceroute must be able to recursivly call itself as the # options are provided from the script itself diff --git a/data/configd-include.json b/data/configd-include.json index 456211caa..2f1d39006 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -85,6 +85,5 @@ "vpn_l2tp.py", "vpn_pptp.py", "vpn_sstp.py", -"vrf.py", -"vrf_vni.py" +"vrf.py" ] diff --git a/data/templates/accel-ppp/pptp.config.j2 b/data/templates/accel-ppp/pptp.config.j2 index 442830b6b..78a629d2d 100644 --- a/data/templates/accel-ppp/pptp.config.j2 +++ b/data/templates/accel-ppp/pptp.config.j2 @@ -93,10 +93,15 @@ bind={{ radius_source_address }} gw-ip-address={{ gw_ip }} {% endif %} -{% if radius_shaper_attr %} +{% if radius_shaper_enable %} [shaper] verbose=1 +{% if radius_shaper_attr %} attr={{ radius_shaper_attr }} +{% endif %} +{% if radius_shaper_multiplier %} +rate-multiplier={{ radius_shaper_multiplier }} +{% endif %} {% if radius_shaper_vendor %} vendor={{ radius_shaper_vendor }} {% endif %} diff --git a/data/templates/container/containers.conf.j2 b/data/templates/container/containers.conf.j2 index 9f66aed27..c635ca213 100644 --- a/data/templates/container/containers.conf.j2 +++ b/data/templates/container/containers.conf.j2 @@ -282,7 +282,7 @@ default_sysctls = [ # Before changing this value all containers must be stopped otherwise it is likely that
# iptables rules and network interfaces might leak on the host. A reboot will fix this.
#
-network_backend = "cni"
+network_backend = "netavark"
# Path to directory where CNI plugin binaries are located.
#
diff --git a/data/templates/ethernet/wpa_supplicant.conf.j2 b/data/templates/ethernet/wpa_supplicant.conf.j2 index 8f140f6cb..cd35d6d1e 100644 --- a/data/templates/ethernet/wpa_supplicant.conf.j2 +++ b/data/templates/ethernet/wpa_supplicant.conf.j2 @@ -67,6 +67,11 @@ network={ # discards such frames to protect against potential attacks by rogue # devices, but this option can be used to disable that protection for cases # where the server/authenticator does not need to be authenticated. - phase1="allow_canned_success=1" + # + # "tls_disable_tlsv1_0=0" is used to allow TLSv1 for compatibility with + # legacy networks. This follows the behavior of Debian's wpa_supplicant, + # which includes a custom patch for allowing TLSv1, but the patch currently + # does not work for VyOS' git builds of wpa_supplicant. + phase1="allow_canned_success=1 tls_disable_tlsv1_0=0" } diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2 index 6cb3b2f95..7a89d29e4 100644 --- a/data/templates/firewall/nftables-policy.j2 +++ b/data/templates/firewall/nftables-policy.j2 @@ -11,7 +11,7 @@ table ip vyos_mangle { type filter hook prerouting priority -150; policy accept; {% if route is vyos_defined %} {% for route_text, conf in route.items() if conf.interface is vyos_defined %} - iifname { {{ ",".join(conf.interface) }} } counter jump VYOS_PBR_{{ route_text }} + iifname { {{ conf.interface | join(",") }} } counter jump VYOS_PBR_{{ route_text }} {% endfor %} {% endif %} } diff --git a/data/templates/frr/isisd.frr.j2 b/data/templates/frr/isisd.frr.j2 index 8df1e9513..3c37e28b9 100644 --- a/data/templates/frr/isisd.frr.j2 +++ b/data/templates/frr/isisd.frr.j2 @@ -25,6 +25,12 @@ interface {{ iface }} {% if iface_config.hello_padding is vyos_defined %} isis hello padding {% endif %} +{% if iface_config.ldp_sync.disable is vyos_defined %} + no isis mpls ldp-sync +{% elif iface_config.ldp_sync.holddown is vyos_defined %} + isis mpls ldp-sync + isis mpls ldp-sync holddown {{ iface_config.ldp_sync.holddown }} +{% endif %} {% if iface_config.metric is vyos_defined %} isis metric {{ iface_config.metric }} {% endif %} @@ -84,6 +90,11 @@ router isis VyOS {{ 'vrf ' + vrf if vrf is vyos_defined }} {% if max_lsp_lifetime is vyos_defined %} max-lsp-lifetime {{ max_lsp_lifetime }} {% endif %} +{% if ldp_sync.holddown is vyos_defined %} + mpls ldp-sync holddown {{ ldp_sync.holddown }} +{% elif ldp_sync is vyos_defined %} + mpls ldp-sync +{% endif %} {% if spf_interval is vyos_defined %} spf-interval {{ spf_interval }} {% endif %} diff --git a/data/templates/frr/ospfd.frr.j2 b/data/templates/frr/ospfd.frr.j2 index 8c4a81c57..3f97b7325 100644 --- a/data/templates/frr/ospfd.frr.j2 +++ b/data/templates/frr/ospfd.frr.j2 @@ -44,6 +44,12 @@ interface {{ iface }} {% if iface_config.bfd.profile is vyos_defined %} ip ospf bfd profile {{ iface_config.bfd.profile }} {% endif %} +{% if iface_config.ldp_sync.disable is vyos_defined %} + no ip ospf mpls ldp-sync +{% elif iface_config.ldp_sync.holddown is vyos_defined %} + ip ospf mpls ldp-sync + ip ospf mpls ldp-sync holddown {{ iface_config.ldp_sync.holddown }} +{% endif %} {% if iface_config.mtu_ignore is vyos_defined %} ip ospf mtu-ignore {% endif %} @@ -133,6 +139,11 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }} {% if maximum_paths is vyos_defined %} maximum-paths {{ maximum_paths }} {% endif %} +{% if ldp_sync.holddown is vyos_defined %} + mpls ldp-sync holddown {{ ldp_sync.holddown }} +{% elif ldp_sync is vyos_defined %} + mpls ldp-sync +{% endif %} {% if distance.global is vyos_defined %} distance {{ distance.global }} {% endif %} diff --git a/data/templates/frr/vrf-vni.frr.j2 b/data/templates/frr/vrf-vni.frr.j2 deleted file mode 100644 index e5f4810a1..000000000 --- a/data/templates/frr/vrf-vni.frr.j2 +++ /dev/null @@ -1,9 +0,0 @@ -{% if name is vyos_defined %} -{% for vrf, vrf_config in name.items() %} -vrf {{ vrf }} -{% if vrf_config.vni is vyos_defined %} - vni {{ vrf_config.vni }} -{% endif %} - exit-vrf -{% endfor %} -{% endif %} diff --git a/data/templates/frr/vrf.route-map.frr.j2 b/data/templates/frr/vrf.route-map.frr.j2 deleted file mode 100644 index 5e0c56a7b..000000000 --- a/data/templates/frr/vrf.route-map.frr.j2 +++ /dev/null @@ -1,10 +0,0 @@ -! -{% if vrf is vyos_defined and route_map is vyos_defined %} -vrf {{ vrf }} - ip protocol {{ protocol }} route-map {{ route_map }} - exit-vrf -! -{% elif route_map is vyos_defined %} -ip protocol {{ protocol }} route-map {{ route_map }} -{% endif %} -! diff --git a/data/templates/frr/vrf.route-map.v6.frr.j2 b/data/templates/frr/vrf.route-map.v6.frr.j2 deleted file mode 100644 index 7dc59a046..000000000 --- a/data/templates/frr/vrf.route-map.v6.frr.j2 +++ /dev/null @@ -1,10 +0,0 @@ -! -{% if vrf is vyos_defined and route_map is vyos_defined %} -vrf {{ vrf }} - ipv6 protocol {{ protocol }} route-map {{ route_map }} - exit-vrf -! -{% elif route_map is vyos_defined %} -ipv6 protocol {{ protocol }} route-map {{ route_map }} -{% endif %} -! diff --git a/data/templates/frr/zebra.route-map.frr.j2 b/data/templates/frr/zebra.route-map.frr.j2 new file mode 100644 index 000000000..8e18abbde --- /dev/null +++ b/data/templates/frr/zebra.route-map.frr.j2 @@ -0,0 +1,9 @@ +! +{% if protocol is vyos_defined %} +{% for protocol_name, protocol_config in protocol.items() %} +{% if protocol_name is vyos_defined('ospfv3') %} +{% set protocol_name = 'ospf6' %} +{% endif %} +{{ afi }} protocol {{ protocol_name }} route-map {{ protocol_config.route_map }} +{% endfor %} +{% endif %} diff --git a/data/templates/frr/zebra.vrf.route-map.frr.j2 b/data/templates/frr/zebra.vrf.route-map.frr.j2 new file mode 100644 index 000000000..eb6abd8e7 --- /dev/null +++ b/data/templates/frr/zebra.vrf.route-map.frr.j2 @@ -0,0 +1,24 @@ +! +{% if name is vyos_defined %} +{% for vrf, vrf_config in name.items() %} +vrf {{ vrf }} +{% if vrf_config.ip.protocol is vyos_defined %} +{% for protocol_name, protocol_config in vrf_config.ip.protocol.items() %} + ip protocol {{ protocol_name }} route-map {{ protocol_config.route_map }} +{% endfor %} +{% endif %} +{% if vrf_config.ipv6.protocol is vyos_defined %} +{% for protocol_name, protocol_config in vrf_config.ipv6.protocol.items() %} +{% if protocol_name is vyos_defined('ospfv3') %} +{% set protocol_name = 'ospf6' %} +{% endif %} + ipv6 protocol {{ protocol_name }} route-map {{ protocol_config.route_map }} +{% endfor %} +{% endif %} +{% if vrf_config.vni is vyos_defined %} + vni {{ vrf_config.vni }} +{% endif %} +{% endfor %} + exit-vrf +! +{% endif %} diff --git a/data/templates/https/nginx.default.j2 b/data/templates/https/nginx.default.j2 index d42b3b389..b541ff309 100644 --- a/data/templates/https/nginx.default.j2 +++ b/data/templates/https/nginx.default.j2 @@ -50,6 +50,12 @@ server { {% else %} return 503; {% endif %} +{% if server.allow_client %} +{% for client in server.allow_client %} + allow {{ client }}; +{% endfor %} + deny all; +{% endif %} } error_page 497 =301 https://$host:{{ server.port }}$request_uri; diff --git a/data/templates/login/limits.j2 b/data/templates/login/limits.j2 new file mode 100644 index 000000000..5e2c11f35 --- /dev/null +++ b/data/templates/login/limits.j2 @@ -0,0 +1,5 @@ +# Generated by /usr/libexec/vyos/conf_mode/system-login.py + +{% if max_login_session is vyos_defined %} +* - maxsyslogins {{ max_login_session }} +{% endif %} diff --git a/data/templates/sflow/hsflowd.conf.j2 b/data/templates/sflow/hsflowd.conf.j2 index 94f5939be..5000956bd 100644 --- a/data/templates/sflow/hsflowd.conf.j2 +++ b/data/templates/sflow/hsflowd.conf.j2 @@ -28,4 +28,5 @@ sflow { {% if drop_monitor_limit is vyos_defined %} dropmon { limit={{ drop_monitor_limit }} start=on sw=on hw=off } {% endif %} + dbus { } } diff --git a/data/templates/telegraf/telegraf.j2 b/data/templates/telegraf/telegraf.j2 index c9f402281..5852d6232 100644 --- a/data/templates/telegraf/telegraf.j2 +++ b/data/templates/telegraf/telegraf.j2 @@ -12,7 +12,7 @@ debug = false quiet = false logfile = "" - hostname = "" + hostname = "{{ hostname }}" omit_hostname = false {% if azure_data_explorer is vyos_defined %} ### Azure Data Explorer ### diff --git a/debian/control b/debian/control index 028b7cd43..3126e6ad9 100644 --- a/debian/control +++ b/debian/control @@ -34,7 +34,9 @@ Package: vyos-1x Architecture: amd64 arm64 Depends: ${python3:Depends}, + aardvark-dns, accel-ppp, + auditd, avahi-daemon, beep, bmon, @@ -80,6 +82,7 @@ Depends: lcdproc, lcdproc-extra-drivers, libatomic1, + libauparse0, libbpf1 [amd64], libcharon-extra-plugins (>=5.9), libcharon-extauth-plugins (>=5.9), @@ -99,6 +102,7 @@ Depends: mtr-tiny, ndisc6, ndppd, + netavark, netplug, nfct, nftables (>= 0.9.3), diff --git a/interface-definitions/bcast-relay.xml.in b/interface-definitions/bcast-relay.xml.in index aeaa5ab37..e2993f3f3 100644 --- a/interface-definitions/bcast-relay.xml.in +++ b/interface-definitions/bcast-relay.xml.in @@ -34,11 +34,7 @@ </constraint> </properties> </leafNode> - <leafNode name="description"> - <properties> - <help>Description</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> #include <include/generic-interface-multi.xml.i> #include <include/port-number.xml.i> </children> diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in index 6947ed500..9b6d2369d 100644 --- a/interface-definitions/container.xml.in +++ b/interface-definitions/container.xml.in @@ -117,7 +117,7 @@ <properties> <help>Container host name</help> <constraint> - #include <include/constraint/host-name.xml.in> + #include <include/constraint/host-name.xml.i> </constraint> <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage> </properties> @@ -191,15 +191,20 @@ <children> <leafNode name="address"> <properties> - <!-- PODMAN currently does not support more then one IPv4 or IPv6 address assignments to a container --> <help>Assign static IP address to container</help> <valueHelp> <format>ipv4</format> <description>IPv4 address</description> </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> <constraint> <validator name="ipv4-address"/> + <validator name="ipv6-address"/> </constraint> + <multi/> </properties> </leafNode> </children> @@ -343,11 +348,7 @@ <constraintErrorMessage>Network name cannot be longer than 11 characters</constraintErrorMessage> </properties> <children> - <leafNode name="description"> - <properties> - <help>Network description</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <leafNode name="prefix"> <properties> <help>Prefix which allocated to that network</help> @@ -366,6 +367,7 @@ <multi/> </properties> </leafNode> + #include <include/interface/vrf.xml.i> </children> </tagNode> <tagNode name="registry"> diff --git a/interface-definitions/dhcp-relay.xml.in b/interface-definitions/dhcp-relay.xml.in index 79ad2c01c..2a2597dd5 100644 --- a/interface-definitions/dhcp-relay.xml.in +++ b/interface-definitions/dhcp-relay.xml.in @@ -21,7 +21,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> @@ -37,7 +37,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> diff --git a/interface-definitions/dns-domain-name.xml.in b/interface-definitions/dns-domain-name.xml.in index c0ac16a80..e93c49ebd 100644 --- a/interface-definitions/dns-domain-name.xml.in +++ b/interface-definitions/dns-domain-name.xml.in @@ -25,7 +25,7 @@ <constraint> <validator name="ipv4-address"/> <validator name="ipv6-address"/> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> @@ -34,7 +34,7 @@ <properties> <help>System host name (default: vyos)</help> <constraint> - #include <include/constraint/host-name.xml.in> + #include <include/constraint/host-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in index 14b38b24d..6b7344b1d 100644 --- a/interface-definitions/dns-forwarding.xml.in +++ b/interface-definitions/dns-forwarding.xml.in @@ -83,6 +83,13 @@ <tagNode name="domain"> <properties> <help>Domain to forward to a custom DNS server</help> + <valueHelp> + <format>txt</format> + <description>An absolute DNS domain name</description> + </valueHelp> + <constraint> + <validator name="fqdn"/> + </constraint> </properties> <children> #include <include/name-server-ipv4-ipv6-port.xml.i> @@ -104,11 +111,11 @@ <properties> <help>Domain to host authoritative records for</help> <valueHelp> - <format>text</format> - <description>An absolute DNS name</description> + <format>txt</format> + <description>An absolute DNS domain name</description> </valueHelp> <constraint> - <regex>[-_a-zA-Z0-9.]{1,63}</regex> + <validator name="fqdn"/> </constraint> </properties> <children> @@ -121,7 +128,7 @@ <properties> <help>"A" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -158,7 +165,7 @@ <properties> <help>"AAAA" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -195,7 +202,7 @@ <properties> <help>"CNAME" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -227,7 +234,7 @@ <properties> <help>"MX" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -274,7 +281,7 @@ <properties> <help>"PTR" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -306,7 +313,7 @@ <properties> <help>"TXT" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -322,7 +329,7 @@ <properties> <help>Record contents</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>Record contents</description> </valueHelp> <multi/> @@ -336,7 +343,7 @@ <properties> <help>"SPF" record (type=SPF)</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -352,7 +359,7 @@ <properties> <help>Record contents</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>Record contents</description> </valueHelp> </properties> @@ -365,7 +372,7 @@ <properties> <help>"SRV" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> @@ -449,7 +456,7 @@ <properties> <help>"NAPTR" record</help> <valueHelp> - <format>text</format> + <format>txt</format> <description>A DNS name relative to the root record</description> </valueHelp> <valueHelp> diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index 624d61759..69901e5d3 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -349,6 +349,9 @@ <completionHelp> <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> + <constraint> + #include <include/constraint/interface-name-with-wildcard.xml.i> + </constraint> </properties> <children> <node name="in"> diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in index 1fa051df9..ce6603796 100644 --- a/interface-definitions/high-availability.xml.in +++ b/interface-definitions/high-availability.xml.in @@ -220,7 +220,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in index 6adb07598..cf30ab2be 100644 --- a/interface-definitions/https.xml.in +++ b/interface-definitions/https.xml.in @@ -60,6 +60,7 @@ <multi/> </properties> </leafNode> + #include <include/allow-client.xml.i> </children> </tagNode> <node name="api" owner="${vyos_conf_scripts_dir}/http-api.py"> diff --git a/interface-definitions/include/allow-client.xml.i b/interface-definitions/include/allow-client.xml.i new file mode 100644 index 000000000..1b06e2c17 --- /dev/null +++ b/interface-definitions/include/allow-client.xml.i @@ -0,0 +1,35 @@ +<!-- include start from allow-client.xml.i --> +<node name="allow-client"> + <properties> + <help>Restrict to allowed IP client addresses</help> + </properties> + <children> + <leafNode name="address"> + <properties> + <help>Allowed IP client addresses</help> + <valueHelp> + <format>ipv4</format> + <description>IPv4 address</description> + </valueHelp> + <valueHelp> + <format>ipv6</format> + <description>IPv6 address</description> + </valueHelp> + <valueHelp> + <format>ipv4net</format> + <description>IPv4 address and prefix length</description> + </valueHelp> + <valueHelp> + <format>ipv6net</format> + <description>IPv6 address and prefix length</description> + </valueHelp> + <constraint> + <validator name="ip-address"/> + <validator name="ip-cidr"/> + </constraint> + <multi/> + </properties> + </leafNode> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/babel/interface.xml.i b/interface-definitions/include/babel/interface.xml.i index 586eca7a5..a122ef024 100644 --- a/interface-definitions/include/babel/interface.xml.i +++ b/interface-definitions/include/babel/interface.xml.i @@ -10,7 +10,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/include/bgp/afi-ipv4-prefix-list.xml.i b/interface-definitions/include/bgp/afi-ipv4-prefix-list.xml.i index ee902b0b6..0f760daae 100644 --- a/interface-definitions/include/bgp/afi-ipv4-prefix-list.xml.i +++ b/interface-definitions/include/bgp/afi-ipv4-prefix-list.xml.i @@ -15,7 +15,7 @@ <description>Name of IPv4 prefix-list</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Name of prefix-list can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> </properties> @@ -31,7 +31,7 @@ <description>Name of IPv4 prefix-list</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Name of prefix-list can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> </properties> diff --git a/interface-definitions/include/bgp/afi-ipv6-prefix-list.xml.i b/interface-definitions/include/bgp/afi-ipv6-prefix-list.xml.i index da966f193..268d9cbc0 100644 --- a/interface-definitions/include/bgp/afi-ipv6-prefix-list.xml.i +++ b/interface-definitions/include/bgp/afi-ipv6-prefix-list.xml.i @@ -15,7 +15,7 @@ <description>Name of IPv6 prefix-list</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Name of prefix-list6 can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> </properties> @@ -31,7 +31,7 @@ <description>Name of IPv6 prefix-list</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Name of prefix-list6 can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> </properties> diff --git a/interface-definitions/include/bgp/neighbor-update-source.xml.i b/interface-definitions/include/bgp/neighbor-update-source.xml.i index 0acec4126..c6aa776c2 100644 --- a/interface-definitions/include/bgp/neighbor-update-source.xml.i +++ b/interface-definitions/include/bgp/neighbor-update-source.xml.i @@ -22,7 +22,7 @@ <constraint> <validator name="ipv4-address"/> <validator name="ipv6-address"/> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i index 089c36ef3..bcc131f83 100644 --- a/interface-definitions/include/bgp/protocol-common-config.xml.i +++ b/interface-definitions/include/bgp/protocol-common-config.xml.i @@ -810,12 +810,6 @@ </node> </children> </node> - <leafNode name="advertise-all-vni"> - <properties> - <help>Advertise All local VNIs</help> - <valueless/> - </properties> - </leafNode> #include <include/bgp/afi-l2vpn-common.xml.i> <leafNode name="advertise-pip"> <properties> @@ -942,7 +936,7 @@ <constraint> <validator name="ipv4-address"/> <validator name="ipv6-address"/> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> @@ -1530,7 +1524,7 @@ <properties> <help>Name of peer-group</help> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> </properties> <children> @@ -1565,7 +1559,6 @@ #include <include/port-number.xml.i> </children> </tagNode> -#include <include/route-map.xml.i> <node name="timers"> <properties> <help>BGP protocol timers</help> diff --git a/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore.xml.in b/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore.xml.i index eb568d7d9..eb568d7d9 100644 --- a/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore.xml.in +++ b/interface-definitions/include/constraint/alpha-numeric-hyphen-underscore.xml.i diff --git a/interface-definitions/include/constraint/host-name.xml.in b/interface-definitions/include/constraint/host-name.xml.i index 202c200f4..202c200f4 100644 --- a/interface-definitions/include/constraint/host-name.xml.in +++ b/interface-definitions/include/constraint/host-name.xml.i diff --git a/interface-definitions/include/constraint/interface-name-with-wildcard.xml.i b/interface-definitions/include/constraint/interface-name-with-wildcard.xml.i new file mode 100644 index 000000000..09867b380 --- /dev/null +++ b/interface-definitions/include/constraint/interface-name-with-wildcard.xml.i @@ -0,0 +1,4 @@ +<!-- include start from constraint/interface-name-with-wildcard.xml.in --> +<regex>(bond|br|dum|en|ersp|eth|gnv|ifb|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|veth|vti|vtun|vxlan|wg|wlan|wwan)([0-9]?)(\*?)(.+)?|lo</regex> +<validator name="file-path --lookup-path /sys/class/net --directory"/> +<!-- include end --> diff --git a/interface-definitions/include/constraint/interface-name.xml.in b/interface-definitions/include/constraint/interface-name.xml.i index e540e4418..e540e4418 100644 --- a/interface-definitions/include/constraint/interface-name.xml.in +++ b/interface-definitions/include/constraint/interface-name.xml.i diff --git a/interface-definitions/include/dhcp-interface-multi.xml.i b/interface-definitions/include/dhcp-interface-multi.xml.i index e10341037..0db11cf79 100644 --- a/interface-definitions/include/dhcp-interface-multi.xml.i +++ b/interface-definitions/include/dhcp-interface-multi.xml.i @@ -10,7 +10,7 @@ <description>DHCP interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> diff --git a/interface-definitions/include/dhcp-interface.xml.i b/interface-definitions/include/dhcp-interface.xml.i index 24edbbd15..b5c94cb24 100644 --- a/interface-definitions/include/dhcp-interface.xml.i +++ b/interface-definitions/include/dhcp-interface.xml.i @@ -9,7 +9,7 @@ <description>DHCP interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/eigrp/protocol-common-config.xml.i b/interface-definitions/include/eigrp/protocol-common-config.xml.i index 88365187a..a21d18424 100644 --- a/interface-definitions/include/eigrp/protocol-common-config.xml.i +++ b/interface-definitions/include/eigrp/protocol-common-config.xml.i @@ -107,7 +107,6 @@ <multi/> </properties> </leafNode> -#include <include/route-map.xml.i> #include <include/router-id.xml.i> <!-- FRR timers not implemented yet --> <leafNode name="variance"> diff --git a/interface-definitions/include/generic-interface-broadcast.xml.i b/interface-definitions/include/generic-interface-broadcast.xml.i index 82bfc139b..e37e75012 100644 --- a/interface-definitions/include/generic-interface-broadcast.xml.i +++ b/interface-definitions/include/generic-interface-broadcast.xml.i @@ -1,7 +1,7 @@ <!-- include start from generic-interface-broadcast.xml.i --> <leafNode name="interface"> <properties> - <help>Interface Name to use</help> + <help>Interface to use</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces --broadcast</script> </completionHelp> @@ -10,7 +10,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/generic-interface-multi-broadcast.xml.i b/interface-definitions/include/generic-interface-multi-broadcast.xml.i index 8160f816d..ed13cf2cf 100644 --- a/interface-definitions/include/generic-interface-multi-broadcast.xml.i +++ b/interface-definitions/include/generic-interface-multi-broadcast.xml.i @@ -1,7 +1,7 @@ <!-- include start from generic-interface-multi-broadcast.xml.i --> <leafNode name="interface"> <properties> - <help>Interface Name to use</help> + <help>Interface to use</help> <completionHelp> <script>${vyos_completion_dir}/list_interfaces --broadcast</script> </completionHelp> @@ -10,7 +10,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> diff --git a/interface-definitions/include/generic-interface-multi-wildcard.xml.i b/interface-definitions/include/generic-interface-multi-wildcard.xml.i new file mode 100644 index 000000000..6c846a795 --- /dev/null +++ b/interface-definitions/include/generic-interface-multi-wildcard.xml.i @@ -0,0 +1,18 @@ +<!-- include start from generic-interface-multi-wildcard.xml.i --> +<leafNode name="interface"> + <properties> + <help>Interface to use</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + <valueHelp> + <format>txt</format> + <description>Interface name, wildcard (*) supported</description> + </valueHelp> + <constraint> + #include <include/constraint/interface-name-with-wildcard.xml.i> + </constraint> + <multi/> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/generic-interface-multi.xml.i b/interface-definitions/include/generic-interface-multi.xml.i index 1b8dc102b..cfc77af3a 100644 --- a/interface-definitions/include/generic-interface-multi.xml.i +++ b/interface-definitions/include/generic-interface-multi.xml.i @@ -10,7 +10,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> diff --git a/interface-definitions/include/generic-interface.xml.i b/interface-definitions/include/generic-interface.xml.i index 9417f9ef0..65f5bfbb8 100644 --- a/interface-definitions/include/generic-interface.xml.i +++ b/interface-definitions/include/generic-interface.xml.i @@ -10,7 +10,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/interface/redirect.xml.i b/interface-definitions/include/interface/redirect.xml.i index 0421f4074..9b41cd8ff 100644 --- a/interface-definitions/include/interface/redirect.xml.i +++ b/interface-definitions/include/interface/redirect.xml.i @@ -10,7 +10,7 @@ <description>Destination interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/isis/ldp-sync-holddown.xml.i b/interface-definitions/include/isis/ldp-sync-holddown.xml.i new file mode 100644 index 000000000..15ac26f07 --- /dev/null +++ b/interface-definitions/include/isis/ldp-sync-holddown.xml.i @@ -0,0 +1,14 @@ +<!-- include start from isis/ldp-sync-holddown.xml.i --> +<leafNode name="holddown"> + <properties> + <help>Hold down timer for LDP-IGP cost restoration</help> + <valueHelp> + <format>u32:0-10000</format> + <description>Time to wait in seconds for LDP-IGP synchronization to occur before restoring interface cost</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 0-10000"/> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/isis/ldp-sync-interface.xml.i b/interface-definitions/include/isis/ldp-sync-interface.xml.i new file mode 100644 index 000000000..222a35256 --- /dev/null +++ b/interface-definitions/include/isis/ldp-sync-interface.xml.i @@ -0,0 +1,11 @@ +<!-- include start from isis/ldp-igp-sync.xml.i --> +<node name="ldp-sync"> + <properties> + <help>LDP-IGP synchronization configuration for interface</help> + </properties> + <children> + #include <include/generic-disable-node.xml.i> + #include <include/isis/ldp-sync-holddown.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/isis/ldp-sync-protocol.xml.i b/interface-definitions/include/isis/ldp-sync-protocol.xml.i new file mode 100644 index 000000000..b2e696a70 --- /dev/null +++ b/interface-definitions/include/isis/ldp-sync-protocol.xml.i @@ -0,0 +1,10 @@ +<!-- include start from isis/ldp-igp-sync.xml.i --> +<node name="ldp-sync"> + <properties> + <help>Protocol wide LDP-IGP synchronization configuration</help> + </properties> + <children> + #include <include/isis/ldp-sync-holddown.xml.i> + </children> +</node> +<!-- include end --> diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i index 0e6f19480..4ca7061db 100644 --- a/interface-definitions/include/isis/protocol-common-config.xml.i +++ b/interface-definitions/include/isis/protocol-common-config.xml.i @@ -152,6 +152,7 @@ </constraint> </properties> </leafNode> +#include <include/isis/ldp-sync-protocol.xml.i> <leafNode name="net"> <properties> <help>A Network Entity Title for this process (ISO only)</help> @@ -172,7 +173,7 @@ </leafNode> <node name="traffic-engineering"> <properties> - <help>Show IS-IS neighbor adjacencies</help> + <help>IS-IS traffic engineering extensions</help> </properties> <children> <leafNode name="enable"> @@ -631,6 +632,7 @@ </properties> </leafNode> #include <include/isis/metric.xml.i> + #include <include/isis/ldp-sync-interface.xml.i> <node name="network"> <properties> <help>Set network type</help> @@ -685,5 +687,4 @@ </leafNode> </children> </tagNode> -#include <include/route-map.xml.i> <!-- 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 25b54b181..b7f22cb88 100644 --- a/interface-definitions/include/ospf/protocol-common-config.xml.i +++ b/interface-definitions/include/ospf/protocol-common-config.xml.i @@ -331,6 +331,7 @@ </constraint> </properties> </leafNode> +#include <include/isis/ldp-sync-protocol.xml.i> <node name="distance"> <properties> <help>Administrative distance</help> @@ -358,7 +359,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> @@ -385,6 +386,7 @@ #include <include/ospf/authentication.xml.i> #include <include/ospf/intervals.xml.i> #include <include/ospf/interface-common.xml.i> + #include <include/isis/ldp-sync-interface.xml.i> <leafNode name="bandwidth"> <properties> <help>Interface bandwidth (Mbit/s)</help> @@ -814,7 +816,6 @@ </leafNode> </children> </node> -#include <include/route-map.xml.i> <node name="timers"> <properties> <help>Adjust routing timers</help> @@ -875,4 +876,4 @@ </node> </children> </node> -<!-- include end --> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/ospfv3/protocol-common-config.xml.i b/interface-definitions/include/ospfv3/protocol-common-config.xml.i index 014bf9e49..a7de50638 100644 --- a/interface-definitions/include/ospfv3/protocol-common-config.xml.i +++ b/interface-definitions/include/ospfv3/protocol-common-config.xml.i @@ -118,7 +118,7 @@ <description>Interface used for routing information exchange</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> @@ -256,5 +256,4 @@ </node> </children> </node> -#include <include/route-map.xml.i> <!-- include end --> diff --git a/interface-definitions/include/rip/interface.xml.i b/interface-definitions/include/rip/interface.xml.i index 0a89f4d92..8007f0208 100644 --- a/interface-definitions/include/rip/interface.xml.i +++ b/interface-definitions/include/rip/interface.xml.i @@ -10,7 +10,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/include/routing-passive-interface.xml.i b/interface-definitions/include/routing-passive-interface.xml.i index 715468e59..8fa0d0fe7 100644 --- a/interface-definitions/include/routing-passive-interface.xml.i +++ b/interface-definitions/include/routing-passive-interface.xml.i @@ -16,7 +16,7 @@ </valueHelp> <constraint> <regex>(default)</regex> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> diff --git a/interface-definitions/include/source-interface.xml.i b/interface-definitions/include/source-interface.xml.i index c25a6a6d0..40fdc6c5e 100644 --- a/interface-definitions/include/source-interface.xml.i +++ b/interface-definitions/include/source-interface.xml.i @@ -10,7 +10,7 @@ <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/static/static-route-interface.xml.i b/interface-definitions/include/static/static-route-interface.xml.i index db2f0baa6..cb5436847 100644 --- a/interface-definitions/include/static/static-route-interface.xml.i +++ b/interface-definitions/include/static/static-route-interface.xml.i @@ -10,7 +10,7 @@ <description>Gateway interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/static/static-route.xml.i b/interface-definitions/include/static/static-route.xml.i index 34e36f5a7..268cfa005 100644 --- a/interface-definitions/include/static/static-route.xml.i +++ b/interface-definitions/include/static/static-route.xml.i @@ -26,7 +26,7 @@ <description>Gateway interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/include/static/static-route6.xml.i b/interface-definitions/include/static/static-route6.xml.i index aac02062f..1f8d54108 100644 --- a/interface-definitions/include/static/static-route6.xml.i +++ b/interface-definitions/include/static/static-route6.xml.i @@ -25,7 +25,7 @@ <description>Gateway interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/include/system-ip-protocol.xml.i b/interface-definitions/include/system-ip-protocol.xml.i new file mode 100644 index 000000000..c630eb3f7 --- /dev/null +++ b/interface-definitions/include/system-ip-protocol.xml.i @@ -0,0 +1,56 @@ +<!-- include start from system-ip-protocol.xml.i --> +<tagNode name="protocol"> + <properties> + <help>Filter routing info exchanged between routing protocol and zebra</help> + <completionHelp> + <list>any babel bgp connected eigrp isis kernel ospf rip static table</list> + </completionHelp> + <valueHelp> + <format>any</format> + <description>Any of the above protocols</description> + </valueHelp> + <valueHelp> + <format>babel</format> + <description>Babel routing protocol</description> + </valueHelp> + <valueHelp> + <format>bgp</format> + <description>Border Gateway Protocol</description> + </valueHelp> + <valueHelp> + <format>connected</format> + <description>Connected routes (directly attached subnet or host)</description> + </valueHelp> + <valueHelp> + <format>eigrp</format> + <description>Enhanced Interior Gateway Routing Protocol</description> + </valueHelp> + <valueHelp> + <format>isis</format> + <description>Intermediate System to Intermediate System</description> + </valueHelp> + <valueHelp> + <format>kernel</format> + <description>Kernel routes (not installed via the zebra RIB)</description> + </valueHelp> + <valueHelp> + <format>ospf</format> + <description>Open Shortest Path First (OSPFv2)</description> + </valueHelp> + <valueHelp> + <format>rip</format> + <description>Routing Information Protocol</description> + </valueHelp> + <valueHelp> + <format>static</format> + <description>Statically configured routes</description> + </valueHelp> + <constraint> + <regex>(any|babel|bgp|connected|eigrp|isis|kernel|ospf|rip|static|table)</regex> + </constraint> + </properties> + <children> + #include <include/route-map.xml.i> + </children> +</tagNode> +<!-- include end -->
\ No newline at end of file diff --git a/interface-definitions/include/system-ipv6-protocol.xml.i b/interface-definitions/include/system-ipv6-protocol.xml.i new file mode 100644 index 000000000..485776a71 --- /dev/null +++ b/interface-definitions/include/system-ipv6-protocol.xml.i @@ -0,0 +1,52 @@ +<!-- include start from system-ipv6-protocol.xml.i --> +<tagNode name="protocol"> + <properties> + <help>Filter routing info exchanged between routing protocol and zebra</help> + <completionHelp> + <list>any babel bgp connected isis kernel ospfv3 ripng static table</list> + </completionHelp> + <valueHelp> + <format>any</format> + <description>Any of the above protocols</description> + </valueHelp> + <valueHelp> + <format>babel</format> + <description>Babel routing protocol</description> + </valueHelp> + <valueHelp> + <format>bgp</format> + <description>Border Gateway Protocol</description> + </valueHelp> + <valueHelp> + <format>connected</format> + <description>Connected routes (directly attached subnet or host)</description> + </valueHelp> + <valueHelp> + <format>isis</format> + <description>Intermediate System to Intermediate System</description> + </valueHelp> + <valueHelp> + <format>kernel</format> + <description>Kernel routes (not installed via the zebra RIB)</description> + </valueHelp> + <valueHelp> + <format>ospfv3</format> + <description>Open Shortest Path First (OSPFv3)</description> + </valueHelp> + <valueHelp> + <format>ripng</format> + <description>Routing Information Protocol next-generation</description> + </valueHelp> + <valueHelp> + <format>static</format> + <description>Statically configured routes</description> + </valueHelp> + <constraint> + <regex>(any|babel|bgp|connected|isis|kernel|ospfv3|ripng|static|table)</regex> + </constraint> + </properties> + <children> + #include <include/route-map.xml.i> + </children> +</tagNode> +<!-- include end --> diff --git a/interface-definitions/include/version/bgp-version.xml.i b/interface-definitions/include/version/bgp-version.xml.i index ced49e729..1386ea9bc 100644 --- a/interface-definitions/include/version/bgp-version.xml.i +++ b/interface-definitions/include/version/bgp-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/bgp-version.xml.i --> -<syntaxVersion component='bgp' version='3'></syntaxVersion> +<syntaxVersion component='bgp' version='4'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/isis-version.xml.i b/interface-definitions/include/version/isis-version.xml.i index 7bf12e81a..f50329b09 100644 --- a/interface-definitions/include/version/isis-version.xml.i +++ b/interface-definitions/include/version/isis-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/isis-version.xml.i --> -<syntaxVersion component='isis' version='2'></syntaxVersion> +<syntaxVersion component='isis' version='3'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/ospf-version.xml.i b/interface-definitions/include/version/ospf-version.xml.i index 755965daa..df108837b 100644 --- a/interface-definitions/include/version/ospf-version.xml.i +++ b/interface-definitions/include/version/ospf-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/ospf-version.xml.i --> -<syntaxVersion component='ospf' version='1'></syntaxVersion> +<syntaxVersion component='ospf' version='2'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/quagga-version.xml.i b/interface-definitions/include/version/quagga-version.xml.i index f9944acce..23d884cd4 100644 --- a/interface-definitions/include/version/quagga-version.xml.i +++ b/interface-definitions/include/version/quagga-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/quagga-version.xml.i --> -<syntaxVersion component='quagga' version='10'></syntaxVersion> +<syntaxVersion component='quagga' version='11'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/include/version/rip-version.xml.i b/interface-definitions/include/version/rip-version.xml.i new file mode 100644 index 000000000..30ace486a --- /dev/null +++ b/interface-definitions/include/version/rip-version.xml.i @@ -0,0 +1,3 @@ +<!-- include start from include/version/rip-version.xml.i --> +<syntaxVersion component='rip' version='1'></syntaxVersion> +<!-- include end --> diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in index f5f1eb1b6..14b1036b4 100644 --- a/interface-definitions/interfaces-bonding.xml.in +++ b/interface-definitions/interfaces-bonding.xml.in @@ -199,7 +199,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> <multi/> </properties> @@ -218,7 +218,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/load-balancing-wan.xml.in b/interface-definitions/load-balancing-wan.xml.in index 2b812eb4d..c1d7e2c67 100644 --- a/interface-definitions/load-balancing-wan.xml.in +++ b/interface-definitions/load-balancing-wan.xml.in @@ -191,15 +191,7 @@ </constraint> </properties> <children> - <leafNode name="description"> - <properties> - <help>Description for this rule</help> - <valueHelp> - <format>txt</format> - <description>Description for this rule</description> - </valueHelp> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <node name="destination"> <properties> <help>Destination</help> diff --git a/interface-definitions/nat66.xml.in b/interface-definitions/nat66.xml.in index 6ea611789..7a8970bdf 100644 --- a/interface-definitions/nat66.xml.in +++ b/interface-definitions/nat66.xml.in @@ -24,11 +24,7 @@ <constraintErrorMessage>NAT66 rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> - <leafNode name="description"> - <properties> - <help>Rule description</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <leafNode name="disable"> <properties> <help>Disable NAT66 rule</help> @@ -156,11 +152,7 @@ <constraintErrorMessage>NAT66 rule number must be between 1 and 999999</constraintErrorMessage> </properties> <children> - <leafNode name="description"> - <properties> - <help>Rule description</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <leafNode name="disable"> <properties> <help>Disable NAT66 rule</help> diff --git a/interface-definitions/ntp.xml.in b/interface-definitions/ntp.xml.in index 287401ed6..558204a06 100644 --- a/interface-definitions/ntp.xml.in +++ b/interface-definitions/ntp.xml.in @@ -57,39 +57,7 @@ </leafNode> </children> </tagNode> - <node name="allow-client"> - <properties> - <help>Specify NTP clients allowed to access the server</help> - </properties> - <children> - <leafNode name="address"> - <properties> - <help>IP address</help> - <valueHelp> - <format>ipv4</format> - <description>Allowed IPv4 address</description> - </valueHelp> - <valueHelp> - <format>ipv4net</format> - <description>Allowed IPv4 prefix</description> - </valueHelp> - <valueHelp> - <format>ipv6</format> - <description>Allowed IPv6 address</description> - </valueHelp> - <valueHelp> - <format>ipv6net</format> - <description>Allowed IPv6 prefix</description> - </valueHelp> - <multi/> - <constraint> - <validator name="ip-address"/> - <validator name="ip-prefix"/> - </constraint> - </properties> - </leafNode> - </children> - </node> + #include <include/allow-client.xml.i> #include <include/generic-interface-multi.xml.i> #include <include/listen-address.xml.i> #include <include/interface/vrf.xml.i> diff --git a/interface-definitions/pki.xml.in b/interface-definitions/pki.xml.in index c4fde2c78..a13a357fd 100644 --- a/interface-definitions/pki.xml.in +++ b/interface-definitions/pki.xml.in @@ -16,11 +16,7 @@ <help>CA certificate in PEM format</help> </properties> </leafNode> - <leafNode name="description"> - <properties> - <help>Description</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <node name="private"> <properties> <help>CA private key in PEM format</help> @@ -63,11 +59,7 @@ <help>Certificate in PEM format</help> </properties> </leafNode> - <leafNode name="description"> - <properties> - <help>Description</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <node name="private"> <properties> <help>Certificate private key</help> diff --git a/interface-definitions/policy-route.xml.in b/interface-definitions/policy-route.xml.in index bbd6dbf56..d4ec75786 100644 --- a/interface-definitions/policy-route.xml.in +++ b/interface-definitions/policy-route.xml.in @@ -12,8 +12,8 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/generic-interface-multi.xml.i> #include <include/firewall/enable-default-log.xml.i> + #include <include/generic-interface-multi-wildcard.xml.i> <tagNode name="rule"> <properties> <help>Policy rule number</help> @@ -67,8 +67,8 @@ </properties> <children> #include <include/generic-description.xml.i> - #include <include/generic-interface-multi.xml.i> #include <include/firewall/enable-default-log.xml.i> + #include <include/generic-interface-multi-wildcard.xml.i> <tagNode name="rule"> <properties> <help>Policy rule number</help> diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index 66608be6f..7d5fe79ef 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -242,7 +242,7 @@ <description>BGP extended community-list name</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Should be an alphanumeric name</constraintErrorMessage> </properties> @@ -291,7 +291,7 @@ <description>BGP large-community-list name</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Should be an alphanumeric name</constraintErrorMessage> </properties> @@ -340,7 +340,7 @@ <description>Name of IPv4 prefix-list</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Name of prefix-list can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> </properties> @@ -408,7 +408,7 @@ <description>Name of IPv6 prefix-list</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Name of prefix-list6 can only contain alpha-numeric letters, hyphen and underscores</constraintErrorMessage> </properties> diff --git a/interface-definitions/protocols-babel.xml.in b/interface-definitions/protocols-babel.xml.in index b3377aac1..49fffe230 100644 --- a/interface-definitions/protocols-babel.xml.in +++ b/interface-definitions/protocols-babel.xml.in @@ -206,7 +206,7 @@ <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> @@ -234,7 +234,7 @@ <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/protocols-bgp.xml.in b/interface-definitions/protocols-bgp.xml.in index e1a822999..78b461f9d 100644 --- a/interface-definitions/protocols-bgp.xml.in +++ b/interface-definitions/protocols-bgp.xml.in @@ -9,6 +9,20 @@ </properties> <children> #include <include/bgp/protocol-common-config.xml.i> + <node name="address-family"> + <children> + <node name="l2vpn-evpn"> + <children> + <leafNode name="advertise-all-vni"> + <properties> + <help>Advertise all local VNIs</help> + <valueless/> + </properties> + </leafNode> + </children> + </node> + </children> + </node> </children> </node> </children> diff --git a/interface-definitions/protocols-failover.xml.in b/interface-definitions/protocols-failover.xml.in index 900c76eab..a8c5c717f 100644 --- a/interface-definitions/protocols-failover.xml.in +++ b/interface-definitions/protocols-failover.xml.in @@ -48,6 +48,7 @@ <constraint> <validator name="ipv4-address"/> </constraint> + <multi/> </properties> </leafNode> <leafNode name="timeout"> diff --git a/interface-definitions/protocols-rip.xml.in b/interface-definitions/protocols-rip.xml.in index 68d2b64ca..0edd8f2ce 100644 --- a/interface-definitions/protocols-rip.xml.in +++ b/interface-definitions/protocols-rip.xml.in @@ -39,7 +39,7 @@ <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/protocols-ripng.xml.in b/interface-definitions/protocols-ripng.xml.in index be643896f..9d4d87422 100644 --- a/interface-definitions/protocols-ripng.xml.in +++ b/interface-definitions/protocols-ripng.xml.in @@ -40,7 +40,7 @@ <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/protocols-static-arp.xml.in b/interface-definitions/protocols-static-arp.xml.in index 63f450bd8..4b338df63 100644 --- a/interface-definitions/protocols-static-arp.xml.in +++ b/interface-definitions/protocols-static-arp.xml.in @@ -20,7 +20,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in index 9b1430ea0..c7bd8606a 100644 --- a/interface-definitions/qos.xml.in +++ b/interface-definitions/qos.xml.in @@ -17,7 +17,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> <children> diff --git a/interface-definitions/service-monitoring-telegraf.xml.in b/interface-definitions/service-monitoring-telegraf.xml.in index 5ba932400..ae0bae900 100644 --- a/interface-definitions/service-monitoring-telegraf.xml.in +++ b/interface-definitions/service-monitoring-telegraf.xml.in @@ -74,7 +74,7 @@ <properties> <help>Application client id</help> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Client-id is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> </properties> @@ -83,7 +83,7 @@ <properties> <help>Application client secret</help> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Client-secret is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> </properties> @@ -92,7 +92,7 @@ <properties> <help>Set tenant id</help> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Tenant-id is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> </properties> @@ -107,7 +107,7 @@ <description>Remote database name</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Database is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> </properties> @@ -140,7 +140,7 @@ <description>Table name</description> </valueHelp> <constraint> - #include <include/constraint/alpha-numeric-hyphen-underscore.xml.in> + #include <include/constraint/alpha-numeric-hyphen-underscore.xml.i> </constraint> <constraintErrorMessage>Table is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> </properties> diff --git a/interface-definitions/service-upnp.xml.in b/interface-definitions/service-upnp.xml.in index 9e222d29a..1b2e00d91 100644 --- a/interface-definitions/service-upnp.xml.in +++ b/interface-definitions/service-upnp.xml.in @@ -24,7 +24,7 @@ <script>${vyos_completion_dir}/list_interfaces</script> </completionHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> @@ -119,7 +119,7 @@ </valueHelp> <multi/> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> <validator name="ipv4-address"/> <validator name="ipv4-prefix"/> <validator name="ipv6-address"/> diff --git a/interface-definitions/service-webproxy.xml.in b/interface-definitions/service-webproxy.xml.in index a315aa2ef..b24997816 100644 --- a/interface-definitions/service-webproxy.xml.in +++ b/interface-definitions/service-webproxy.xml.in @@ -538,11 +538,7 @@ <multi/> </properties> </leafNode> - <leafNode name="description"> - <properties> - <help>Description for source-group</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <leafNode name="domain"> <properties> <help>Domain for source-group</help> @@ -644,11 +640,7 @@ </leafNode> </children> </tagNode> - <leafNode name="description"> - <properties> - <help>Time-period description</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in index 592db7f4e..559e09388 100644 --- a/interface-definitions/snmp.xml.in +++ b/interface-definitions/snmp.xml.in @@ -78,15 +78,7 @@ <constraintErrorMessage>Contact information is limited to 255 characters or less</constraintErrorMessage> </properties> </leafNode> - <leafNode name="description"> - <properties> - <help>Description information</help> - <constraint> - <regex>.{1,255}</regex> - </constraint> - <constraintErrorMessage>Description is limited to 255 characters or less</constraintErrorMessage> - </properties> - </leafNode> + #include <include/generic-description.xml.i> <tagNode name="listen-address"> <properties> <help>IP address to listen for incoming SNMP requests</help> diff --git a/interface-definitions/system-config-mgmt.xml.in b/interface-definitions/system-config-mgmt.xml.in index 1f852d284..716332d2a 100644 --- a/interface-definitions/system-config-mgmt.xml.in +++ b/interface-definitions/system-config-mgmt.xml.in @@ -32,7 +32,7 @@ <constraint> <validator name="ipv4-address"/> <validator name="ipv6-address"/> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/system-ip.xml.in b/interface-definitions/system-ip.xml.in index e00dbf252..abdede979 100644 --- a/interface-definitions/system-ip.xml.in +++ b/interface-definitions/system-ip.xml.in @@ -48,6 +48,7 @@ </leafNode> </children> </node> + #include <include/system-ip-protocol.xml.i> </children> </node> </children> diff --git a/interface-definitions/system-ipv6.xml.in b/interface-definitions/system-ipv6.xml.in index 63260d00c..e17e1c01c 100644 --- a/interface-definitions/system-ipv6.xml.in +++ b/interface-definitions/system-ipv6.xml.in @@ -36,6 +36,7 @@ #include <include/arp-ndp-table-size.xml.i> </children> </node> + #include <include/system-ipv6-protocol.xml.i> <leafNode name="strict-dad"> <properties> <help>Disable IPv6 operation on interface when DAD fails on LL addr</help> diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index b00741ffe..258913929 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -225,6 +225,19 @@ #include <include/interface/vrf.xml.i> </children> </node> + <leafNode name="max-login-session"> + <properties> + <help>Maximum number of all login sessions</help> + <valueHelp> + <format>u32:1-65536</format> + <description>Maximum number of all login sessions</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-65536"/> + </constraint> + <constraintErrorMessage>Maximum logins must be between 1 and 65536</constraintErrorMessage> + </properties> + </leafNode> <leafNode name="timeout"> <properties> <help>Session timeout</help> diff --git a/interface-definitions/system-sflow.xml.in b/interface-definitions/system-sflow.xml.in index 335181fe1..9c748c24a 100644 --- a/interface-definitions/system-sflow.xml.in +++ b/interface-definitions/system-sflow.xml.in @@ -42,7 +42,7 @@ <description>Interface name</description> </valueHelp> <constraint> - #include <include/constraint/interface-name.xml.in> + #include <include/constraint/interface-name.xml.i> </constraint> </properties> </leafNode> diff --git a/interface-definitions/vpn-ipsec.xml.in b/interface-definitions/vpn-ipsec.xml.in index 1b3a5532e..64cfbda08 100644 --- a/interface-definitions/vpn-ipsec.xml.in +++ b/interface-definitions/vpn-ipsec.xml.in @@ -357,11 +357,11 @@ <properties> <help>IKE lifetime</help> <valueHelp> - <format>u32:30-86400</format> + <format>u32:0-86400</format> <description>IKE lifetime in seconds</description> </valueHelp> <constraint> - <validator name="numeric" argument="--range 30-86400"/> + <validator name="numeric" argument="--range 0-86400"/> </constraint> </properties> <defaultValue>28800</defaultValue> diff --git a/interface-definitions/vpn-l2tp.xml.in b/interface-definitions/vpn-l2tp.xml.in index 0a92017bd..6b64c5f5d 100644 --- a/interface-definitions/vpn-l2tp.xml.in +++ b/interface-definitions/vpn-l2tp.xml.in @@ -124,11 +124,7 @@ </children> </node> #include <include/accel-ppp/client-ipv6-pool.xml.i> - <leafNode name="description"> - <properties> - <help>Description for L2TP remote-access settings</help> - </properties> - </leafNode> + #include <include/generic-description.xml.i> #include <include/dhcp-interface.xml.i> <leafNode name="idle"> <properties> diff --git a/interface-definitions/vpn-pptp.xml.in b/interface-definitions/vpn-pptp.xml.in index 00ffd26f9..5a8b4a78a 100644 --- a/interface-definitions/vpn-pptp.xml.in +++ b/interface-definitions/vpn-pptp.xml.in @@ -108,9 +108,13 @@ </tagNode> </children> </node> + <node name="radius"> + <children> + #include <include/accel-ppp/radius-additions-rate-limit.xml.i> + </children> + </node> #include <include/radius-auth-server-ipv4.xml.i> #include <include/accel-ppp/radius-additions.xml.i> - #include <include/accel-ppp/radius-additions-rate-limit.xml.i> </children> </node> </children> diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 96c6d8be2..a7efe146a 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -34,6 +34,7 @@ </properties> <children> #include <include/interface/disable-forwarding.xml.i> + #include <include/system-ip-protocol.xml.i> </children> </node> <node name="ipv6"> @@ -42,6 +43,7 @@ </properties> <children> #include <include/interface/disable-forwarding.xml.i> + #include <include/system-ipv6-protocol.xml.i> </children> </node> <node name="protocols"> @@ -119,20 +121,7 @@ <constraintErrorMessage>VRF routing table must be in range from 100 to 65535</constraintErrorMessage> </properties> </leafNode> - <leafNode name="vni" owner="${vyos_conf_scripts_dir}/vrf_vni.py"> - <properties> - <help>Virtual Network Identifier</help> - <!-- priority must be after BGP --> - <priority>822</priority> - <valueHelp> - <format>u32:0-16777214</format> - <description>VXLAN virtual network identifier</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 0-16777214"/> - </constraint> - </properties> - </leafNode> + #include <include/vni.xml.i> </children> </tagNode> </children> diff --git a/interface-definitions/xml-component-version.xml.in b/interface-definitions/xml-component-version.xml.in index 2e6506efc..e05f64643 100644 --- a/interface-definitions/xml-component-version.xml.in +++ b/interface-definitions/xml-component-version.xml.in @@ -33,6 +33,7 @@ #include <include/version/pptp-version.xml.i> #include <include/version/qos-version.xml.i> #include <include/version/quagga-version.xml.i> + #include <include/version/rip-version.xml.i> #include <include/version/rpki-version.xml.i> #include <include/version/salt-version.xml.i> #include <include/version/snmp-version.xml.i> 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 7dbc4fde5..820d507fd 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 @@ -195,6 +195,12 @@ </leafNode> </children> </node> + <leafNode name="filtered-routes"> + <properties> + <help>Show filtered routes from BGP neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> <leafNode name="received-routes"> <properties> <help>Show received routes from BGP neighbor</help> diff --git a/op-mode-definitions/include/bgp/evpn-type-1.xml.i b/op-mode-definitions/include/bgp/evpn-type-1.xml.i new file mode 100644 index 000000000..b5097c8b1 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-1.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-1.xml.i --> +<leafNode name="1"> + <properties> + <help>EAD (Type-1) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-2.xml.i b/op-mode-definitions/include/bgp/evpn-type-2.xml.i new file mode 100644 index 000000000..827298d62 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-2.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-2.xml.i --> +<leafNode name="2"> + <properties> + <help>MAC-IP (Type-2) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-3.xml.i b/op-mode-definitions/include/bgp/evpn-type-3.xml.i new file mode 100644 index 000000000..ae90b2e5c --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-3.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-3.xml.i --> +<leafNode name="3"> + <properties> + <help>Multicast (Type-3) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-4.xml.i b/op-mode-definitions/include/bgp/evpn-type-4.xml.i new file mode 100644 index 000000000..7248b4753 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-4.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-4.xml.i --> +<leafNode name="4"> + <properties> + <help>Ethernet Segment (Type-4) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-5.xml.i b/op-mode-definitions/include/bgp/evpn-type-5.xml.i new file mode 100644 index 000000000..e3a72168a --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-5.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-5.xml.i --> +<leafNode name="5"> + <properties> + <help>Prefix (Type-5) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-ead.xml.i b/op-mode-definitions/include/bgp/evpn-type-ead.xml.i new file mode 100644 index 000000000..452de2f9a --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-ead.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-ead.xml.i --> +<leafNode name="ead"> + <properties> + <help>EAD (Type-1) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-es.xml.i b/op-mode-definitions/include/bgp/evpn-type-es.xml.i new file mode 100644 index 000000000..50c40151a --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-es.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-es.xml.i --> +<leafNode name="es"> + <properties> + <help>Ethernet Segment (Type-4) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-macip.xml.i b/op-mode-definitions/include/bgp/evpn-type-macip.xml.i new file mode 100644 index 000000000..6f601eb3f --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-macip.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-macip.xml.i --> +<leafNode name="macip"> + <properties> + <help>MAC-IP (Type-2) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-multicast.xml.i b/op-mode-definitions/include/bgp/evpn-type-multicast.xml.i new file mode 100644 index 000000000..5194dbb56 --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-multicast.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-multicast.xml.i --> +<leafNode name="multicast"> + <properties> + <help>Multicast (Type-3) route</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</leafNode> +<!-- included end --> diff --git a/op-mode-definitions/include/bgp/evpn-type-prefix.xml.i b/op-mode-definitions/include/bgp/evpn-type-prefix.xml.i new file mode 100644 index 000000000..d5054d86b --- /dev/null +++ b/op-mode-definitions/include/bgp/evpn-type-prefix.xml.i @@ -0,0 +1,8 @@ +<!-- included start from bgp/evpn-type-prefix.xml.i --> +<leafNode name="prefix"> + <properties> + <help>Prefix (Type-5) route</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 c9a112fca..de794a879 100644 --- a/op-mode-definitions/include/bgp/show-bgp-common.xml.i +++ b/op-mode-definitions/include/bgp/show-bgp-common.xml.i @@ -171,66 +171,16 @@ <help>Specify Route type</help> </properties> <children> - <leafNode name="1"> - <properties> - <help>EAD (Type-1) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="2"> - <properties> - <help>MAC-IP (Type-2) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="3"> - <properties> - <help>Multicast (Type-3) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="4"> - <properties> - <help>Ethernet Segment (Type-4) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="5"> - <properties> - <help>Prefix (Type-5) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="ead"> - <properties> - <help>EAD (Type-1) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="es"> - <properties> - <help>Ethernet Segment (Type-4) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="macip"> - <properties> - <help>MAC-IP (Type-2) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="multicast"> - <properties> - <help>Multicast (Type-3) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> - <leafNode name="prefix"> - <properties> - <help>Prefix (Type-5) route</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> + #include <include/bgp/evpn-type-1.xml.i> + #include <include/bgp/evpn-type-2.xml.i> + #include <include/bgp/evpn-type-3.xml.i> + #include <include/bgp/evpn-type-4.xml.i> + #include <include/bgp/evpn-type-5.xml.i> + #include <include/bgp/evpn-type-ead.xml.i> + #include <include/bgp/evpn-type-es.xml.i> + #include <include/bgp/evpn-type-macip.xml.i> + #include <include/bgp/evpn-type-multicast.xml.i> + #include <include/bgp/evpn-type-prefix.xml.i> </children> </node> #include <include/vni-tagnode-all.xml.i> diff --git a/op-mode-definitions/include/bgp/show-ip-bgp-common.xml.i b/op-mode-definitions/include/bgp/show-ip-bgp-common.xml.i index 36cc9a3fa..db9021f3e 100644 --- a/op-mode-definitions/include/bgp/show-ip-bgp-common.xml.i +++ b/op-mode-definitions/include/bgp/show-ip-bgp-common.xml.i @@ -93,6 +93,12 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> + <leafNode name="filtered-routes"> + <properties> + <help>Show the filtered routes from neighbor</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> <leafNode name="received-routes"> <properties> <help>Show the received routes from neighbor</help> diff --git a/op-mode-definitions/include/isis-common.xml.i b/op-mode-definitions/include/isis-common.xml.i index 95a171515..e94d868e8 100644 --- a/op-mode-definitions/include/isis-common.xml.i +++ b/op-mode-definitions/include/isis-common.xml.i @@ -4,12 +4,7 @@ <help>Show IS-IS link state database</help> </properties> <children> - <leafNode name="detail"> - <properties> - <help>Show detailed information</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> + #include <include/vtysh-generic-detail.xml.i> </children> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </node> @@ -36,27 +31,22 @@ </completionHelp> </properties> <children> - <leafNode name="detail"> - <properties> - <help>Show detailed information</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> + #include <include/vtysh-generic-detail.xml.i> </children> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </node> -<tagNode name="interface"> +#include <include/vtysh-generic-interface-tagNode.xml.i> +<node name="mpls"> <properties> - <help>Show specific IS-IS interface</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces</script> - </completionHelp> + <help>Show MPLS information</help> </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> -</tagNode> + <children> + #include <include/ldp-sync.xml.i> + </children> +</node> <node name="mpls-te"> <properties> - <help>Show IS-IS MPLS traffic engineering information</help> + <help>Show MPLS traffic engineering information</help> </properties> <children> <leafNode name="router"> @@ -71,15 +61,7 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> - <tagNode name="interface"> - <properties> - <help>Show specific IS-IS interface</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces</script> - </completionHelp> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </tagNode> + #include <include/vtysh-generic-interface-tagNode.xml.i> </children> </node> <node name="neighbor"> @@ -87,14 +69,9 @@ <help>Show IS-IS neighbor adjacencies</help> </properties> <children> - <leafNode name="detail"> - <properties> - <help>Show detailed information</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> + #include <include/vtysh-generic-detail.xml.i> </children> - <command>vtysh -c "show isis neighbor"</command> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </node> <tagNode name="neighbor"> <properties> @@ -122,8 +99,14 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> + <leafNode name="prefix-sid"> + <properties> + <help>Show Prefix-SID information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </leafNode> </children> - <command>vtysh -c "show isis route"</command> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </node> <node name="segment-routing"> <properties> @@ -136,12 +119,6 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> - <leafNode name="prefix-sids"> - <properties> - <help>Show prefix segment IDs</help> - </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> - </leafNode> </children> </node> <leafNode name="spf-delay-ietf"> @@ -176,4 +153,4 @@ </children> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </node> -<!-- included end --> +<!-- included end -->
\ No newline at end of file diff --git a/op-mode-definitions/include/ldp-sync.xml.i b/op-mode-definitions/include/ldp-sync.xml.i new file mode 100644 index 000000000..b7b04e7e5 --- /dev/null +++ b/op-mode-definitions/include/ldp-sync.xml.i @@ -0,0 +1,11 @@ +<!-- included start from ldp-sync.xml.i --> +<node name="ldp-sync"> + <properties> + <help>Show LDP-IGP synchronization information</help> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + <children> + #include <include/vtysh-generic-interface-tagNode.xml.i> + </children> +</node> +<!-- included end -->
\ No newline at end of file diff --git a/op-mode-definitions/include/ospf-common.xml.i b/op-mode-definitions/include/ospf-common.xml.i index 14b7839ec..979ffb07e 100644 --- a/op-mode-definitions/include/ospf-common.xml.i +++ b/op-mode-definitions/include/ospf-common.xml.i @@ -508,15 +508,15 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </node> -<tagNode name="interface"> +#include <include/vtysh-generic-interface-tagNode.xml.i> +<node name="mpls"> <properties> - <help>Show IPv4 OSPF information for specified interface</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces</script> - </completionHelp> + <help>Show MPLS information</help> </properties> - <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> -</tagNode> + <children> + #include <include/ldp-sync.xml.i> + </children> +</node> <node name="neighbor"> <properties> <help>Show IPv4 OSPF neighbor information</help> @@ -556,3 +556,4 @@ </children> </node> <!-- included end --> + diff --git a/op-mode-definitions/include/vtysh-generic-interface-tagNode.xml.i b/op-mode-definitions/include/vtysh-generic-interface-tagNode.xml.i new file mode 100644 index 000000000..e95961177 --- /dev/null +++ b/op-mode-definitions/include/vtysh-generic-interface-tagNode.xml.i @@ -0,0 +1,11 @@ +<!-- included start from vtysh-generic-interface.xml.i --> +<tagNode name="interface"> + <properties> + <help>Show information about specific interface</help> + <completionHelp> + <script>${vyos_completion_dir}/list_interfaces</script> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> +</tagNode> +<!-- included end --> diff --git a/op-mode-definitions/sflow.xml.in b/op-mode-definitions/sflow.xml.in new file mode 100644 index 000000000..9f02dacda --- /dev/null +++ b/op-mode-definitions/sflow.xml.in @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- sflow op mode commands --> +<interfaceDefinition> + <node name="show"> + <children> + <node name="sflow"> + <properties> + <help>Show sFlow statistics</help> + </properties> + <!-- requires sudo, do not remove it --> + <command>sudo ${vyos_op_scripts_dir}/sflow.py show</command> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/show-bgp.xml.in b/op-mode-definitions/show-bgp.xml.in index 974147621..3c212614c 100644 --- a/op-mode-definitions/show-bgp.xml.in +++ b/op-mode-definitions/show-bgp.xml.in @@ -51,6 +51,33 @@ </properties> <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> </leafNode> + #include <include/vni-tagnode-all.xml.i> + <tagNode name="vni"> + <children> + <tagNode name="vtep"> + <properties> + <help>Remote VTEP IP address</help> + <completionHelp> + <list><x.x.x.x></list> + </completionHelp> + </properties> + <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command> + </tagNode> + <node name="type"> + <properties> + <help>Display number of prefixes for all afi/safi</help> + </properties> + <children> + #include <include/bgp/evpn-type-1.xml.i> + #include <include/bgp/evpn-type-2.xml.i> + #include <include/bgp/evpn-type-3.xml.i> + #include <include/bgp/evpn-type-ead.xml.i> + #include <include/bgp/evpn-type-macip.xml.i> + #include <include/bgp/evpn-type-multicast.xml.i> + </children> + </node> + </children> + </tagNode> <leafNode name="vrf"> <properties> <help>Show BGP VRF information</help> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index 7f6469ca9..7663e4c00 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -8,6 +8,12 @@ </properties> <command>journalctl --no-hostname --boot</command> <children> + <leafNode name="audit"> + <properties> + <help>Show audit logs</help> + </properties> + <command>cat /var/log/audit/audit.log</command> + </leafNode> <leafNode name="all"> <properties> <help>Show contents of all master log files</help> diff --git a/op-mode-definitions/show-techsupport_report.xml.in b/op-mode-definitions/show-techsupport_report.xml.in new file mode 100644 index 000000000..aa51eacd9 --- /dev/null +++ b/op-mode-definitions/show-techsupport_report.xml.in @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="show"> + <children> + <node name="tech-support"> + <children> + <node name="report"> + <properties> + <help>Show consolidated tech-support report (contains private information)</help> + </properties> + <command>${vyos_op_scripts_dir}/show_techsupport_report.py</command> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 5baaec7ce..5a7e6dd63 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -14,7 +14,7 @@ <children> <tagNode name="profile"> <properties> - <help>Reset all tunnels for given DMVPN profile</help> + <help>Reset a specific tunnel for given DMVPN profile</help> <completionHelp> <path>vpn ipsec profile</path> </completionHelp> @@ -23,11 +23,24 @@ <tagNode name="tunnel"> <properties> <help>Reset a specific tunnel for given DMVPN profile</help> + <completionHelp> + <script>sudo ${vyos_completion_dir}/list_ipsec_profile_tunnels.py --profile ${COMP_WORDS[4]}</script> + </completionHelp> </properties> - <command>sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="$8"</command> + <children> + <tagNode name="remote-host"> + <properties> + <help>Reset a specific tunnel for given DMVPN NBMA</help> + <completionHelp> + <list><x.x.x.x> <h:h:h:h:h:h:h:h></list> + </completionHelp> + </properties> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_profile_dst --profile="$5" --tunnel="$7" --nbma_dst="$9"</command> + </tagNode> + </children> + <command>sudo ${vyos_op_scripts_dir}/ipsec.py reset_profile_all --profile="$5" --tunnel="$7"</command> </tagNode> </children> - <command>sudo ${vyos_op_scripts_dir}/vpn_ipsec.py --action="reset-profile" --name="$6" --tunnel="all"</command> </tagNode> <node name="remote-access"> <properties> diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index fc33430eb..f62b9f7d2 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -1709,6 +1709,14 @@ class VLANIf(Interface): if self.exists(f'{self.ifname}'): return + # If source_interface or vlan_id was not explicitly defined (e.g. when + # calling VLANIf('eth0.1').remove() we can define source_interface and + # vlan_id here, as it's quiet obvious that it would be eth0 in that case. + if 'source_interface' not in self.config: + self.config['source_interface'] = '.'.join(self.ifname.split('.')[:-1]) + if 'vlan_id' not in self.config: + self.config['vlan_id'] = self.ifname.split('.')[-1] + cmd = 'ip link add link {source_interface} name {ifname} type vlan id {vlan_id}' if 'protocol' in self.config: cmd += ' protocol {protocol}' diff --git a/python/vyos/ipsec.py b/python/vyos/ipsec.py index cb7c39ff6..bb5611025 100644 --- a/python/vyos/ipsec.py +++ b/python/vyos/ipsec.py @@ -139,3 +139,41 @@ def terminate_vici_by_name(ike_name: str, child_name: str) -> None: else: raise ViciCommandError( f'Failed to terminate SA for IKE {ike_name}') + + +def vici_initiate(ike_sa_name: str, child_sa_name: str, src_addr: str, + dst_addr: str) -> bool: + """Initiate IKE SA connection with specific peer + + Args: + ike_sa_name (str): an IKE SA connection name + child_sa_name (str): a child SA profile name + src_addr (str): source address + dst_addr (str): remote address + + Returns: + bool: a result of initiation command + """ + from vici import Session as vici_session + + try: + session = vici_session() + except Exception: + raise ViciInitiateError("IPsec not initialized") + + try: + session_generator = session.initiate({ + 'ike': ike_sa_name, + 'child': child_sa_name, + 'timeout': '-1', + 'my-host': src_addr, + 'other-host': dst_addr + }) + # a dummy `for` loop is required because of requirements + # from vici. Without a full iteration on the output, the + # command to vici may not be executed completely + for _ in session_generator: + pass + return True + except Exception: + raise ViciCommandError(f'Failed to initiate SA for IKE {ike_sa_name}')
\ No newline at end of file diff --git a/smoketest/configs/egb-igp-route-maps b/smoketest/configs/egb-igp-route-maps new file mode 100644 index 000000000..ca36691d4 --- /dev/null +++ b/smoketest/configs/egb-igp-route-maps @@ -0,0 +1,127 @@ +interfaces { + ethernet eth0 { + address 192.0.2.1/25 + duplex auto + smp-affinity auto + speed auto + } + ethernet eth1 { + address 192.0.2.129/25 + address 2001:db8::1234/64 + duplex auto + smp-affinity auto + speed auto + } + loopback lo { + } +} +policy { + route-map zebra-bgp { + rule 10 { + action permit + } + } + route-map zebra-isis { + rule 10 { + action permit + } + } + route-map zebra-ospf { + rule 10 { + action permit + } + } + route-map zebra-ospfv3 { + rule 10 { + action permit + } + } + route-map zebra-ripng { + rule 10 { + action permit + } + } + route-map zebra-static { + rule 10 { + action permit + } + } +} +protocols { + bgp 100 { + route-map zebra-bgp + } + isis { + interface eth0 { + } + net 49.0001.1921.6800.1002.00 + route-map zebra-isis + } + ospf { + area 0 { + network 192.0.2.0/25 + network 192.0.2.128/25 + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 1.1.1.1 + } + passive-interface default + passive-interface-exclude eth0 + passive-interface-exclude eth1 + route-map zebra-ospf + } + ospfv3 { + area 0 { + interface eth1 + } + parameters { + router-id 1.1.1.1 + } + route-map zebra-ospfv3 + } + ripng { + interface eth1 + route-map zebra-ripng + } + static { + route-map zebra-static + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + name-server 192.168.0.1 + syslog { + global { + archive { + file 5 + size 512 + } + facility all { + level info + } + } + } + time-zone Europe/Berlin +} +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3.2 diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index e071b7df9..99d3b3ca1 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -198,6 +198,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): def test_ipv4_basic_rules(self): name = 'smoketest' interface = 'eth0' + interface_wc = 'l2tp*' mss_range = '501-1460' conn_mark = '555' @@ -240,6 +241,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '6', 'connection-mark', conn_mark]) self.cli_set(['firewall', 'interface', interface, 'in', 'name', name]) + self.cli_set(['firewall', 'interface', interface_wc, 'in', 'name', name]) self.cli_commit() @@ -247,6 +249,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ [f'iifname "{interface}"', f'jump NAME_{name}'], + [f'iifname "{interface_wc}"', f'jump NAME_{name}'], ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'return'], ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'], ['tcp dport 22', 'limit rate 5/minute', 'return'], diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py index b2143d16e..c80c7cf80 100755 --- a/smoketest/scripts/cli/test_interfaces_openvpn.py +++ b/smoketest/scripts/cli/test_interfaces_openvpn.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2023 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 @@ -368,6 +368,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase): self.cli_set(path + ['hash', auth_hash]) self.cli_set(path + ['mode', 'server']) self.cli_set(path + ['local-port', port]) + self.cli_set(path + ['server', 'mfa', 'totp']) self.cli_set(path + ['server', 'subnet', subnet]) self.cli_set(path + ['server', 'topology', 'subnet']) self.cli_set(path + ['keep-alive', 'failure-count', '5']) @@ -388,6 +389,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase): for ii in num_range: interface = f'vtun{ii}' + plugin = f'plugin "/usr/lib/openvpn/openvpn-otp.so" "otp_secrets=/config/auth/openvpn/{interface}-otp-secrets otp_slop=180 totp_t0=0 totp_step=30 totp_digits=6 password_is_cr=1"' subnet = f'192.0.{ii}.0/24' start_addr = inc_ip(subnet, '2') @@ -411,6 +413,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase): self.assertIn(f'topology subnet', config) self.assertIn(f'lport {port}', config) self.assertIn(f'push "redirect-gateway def1"', config) + self.assertIn(f'{plugin}', config) self.assertIn(f'keepalive 5 25', config) # TLS options diff --git a/smoketest/scripts/cli/test_load_balancing_wan.py b/smoketest/scripts/cli/test_load_balancing_wan.py index 0e1806f66..8df3471f7 100755 --- a/smoketest/scripts/cli/test_load_balancing_wan.py +++ b/smoketest/scripts/cli/test_load_balancing_wan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2023 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 @@ -177,7 +177,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): nat_vyos_pre_snat_hook = """table ip nat { chain VYOS_PRE_SNAT_HOOK { type nat hook postrouting priority srcnat - 1; policy accept; - return + counter jump WANLOADBALANCE } }""" diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py index 4be36b134..a3df6bf4d 100755 --- a/smoketest/scripts/cli/test_policy_route.py +++ b/smoketest/scripts/cli/test_policy_route.py @@ -26,6 +26,7 @@ conn_mark_set = '111' table_mark_offset = 0x7fffffff table_id = '101' interface = 'eth0' +interface_wc = 'ppp*' interface_ip = '172.16.10.1/24' class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): @@ -236,7 +237,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'set', 'table', table_id]) self.cli_set(['policy', 'route', 'smoketest', 'interface', interface]) - self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface]) + self.cli_set(['policy', 'route', 'smoketest', 'interface', interface_wc]) + self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface_wc]) self.cli_commit() @@ -244,7 +246,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): # IPv4 nftables_search = [ - [f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'], + ['iifname { "' + interface + '", "' + interface_wc + '" }', 'jump VYOS_PBR_smoketest'], ['meta l4proto udp', 'drop'], ['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark set ' + mark_hex], @@ -256,7 +258,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): # IPv6 nftables6_search = [ - [f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'], + [f'iifname "{interface_wc}"', 'jump VYOS_PBR6_smoketest'], ['meta l4proto udp', 'drop'], ['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip6 saddr 2001:db8::/64', 'ip6 hoplimit > 2', 'meta mark set ' + mark_hex], diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index f6eede87a..2fd5d0c9b 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -713,7 +713,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): for prefix in listen_ranges: self.assertIn(f' bgp listen range {prefix} peer-group {peer_group}', frrconfig) - def test_bgp_07_l2vpn_evpn(self): vnis = ['10010', '10020', '10030'] neighbors = ['192.0.2.10', '192.0.2.20', '192.0.2.30'] @@ -743,26 +742,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' advertise-default-gw', vniconfig) self.assertIn(f' advertise-svi-ip', vniconfig) - def test_bgp_08_zebra_route_map(self): - # Implemented because of T3328 - self.cli_set(base_path + ['route-map', route_map_in]) - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol bgp route-map {route_map_in}' - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertIn(zebra_route_map, frrconfig) - - # Remove the route-map again - self.cli_delete(base_path + ['route-map']) - # commit changes - self.cli_commit() - - # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertNotIn(zebra_route_map, frrconfig) - def test_bgp_09_distance_and_flowspec(self): distance_external = '25' distance_internal = '30' @@ -830,7 +809,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(vrf_base + ['table', table]) self.cli_set(vrf_base + ['protocols', 'bgp', 'system-as', ASN]) self.cli_set(vrf_base + ['protocols', 'bgp', 'parameters', 'router-id', router_id]) - self.cli_set(vrf_base + ['protocols', 'bgp', 'route-map', route_map_in]) table = str(int(table) + 1000) # import VRF routes do main RIB @@ -843,7 +821,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): 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) @@ -852,15 +829,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'router bgp {ASN} vrf {vrf}', frr_vrf_config) self.assertIn(f' bgp router-id {router_id}', frr_vrf_config) - # 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. - # - # vrfconfig = self.getFRRconfig(f'vrf {vrf}') - # zebra_route_map = f' ip protocol bgp route-map {route_map_in}' - # self.assertIn(zebra_route_map, vrfconfig) - - def test_bgp_11_confederation(self): router_id = '127.10.10.2' confed_id = str(int(ASN) + 1) diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py index d11d80a1f..f1a030e77 100755 --- a/smoketest/scripts/cli/test_protocols_isis.py +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -119,39 +119,6 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_delete(['vrf', 'name', vrf]) self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) - def test_isis_03_zebra_route_map(self): - # Implemented because of T3328 - route_map = 'foo-isis-in' - - self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) - - self.isis_base_config() - self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map]) - self.cli_set(base_path + ['route-map', route_map]) - self.cli_set(base_path + ['level', 'level-2']) - - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol isis route-map {route_map}' - frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra') - self.assertIn(zebra_route_map, frrconfig) - - tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') - self.assertIn(' is-type level-2-only', tmp) - - # Remove the route-map again - self.cli_delete(base_path + ['route-map']) - # commit changes - self.cli_commit() - - # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra') - self.assertNotIn(zebra_route_map, frrconfig) - - self.cli_delete(['policy', 'route-map', route_map]) - def test_isis_04_default_information(self): metric = '50' route_map = 'default-foo-' @@ -293,7 +260,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['segment-routing', 'prefix', prefix_three, 'absolute', 'explicit-null']) self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'value', prefix_four_value]) self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'no-php-flag']) - + # Commit all changes self.cli_commit() @@ -308,5 +275,48 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.assertIn(f' segment-routing prefix {prefix_three} absolute {prefix_three_value} explicit-null', tmp) self.assertIn(f' segment-routing prefix {prefix_four} absolute {prefix_four_value} no-php-flag', tmp) + def test_isis_08_ldp_sync(self): + holddown = "500" + interface = 'lo' + + self.cli_set(base_path + ['net', net]) + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['ldp-sync', 'holddown', holddown]) + + # Commit main ISIS changes + self.cli_commit() + + # Verify main ISIS changes + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') + self.assertIn(f' net {net}', tmp) + self.assertIn(f' mpls ldp-sync', tmp) + self.assertIn(f' mpls ldp-sync holddown {holddown}', tmp) + + for interface in self._interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown]) + + # Commit interface changes for holddown + self.cli_commit() + + # Verify interface changes for holddown + tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') + self.assertIn(f'interface {interface}', tmp) + self.assertIn(f' ip router isis {domain}', tmp) + self.assertIn(f' ipv6 router isis {domain}', tmp) + self.assertIn(f' isis mpls ldp-sync holddown {holddown}', tmp) + + for interface in self._interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable']) + + # Commit interface changes for disable + self.cli_commit() + + # Verify interface changes for disable + tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') + self.assertIn(f'interface {interface}', tmp) + self.assertIn(f' ip router isis {domain}', tmp) + self.assertIn(f' ipv6 router isis {domain}', tmp) + self.assertIn(f' no isis mpls ldp-sync', tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 581959b15..6fe6dd979 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -300,26 +300,6 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' no ip ospf passive', config) self.assertIn(f' bandwidth {bandwidth}', config) - def test_ospf_10_zebra_route_map(self): - # Implemented because of T3328 - self.cli_set(base_path + ['route-map', route_map]) - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol ospf route-map {route_map}' - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertIn(zebra_route_map, frrconfig) - - # Remove the route-map again - self.cli_delete(base_path + ['route-map']) - # commit changes - self.cli_commit() - - # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertNotIn(zebra_route_map, frrconfig) - def test_ospf_11_interface_area(self): area = '0' interfaces = Section.interfaces('ethernet') @@ -434,6 +414,47 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' segment-routing prefix {prefix_one} index {prefix_one_value} explicit-null', frrconfig) self.assertIn(f' segment-routing prefix {prefix_two} index {prefix_two_value} no-php-flag', frrconfig) + def test_ospf_15_ldp_sync(self): + holddown = "500" + interface = 'lo' + interfaces = Section.interfaces('ethernet') + + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['ldp-sync', 'holddown', holddown]) + + # Commit main OSPF changes + self.cli_commit() + + # Verify main OSPF changes + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) + self.assertIn(f' mpls ldp-sync holddown {holddown}', frrconfig) + + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown]) + + # Commit interface changes for holddown + self.cli_commit() + + # Verify interface changes for holddown + config = self.getFRRconfig(f'interface {interface}') + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ip ospf dead-interval 40', config) + self.assertIn(f' ip ospf mpls ldp-sync', config) + self.assertIn(f' ip ospf mpls ldp-sync holddown {holddown}', config) + + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable']) + + # Commit interface changes for disable + self.cli_commit() + + # Verify interface changes for disable + config = self.getFRRconfig(f'interface {interface}') + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ip ospf dead-interval 40', config) + self.assertIn(f' no ip ospf mpls ldp-sync', config) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py index 19efe7786..bc023f3f2 100755 --- a/smoketest/scripts/cli/test_protocols_static.py +++ b/smoketest/scripts/cli/test_protocols_static.py @@ -433,30 +433,5 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): self.assertIn(tmp, frrconfig) - def test_04_static_zebra_route_map(self): - # Implemented because of T3328 - route_map = 'foo-static-in' - self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) - - self.cli_set(base_path + ['route-map', route_map]) - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol static route-map {route_map}' - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertIn(zebra_route_map, frrconfig) - - # Remove the route-map again - self.cli_delete(base_path + ['route-map']) - # commit changes - self.cli_commit() - - # Verify FRR configuration - frrconfig = self.getFRRconfig(zebra_route_map) - self.assertNotIn(zebra_route_map, frrconfig) - - self.cli_delete(['policy', 'route-map', route_map]) - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py index f71ef5b3f..e7f7e3345 100755 --- a/smoketest/scripts/cli/test_system_ip.py +++ b/smoketest/scripts/cli/test_system_ip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,6 +17,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError from vyos.util import read_file base_path = ['system', 'ip'] @@ -82,5 +83,31 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase): self.assertEqual(read_file(gc_thresh2), str(size // 2)) self.assertEqual(read_file(gc_thresh1), str(size // 8)) + def test_system_ip_protocol_route_map(self): + protocols = ['any', 'babel', 'bgp', 'connected', 'eigrp', 'isis', + 'kernel', 'ospf', 'rip', 'static', 'table'] + + for protocol in protocols: + self.cli_set(['policy', 'route-map', f'route-map-{protocol}', 'rule', '10', 'action', 'permit']) + self.cli_set(base_path + ['protocol', protocol, 'route-map', f'route-map-{protocol}']) + + self.cli_commit() + + # Verify route-map properly applied to FRR + frrconfig = self.getFRRconfig('ip protocol', end='', daemon='zebra') + for protocol in protocols: + self.assertIn(f'ip protocol {protocol} route-map route-map-{protocol}', frrconfig) + + def test_system_ip_protocol_non_existing_route_map(self): + non_existing = 'non-existing' + self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing]) + + # VRF does yet not exist - an error must be thrown + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny']) + # Commit again + self.cli_commit() + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py index c8aea9100..e91b924fc 100755 --- a/smoketest/scripts/cli/test_system_ipv6.py +++ b/smoketest/scripts/cli/test_system_ipv6.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2023 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 @@ -18,6 +18,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError from vyos.template import is_ipv4 from vyos.util import read_file from vyos.util import get_interface_config @@ -88,5 +89,36 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase): self.assertEqual(read_file(gc_thresh2), str(size // 2)) self.assertEqual(read_file(gc_thresh1), str(size // 8)) + def test_system_ipv6_protocol_route_map(self): + protocols = ['any', 'babel', 'bgp', 'connected', 'isis', + 'kernel', 'ospfv3', 'ripng', 'static', 'table'] + + for protocol in protocols: + route_map = 'route-map-' + protocol.replace('ospfv3', 'ospf6') + + self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + self.cli_set(base_path + ['protocol', protocol, 'route-map', route_map]) + + self.cli_commit() + + # Verify route-map properly applied to FRR + frrconfig = self.getFRRconfig('ipv6 protocol', end='', daemon='zebra') + for protocol in protocols: + # VyOS and FRR use a different name for OSPFv3 (IPv6) + if protocol == 'ospfv3': + protocol = 'ospf6' + self.assertIn(f'ipv6 protocol {protocol} route-map route-map-{protocol}', frrconfig) + + def test_system_ipv6_protocol_non_existing_route_map(self): + non_existing = 'non-existing6' + self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing]) + + # VRF does yet not exist - an error must be thrown + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny']) + # Commit again + self.cli_commit() + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py index 6006fe0f6..a1d2ba2ad 100755 --- a/smoketest/scripts/cli/test_system_login.py +++ b/smoketest/scripts/cli/test_system_login.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2022 VyOS maintainers and contributors +# Copyright (C) 2019-2023 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 @@ -264,5 +264,26 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase): tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf) self.assertTrue(tmp) + def test_system_login_max_login_session(self): + max_logins = '2' + timeout = '600' + + self.cli_set(base_path + ['max-login-session', max_logins]) + + # 'max-login-session' must be only with 'timeout' option + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + ['timeout', timeout]) + + self.cli_commit() + + security_limits = read_file('/etc/security/limits.d/10-vyos.conf') + self.assertIn(f'* - maxsyslogins {max_logins}', security_limits) + + self.cli_delete(base_path + ['timeout']) + self.cli_delete(base_path + ['max-login-session']) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_sflow.py b/smoketest/scripts/cli/test_system_sflow.py index fef88b56a..1aec050a4 100755 --- a/smoketest/scripts/cli/test_system_sflow.py +++ b/smoketest/scripts/cli/test_system_sflow.py @@ -91,6 +91,7 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase): self.assertIn(f'collector {{ ip = {server} udpport = {port} }}', hsflowd) self.assertIn(f'collector {{ ip = {local_server} udpport = {default_port} }}', hsflowd) self.assertIn(f'dropmon {{ limit={mon_limit} start=on sw=on hw=off }}', hsflowd) + self.assertIn('dbus { }', hsflowd) for interface in Section.interfaces('ethernet'): self.assertIn(f'pcap {{ dev={interface} }}', hsflowd) diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index 176c095fb..8016c0105 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2023 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 @@ -33,6 +33,8 @@ from vyos.validate import is_intf_addr_assigned base_path = ['vrf'] vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo'] +v4_protocols = ['any', 'babel', 'bgp', 'connected', 'eigrp', 'isis', 'kernel', 'ospf', 'rip', 'static', 'table'] +v6_protocols = ['any', 'babel', 'bgp', 'connected', 'isis', 'kernel', 'ospfv3', 'ripng', 'static', 'table'] class VRFTest(VyOSUnitTestSHIM.TestCase): _interfaces = [] @@ -291,5 +293,81 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '0') self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '0') + def test_vrf_ip_protocol_route_map(self): + table = '6000' + + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + + for protocol in v4_protocols: + self.cli_set(['policy', 'route-map', f'route-map-{vrf}-{protocol}', 'rule', '10', 'action', 'permit']) + self.cli_set(base + ['ip', 'protocol', protocol, 'route-map', f'route-map-{vrf}-{protocol}']) + + table = str(int(table) + 1) + + self.cli_commit() + + # Verify route-map properly applied to FRR + for vrf in vrfs: + frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon='zebra') + self.assertIn(f'vrf {vrf}', frrconfig) + for protocol in v4_protocols: + self.assertIn(f' ip protocol {protocol} route-map route-map-{vrf}-{protocol}', frrconfig) + + def test_vrf_ip_ipv6_protocol_non_existing_route_map(self): + table = '6100' + non_existing = 'non-existing' + + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + for protocol in v4_protocols: + self.cli_set(base + ['ip', 'protocol', protocol, 'route-map', f'v4-{non_existing}']) + for protocol in v6_protocols: + self.cli_set(base + ['ipv6', 'protocol', protocol, 'route-map', f'v6-{non_existing}']) + + table = str(int(table) + 1) + + # Both v4 and v6 route-maps do not exist yet + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', f'v4-{non_existing}', 'rule', '10', 'action', 'deny']) + + # v6 route-map does not exist yet + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', f'v6-{non_existing}', 'rule', '10', 'action', 'deny']) + + # Commit again + self.cli_commit() + + def test_vrf_ipv6_protocol_route_map(self): + table = '6200' + + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + + for protocol in v6_protocols: + route_map = f'route-map-{vrf}-{protocol.replace("ospfv3", "ospf6")}' + self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + self.cli_set(base + ['ipv6', 'protocol', protocol, 'route-map', route_map]) + + table = str(int(table) + 1) + + self.cli_commit() + + # Verify route-map properly applied to FRR + for vrf in vrfs: + frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon='zebra') + self.assertIn(f'vrf {vrf}', frrconfig) + for protocol in v6_protocols: + # VyOS and FRR use a different name for OSPFv3 (IPv6) + if protocol == 'ospfv3': + protocol = 'ospf6' + route_map = f'route-map-{vrf}-{protocol}' + self.assertIn(f' ipv6 protocol {protocol} route-map {route_map}', frrconfig) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/completion/list_ipsec_profile_tunnels.py b/src/completion/list_ipsec_profile_tunnels.py new file mode 100644 index 000000000..df6c52f6d --- /dev/null +++ b/src/completion/list_ipsec_profile_tunnels.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019-2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import sys +import argparse + +from vyos.config import Config +from vyos.util import dict_search + +def get_tunnels_from_ipsecprofile(profile): + config = Config() + base = ['vpn', 'ipsec', 'profile', profile, 'bind'] + profile_conf = config.get_config_dict(base, effective=True, key_mangling=('-', '_')) + tunnels = [] + + try: + for tunnel in (dict_search('bind.tunnel', profile_conf) or []): + tunnels.append(tunnel) + except: + pass + + return tunnels + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--profile", type=str, help="List tunnels per profile") + args = parser.parse_args() + + tunnels = [] + + tunnels = get_tunnels_from_ipsecprofile(args.profile) + + print(" ".join(tunnels)) + diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index bf83416b2..aceb27fb0 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -16,6 +16,7 @@ import os +from hashlib import sha256 from ipaddress import ip_address from ipaddress import ip_network from json import dumps as json_write @@ -25,9 +26,10 @@ from vyos.config import Config from vyos.configdict import dict_merge from vyos.configdict import node_changed from vyos.configdict import is_node_changed +from vyos.configverify import verify_vrf +from vyos.ifconfig import Interface from vyos.util import call from vyos.util import cmd -from vyos.util import dict_search from vyos.util import run from vyos.util import rc_cmd from vyos.util import write_file @@ -166,21 +168,29 @@ def verify(container): raise ConfigError(f'Container network "{network_name}" does not exist!') if 'address' in container_config['network'][network_name]: - address = container_config['network'][network_name]['address'] - network = None - if is_ipv4(address): - network = [x for x in container['network'][network_name]['prefix'] if is_ipv4(x)][0] - elif is_ipv6(address): - network = [x for x in container['network'][network_name]['prefix'] if is_ipv6(x)][0] - - # Specified container IP address must belong to network prefix - if ip_address(address) not in ip_network(network): - raise ConfigError(f'Used container address "{address}" not in network "{network}"!') - - # 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'IP address "{address}" can not be used for a container, '\ - 'reserved for the container engine!') + cnt_ipv4 = 0 + cnt_ipv6 = 0 + for address in container_config['network'][network_name]['address']: + network = None + if is_ipv4(address): + network = [x for x in container['network'][network_name]['prefix'] if is_ipv4(x)][0] + cnt_ipv4 += 1 + elif is_ipv6(address): + network = [x for x in container['network'][network_name]['prefix'] if is_ipv6(x)][0] + cnt_ipv6 += 1 + + # Specified container IP address must belong to network prefix + if ip_address(address) not in ip_network(network): + raise ConfigError(f'Used container address "{address}" not in network "{network}"!') + + # 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'IP address "{address}" can not be used for a container, '\ + 'reserved for the container engine!') + + if cnt_ipv4 > 1 or cnt_ipv6 > 1: + raise ConfigError(f'Only one IP address per address family can be used for '\ + f'container "{name}". {cnt_ipv4} IPv4 and {cnt_ipv6} IPv6 address(es)!') if 'device' in container_config: for dev, dev_config in container_config['device'].items(): @@ -242,6 +252,8 @@ def verify(container): if v6_prefix > 1: raise ConfigError(f'Only one IPv6 prefix can be defined for network "{network}"!') + # Verify VRF exists + verify_vrf(network_config) # A network attached to a container can not be deleted if {'network_remove', 'name'} <= set(container): @@ -250,9 +262,11 @@ def verify(container): if 'network' in container_config and network in container_config['network']: raise ConfigError(f'Can not remove network "{network}", used by container "{container}"!') - if 'registry' in container and 'authentication' in container['registry']: - for registry, registry_config in container['registry']['authentication'].items(): - if not {'username', 'password'} <= set(registry_config): + if 'registry' in container: + for registry, registry_config in container['registry'].items(): + if 'authentication' not in registry_config: + continue + if not {'username', 'password'} <= set(registry_config['authentication']): raise ConfigError('If registry username or or password is defined, so must be the other!') return None @@ -338,9 +352,13 @@ def generate_run_arguments(name, container_config): ip_param = '' networks = ",".join(container_config['network']) for network in container_config['network']: - if 'address' in container_config['network'][network]: - address = container_config['network'][network]['address'] - ip_param = f'--ip {address}' + if 'address' not in container_config['network'][network]: + continue + for address in container_config['network'][network]['address']: + if is_ipv6(address): + ip_param += f' --ip6 {address}' + else: + ip_param += f' --ip {address}' return f'{container_base_cmd} --net {networks} {ip_param} {entrypoint} {image} {command} {command_arguments}'.strip() @@ -355,33 +373,26 @@ def generate(container): if 'network' in container: for network, network_config in container['network'].items(): tmp = { - 'cniVersion' : '0.4.0', - 'name' : network, - 'plugins' : [{ - 'type': 'bridge', - 'bridge': f'cni-{network}', - 'isGateway': True, - 'ipMasq': False, - 'hairpinMode': False, - 'ipam' : { - 'type': 'host-local', - 'routes': [], - 'ranges' : [], - }, - }] + 'name': network, + 'id' : sha256(f'{network}'.encode()).hexdigest(), + 'driver': 'bridge', + 'network_interface': f'pod-{network}', + 'subnets': [], + 'ipv6_enabled': False, + 'internal': False, + 'dns_enabled': True, + 'ipam_options': { + 'driver': 'host-local' + } } - for prefix in network_config['prefix']: - net = [{'gateway' : inc_ip(prefix, 1), 'subnet' : prefix}] - tmp['plugins'][0]['ipam']['ranges'].append(net) + net = {'subnet' : prefix, 'gateway' : inc_ip(prefix, 1)} + tmp['subnets'].append(net) - # install per address-family default orutes - default_route = '0.0.0.0/0' if is_ipv6(prefix): - default_route = '::/0' - tmp['plugins'][0]['ipam']['routes'].append({'dst': default_route}) + tmp['ipv6_enabled'] = True - write_file(f'/etc/cni/net.d/{network}.conflist', json_write(tmp, indent=2)) + write_file(f'/etc/containers/networks/{network}.json', json_write(tmp, indent=2)) if 'registry' in container: cmd = f'podman logout --all' @@ -432,10 +443,7 @@ def apply(container): # Delete old networks if needed if 'network_remove' in container: for network in container['network_remove']: - call(f'podman network rm {network}') - tmp = f'/etc/cni/net.d/{network}.conflist' - if os.path.exists(tmp): - os.unlink(tmp) + call(f'podman network rm {network} >/dev/null 2>&1') # Add container disabled_new = False @@ -459,12 +467,26 @@ def apply(container): os.unlink(file_path) continue - if name in dict_search('container_restart', container): + if 'container_restart' in container and name in container['container_restart']: cmd(f'systemctl restart vyos-container-{name}.service') if disabled_new: call('systemctl daemon-reload') + # Start network and assign it to given VRF if requested. this can only be done + # after the containers got started as the podman network interface will + # only be enabled by the first container and yet I do not know how to enable + # the network interface in advance + if 'network' in container: + for network, network_config in container['network'].items(): + network_name = f'pod-{network}' + # T5147: Networks are started only as soon as there is a consumer. + # If only a network is created in the first place, no need to assign + # it to a VRF as there's no consumer, yet. + if os.path.exists(f'/sys/class/net/{network_name}'): + tmp = Interface(network_name) + tmp.set_vrf(network_config.get('vrf', '')) + return None if __name__ == '__main__': diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index c41a442df..190587980 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -282,6 +282,9 @@ def verify_rule(firewall, rule_conf, ipv6): if rule_conf['protocol'] not in ['tcp', 'udp', 'tcp_udp']: raise ConfigError('Protocol must be tcp, udp, or tcp_udp when specifying a port or port-group') + if 'port' in side_conf and dict_search_args(side_conf, 'group', 'port_group'): + raise ConfigError(f'{side} port-group and port cannot both be defined') + if 'log_options' in rule_conf: if 'log' not in rule_conf or 'enable' not in rule_conf['log']: raise ConfigError('log-options defined, but log is not enable') diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index ce5e63928..b0c38e8d3 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -159,6 +159,8 @@ def generate(https): server_block['port'] = data.get('listen-port', '443') name = data.get('server-name', ['_']) server_block['name'] = name + allow_client = data.get('allow-client', {}) + server_block['allow_client'] = allow_client.get('address', []) server_block_list.append(server_block) # get certificate data diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py index 13d84a6fe..6f227b0d1 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-2022 VyOS maintainers and contributors +# Copyright (C) 2019-2023 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 @@ -597,7 +597,7 @@ def generate_pki_files(openvpn): def generate(openvpn): interface = openvpn['ifname'] directory = os.path.dirname(cfg_file.format(**openvpn)) - plugin_dir = '/usr/lib/openvpn' + openvpn['plugin_dir'] = '/usr/lib/openvpn' # create base config directory on demand makedir(directory, user, group) # enforce proper permissions on /run/openvpn diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index cf553f0e8..66505e58d 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -484,26 +484,15 @@ def generate(bgp): if not bgp or 'deleted' in bgp: return None - bgp['protocol'] = 'bgp' # required for frr/vrf.route-map.frr.j2 - bgp['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.j2', bgp) bgp['frr_bgpd_config'] = render_to_string('frr/bgpd.frr.j2', bgp) - return None def apply(bgp): bgp_daemon = 'bgpd' - zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - # The route-map used for the FIB (zebra) is part of the zebra daemon - frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'(\s+)?ip protocol bgp route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') - if 'frr_zebra_config' in bgp: - frr_cfg.add_before(frr.default_add_before, bgp['frr_zebra_config']) - frr_cfg.commit_configuration(zebra_daemon) - # Generate empty helper string which can be ammended to FRR commands, it # will be either empty (default VRF) or contain the "vrf <name" statement vrf = '' diff --git a/src/conf_mode/protocols_eigrp.py b/src/conf_mode/protocols_eigrp.py index c1a1a45e1..609b39065 100755 --- a/src/conf_mode/protocols_eigrp.py +++ b/src/conf_mode/protocols_eigrp.py @@ -69,8 +69,6 @@ def get_config(config=None): # Merge policy dict into "regular" config dict eigrp = dict_merge(tmp, eigrp) - import pprint - pprint.pprint(eigrp) return eigrp def verify(eigrp): @@ -80,24 +78,14 @@ def generate(eigrp): if not eigrp or 'deleted' in eigrp: return None - eigrp['protocol'] = 'eigrp' # required for frr/vrf.route-map.frr.j2 - eigrp['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.j2', eigrp) eigrp['frr_eigrpd_config'] = render_to_string('frr/eigrpd.frr.j2', eigrp) def apply(eigrp): eigrp_daemon = 'eigrpd' - zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - # The route-map used for the FIB (zebra) is part of the zebra daemon - frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'(\s+)?ip protocol eigrp route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') - if 'frr_zebra_config' in eigrp: - frr_cfg.add_before(frr.default_add_before, eigrp['frr_zebra_config']) - frr_cfg.commit_configuration(zebra_daemon) - # Generate empty helper string which can be ammended to FRR commands, it # will be either empty (default VRF) or contain the "vrf <name" statement vrf = '' diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py index cb8ea3be4..af2937db8 100755 --- a/src/conf_mode/protocols_isis.py +++ b/src/conf_mode/protocols_isis.py @@ -203,7 +203,7 @@ def verify(isis): if list(set(global_range) & set(local_range)): raise ConfigError(f'Segment-Routing Global Block ({g_low_label_value}/{g_high_label_value}) '\ f'conflicts with Local Block ({l_low_label_value}/{l_high_label_value})!') - + # Check for a blank or invalid value per prefix if dict_search('segment_routing.prefix', isis): for prefix, prefix_config in isis['segment_routing']['prefix'].items(): @@ -218,7 +218,7 @@ def verify(isis): if dict_search('segment_routing.prefix', isis): for prefix, prefix_config in isis['segment_routing']['prefix'].items(): if 'absolute' in prefix_config: - if ("explicit_null" in prefix_config['absolute']) and ("no_php_flag" in prefix_config['absolute']): + if ("explicit_null" in prefix_config['absolute']) and ("no_php_flag" in prefix_config['absolute']): raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\ f'and no-php-flag configured at the same time.') elif 'index' in prefix_config: @@ -232,25 +232,15 @@ def generate(isis): if not isis or 'deleted' in isis: return None - isis['protocol'] = 'isis' # required for frr/vrf.route-map.frr.j2 - isis['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.j2', isis) isis['frr_isisd_config'] = render_to_string('frr/isisd.frr.j2', isis) return None def apply(isis): isis_daemon = 'isisd' - zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - # The route-map used for the FIB (zebra) is part of the zebra daemon - frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section('(\s+)?ip protocol isis route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') - if 'frr_zebra_config' in isis: - frr_cfg.add_before(frr.default_add_before, isis['frr_zebra_config']) - frr_cfg.commit_configuration(zebra_daemon) - # Generate empty helper string which can be ammended to FRR commands, it # will be either empty (default VRF) or contain the "vrf <name" statement vrf = '' diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py index eb64afa0c..fbb876123 100755 --- a/src/conf_mode/protocols_ospf.py +++ b/src/conf_mode/protocols_ospf.py @@ -256,25 +256,15 @@ def generate(ospf): if not ospf or 'deleted' in ospf: return None - ospf['protocol'] = 'ospf' # required for frr/vrf.route-map.frr.j2 - ospf['frr_zebra_config'] = render_to_string('frr/vrf.route-map.frr.j2', ospf) ospf['frr_ospfd_config'] = render_to_string('frr/ospfd.frr.j2', ospf) return None def apply(ospf): ospf_daemon = 'ospfd' - zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - # The route-map used for the FIB (zebra) is part of the zebra daemon - frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section('(\s+)?ip protocol ospf route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') - if 'frr_zebra_config' in ospf: - frr_cfg.add_before(frr.default_add_before, ospf['frr_zebra_config']) - frr_cfg.commit_configuration(zebra_daemon) - # Generate empty helper string which can be ammended to FRR commands, it # will be either empty (default VRF) or contain the "vrf <name" statement vrf = '' @@ -292,6 +282,7 @@ def apply(ospf): if 'frr_ospfd_config' in ospf: frr_cfg.add_before(frr.default_add_before, ospf['frr_ospfd_config']) + frr_cfg.commit_configuration(ospf_daemon) return None diff --git a/src/conf_mode/protocols_ospfv3.py b/src/conf_mode/protocols_ospfv3.py index 1e2c02d03..ee1fdd399 100755 --- a/src/conf_mode/protocols_ospfv3.py +++ b/src/conf_mode/protocols_ospfv3.py @@ -146,25 +146,15 @@ def generate(ospfv3): if not ospfv3 or 'deleted' in ospfv3: return None - ospfv3['protocol'] = 'ospf6' # required for frr/vrf.route-map.v6.frr.j2 - ospfv3['frr_zebra_config'] = render_to_string('frr/vrf.route-map.v6.frr.j2', ospfv3) ospfv3['new_frr_config'] = render_to_string('frr/ospf6d.frr.j2', ospfv3) return None def apply(ospfv3): ospf6_daemon = 'ospf6d' - zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - # The route-map used for the FIB (zebra) is part of the zebra daemon - frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section('(\s+)?ipv6 protocol ospf6 route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') - if 'frr_zebra_config' in ospfv3: - frr_cfg.add_before(frr.default_add_before, ospfv3['frr_zebra_config']) - frr_cfg.commit_configuration(zebra_daemon) - # Generate empty helper string which can be ammended to FRR commands, it # will be either empty (default VRF) or contain the "vrf <name" statement vrf = '' diff --git a/src/conf_mode/protocols_static.py b/src/conf_mode/protocols_static.py index 3e5ebb805..7b6150696 100755 --- a/src/conf_mode/protocols_static.py +++ b/src/conf_mode/protocols_static.py @@ -105,20 +105,14 @@ def generate(static): def apply(static): static_daemon = 'staticd' - zebra_daemon = 'zebra' # Save original configuration prior to starting any commit actions frr_cfg = frr.FRRConfig() - - # The route-map used for the FIB (zebra) is part of the zebra daemon - frr_cfg.load_configuration(zebra_daemon) - frr_cfg.modify_section(r'^ip protocol static route-map [-a-zA-Z0-9.]+', '') - frr_cfg.commit_configuration(zebra_daemon) frr_cfg.load_configuration(static_daemon) if 'vrf' in static: vrf = static['vrf'] - frr_cfg.modify_section(f'^vrf {vrf}', stop_pattern='^exit', remove_stop_mark=True) + frr_cfg.modify_section(f'^vrf {vrf}', stop_pattern='^exit-vrf', remove_stop_mark=True) else: frr_cfg.modify_section(r'^ip route .*') frr_cfg.modify_section(r'^ipv6 route .*') diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py index 363408679..47510ce80 100755 --- a/src/conf_mode/service_monitoring_telegraf.py +++ b/src/conf_mode/service_monitoring_telegraf.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2023 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 @@ -15,6 +15,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import os +import socket import json from sys import exit @@ -57,6 +58,13 @@ def get_nft_filter_chains(): return chain_list +def get_hostname() -> str: + try: + hostname = socket.getfqdn() + except socket.gaierror: + hostname = socket.gethostname() + return hostname + def get_config(config=None): if config: conf = config @@ -79,6 +87,7 @@ def get_config(config=None): monitoring = dict_merge(default_values, monitoring) monitoring['custom_scripts_dir'] = custom_scripts_dir + monitoring['hostname'] = get_hostname() monitoring['interfaces_ethernet'] = Section.interfaces('ethernet', vlan=False) monitoring['nft_chains'] = get_nft_filter_chains() diff --git a/src/conf_mode/system-ip.py b/src/conf_mode/system-ip.py index 0c5063ed3..95865c690 100755 --- a/src/conf_mode/system-ip.py +++ b/src/conf_mode/system-ip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2022 VyOS maintainers and contributors +# Copyright (C) 2019-2023 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 @@ -18,12 +18,15 @@ from sys import exit from vyos.config import Config from vyos.configdict import dict_merge +from vyos.configverify import verify_route_map +from vyos.template import render_to_string from vyos.util import call from vyos.util import dict_search from vyos.util import sysctl_write from vyos.util import write_file from vyos.xml import defaults from vyos import ConfigError +from vyos import frr from vyos import airbag airbag.enable() @@ -40,13 +43,30 @@ def get_config(config=None): default_values = defaults(base) opt = dict_merge(default_values, opt) + # When working with FRR we need to know the corresponding address-family + opt['afi'] = 'ip' + + # We also need the route-map information from the config + # + # XXX: one MUST always call this without the key_mangling() option! See + # vyos.configverify.verify_common_route_maps() for more information. + tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'], + get_first_key=True)}} + # Merge policy dict into "regular" config dict + opt = dict_merge(tmp, opt) return opt def verify(opt): - pass + if 'protocol' in opt: + for protocol, protocol_options in opt['protocol'].items(): + if 'route_map' in protocol_options: + verify_route_map(protocol_options['route_map'], opt) + return def generate(opt): - pass + if 'protocol' in opt: + opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt) + return def apply(opt): # Apply ARP threshold values @@ -78,6 +98,18 @@ def apply(opt): value = '1' if (tmp != None) else '0' sysctl_write('net.ipv4.fib_multipath_hash_policy', value) + if 'protocol' in opt: + zebra_daemon = 'zebra' + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + + # The route-map used for the FIB (zebra) is part of the zebra daemon + frr_cfg.load_configuration(zebra_daemon) + frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') + if 'frr_zebra_config' in opt: + frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config']) + frr_cfg.commit_configuration(zebra_daemon) + if __name__ == '__main__': try: c = get_config() diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py index 26aacf46b..b6d3a79c3 100755 --- a/src/conf_mode/system-ipv6.py +++ b/src/conf_mode/system-ipv6.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2022 VyOS maintainers and contributors +# Copyright (C) 2019-2023 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 @@ -19,11 +19,14 @@ import os from sys import exit from vyos.config import Config from vyos.configdict import dict_merge +from vyos.configverify import verify_route_map +from vyos.template import render_to_string from vyos.util import dict_search from vyos.util import sysctl_write from vyos.util import write_file from vyos.xml import defaults from vyos import ConfigError +from vyos import frr from vyos import airbag airbag.enable() @@ -41,13 +44,30 @@ def get_config(config=None): default_values = defaults(base) opt = dict_merge(default_values, opt) + # When working with FRR we need to know the corresponding address-family + opt['afi'] = 'ipv6' + + # We also need the route-map information from the config + # + # XXX: one MUST always call this without the key_mangling() option! See + # vyos.configverify.verify_common_route_maps() for more information. + tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'], + get_first_key=True)}} + # Merge policy dict into "regular" config dict + opt = dict_merge(tmp, opt) return opt def verify(opt): - pass + if 'protocol' in opt: + for protocol, protocol_options in opt['protocol'].items(): + if 'route_map' in protocol_options: + verify_route_map(protocol_options['route_map'], opt) + return def generate(opt): - pass + if 'protocol' in opt: + opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt) + return def apply(opt): # configure multipath @@ -78,6 +98,18 @@ def apply(opt): if name == 'accept_dad': write_file(os.path.join(root, name), value) + if 'protocol' in opt: + zebra_daemon = 'zebra' + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + + # The route-map used for the FIB (zebra) is part of the zebra daemon + frr_cfg.load_configuration(zebra_daemon) + frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)') + if 'frr_zebra_config' in opt: + frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config']) + frr_cfg.commit_configuration(zebra_daemon) + if __name__ == '__main__': try: c = get_config() diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index d15fe399d..fbb013cf3 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2023 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 @@ -40,6 +40,7 @@ from vyos import airbag airbag.enable() autologout_file = "/etc/profile.d/autologout.sh" +limits_file = "/etc/security/limits.d/10-vyos.conf" radius_config_file = "/etc/pam_radius_auth.conf" # LOGIN_TIMEOUT from /etc/loign.defs minus 10 sec @@ -164,6 +165,9 @@ def verify(login): if ipv6_count > 1: raise ConfigError('Only one IPv6 source-address can be set!') + if 'max_login_session' in login and 'timeout' not in login: + raise ConfigError('"login timeout" must be configured!') + return None @@ -226,6 +230,14 @@ def generate(login): if os.path.isfile(radius_config_file): os.unlink(radius_config_file) + # /etc/security/limits.d/10-vyos.conf + if 'max_login_session' in login: + render(limits_file, 'login/limits.j2', login, + permission=0o644, user='root', group='root') + else: + if os.path.isfile(limits_file): + os.unlink(limits_file) + if 'timeout' in login: render(autologout_file, 'login/autologout.j2', login, permission=0o755, user='root', group='root') diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py index 7550c411e..986a19972 100755 --- a/src/conf_mode/vpn_pptp.py +++ b/src/conf_mode/vpn_pptp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2023 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,6 +44,8 @@ default_pptp = { 'radius_nas_ip' : '', 'radius_source_address' : '', 'radius_shaper_attr' : '', + 'radius_shaper_enable': False, + 'radius_shaper_multiplier': '', 'radius_shaper_vendor': '', 'radius_dynamic_author' : '', 'chap_secrets_file': pptp_chap_secrets, # used in Jinja2 template @@ -183,15 +185,18 @@ def get_config(config=None): pptp['radius_dynamic_author'] = dae + # Rate limit + if conf.exists(['rate-limit', 'attribute']): + pptp['radius_shaper_attr'] = conf.return_value(['rate-limit', 'attribute']) + if conf.exists(['rate-limit', 'enable']): - pptp['radius_shaper_attr'] = 'Filter-Id' - c_attr = ['rate-limit', 'enable', 'attribute'] - if conf.exists(c_attr): - pptp['radius_shaper_attr'] = conf.return_value(c_attr) - - c_vendor = ['rate-limit', 'enable', 'vendor'] - if conf.exists(c_vendor): - pptp['radius_shaper_vendor'] = conf.return_value(c_vendor) + pptp['radius_shaper_enable'] = True + + if conf.exists(['rate-limit', 'multiplier']): + pptp['radius_shaper_multiplier'] = conf.return_value(['rate-limit', 'multiplier']) + + if conf.exists(['rate-limit', 'vendor']): + pptp['radius_shaper_vendor'] = conf.return_value(['rate-limit', 'vendor']) conf.set_level(base_path) if conf.exists(['client-ip-pool']): diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index c17cca3bd..a7ef4cb5c 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -20,9 +20,12 @@ from sys import exit from json import loads from vyos.config import Config +from vyos.configdict import dict_merge from vyos.configdict import node_changed +from vyos.configverify import verify_route_map 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 @@ -99,6 +102,14 @@ def get_config(config=None): routes = vrf_routing(conf, name) if routes: vrf['vrf_remove'][name]['route'] = routes + # We also need the route-map information from the config + # + # XXX: one MUST always call this without the key_mangling() option! See + # vyos.configverify.verify_common_route_maps() for more information. + tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'], + get_first_key=True)}} + # Merge policy dict into "regular" config dict + vrf = dict_merge(tmp, vrf) return vrf def verify(vrf): @@ -116,35 +127,50 @@ def verify(vrf): reserved_names = ["add", "all", "broadcast", "default", "delete", "dev", "get", "inet", "mtu", "link", "type", "vrf"] table_ids = [] - for name, config in vrf['name'].items(): + for name, vrf_config in vrf['name'].items(): # Reserved VRF names if name in reserved_names: raise ConfigError(f'VRF name "{name}" is reserved and connot be used!') # table id is mandatory - if 'table' not in config: + if 'table' not in vrf_config: raise ConfigError(f'VRF "{name}" table id is mandatory!') # routing table id can't be changed - OS restriction if os.path.isdir(f'/sys/class/net/{name}'): tmp = str(dict_search('linkinfo.info_data.table', get_interface_config(name))) - if tmp and tmp != config['table']: + if tmp and tmp != vrf_config['table']: raise ConfigError(f'VRF "{name}" table id modification not possible!') # VRf routing table ID must be unique on the system - if config['table'] in table_ids: + if vrf_config['table'] in table_ids: raise ConfigError(f'VRF "{name}" table id is not unique!') - table_ids.append(config['table']) + table_ids.append(vrf_config['table']) + + tmp = dict_search('ip.protocol', vrf_config) + if tmp != None: + for protocol, protocol_options in tmp.items(): + if 'route_map' in protocol_options: + verify_route_map(protocol_options['route_map'], vrf) + + tmp = dict_search('ipv6.protocol', vrf_config) + if tmp != None: + for protocol, protocol_options in tmp.items(): + if 'route_map' in protocol_options: + verify_route_map(protocol_options['route_map'], vrf) return None def generate(vrf): + # Render iproute2 VR helper names render(config_file, 'iproute2/vrf.conf.j2', vrf) # Render nftables zones config render(nft_vrf_config, 'firewall/nftables-vrf-zones.j2', vrf) - return None + # Render VRF Kernel/Zebra route-map filters + vrf['frr_zebra_config'] = render_to_string('frr/zebra.vrf.route-map.frr.j2', vrf) + return None def apply(vrf): # Documentation @@ -249,6 +275,17 @@ def apply(vrf): nft_add_element = f'add element inet vrf_zones ct_iface_map {{ "{name}" : {table} }}' cmd(f'nft {nft_add_element}') + # Apply FRR filters + zebra_daemon = 'zebra' + # Save original configuration prior to starting any commit actions + frr_cfg = frr.FRRConfig() + + # The route-map used for the FIB (zebra) is part of the zebra daemon + frr_cfg.load_configuration(zebra_daemon) + frr_cfg.modify_section(f'^vrf .+', stop_pattern='^exit-vrf', remove_stop_mark=True) + if 'frr_zebra_config' in vrf: + frr_cfg.add_before(frr.default_add_before, vrf['frr_zebra_config']) + frr_cfg.commit_configuration(zebra_daemon) # return to default lookup preference when no VRF is configured if 'name' not in vrf: diff --git a/src/conf_mode/vrf_vni.py b/src/conf_mode/vrf_vni.py deleted file mode 100755 index 585fdbebf..000000000 --- a/src/conf_mode/vrf_vni.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/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() - - base = ['vrf'] - vrf = conf.get_config_dict(base, get_first_key=True) - return vrf - -def verify(vrf): - return None - -def generate(vrf): - vrf['new_frr_config'] = render_to_string('frr/vrf-vni.frr.j2', vrf) - return None - -def apply(vrf): - # add configuration to FRR - frr_cfg = frr.FRRConfig() - frr_cfg.load_configuration(frr_daemon) - frr_cfg.modify_section(f'^vrf .+', stop_pattern='^exit-vrf', remove_stop_mark=True) - if 'new_frr_config' in vrf: - frr_cfg.add_before(frr.default_add_before, vrf['new_frr_config']) - frr_cfg.commit_configuration(frr_daemon) - - return None - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/vyatta-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyatta-dhclient-hook index 49bb18372..49bb18372 100644 --- a/src/etc/dhcp/dhclient-exit-hooks.d/vyatta-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/03-vyatta-dhclient-hook diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-run-user-hooks b/src/etc/dhcp/dhclient-exit-hooks.d/98-run-user-hooks index 442419d79..442419d79 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/99-run-user-hooks +++ b/src/etc/dhcp/dhclient-exit-hooks.d/98-run-user-hooks diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook index 1f1926e17..1f1926e17 100755 --- a/src/etc/dhcp/dhclient-exit-hooks.d/ipsec-dhclient-hook +++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-ipsec-dhclient-hook diff --git a/src/etc/opennhrp/opennhrp-script.py b/src/etc/opennhrp/opennhrp-script.py index bf25a7331..688c7af2a 100755 --- a/src/etc/opennhrp/opennhrp-script.py +++ b/src/etc/opennhrp/opennhrp-script.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,7 +17,7 @@ import os import re import sys -import vici +import vyos.ipsec from json import loads from pathlib import Path @@ -51,9 +51,8 @@ def vici_get_ipsec_uniqueid(conn: str, src_nbma: str, logger.info( f'Resolving IKE unique ids for: conn: {conn}, ' f'src_nbma: {src_nbma}, dst_nbma: {dst_nbma}') - session: vici.Session = vici.Session() list_ikeid: list[str] = [] - list_sa = session.list_sas({'ike': conn}) + list_sa: list = vyos.ipsec.get_vici_sas_by_name(conn, None) for sa in list_sa: if sa[conn]['local-host'].decode('ascii') == src_nbma \ and sa[conn]['remote-host'].decode('ascii') == dst_nbma: @@ -78,16 +77,7 @@ def vici_ike_terminate(list_ikeid: list[str]) -> bool: return False try: - session = vici.Session() - for ikeid in list_ikeid: - logger.info(f'Terminating IKE SA with id {ikeid}') - session_generator = session.terminate( - {'ike-id': ikeid, 'timeout': '-1'}) - # a dummy `for` loop is required because of requirements - # from vici. Without a full iteration on the output, the - # command to vici may not be executed completely - for _ in session_generator: - pass + vyos.ipsec.terminate_vici_ikeid_list(list_ikeid) return True except Exception as err: logger.error(f'Failed to terminate SA for IKE ids {list_ikeid}: {err}') @@ -180,19 +170,7 @@ def vici_initiate(conn: str, child_sa: str, src_addr: str, f'Trying to initiate connection. Name: {conn}, child sa: {child_sa}, ' f'src_addr: {src_addr}, dst_addr: {dest_addr}') try: - session = vici.Session() - session_generator = session.initiate({ - 'ike': conn, - 'child': child_sa, - 'timeout': '-1', - 'my-host': src_addr, - 'other-host': dest_addr - }) - # a dummy `for` loop is required because of requirements - # from vici. Without a full iteration on the output, the - # command to vici may not be executed completely - for _ in session_generator: - pass + vyos.ipsec.vici_initiate(conn, child_sa, src_addr, dest_addr) return True except Exception as err: logger.error(f'Unable to initiate connection {err}') @@ -218,8 +196,11 @@ def vici_terminate(conn: str, src_addr: str, dest_addr: str) -> None: f'No active sessions found for IKE profile {conn}, ' f'local NBMA {src_addr}, remote NBMA {dest_addr}') else: - vici_ike_terminate(ikeid_list) - + try: + vyos.ipsec.terminate_vici_ikeid_list(ikeid_list) + except Exception as err: + logger.error( + f'Failed to terminate SA for IKE ids {ikeid_list}: {err}') def iface_up(interface: str) -> None: """Proceed tunnel interface UP event diff --git a/src/etc/systemd/system/hostapd@.service.d/override.conf b/src/etc/systemd/system/hostapd@.service.d/override.conf index bb8e81d7a..926c07f94 100644 --- a/src/etc/systemd/system/hostapd@.service.d/override.conf +++ b/src/etc/systemd/system/hostapd@.service.d/override.conf @@ -1,6 +1,8 @@ [Unit] After= After=vyos-router.service +ConditionFileNotEmpty= +ConditionFileNotEmpty=/run/hostapd/%i.conf [Service] WorkingDirectory=/run/hostapd diff --git a/src/helpers/vyos-failover.py b/src/helpers/vyos-failover.py index 0de945f20..03fb42f57 100755 --- a/src/helpers/vyos-failover.py +++ b/src/helpers/vyos-failover.py @@ -30,7 +30,7 @@ my_name = Path(__file__).stem def is_route_exists(route, gateway, interface, metric): """Check if route with expected gateway, dev and metric exists""" - rc, data = rc_cmd(f'sudo ip --json route show protocol failover {route} ' + rc, data = rc_cmd(f'ip --json route show protocol failover {route} ' f'via {gateway} dev {interface} metric {metric}') if rc == 0: data = json.loads(data) @@ -72,6 +72,7 @@ def get_best_route_options(route, debug=False): f'best_metric: {best_metric}, best_iface: {best_interface}') return best_gateway, best_interface, best_metric + def is_port_open(ip, port): """ Check connection to remote host and port @@ -91,32 +92,54 @@ def is_port_open(ip, port): finally: s.close() -def is_target_alive(target=None, iface='', proto='icmp', port=None, debug=False): - """ - Host availability check by ICMP, ARP, TCP - Return True if target checks is successful - % is_target_alive('192.0.2.1', 'eth1', proto='arp') - True +def is_target_alive(target_list=None, iface='', proto='icmp', port=None, debug=False): + """Check the availability of each target in the target_list using + the specified protocol ICMP, ARP, TCP + + Args: + target_list (list): A list of IP addresses or hostnames to check. + iface (str): The name of the network interface to use for the check. + proto (str): The protocol to use for the check. Options are 'icmp', 'arp', or 'tcp'. + port (int): The port number to use for the TCP check. Only applicable if proto is 'tcp'. + debug (bool): If True, print debug information during the check. + + Returns: + bool: True if all targets are reachable, False otherwise. + + Example: + % is_target_alive(['192.0.2.1', '192.0.2.5'], 'eth1', proto='arp') + True """ if iface != '': iface = f'-I {iface}' - if proto == 'icmp': - command = f'/usr/bin/ping -q {target} {iface} -n -c 2 -W 1' - rc, response = rc_cmd(command) - if debug: print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') - if rc == 0: - return True - elif proto == 'arp': - command = f'/usr/bin/arping -b -c 2 -f -w 1 -i 1 {iface} {target}' - rc, response = rc_cmd(command) - if debug: print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') - if rc == 0: - return True - elif proto == 'tcp' and port is not None: - return True if is_port_open(target, port) else False - else: - return False + + for target in target_list: + match proto: + case 'icmp': + command = f'/usr/bin/ping -q {target} {iface} -n -c 2 -W 1' + rc, response = rc_cmd(command) + if debug: + print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') + if rc != 0: + return False + + case 'arp': + command = f'/usr/bin/arping -b -c 2 -f -w 1 -i 1 {iface} {target}' + rc, response = rc_cmd(command) + if debug: + print(f' [ CHECK-TARGET ]: [{command}] -- return-code [RC: {rc}]') + if rc != 0: + return False + + case _ if proto == 'tcp' and port is not None: + if not is_port_open(target, port): + return False + + case _: + return False + + return True if __name__ == '__main__': diff --git a/src/migration-scripts/bgp/3-to-4 b/src/migration-scripts/bgp/3-to-4 new file mode 100755 index 000000000..0df2fbec4 --- /dev/null +++ b/src/migration-scripts/bgp/3-to-4 @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5150: Rework CLI definitions to apply route-maps between routing daemons +# and zebra/kernel + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +bgp_base = ['protocols', 'bgp'] +# Check if BGP is configured - if so, migrate the CLI node +if config.exists(bgp_base): + if config.exists(bgp_base + ['route-map']): + tmp = config.return_value(bgp_base + ['route-map']) + + config.set(['system', 'ip', 'protocol', 'bgp', 'route-map'], value=tmp) + config.set_tag(['system', 'ip', 'protocol']) + config.delete(bgp_base + ['route-map']) + + +# Check if vrf names are configured. Check if BGP is configured - if so, migrate +# the CLI node(s) +if config.exists(['vrf', 'name']): + for vrf in config.list_nodes(['vrf', 'name']): + vrf_base = ['vrf', 'name', vrf] + if config.exists(vrf_base + ['protocols', 'bgp', 'route-map']): + tmp = config.return_value(vrf_base + ['protocols', 'bgp', 'route-map']) + + config.set(vrf_base + ['ip', 'protocol', 'bgp', 'route-map'], value=tmp) + config.set_tag(vrf_base + ['ip', 'protocol', 'bgp']) + config.delete(vrf_base + ['protocols', 'bgp', 'route-map']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/migration-scripts/isis/2-to-3 b/src/migration-scripts/isis/2-to-3 new file mode 100755 index 000000000..4490feb0a --- /dev/null +++ b/src/migration-scripts/isis/2-to-3 @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5150: Rework CLI definitions to apply route-maps between routing daemons +# and zebra/kernel + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +isis_base = ['protocols', 'isis'] +# Check if IS-IS is configured - if so, migrate the CLI node +if config.exists(isis_base): + if config.exists(isis_base + ['route-map']): + tmp = config.return_value(isis_base + ['route-map']) + + config.set(['system', 'ip', 'protocol', 'isis', 'route-map'], value=tmp) + config.set_tag(['system', 'ip', 'protocol']) + config.delete(isis_base + ['route-map']) + +# Check if vrf names are configured. Check if IS-IS is configured - if so, +# migrate the CLI node(s) +if config.exists(['vrf', 'name']): + for vrf in config.list_nodes(['vrf', 'name']): + vrf_base = ['vrf', 'name', vrf] + if config.exists(vrf_base + ['protocols', 'isis', 'route-map']): + tmp = config.return_value(vrf_base + ['protocols', 'isis', 'route-map']) + + config.set(vrf_base + ['ip', 'protocol', 'isis', 'route-map'], value=tmp) + config.set_tag(vrf_base + ['ip', 'protocol', 'isis']) + config.delete(vrf_base + ['protocols', 'isis', 'route-map']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/migration-scripts/ospf/1-to-2 b/src/migration-scripts/ospf/1-to-2 new file mode 100755 index 000000000..a6beaf04e --- /dev/null +++ b/src/migration-scripts/ospf/1-to-2 @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5150: Rework CLI definitions to apply route-maps between routing daemons +# and zebra/kernel + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +ospf_base = ['protocols', 'ospf'] +# Check if OSPF is configured - if so, migrate the CLI node +if config.exists(ospf_base): + if config.exists(ospf_base + ['route-map']): + tmp = config.return_value(ospf_base + ['route-map']) + + config.set(['system', 'ip', 'protocol', 'ospf', 'route-map'], value=tmp) + config.set_tag(['system', 'ip', 'protocol']) + config.delete(ospf_base + ['route-map']) + +ospfv3_base = ['protocols', 'ospfv3'] +# Check if OSPFv3 is configured - if so, migrate the CLI node +if config.exists(ospfv3_base): + if config.exists(ospfv3_base + ['route-map']): + tmp = config.return_value(ospfv3_base + ['route-map']) + + config.set(['system', 'ipv6', 'protocol', 'ospfv3', 'route-map'], value=tmp) + config.set_tag(['system', 'ipv6', 'protocol']) + config.delete(ospfv3_base + ['route-map']) + +# Check if vrf names are configured. Check if OSPF/OSPFv3 is configured - if so, +# migrate the CLI node(s) +if config.exists(['vrf', 'name']): + for vrf in config.list_nodes(['vrf', 'name']): + vrf_base = ['vrf', 'name', vrf] + if config.exists(vrf_base + ['protocols', 'ospf', 'route-map']): + tmp = config.return_value(vrf_base + ['protocols', 'ospf', 'route-map']) + + config.set(vrf_base + ['ip', 'protocol', 'ospf', 'route-map'], value=tmp) + config.set_tag(vrf_base + ['ip', 'protocol', 'ospf']) + config.delete(vrf_base + ['protocols', 'ospf', 'route-map']) + + if config.exists(vrf_base + ['protocols', 'ospfv3', 'route-map']): + tmp = config.return_value(vrf_base + ['protocols', 'ospfv3', 'route-map']) + + config.set(vrf_base + ['ipv6', 'protocol', 'ospfv3', 'route-map'], value=tmp) + config.set_tag(vrf_base + ['ipv6', 'protocol', 'ospfv6']) + config.delete(vrf_base + ['protocols', 'ospfv3', 'route-map']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/migration-scripts/quagga/10-to-11 b/src/migration-scripts/quagga/10-to-11 new file mode 100755 index 000000000..04fc16f79 --- /dev/null +++ b/src/migration-scripts/quagga/10-to-11 @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5150: Rework CLI definitions to apply route-maps between routing daemons +# and zebra/kernel + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +static_base = ['protocols', 'static'] +# Check if static routes are configured - if so, migrate the CLI node +if config.exists(static_base): + if config.exists(static_base + ['route-map']): + tmp = config.return_value(static_base + ['route-map']) + + config.set(['system', 'ip', 'protocol', 'static', 'route-map'], value=tmp) + config.set_tag(['system', 'ip', 'protocol']) + config.delete(static_base + ['route-map']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/migration-scripts/rip/0-to-1 b/src/migration-scripts/rip/0-to-1 new file mode 100755 index 000000000..60d510001 --- /dev/null +++ b/src/migration-scripts/rip/0-to-1 @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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/>. + +# T5150: Rework CLI definitions to apply route-maps between routing daemons +# and zebra/kernel + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) + +ripng_base = ['protocols', 'ripng'] +# Check if RIPng is configured - if so, migrate the CLI node +if config.exists(ripng_base): + if config.exists(ripng_base + ['route-map']): + tmp = config.return_value(ripng_base + ['route-map']) + + config.set(['system', 'ipv6', 'protocol', 'ripng', 'route-map'], value=tmp) + config.set_tag(['system', 'ipv6', 'protocol']) + config.delete(ripng_base + ['route-map']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print(f'Failed to save the modified config: {e}') + exit(1) diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py index 41da14065..fe7f252ba 100755 --- a/src/op_mode/dhcp.py +++ b/src/op_mode/dhcp.py @@ -264,8 +264,10 @@ def show_pool_statistics(raw: bool, family: ArgFamily, pool: typing.Optional[str def show_server_leases(raw: bool, family: ArgFamily, pool: typing.Optional[str], sorted: typing.Optional[str], state: typing.Optional[ArgState]): # if dhcp server is down, inactive leases may still be shown as active, so warn the user. - if not is_systemd_service_running('isc-dhcp-server.service'): - Warning('DHCP server is configured but not started. Data may be stale.') + v = '6' if family == 'inet6' else '' + service_name = 'DHCPv6' if family == 'inet6' else 'DHCP' + if not is_systemd_service_running(f'isc-dhcp-server{v}.service'): + Warning(f'{service_name} server is configured but not started. Data may be stale.') v = 'v6' if family == 'inet6' else '' if pool and pool not in _get_dhcp_pools(family=family): diff --git a/src/op_mode/dynamic_dns.py b/src/op_mode/dynamic_dns.py index 263a3b6a5..2cba33cc8 100755 --- a/src/op_mode/dynamic_dns.py +++ b/src/op_mode/dynamic_dns.py @@ -16,69 +16,63 @@ import os import argparse -import jinja2 import sys import time +from tabulate import tabulate from vyos.config import Config from vyos.util import call cache_file = r'/run/ddclient/ddclient.cache' -OUT_TMPL_SRC = """ -{% for entry in hosts %} -ip address : {{ entry.ip }} -host-name : {{ entry.host }} -last update : {{ entry.time }} -update-status: {{ entry.status }} +columns = { + 'host': 'Hostname', + 'ipv4': 'IPv4 address', + 'status-ipv4': 'IPv4 status', + 'ipv6': 'IPv6 address', + 'status-ipv6': 'IPv6 status', + 'mtime': 'Last update', +} + + +def _get_formatted_host_records(host_data): + data_entries = [] + for entry in host_data: + data_entries.append([entry.get(key) for key in columns.keys()]) + + header = columns.values() + output = tabulate(data_entries, header, numalign='left') + return output -{% endfor %} -""" def show_status(): # A ddclient status file must not always exist if not os.path.exists(cache_file): sys.exit(0) - data = { - 'hosts': [] - } + data = [] with open(cache_file, 'r') as f: for line in f: if line.startswith('#'): continue - outp = { - 'host': '', - 'ip': '', - 'time': '' - } - - if 'host=' in line: - host = line.split('host=')[1] - if host: - outp['host'] = host.split(',')[0] - - if 'ip=' in line: - ip = line.split('ip=')[1] - if ip: - outp['ip'] = ip.split(',')[0] - - if 'mtime=' in line: - mtime = line.split('mtime=')[1] - if mtime: - outp['time'] = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(mtime.split(',')[0], base=10))) + props = {} + # ddclient cache rows have properties in 'key=value' format separated by comma + # we pick up the ones we are interested in + for kvraw in line.split(' ')[0].split(','): + k, v = kvraw.split('=') + if k in columns.keys(): + props[k] = v - if 'status=' in line: - status = line.split('status=')[1] - if status: - outp['status'] = status.split(',')[0] + # Convert mtime to human readable format + if 'mtime' in props: + props['mtime'] = time.strftime( + "%Y-%m-%d %H:%M:%S", time.localtime(int(props['mtime'], base=10))) - data['hosts'].append(outp) + data.append(props) - tmpl = jinja2.Template(OUT_TMPL_SRC) - print(tmpl.render(data)) + print(_get_formatted_host_records(data)) def update_ddns(): diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py index 6acde08ea..7f4fb72e5 100755 --- a/src/op_mode/ipsec.py +++ b/src/op_mode/ipsec.py @@ -13,6 +13,7 @@ # # 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 import typing @@ -487,6 +488,67 @@ def reset_ra(username: typing.Optional[str] = None): vyos.ipsec.terminate_vici_ikeid_list(list_sa_id) +def reset_profile_dst(profile: str, tunnel: str, nbma_dst: str): + if profile and tunnel and nbma_dst: + ike_sa_name = f'dmvpn-{profile}-{tunnel}' + try: + # Get IKE SAs + sa_list = convert_data( + vyos.ipsec.get_vici_sas_by_name(ike_sa_name, None)) + if not sa_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} not found, aborting') + sa_nbma_list = list([x for x in sa_list if + ike_sa_name in x and x[ike_sa_name][ + 'remote-host'] == nbma_dst]) + if not sa_nbma_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} remote-host {nbma_dst} not found, aborting') + # terminate IKE SAs + vyos.ipsec.terminate_vici_ikeid_list(list( + [x[ike_sa_name]['uniqueid'] for x in sa_nbma_list if + ike_sa_name in x])) + # initiate IKE SAs + for ike in sa_nbma_list: + if ike_sa_name in ike: + vyos.ipsec.vici_initiate(ike_sa_name, 'dmvpn', + ike[ike_sa_name]['local-host'], + ike[ike_sa_name]['remote-host']) + print( + f'Profile {profile} tunnel {tunnel} remote-host {nbma_dst} reset result: success') + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) + except (vyos.ipsec.ViciCommandError) as err: + raise vyos.opmode.IncorrectValue(err) + + +def reset_profile_all(profile: str, tunnel: str): + if profile and tunnel: + ike_sa_name = f'dmvpn-{profile}-{tunnel}' + try: + # Get IKE SAs + sa_list: list = convert_data( + vyos.ipsec.get_vici_sas_by_name(ike_sa_name, None)) + if not sa_list: + raise vyos.opmode.IncorrectValue( + f'SA(s) for profile {profile} tunnel {tunnel} not found, aborting') + # terminate IKE SAs + vyos.ipsec.terminate_vici_by_name(ike_sa_name, None) + # initiate IKE SAs + for ike in sa_list: + if ike_sa_name in ike: + vyos.ipsec.vici_initiate(ike_sa_name, 'dmvpn', + ike[ike_sa_name]['local-host'], + ike[ike_sa_name]['remote-host']) + print( + f'Profile {profile} tunnel {tunnel} remote-host {ike[ike_sa_name]["remote-host"]} reset result: success') + print(f'Profile {profile} tunnel {tunnel} reset result: success') + except (vyos.ipsec.ViciInitiateError) as err: + raise vyos.opmode.UnconfiguredSubsystem(err) + except (vyos.ipsec.ViciCommandError) as err: + raise vyos.opmode.IncorrectValue(err) + + def show_sa(raw: bool): sa_data = _get_raw_data_sas() if raw: diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py index 37fdbcbeb..d9ae965c5 100755 --- a/src/op_mode/openvpn.py +++ b/src/op_mode/openvpn.py @@ -16,6 +16,7 @@ # # +import json import os import sys import typing @@ -25,6 +26,7 @@ import vyos.opmode from vyos.util import bytes_to_human from vyos.util import commit_in_progress from vyos.util import call +from vyos.util import rc_cmd from vyos.config import Config ArgMode = typing.Literal['client', 'server', 'site_to_site'] @@ -63,7 +65,7 @@ def _get_interface_status(mode: str, interface: str) -> dict: } if not os.path.exists(status_file): - raise vyos.opmode.DataUnavailable('No information for interface {interface}') + return data with open(status_file, 'r') as f: lines = f.readlines() @@ -142,6 +144,25 @@ def _get_interface_status(mode: str, interface: str) -> dict: return data + +def _get_interface_state(iface): + rc, out = rc_cmd(f'ip --json link show dev {iface}') + try: + data = json.loads(out) + except: + return 'DOWN' + return data[0].get('operstate', 'DOWN') + + +def _get_interface_description(iface): + rc, out = rc_cmd(f'ip --json link show dev {iface}') + try: + data = json.loads(out) + except: + return '' + return data[0].get('ifalias', '') + + def _get_raw_data(mode: str) -> list: data: list = [] conf = Config() @@ -154,6 +175,8 @@ def _get_raw_data(mode: str) -> list: conf_dict[x]['mode'].replace('-', '_') == mode] for intf in interfaces: d = _get_interface_status(mode, intf) + d['state'] = _get_interface_state(intf) + d['description'] = _get_interface_description(intf) d['local_host'] = conf_dict[intf].get('local-host', '') d['local_port'] = conf_dict[intf].get('local-port', '') if conf.exists(f'interfaces openvpn {intf} server client'): diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py index 1e78c3a03..b054690b0 100755 --- a/src/op_mode/pki.py +++ b/src/op_mode/pki.py @@ -87,6 +87,9 @@ def get_config_certificate(name=None): def get_certificate_ca(cert, ca_certs): # Find CA certificate for given certificate + if not ca_certs: + return None + for ca_name, ca_dict in ca_certs.items(): if 'certificate' not in ca_dict: continue diff --git a/src/op_mode/sflow.py b/src/op_mode/sflow.py new file mode 100755 index 000000000..88f70d6bd --- /dev/null +++ b/src/op_mode/sflow.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 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 dbus +import sys + +from tabulate import tabulate + +from vyos.configquery import ConfigTreeQuery +from vyos.util import cmd + +import vyos.opmode + + +def _get_raw_sflow(): + bus = dbus.SystemBus() + config = ConfigTreeQuery() + + interfaces = config.values('system sflow interface') + servers = config.list_nodes('system sflow server') + + sflow = bus.get_object('net.sflow.hsflowd', '/net/sflow/hsflowd') + sflow_telemetry = dbus.Interface( + sflow, dbus_interface='net.sflow.hsflowd.telemetry') + agent_address = sflow_telemetry.GetAgent() + samples_dropped = int(sflow_telemetry.Get('dropped_samples')) + packet_drop_sent = int(sflow_telemetry.Get('event_samples')) + samples_packet_sent = int(sflow_telemetry.Get('flow_samples')) + samples_counter_sent = int(sflow_telemetry.Get('counter_samples')) + datagrams_sent = int(sflow_telemetry.Get('datagrams')) + rtmetric_samples = int(sflow_telemetry.Get('rtmetric_samples')) + event_samples_suppressed = int(sflow_telemetry.Get('event_samples_suppressed')) + samples_suppressed = int(sflow_telemetry.Get('flow_samples_suppressed')) + counter_samples_suppressed = int( + sflow_telemetry.Get("counter_samples_suppressed")) + version = sflow_telemetry.GetVersion() + + sflow_dict = { + 'agent_address': agent_address, + 'sflow_interfaces': interfaces, + 'sflow_servers': servers, + 'counter_samples_sent': samples_counter_sent, + 'datagrams_sent': datagrams_sent, + 'packet_drop_sent': packet_drop_sent, + 'packet_samples_dropped': samples_dropped, + 'packet_samples_sent': samples_packet_sent, + 'rtmetric_samples': rtmetric_samples, + 'event_samples_suppressed': event_samples_suppressed, + 'flow_samples_suppressed': samples_suppressed, + 'counter_samples_suppressed': counter_samples_suppressed, + 'hsflowd_version': version + } + return sflow_dict + + +def _get_formatted_sflow(data): + table = [ + ['Agent address', f'{data.get("agent_address")}'], + ['sFlow interfaces', f'{data.get("sflow_interfaces", "n/a")}'], + ['sFlow servers', f'{data.get("sflow_servers", "n/a")}'], + ['Counter samples sent', f'{data.get("counter_samples_sent")}'], + ['Datagrams sent', f'{data.get("datagrams_sent")}'], + ['Packet samples sent', f'{data.get("packet_samples_sent")}'], + ['Packet samples dropped', f'{data.get("packet_samples_dropped")}'], + ['Packet drops sent', f'{data.get("packet_drop_sent")}'], + ['Packet drops suppressed', f'{data.get("event_samples_suppressed")}'], + ['Flow samples suppressed', f'{data.get("flow_samples_suppressed")}'], + ['Counter samples suppressed', f'{data.get("counter_samples_suppressed")}'] + ] + + return tabulate(table) + + +def show(raw: bool): + + config = ConfigTreeQuery() + if not config.exists('system sflow'): + raise vyos.opmode.UnconfiguredSubsystem( + '"system sflow" is not configured!') + + sflow_data = _get_raw_sflow() + if raw: + return sflow_data + else: + return _get_formatted_sflow(sflow_data) + + +if __name__ == '__main__': + try: + res = vyos.opmode.run(sys.modules[__name__]) + if res: + print(res) + except (ValueError, vyos.opmode.Error) as e: + print(e) + sys.exit(1) diff --git a/src/op_mode/show_interfaces.py b/src/op_mode/show_interfaces.py deleted file mode 100755 index eac068274..000000000 --- a/src/op_mode/show_interfaces.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright 2017-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 -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# 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 -import re -import sys -import glob -import argparse - -from vyos.ifconfig import Section -from vyos.ifconfig import Interface -from vyos.ifconfig import VRRP -from vyos.util import cmd, call - - -# interfaces = Sections.reserved() -interfaces = ['eno', 'ens', 'enp', 'enx', 'eth', 'vmnet', 'lo', 'tun', 'wan', 'pppoe'] -glob_ifnames = '/sys/class/net/({})*'.format('|'.join(interfaces)) - - -actions = {} -def register(name): - """ - Decorator to register a function into actions with a name. - `actions[name]' can be used to call the registered functions. - We wrap each function in a SIGPIPE handler as all registered functions - can be subject to a broken pipe if there are a lot of interfaces. - """ - def _register(function): - def handled_function(*args, **kwargs): - try: - function(*args, **kwargs) - except BrokenPipeError: - # Flush output to /dev/null and bail out. - os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno()) - sys.exit(1) - actions[name] = handled_function - return handled_function - return _register - - -def filtered_interfaces(ifnames, iftypes, vif, vrrp): - """ - get all the interfaces from the OS and returns them - ifnames can be used to filter which interfaces should be considered - - ifnames: a list of interfaces names to consider, empty do not filter - return an instance of the interface class - """ - if isinstance(iftypes, list): - for iftype in iftypes: - yield from filtered_interfaces(ifnames, iftype, vif, vrrp) - - for ifname in Section.interfaces(iftypes): - # Bail out early if interface name not part of our search list - if ifnames and ifname not in ifnames: - continue - - # As we are only "reading" from the interface - we must use the - # generic base class which exposes all the data via a common API - interface = Interface(ifname, create=False, debug=False) - - # VLAN interfaces have a '.' in their name by convention - if vif and not '.' in ifname: - continue - - if vrrp: - vrrp_interfaces = VRRP.active_interfaces() - if ifname not in vrrp_interfaces: - continue - - yield interface - - -def split_text(text, used=0): - """ - take a string and attempt to split it to fit with the width of the screen - - text: the string to split - used: number of characted already used in the screen - """ - no_tty = call('tty -s') - - returned = cmd('stty size') if not no_tty else '' - if len(returned) == 2: - rows, columns = [int(_) for _ in returned] - else: - rows, columns = (40, 80) - - desc_len = columns - used - - line = '' - for word in text.split(): - if len(line) + len(word) < desc_len: - line = f'{line} {word}' - continue - if line: - yield line[1:] - else: - line = f'{line} {word}' - - yield line[1:] - - -def get_counter_val(clear, now): - """ - attempt to correct a counter if it wrapped, copied from perl - - clear: previous counter - now: the current counter - """ - # This function has to deal with both 32 and 64 bit counters - if clear == 0: - return now - - # device is using 64 bit values assume they never wrap - value = now - clear - if (now >> 32) != 0: - return value - - # The counter has rolled. If the counter has rolled - # multiple times since the clear value, then this math - # is meaningless. - if (value < 0): - value = (4294967296 - clear) + now - - return value - - -@register('help') -def usage(*args): - print(f"Usage: {sys.argv[0]} [intf=NAME|intf-type=TYPE|vif|vrrp] action=ACTION") - print(f" NAME = " + ' | '.join(Section.interfaces())) - print(f" TYPE = " + ' | '.join(Section.sections())) - print(f" ACTION = " + ' | '.join(actions)) - sys.exit(1) - - -@register('allowed') -def run_allowed(**kwarg): - sys.stdout.write(' '.join(Section.interfaces())) - - -def pppoe(ifname): - out = cmd(f'ps -C pppd -f') - if ifname in out: - return 'C' - elif ifname in [_.split('/')[-1] for _ in glob.glob('/etc/ppp/peers/pppoe*')]: - return 'D' - return '' - - -@register('show') -def run_show_intf(ifnames, iftypes, vif, vrrp): - handled = [] - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - handled.append(interface.ifname) - cache = interface.operational.load_counters() - - out = cmd(f'ip addr show {interface.ifname}') - out = re.sub(f'^\d+:\s+','',out) - if re.search('link/tunnel6', out): - tunnel = cmd(f'ip -6 tun show {interface.ifname}') - # tun0: ip/ipv6 remote ::2 local ::1 encaplimit 4 hoplimit 64 tclass inherit flowlabel inherit (flowinfo 0x00000000) - tunnel = re.sub('.*encap', 'encap', tunnel) - out = re.sub('(\n\s+)(link/tunnel6)', f'\g<1>{tunnel}\g<1>\g<2>', out) - - print(out) - - timestamp = int(cache.get('timestamp', 0)) - if timestamp: - when = interface.operational.strtime(timestamp) - print(f' Last clear: {when}') - - description = interface.get_alias() - if description: - print(f' Description: {description}') - - print() - print(interface.operational.formated_stats()) - - for ifname in ifnames: - if ifname not in handled and ifname.startswith('pppoe'): - state = pppoe(ifname) - if not state: - continue - string = { - 'C': 'Coming up', - 'D': 'Link down', - }[state] - print('{}: {}'.format(ifname, string)) - - -@register('show-brief') -def run_show_intf_brief(ifnames, iftypes, vif, vrrp): - format1 = '%-16s %-33s %-4s %s' - format2 = '%-16s %s' - - print('Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down') - print(format1 % ("Interface", "IP Address", "S/L", "Description")) - print(format1 % ("---------", "----------", "---", "-----------")) - - handled = [] - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - handled.append(interface.ifname) - - oper_state = interface.operational.get_state() - admin_state = interface.get_admin_state() - - intf = [interface.ifname,] - - oper = ['u', ] if oper_state in ('up', 'unknown') else ['D', ] - admin = ['u', ] if admin_state in ('up', 'unknown') else ['A', ] - addrs = [_ for _ in interface.get_addr() if not _.startswith('fe80::')] or ['-', ] - descs = list(split_text(interface.get_alias(),0)) - - while intf or oper or admin or addrs or descs: - i = intf.pop(0) if intf else '' - a = addrs.pop(0) if addrs else '' - d = descs.pop(0) if descs else '' - s = [admin.pop(0)] if admin else [] - l = [oper.pop(0)] if oper else [] - if len(a) < 33: - print(format1 % (i, a, '/'.join(s+l), d)) - else: - print(format2 % (i, a)) - print(format1 % ('', '', '/'.join(s+l), d)) - - for ifname in ifnames: - if ifname not in handled and ifname.startswith('pppoe'): - state = pppoe(ifname) - if not state: - continue - string = { - 'C': 'u/D', - 'D': 'A/D', - }[state] - print(format1 % (ifname, '', string, '')) - - -@register('show-count') -def run_show_counters(ifnames, iftypes, vif, vrrp): - formating = '%-12s %10s %10s %10s %10s' - print(formating % ('Interface', 'Rx Packets', 'Rx Bytes', 'Tx Packets', 'Tx Bytes')) - - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - oper = interface.operational.get_state() - - if oper not in ('up','unknown'): - continue - - stats = interface.operational.get_stats() - cache = interface.operational.load_counters() - print(formating % ( - interface.ifname, - get_counter_val(cache['rx_packets'], stats['rx_packets']), - get_counter_val(cache['rx_bytes'], stats['rx_bytes']), - get_counter_val(cache['tx_packets'], stats['tx_packets']), - get_counter_val(cache['tx_bytes'], stats['tx_bytes']), - )) - - -@register('clear') -def run_clear_intf(ifnames, iftypes, vif, vrrp): - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - print(f'Clearing {interface.ifname}') - interface.operational.clear_counters() - - -@register('reset') -def run_reset_intf(ifnames, iftypes, vif, vrrp): - for interface in filtered_interfaces(ifnames, iftypes, vif, vrrp): - interface.operational.reset_counters() - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(add_help=False, description='Show interface information') - parser.add_argument('--intf', action="store", type=str, default='', help='only show the specified interface(s)') - parser.add_argument('--intf-type', action="store", type=str, default='', help='only show the specified interface type') - parser.add_argument('--action', action="store", type=str, default='show', help='action to perform') - parser.add_argument('--vif', action='store_true', default=False, help="only show vif interfaces") - parser.add_argument('--vrrp', action='store_true', default=False, help="only show vrrp interfaces") - parser.add_argument('--help', action='store_true', default=False, help="show help") - - args = parser.parse_args() - - def missing(*args): - print('Invalid action [{args.action}]') - usage() - - actions.get(args.action, missing)( - [_ for _ in args.intf.split(' ') if _], - [_ for _ in args.intf_type.split(' ') if _], - args.vif, - args.vrrp - ) diff --git a/src/op_mode/show_techsupport_report.py b/src/op_mode/show_techsupport_report.py new file mode 100644 index 000000000..782004144 --- /dev/null +++ b/src/op_mode/show_techsupport_report.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from typing import List +from vyos.util import rc_cmd +from vyos.ifconfig import Section +from vyos.ifconfig import Interface + + +def print_header(command: str) -> None: + """Prints a command with headers '-'. + + Example: + + % print_header('Example command') + + --------------- + Example command + --------------- + """ + header_length = len(command) * '-' + print(f"\n{header_length}\n{command}\n{header_length}") + + +def execute_command(command: str, header_text: str) -> None: + """Executes a command and prints the output with a header. + + Example: + % execute_command('uptime', "Uptime of the system") + + -------------------- + Uptime of the system + -------------------- + 20:21:57 up 9:04, 5 users, load average: 0.00, 0.00, 0.0 + + """ + print_header(header_text) + try: + rc, output = rc_cmd(command) + print(output) + except Exception as e: + print(f"Error executing command: {command}") + print(f"Error message: {e}") + + +def op(cmd: str) -> str: + """Returns a command with the VyOS operational mode wrapper.""" + return f'/opt/vyatta/bin/vyatta-op-cmd-wrapper {cmd}' + + +def get_ethernet_interfaces() -> List[Interface]: + """Returns a list of Ethernet interfaces.""" + return Section.interfaces('ethernet') + + +def show_version() -> None: + """Prints the VyOS version and package changes.""" + execute_command(op('show version'), 'VyOS Version and Package Changes') + + +def show_config_file() -> None: + """Prints the contents of a configuration file with a header.""" + execute_command('cat /opt/vyatta/etc/config/config.boot', 'Configuration file') + + +def show_running_config() -> None: + """Prints the running configuration.""" + execute_command(op('show configuration'), 'Running configuration') + + +def show_package_repository_config() -> None: + """Prints the package repository configuration file.""" + execute_command('cat /etc/apt/sources.list', 'Package Repository Configuration File') + execute_command('ls -l /etc/apt/sources.list.d/', 'Repositories') + + +def show_user_startup_scripts() -> None: + """Prints the user startup scripts.""" + execute_command('cat /config/scripts/vyos-postconfig-bootup.script', 'User Startup Scripts') + + +def show_frr_config() -> None: + """Prints the FRR configuration.""" + execute_command('vtysh -c "show run"', 'FRR configuration') + + +def show_interfaces() -> None: + """Prints the interfaces.""" + execute_command(op('show interfaces'), 'Interfaces') + + +def show_interface_statistics() -> None: + """Prints the interface statistics.""" + execute_command('ip -s link show', 'Interface statistics') + + +def show_physical_interface_statistics() -> None: + """Prints the physical interface statistics.""" + execute_command('/usr/bin/true', 'Physical Interface statistics') + for iface in get_ethernet_interfaces(): + # Exclude vlans + if '.' in iface: + continue + execute_command(f'ethtool --driver {iface}', f'ethtool --driver {iface}') + execute_command(f'ethtool --statistics {iface}', f'ethtool --statistics {iface}') + execute_command(f'ethtool --show-ring {iface}', f'ethtool --show-ring {iface}') + execute_command(f'ethtool --show-coalesce {iface}', f'ethtool --show-coalesce {iface}') + execute_command(f'ethtool --pause {iface}', f'ethtool --pause {iface}') + execute_command(f'ethtool --show-features {iface}', f'ethtool --show-features {iface}') + execute_command(f'ethtool --phy-statistics {iface}', f'ethtool --phy-statistics {iface}') + execute_command('netstat --interfaces', 'netstat --interfaces') + execute_command('netstat --listening', 'netstat --listening') + execute_command('cat /proc/net/dev', 'cat /proc/net/dev') + + +def show_bridge() -> None: + """Show bridge interfaces.""" + execute_command(op('show bridge'), 'Show bridge') + + +def show_arp() -> None: + """Prints ARP entries.""" + execute_command(op('show arp'), 'ARP Table (Total entries)') + execute_command(op('show ipv6 neighbors'), 'show ipv6 neighbors') + + +def show_route() -> None: + """Prints routing information.""" + + cmd_list_route = [ + "show ip route bgp | head -108", + "show ip route cache", + "show ip route connected", + "show ip route forward", + "show ip route isis | head -108", + "show ip route kernel", + "show ip route ospf | head -108", + "show ip route rip", + "show ip route static", + "show ip route summary", + "show ip route supernets-only", + "show ip route table all", + "show ip route vrf all", + "show ipv6 route bgp | head 108", + "show ipv6 route cache", + "show ipv6 route connected", + "show ipv6 route forward", + "show ipv6 route isis", + "show ipv6 route kernel", + "show ipv6 route ospf", + "show ipv6 route rip", + "show ipv6 route static", + "show ipv6 route summary", + "show ipv6 route table all", + "show ipv6 route vrf all", + ] + for command in cmd_list_route: + execute_command(op(command), command) + + +def show_firewall() -> None: + """Prints firweall information.""" + execute_command('sudo nft list ruleset', 'nft list ruleset') + + +def show_system() -> None: + """Prints system parameters.""" + execute_command(op('show system image version'), 'Show System Image Version') + execute_command(op('show system image storage'), 'Show System Image Storage') + + +def show_date() -> None: + """Print the current date.""" + execute_command('date', 'Current Time') + + +def show_installed_packages() -> None: + """Prints installed packages.""" + execute_command('dpkg --list', 'Installed Packages') + + +def show_loaded_modules() -> None: + """Prints loaded modules /proc/modules""" + execute_command('cat /proc/modules', 'Loaded Modules') + + +def show_cpu_statistics() -> None: + """Prints CPU statistics.""" + execute_command('/usr/bin/true', 'CPU') + execute_command('lscpu', 'Installed CPU\'s') + execute_command('top --iterations 1 --batch-mode --accum-time-toggle', 'Cumulative CPU Time Used by Running Processes') + execute_command('cat /proc/loadavg', 'Load Average') + + +def show_system_interrupts() -> None: + """Prints system interrupts.""" + execute_command('cat /proc/interrupts', 'Hardware Interrupt Counters') + + +def show_soft_irqs() -> None: + """Prints soft IRQ's.""" + execute_command('cat /proc/softirqs', 'Soft IRQ\'s') + + +def show_softnet_statistics() -> None: + """Prints softnet statistics.""" + execute_command('cat /proc/net/softnet_stat', 'cat /proc/net/softnet_stat') + + +def show_running_processes() -> None: + """Prints current running processes""" + execute_command('ps -ef', 'Running Processes') + + +def show_memory_usage() -> None: + """Prints memory usage""" + execute_command('/usr/bin/true', 'Memory') + execute_command('cat /proc/meminfo', 'Installed Memory') + execute_command('free', 'Memory Usage') + + +def list_disks(): + disks = set() + with open('/proc/partitions') as partitions_file: + for line in partitions_file: + fields = line.strip().split() + if len(fields) == 4 and fields[3].isalpha() and fields[3] != 'name': + disks.add(fields[3]) + return disks + + +def show_storage() -> None: + """Prints storage information.""" + execute_command('cat /proc/devices', 'Devices') + execute_command('cat /proc/partitions', 'Partitions') + + for disk in list_disks(): + execute_command(f'fdisk --list /dev/{disk}', f'Partitioning for disk {disk}') + + +def main(): + # Configuration data + show_version() + show_config_file() + show_running_config() + show_package_repository_config() + show_user_startup_scripts() + show_frr_config() + + # Interfaces + show_interfaces() + show_interface_statistics() + show_physical_interface_statistics() + show_bridge() + show_arp() + + # Routing + show_route() + + # Firewall + show_firewall() + + # System + show_system() + show_date() + show_installed_packages() + show_loaded_modules() + + # CPU + show_cpu_statistics() + show_system_interrupts() + show_soft_irqs() + show_softnet_statistics() + + # Memory + show_memory_usage() + + # Storage + show_storage() + + # Processes + show_running_processes() + + # TODO: Get information from clouds + + +if __name__ == "__main__": + main() diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py index 2392cfe92..b81d1693e 100755 --- a/src/op_mode/vpn_ipsec.py +++ b/src/op_mode/vpn_ipsec.py @@ -16,12 +16,12 @@ import re import argparse -from subprocess import TimeoutExpired from vyos.util import call SWANCTL_CONF = '/etc/swanctl/swanctl.conf' + def get_peer_connections(peer, tunnel, return_all = False): search = rf'^[\s]*(peer_{peer}_(tunnel_[\d]+|vti)).*' matches = [] @@ -34,57 +34,6 @@ def get_peer_connections(peer, tunnel, return_all = False): matches.append(result[1]) return matches -def reset_peer(peer, tunnel): - if not peer: - print('Invalid peer, aborting') - return - - conns = get_peer_connections(peer, tunnel, return_all = (not tunnel or tunnel == 'all')) - - if not conns: - print('Tunnel(s) not found, aborting') - return - - result = True - for conn in conns: - try: - call(f'/usr/sbin/ipsec down {conn}{{*}}', timeout = 10) - call(f'/usr/sbin/ipsec up {conn}', timeout = 10) - except TimeoutExpired as e: - print(f'Timed out while resetting {conn}') - result = False - - - print('Peer reset result: ' + ('success' if result else 'failed')) - -def get_profile_connection(profile, tunnel = None): - search = rf'(dmvpn-{profile}-[\w]+)' if tunnel == 'all' else rf'(dmvpn-{profile}-{tunnel})' - with open(SWANCTL_CONF, 'r') as f: - for line in f.readlines(): - result = re.search(search, line) - if result: - return result[1] - return None - -def reset_profile(profile, tunnel): - if not profile: - print('Invalid profile, aborting') - return - - if not tunnel: - print('Invalid tunnel, aborting') - return - - conn = get_profile_connection(profile) - - if not conn: - print('Profile not found, aborting') - return - - call(f'/usr/sbin/ipsec down {conn}') - result = call(f'/usr/sbin/ipsec up {conn}') - - print('Profile reset result: ' + ('success' if result == 0 else 'failed')) def debug_peer(peer, tunnel): peer = peer.replace(':', '-') @@ -119,6 +68,7 @@ def debug_peer(peer, tunnel): for conn in conns: call(f'/usr/sbin/ipsec statusall | grep {conn}') + if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--action', help='Control action', required=True) @@ -127,9 +77,6 @@ if __name__ == '__main__': args = parser.parse_args() - if args.action == 'reset-peer': - reset_peer(args.name, args.tunnel) - elif args.action == "reset-profile": - reset_profile(args.name, args.tunnel) - elif args.action == "vpn-debug": + + if args.action == "vpn-debug": debug_peer(args.name, args.tunnel) |