summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/reviewers.yml7
-rw-r--r--data/op-mode-standardized.json3
-rw-r--r--data/templates/accel-ppp/chap-secrets.ipoe.j225
-rw-r--r--data/templates/accel-ppp/config_ipv6_pool.j22
-rw-r--r--data/templates/accel-ppp/ipoe.config.j2142
-rw-r--r--data/templates/accel-ppp/pppoe.config.j217
-rw-r--r--data/templates/accel-ppp/pptp.config.j29
-rw-r--r--data/templates/conntrackd/conntrackd.conf.j24
-rw-r--r--data/templates/conserver/conserver.conf.j23
-rw-r--r--data/templates/dhcp-client/ipv6.j218
-rw-r--r--data/templates/firewall/nftables-defines.j25
-rw-r--r--data/templates/firewall/nftables-geoip-update.j28
-rw-r--r--data/templates/firewall/nftables-nat.j2181
-rw-r--r--data/templates/firewall/nftables-nat66.j2158
-rw-r--r--data/templates/firewall/nftables-static-nat.j2122
-rw-r--r--data/templates/firewall/nftables-zone.j278
-rw-r--r--data/templates/firewall/nftables.j2250
-rw-r--r--data/templates/frr/bgpd.frr.j224
-rw-r--r--data/templates/frr/isisd.frr.j230
-rw-r--r--data/templates/frr/ospfd.frr.j227
-rw-r--r--data/templates/frr/policy.frr.j237
-rw-r--r--data/templates/high-availability/keepalived.conf.j24
-rw-r--r--data/templates/ids/fastnetmon.j264
-rw-r--r--data/templates/ipsec/ios_profile.j22
-rw-r--r--data/templates/ipsec/swanctl.conf.j212
-rw-r--r--data/templates/ipsec/swanctl/peer.j224
-rw-r--r--data/templates/ipsec/swanctl/remote_access.j24
-rw-r--r--data/templates/login/autologout.j25
-rw-r--r--data/templates/login/pam_radius_auth.conf.j23
-rw-r--r--data/templates/nhrp/nftables.conf.j217
-rw-r--r--data/templates/ocserv/ocserv_config.j220
-rw-r--r--data/templates/ssh/sshd_config.j24
-rw-r--r--data/templates/telegraf/override.conf.j23
-rw-r--r--data/templates/telegraf/telegraf.j24
-rw-r--r--data/templates/zone_policy/nftables.j2113
-rw-r--r--data/vyos-firewall-init.conf110
-rw-r--r--debian/control1
-rw-r--r--debian/vyos-1x.preinst1
-rw-r--r--interface-definitions/firewall.xml.in226
-rw-r--r--interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i26
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i12
-rw-r--r--interface-definitions/include/accel-ppp/vlan.xml.i20
-rw-r--r--interface-definitions/include/bgp/afi-l2vpn-common.xml.i3
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i13
-rw-r--r--interface-definitions/include/firewall/action.xml.i12
-rw-r--r--interface-definitions/include/firewall/common-rule.xml.i16
-rw-r--r--interface-definitions/include/firewall/default-action.xml.i12
-rw-r--r--interface-definitions/include/firewall/dscp.xml.i36
-rw-r--r--interface-definitions/include/firewall/hop-limit.xml.i12
-rw-r--r--interface-definitions/include/firewall/name.xml.i18
-rw-r--r--interface-definitions/include/firewall/packet-length.xml.i6
-rw-r--r--interface-definitions/include/firewall/tcp-flags.xml.i3
-rw-r--r--interface-definitions/include/firewall/ttl.xml.i12
-rw-r--r--interface-definitions/include/ids/threshold.xml.i38
-rw-r--r--interface-definitions/include/interface/adjust-mss.xml.i4
-rw-r--r--interface-definitions/include/interface/interface-firewall-vif-c.xml.i79
-rw-r--r--interface-definitions/include/interface/interface-firewall-vif.xml.i79
-rw-r--r--interface-definitions/include/interface/interface-firewall.xml.i79
-rw-r--r--interface-definitions/include/interface/vif-s.xml.i2
-rw-r--r--interface-definitions/include/interface/vif.xml.i1
-rw-r--r--interface-definitions/include/ipsec/authentication-id.xml.i6
-rw-r--r--interface-definitions/include/ipsec/remote-address.xml.i30
-rw-r--r--interface-definitions/include/isis/high-low-label-value.xml.i2
-rw-r--r--interface-definitions/include/isis/protocol-common-config.xml.i6
-rw-r--r--interface-definitions/include/listen-address-single.xml.i22
-rw-r--r--interface-definitions/include/ospf/protocol-common-config.xml.i80
-rw-r--r--interface-definitions/include/policy/community-clear.xml.i8
-rw-r--r--interface-definitions/include/policy/community-value-list.xml.i90
-rw-r--r--interface-definitions/include/policy/extended-community-value-list.xml.i15
-rw-r--r--interface-definitions/include/policy/large-community-value-list.xml.i10
-rw-r--r--interface-definitions/include/policy/route-common-rule-ipv6.xml.i4
-rw-r--r--interface-definitions/include/policy/route-common-rule.xml.i4
-rw-r--r--interface-definitions/include/qos/limiter-actions.xml.i66
-rw-r--r--interface-definitions/include/url.xml.i15
-rw-r--r--interface-definitions/include/version/firewall-version.xml.i2
-rw-r--r--interface-definitions/include/version/ids-version.xml.i3
-rw-r--r--interface-definitions/include/version/ipsec-version.xml.i2
-rw-r--r--interface-definitions/include/version/isis-version.xml.i2
-rw-r--r--interface-definitions/include/version/policy-version.xml.i2
-rw-r--r--interface-definitions/include/version/pppoe-server-version.xml.i2
-rw-r--r--interface-definitions/interfaces-bonding.xml.in1
-rw-r--r--interface-definitions/interfaces-bridge.xml.in1
-rw-r--r--interface-definitions/interfaces-dummy.xml.in1
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in7
-rw-r--r--interface-definitions/interfaces-geneve.xml.in1
-rw-r--r--interface-definitions/interfaces-input.xml.in1
-rw-r--r--interface-definitions/interfaces-l2tpv3.xml.in1
-rw-r--r--interface-definitions/interfaces-macsec.xml.in1
-rw-r--r--interface-definitions/interfaces-openvpn.xml.in1
-rw-r--r--interface-definitions/interfaces-pppoe.xml.in1
-rw-r--r--interface-definitions/interfaces-pseudo-ethernet.xml.in1
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in1
-rw-r--r--interface-definitions/interfaces-vti.xml.in1
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in1
-rw-r--r--interface-definitions/interfaces-wireguard.xml.in1
-rw-r--r--interface-definitions/interfaces-wireless.xml.in1
-rw-r--r--interface-definitions/interfaces-wwan.xml.in1
-rw-r--r--interface-definitions/policy-local-route.xml.in2
-rw-r--r--interface-definitions/policy-route.xml.in6
-rw-r--r--interface-definitions/policy.xml.in224
-rw-r--r--interface-definitions/qos.xml.in2
-rw-r--r--interface-definitions/service-console-server.xml.in8
-rw-r--r--interface-definitions/service-ids-ddos-protection.xml.in56
-rw-r--r--interface-definitions/service-ipoe-server.xml.in78
-rw-r--r--interface-definitions/service-monitoring-telegraf.xml.in2
-rw-r--r--interface-definitions/service-pppoe-server.xml.in28
-rw-r--r--interface-definitions/ssh.xml.in31
-rw-r--r--interface-definitions/system-conntrack.xml.in8
-rw-r--r--interface-definitions/system-login.xml.in13
-rw-r--r--interface-definitions/system-option.xml.in14
-rw-r--r--interface-definitions/system-update-check.xml.in22
-rw-r--r--interface-definitions/vpn-ipsec.xml.in109
-rw-r--r--interface-definitions/vpn-l2tp.xml.in24
-rw-r--r--interface-definitions/vpn-openconnect.xml.in39
-rw-r--r--interface-definitions/vpn-pptp.xml.in1
-rw-r--r--interface-definitions/xml-component-version.xml.in1
-rw-r--r--interface-definitions/zone-policy.xml.in148
-rw-r--r--op-mode-definitions/clear-session.xml.in16
-rw-r--r--op-mode-definitions/connect.xml.in1
-rw-r--r--op-mode-definitions/include/bgp/afi-ipv4-ipv6-common.xml.i2
-rw-r--r--op-mode-definitions/monitor-log.xml.in2
-rw-r--r--op-mode-definitions/show-console-server.xml.in2
-rw-r--r--op-mode-definitions/show-interfaces-pppoe.xml.in2
-rw-r--r--op-mode-definitions/show-log.xml.in2
-rw-r--r--op-mode-definitions/show-system.xml.in12
-rw-r--r--op-mode-definitions/vpn-ipsec.xml.in22
-rw-r--r--python/vyos/configdict.py41
-rw-r--r--python/vyos/configverify.py6
-rw-r--r--python/vyos/firewall.py35
-rw-r--r--python/vyos/ifconfig/ethernet.py29
-rw-r--r--python/vyos/ifconfig/wireguard.py103
-rw-r--r--python/vyos/nat.py188
-rw-r--r--python/vyos/template.py39
-rw-r--r--python/vyos/util.py8
-rw-r--r--python/vyos/version.py39
-rw-r--r--smoketest/configs/ipoe-server119
-rw-r--r--smoketest/configs/pppoe-server6
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py28
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py151
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_ethernet.py42
-rwxr-xr-xsmoketest/scripts/cli/test_load_balancning_wan.py1
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py141
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py67
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py198
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py82
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py2
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py46
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_nhrp.py104
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py62
-rwxr-xr-xsmoketest/scripts/cli/test_service_ids.py6
-rwxr-xr-xsmoketest/scripts/cli/test_service_ipoe-server.py91
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_telegraf.py1
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py52
-rwxr-xr-xsmoketest/scripts/cli/test_system_conntrack.py2
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py50
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_sstp.py54
-rwxr-xr-xsmoketest/scripts/cli/test_zone_policy.py69
-rwxr-xr-xsrc/completion/list_consoles.sh4
-rwxr-xr-xsrc/conf_mode/firewall-interface.py186
-rwxr-xr-xsrc/conf_mode/firewall.py292
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py3
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py32
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py2
-rwxr-xr-xsrc/conf_mode/nat.py29
-rwxr-xr-xsrc/conf_mode/nat66.py11
-rwxr-xr-xsrc/conf_mode/policy-route.py4
-rwxr-xr-xsrc/conf_mode/policy.py132
-rwxr-xr-xsrc/conf_mode/protocols_isis.py22
-rwxr-xr-xsrc/conf_mode/protocols_nhrp.py32
-rwxr-xr-xsrc/conf_mode/protocols_ospf.py52
-rwxr-xr-xsrc/conf_mode/service_console-server.py7
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py289
-rwxr-xr-xsrc/conf_mode/service_monitoring_telegraf.py2
-rwxr-xr-xsrc/conf_mode/service_pppoe-server.py30
-rwxr-xr-xsrc/conf_mode/ssh.py3
-rwxr-xr-xsrc/conf_mode/system-login.py8
-rwxr-xr-xsrc/conf_mode/system_update_check.py93
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py17
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py22
-rwxr-xr-xsrc/conf_mode/zone_policy.py213
-rw-r--r--src/etc/sysctl.d/30-vyos-router.conf4
-rwxr-xr-xsrc/etc/telegraf/custom_scripts/show_firewall_input_filter.py6
-rwxr-xr-xsrc/migration-scripts/firewall/7-to-898
-rwxr-xr-xsrc/migration-scripts/ids/0-to-156
-rwxr-xr-xsrc/migration-scripts/ipoe-server/0-to-1133
-rwxr-xr-xsrc/migration-scripts/ipsec/9-to-10134
-rwxr-xr-xsrc/migration-scripts/isis/1-to-246
-rwxr-xr-xsrc/migration-scripts/policy/3-to-4162
-rwxr-xr-xsrc/migration-scripts/pppoe-server/5-to-652
-rwxr-xr-xsrc/op_mode/bridge.py8
-rwxr-xr-xsrc/op_mode/conntrack.py11
-rwxr-xr-xsrc/op_mode/firewall.py52
-rwxr-xr-xsrc/op_mode/ikev2_profile_generator.py8
-rwxr-xr-xsrc/op_mode/nat.py13
-rwxr-xr-x[-rw-r--r--]src/op_mode/route.py0
-rwxr-xr-xsrc/op_mode/show_nat66_statistics.py2
-rwxr-xr-xsrc/op_mode/show_nat_statistics.py2
-rwxr-xr-xsrc/op_mode/storage.py60
-rwxr-xr-xsrc/op_mode/system.py92
-rwxr-xr-xsrc/op_mode/uptime.py (renamed from src/op_mode/show_uptime.py)36
-rwxr-xr-xsrc/op_mode/vpn_ike_sa.py2
-rw-r--r--src/services/api/graphql/graphql/directives.py64
-rw-r--r--src/services/api/graphql/graphql/mutations.py26
-rw-r--r--src/services/api/graphql/graphql/queries.py23
-rw-r--r--src/services/api/graphql/graphql/schema/config_file.graphql29
-rw-r--r--src/services/api/graphql/graphql/schema/configsession.graphql115
-rw-r--r--src/services/api/graphql/graphql/schema/dhcp_server.graphql36
-rw-r--r--src/services/api/graphql/graphql/schema/firewall_group.graphql101
-rw-r--r--src/services/api/graphql/graphql/schema/image.graphql31
-rw-r--r--src/services/api/graphql/graphql/schema/interface_ethernet.graphql19
-rw-r--r--src/services/api/graphql/graphql/schema/schema.graphql24
-rw-r--r--src/services/api/graphql/graphql/schema/show.graphql15
-rw-r--r--src/services/api/graphql/graphql/schema/show_config.graphql21
-rwxr-xr-xsrc/services/api/graphql/session/composite/system_status.py4
-rw-r--r--src/services/api/graphql/session/session.py44
-rw-r--r--src/services/api/graphql/utils/config_session_function.py28
-rwxr-xr-xsrc/services/api/graphql/utils/schema_from_config_session.py119
-rwxr-xr-xsrc/services/api/graphql/utils/schema_from_op_mode.py30
-rw-r--r--src/services/api/graphql/utils/util.py24
-rwxr-xr-xsrc/system/vyos-system-update-check.py70
-rw-r--r--src/systemd/vyos-system-update.service11
-rwxr-xr-xsrc/validators/accel-radius-dictionary13
-rwxr-xr-xsrc/validators/bgp-extended-community55
-rwxr-xr-xsrc/validators/bgp-large-community53
-rwxr-xr-xsrc/validators/bgp-regular-community50
-rwxr-xr-xsrc/validators/range56
226 files changed, 5131 insertions, 3775 deletions
diff --git a/.github/reviewers.yml b/.github/reviewers.yml
index c99647bb1..8463681fc 100644
--- a/.github/reviewers.yml
+++ b/.github/reviewers.yml
@@ -1,3 +1,8 @@
---
"**/*":
- - vyos/maintainers
+ - dmbaturin
+ - UnicronNL
+ - zdc
+ - jestabro
+ - sever-sever
+ - c-po
diff --git a/data/op-mode-standardized.json b/data/op-mode-standardized.json
index db13eeb5a..9500d3aa7 100644
--- a/data/op-mode-standardized.json
+++ b/data/op-mode-standardized.json
@@ -8,7 +8,10 @@
"neighbor.py",
"openconnect.py",
"route.py",
+"system.py",
"ipsec.py",
+"storage.py",
+"uptime.py",
"version.py",
"vrf.py"
]
diff --git a/data/templates/accel-ppp/chap-secrets.ipoe.j2 b/data/templates/accel-ppp/chap-secrets.ipoe.j2
index a1430ec22..43083e22e 100644
--- a/data/templates/accel-ppp/chap-secrets.ipoe.j2
+++ b/data/templates/accel-ppp/chap-secrets.ipoe.j2
@@ -1,18 +1,13 @@
# username server password acceptable local IP addresses shaper
-{% for interface in auth_interfaces %}
-{% for mac in interface.mac %}
-{% if mac.rate_upload and mac.rate_download %}
-{% if mac.vlan_id %}
-{{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} * {{ mac.rate_download }}/{{ mac.rate_upload }}
-{% else %}
-{{ interface.name }} * {{ mac.address | lower }} * {{ mac.rate_download }}/{{ mac.rate_upload }}
-{% endif %}
-{% else %}
-{% if mac.vlan_id %}
-{{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} *
-{% else %}
-{{ interface.name }} * {{ mac.address | lower }} *
-{% endif %}
+{% if authentication.interface is vyos_defined %}
+{% for iface, iface_config in authentication.interface.items() %}
+{% if iface_config.mac is vyos_defined %}
+{% for mac, mac_config in iface_config.mac.items() %}
+{% if mac_config.vlan is vyos_defined %}
+{% set iface = iface ~ '.' ~ mac_config.vlan %}
+{% endif %}
+{{ "%-11s" | format(iface) }} * {{ mac | lower }} * {{ mac_config.rate_limit.download ~ '/' ~ mac_config.rate_limit.upload if mac_config.rate_limit.download is vyos_defined and mac_config.rate_limit.upload is vyos_defined }}
+{% endfor %}
{% endif %}
{% endfor %}
-{% endfor %}
+{% endif %}
diff --git a/data/templates/accel-ppp/config_ipv6_pool.j2 b/data/templates/accel-ppp/config_ipv6_pool.j2
index 953469577..a1562a1eb 100644
--- a/data/templates/accel-ppp/config_ipv6_pool.j2
+++ b/data/templates/accel-ppp/config_ipv6_pool.j2
@@ -1,6 +1,7 @@
{% if client_ipv6_pool is vyos_defined %}
[ipv6-nd]
AdvAutonomousFlag=1
+verbose=1
{% if client_ipv6_pool.prefix is vyos_defined %}
[ipv6-pool]
@@ -13,6 +14,7 @@ delegate={{ prefix }},{{ options.delegation_prefix }}
{% endfor %}
{% endif %}
{% endif %}
+
{% if client_ipv6_pool.delegate is vyos_defined %}
[ipv6-dhcp]
verbose=1
diff --git a/data/templates/accel-ppp/ipoe.config.j2 b/data/templates/accel-ppp/ipoe.config.j2
index 6df12db2c..99227ea33 100644
--- a/data/templates/accel-ppp/ipoe.config.j2
+++ b/data/templates/accel-ppp/ipoe.config.j2
@@ -4,18 +4,15 @@
log_syslog
ipoe
shaper
+{# Common authentication backend definitions #}
+{% include 'accel-ppp/config_modules_auth_mode.j2' %}
ipv6pool
ipv6_nd
ipv6_dhcp
ippool
-{% if auth_mode == 'radius' %}
-radius
-{% elif auth_mode == 'local' %}
-chap-secrets
-{% endif %}
[core]
-thread-count={{ thread_cnt }}
+thread-count={{ thread_count }}
[log]
syslog=accel-ipoe,daemon
@@ -24,28 +21,34 @@ level=5
[ipoe]
verbose=1
-{% for interface in interfaces %}
-{% set tmp = 'interface=' %}
-{% if interface.vlan_mon %}
-{% set tmp = tmp ~ 're:' ~ interface.name ~ '\.\d+' %}
-{% else %}
-{% set tmp = tmp ~ interface.name %}
-{% endif %}
-{{ tmp }},shared={{ interface.shared }},mode={{ interface.mode }},ifcfg={{ interface.ifcfg }}{{ ',range=' ~ interface.range if interface.range is defined and interface.range is not none }},start={{ interface.sess_start }},ipv6=1
-{% endfor %}
-{% if auth_mode == 'noauth' %}
+{% if interface is vyos_defined %}
+{% for iface, iface_config in interface.items() %}
+{% set tmp = 'interface=' %}
+{% if iface_config.vlan is vyos_defined %}
+{% set tmp = tmp ~ 're:' ~ iface ~ '\.\d+' %}
+{% else %}
+{% set tmp = tmp ~ iface %}
+{% endif %}
+{% set shared = '' %}
+{% if iface_config.network is vyos_defined('shared') %}
+{% set shared = 'shared=1,' %}
+{% elif iface_config.network is vyos_defined('vlan') %}
+{% set shared = 'shared=0,' %}
+{% endif %}
+{{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,range={{ iface_config.client_subnet }},start=dhcpv4,ipv6=1
+{% endfor %}
+{% endif %}
+{% if authentication.mode is vyos_defined('noauth') %}
noauth=1
-{% if client_named_ip_pool %}
-{% for pool in client_named_ip_pool %}
-{% if pool.subnet is defined %}
-ip-pool={{ pool.name }}
-{% endif %}
-{% if pool.gateway_address is defined %}
-gw-ip-address={{ pool.gateway_address }}/{{ pool.subnet.split('/')[1] }}
+{% if client_ip_pool.name is vyos_defined %}
+{% for pool, pool_options in client_ip_pool.name.items() %}
+{% if pool_options.subnet is vyos_defined and pool_options.gateway_address is vyos_defined %}
+ip-pool={{ pool }}
+gw-ip-address={{ pool_options.gateway_address }}/{{ pool_options.subnet.split('/')[1] }}
{% endif %}
{% endfor %}
{% endif %}
-{% elif auth_mode == 'local' %}
+{% elif authentication.mode is vyos_defined('local') %}
username=ifname
password=csid
{% endif %}
@@ -57,92 +60,27 @@ vlan-mon={{ interface.name }},{{ interface.vlan_mon | join(',') }}
{% endif %}
{% endfor %}
-{% if dnsv4 %}
-[dns]
-{% for dns in dnsv4 %}
-dns{{ loop.index }}={{ dns }}
-{% endfor %}
-{% endif %}
-
-{% if dnsv6 %}
-[ipv6-dns]
-{% for dns in dnsv6 %}
-{{ dns }}
-{% endfor %}
-{% endif %}
-
-[ipv6-nd]
-verbose=1
-
-[ipv6-dhcp]
-verbose=1
-
-{% if client_named_ip_pool %}
+{% if client_ip_pool.name is vyos_defined %}
[ip-pool]
-{% for pool in client_named_ip_pool %}
-{% if pool.subnet is defined %}
-{{ pool.subnet }},name={{ pool.name }}
-{% endif %}
-{% if pool.gateway_address is defined %}
-gw-ip-address={{ pool.gateway_address }}/{{ pool.subnet.split('/')[1] }}
+{% for pool, pool_options in client_ip_pool.name.items() %}
+{% if pool_options.subnet is vyos_defined and pool_options.gateway_address is vyos_defined %}
+{{ pool_options.subnet }},name={{ pool }}
+gw-ip-address={{ pool_options.gateway_address }}/{{ pool_options.subnet.split('/')[1] }}
{% endif %}
{% endfor %}
{% endif %}
-{% if client_ipv6_pool %}
-[ipv6-pool]
-{% for p in client_ipv6_pool %}
-{{ p.prefix }},{{ p.mask }}
-{% endfor %}
-{% for p in client_ipv6_delegate_prefix %}
-delegate={{ p.prefix }},{{ p.mask }}
-{% endfor %}
-{% endif %}
+{# Common IPv6 pool definitions #}
+{% include 'accel-ppp/config_ipv6_pool.j2' %}
-{% if auth_mode == 'local' %}
-[chap-secrets]
-chap-secrets={{ chap_secrets_file }}
-{% elif auth_mode == 'radius' %}
-[radius]
-verbose=1
-{% for r in radius_server %}
-server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
-{% endfor %}
-
-{% if radius_acct_inter_jitter %}
-acct-interim-jitter={{ radius_acct_inter_jitter }}
-{% endif %}
+{# Common DNS name-server definition #}
+{% include 'accel-ppp/config_name_server.j2' %}
-acct-timeout={{ radius_acct_tmo }}
-timeout={{ radius_timeout }}
-max-try={{ radius_max_try }}
-{% if radius_nas_id %}
-nas-identifier={{ radius_nas_id }}
-{% endif %}
-{% if radius_nas_ip %}
-nas-ip-address={{ radius_nas_ip }}
-{% endif %}
-{% if radius_source_address %}
-bind={{ radius_source_address }}
-{% endif %}
-{% if radius_dynamic_author %}
-dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
-{% endif %}
+{# Common chap-secrets and RADIUS server/option definitions #}
+{% include 'accel-ppp/config_chap_secrets_radius.j2' %}
-{% 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 %}
-{% endif %}
-{% endif %}
+{# Common RADIUS shaper configuration #}
+{% include 'accel-ppp/config_shaper_radius.j2' %}
[cli]
tcp=127.0.0.1:2002
diff --git a/data/templates/accel-ppp/pppoe.config.j2 b/data/templates/accel-ppp/pppoe.config.j2
index 0a92e2d54..f4129d3e2 100644
--- a/data/templates/accel-ppp/pppoe.config.j2
+++ b/data/templates/accel-ppp/pppoe.config.j2
@@ -105,20 +105,13 @@ ac-name={{ access_concentrator }}
{% if interface is vyos_defined %}
{% for iface, iface_config in interface.items() %}
-{% if iface_config.vlan_id is not vyos_defined and iface_config.vlan_range is not vyos_defined %}
+{% if iface_config.vlan is not vyos_defined %}
interface={{ iface }}
-{% endif %}
-{% if iface_config.vlan_range is vyos_defined %}
-{% for regex in iface_config.regex %}
-interface=re:^{{ iface | replace('.', '\\.') }}\.({{ regex }})$
-{% endfor %}
-vlan-mon={{ iface }},{{ iface_config.vlan_range | join(',') }}
-{% endif %}
-{% if iface_config.vlan_id is vyos_defined %}
-{% for vlan in iface_config.vlan_id %}
-vlan-mon={{ iface }},{{ vlan }}
-interface=re:^{{ iface | replace('.', '\\.') }}\.{{ vlan }}$
+{% else %}
+{% for vlan in iface_config.vlan %}
+interface=re:^{{ iface }}\.{{ vlan | range_to_regex }}$
{% endfor %}
+vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }}
{% endif %}
{% endfor %}
{% endif %}
diff --git a/data/templates/accel-ppp/pptp.config.j2 b/data/templates/accel-ppp/pptp.config.j2
index cc1a45d6b..442830b6b 100644
--- a/data/templates/accel-ppp/pptp.config.j2
+++ b/data/templates/accel-ppp/pptp.config.j2
@@ -93,6 +93,15 @@ bind={{ radius_source_address }}
gw-ip-address={{ gw_ip }}
{% endif %}
+{% if radius_shaper_attr %}
+[shaper]
+verbose=1
+attr={{ radius_shaper_attr }}
+{% if radius_shaper_vendor %}
+vendor={{ radius_shaper_vendor }}
+{% endif %}
+{% endif %}
+
[cli]
tcp=127.0.0.1:2003
diff --git a/data/templates/conntrackd/conntrackd.conf.j2 b/data/templates/conntrackd/conntrackd.conf.j2
index 66024869d..808a77759 100644
--- a/data/templates/conntrackd/conntrackd.conf.j2
+++ b/data/templates/conntrackd/conntrackd.conf.j2
@@ -9,7 +9,9 @@ Sync {
{% if iface_config.peer is vyos_defined %}
UDP {
{% if listen_address is vyos_defined %}
- IPv4_address {{ listen_address }}
+{% for address in listen_address %}
+ IPv4_address {{ address }}
+{% endfor %}
{% endif %}
IPv4_Destination_Address {{ iface_config.peer }}
Port {{ iface_config.port if iface_config.port is vyos_defined else '3780' }}
diff --git a/data/templates/conserver/conserver.conf.j2 b/data/templates/conserver/conserver.conf.j2
index 1823657d7..ffd29389d 100644
--- a/data/templates/conserver/conserver.conf.j2
+++ b/data/templates/conserver/conserver.conf.j2
@@ -25,6 +25,9 @@ console {{ key }} {
baud {{ value.speed }};
parity {{ value.parity }};
options {{ "!" if value.stop_bits == "1" }}cstopb;
+{% if value.alias is vyos_defined %}
+ aliases "{{ value.alias }}";
+{% endif %}
}
{% endfor %}
diff --git a/data/templates/dhcp-client/ipv6.j2 b/data/templates/dhcp-client/ipv6.j2
index e136b1789..b5e55cdd1 100644
--- a/data/templates/dhcp-client/ipv6.j2
+++ b/data/templates/dhcp-client/ipv6.j2
@@ -40,20 +40,22 @@ id-assoc pd {{ pd }} {
prefix ::/{{ pd_config.length }} infinity;
{% set sla_len = 64 - pd_config.length | int %}
{% set count = namespace(value=0) %}
-{% for interface, interface_config in pd_config.interface.items() if pd_config.interface is vyos_defined %}
+{% if pd_config.interface is vyos_defined %}
+{% for interface, interface_config in pd_config.interface.items() if pd_config.interface is vyos_defined %}
prefix-interface {{ interface }} {
sla-len {{ sla_len }};
-{% if interface_config.sla_id is vyos_defined %}
+{% if interface_config.sla_id is vyos_defined %}
sla-id {{ interface_config.sla_id }};
-{% else %}
+{% else %}
sla-id {{ count.value }};
-{% endif %}
-{% if interface_config.address is vyos_defined %}
+{% endif %}
+{% if interface_config.address is vyos_defined %}
ifid {{ interface_config.address }};
-{% endif %}
+{% endif %}
};
-{% set count.value = count.value + 1 %}
-{% endfor %}
+{% set count.value = count.value + 1 %}
+{% endfor %}
+{% endif %}
};
{% endfor %}
{% endif %}
diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2
index 97fc123d5..5336f7ee6 100644
--- a/data/templates/firewall/nftables-defines.j2
+++ b/data/templates/firewall/nftables-defines.j2
@@ -7,6 +7,7 @@
set A_{{ group_name }} {
type {{ ip_type }}
flags interval
+ auto-merge
{% if group_conf.address is vyos_defined or includes %}
elements = { {{ group_conf.address | nft_nested_group(includes, group.address_group, 'address') | join(",") }} }
{% endif %}
@@ -19,6 +20,7 @@
set A6_{{ group_name }} {
type {{ ip_type }}
flags interval
+ auto-merge
{% if group_conf.address is vyos_defined or includes %}
elements = { {{ group_conf.address | nft_nested_group(includes, group.ipv6_address_group, 'address') | join(",") }} }
{% endif %}
@@ -42,6 +44,7 @@
set N_{{ group_name }} {
type {{ ip_type }}
flags interval
+ auto-merge
{% if group_conf.network is vyos_defined or includes %}
elements = { {{ group_conf.network | nft_nested_group(includes, group.network_group, 'network') | join(",") }} }
{% endif %}
@@ -54,6 +57,7 @@
set N6_{{ group_name }} {
type {{ ip_type }}
flags interval
+ auto-merge
{% if group_conf.network is vyos_defined or includes %}
elements = { {{ group_conf.network | nft_nested_group(includes, group.ipv6_network_group, 'network') | join(",") }} }
{% endif %}
@@ -66,6 +70,7 @@
set P_{{ group_name }} {
type inet_service
flags interval
+ auto-merge
{% if group_conf.port is vyos_defined or includes %}
elements = { {{ group_conf.port | nft_nested_group(includes, group.port_group, 'port') | join(",") }} }
{% endif %}
diff --git a/data/templates/firewall/nftables-geoip-update.j2 b/data/templates/firewall/nftables-geoip-update.j2
index f9e61a274..832ccc3e9 100644
--- a/data/templates/firewall/nftables-geoip-update.j2
+++ b/data/templates/firewall/nftables-geoip-update.j2
@@ -2,10 +2,10 @@
{% if ipv4_sets is vyos_defined %}
{% for setname, ip_list in ipv4_sets.items() %}
-flush set ip filter {{ setname }}
+flush set ip vyos_filter {{ setname }}
{% endfor %}
-table ip filter {
+table ip vyos_filter {
{% for setname, ip_list in ipv4_sets.items() %}
set {{ setname }} {
type ipv4_addr
@@ -18,10 +18,10 @@ table ip filter {
{% if ipv6_sets is vyos_defined %}
{% for setname, ip_list in ipv6_sets.items() %}
-flush set ip6 filter {{ setname }}
+flush set ip6 vyos_filter {{ setname }}
{% endfor %}
-table ip6 filter {
+table ip6 vyos_filter {
{% for setname, ip_list in ipv6_sets.items() %}
set {{ setname }} {
type ipv6_addr
diff --git a/data/templates/firewall/nftables-nat.j2 b/data/templates/firewall/nftables-nat.j2
index 1481e9104..55fe6024b 100644
--- a/data/templates/firewall/nftables-nat.j2
+++ b/data/templates/firewall/nftables-nat.j2
@@ -1,146 +1,5 @@
#!/usr/sbin/nft -f
-{% macro nat_rule(rule, config, chain) %}
-{% set comment = '' %}
-{% set base_log = '' %}
-{% set src_addr = 'ip saddr ' ~ config.source.address.replace('!','!= ') if config.source.address is vyos_defined %}
-{% set dst_addr = 'ip daddr ' ~ config.destination.address.replace('!','!= ') if config.destination.address is vyos_defined %}
-{# negated port groups need special treatment, move != in front of { } group #}
-{% if config.source.port is vyos_defined and config.source.port.startswith('!') %}
-{% set src_port = 'sport != { ' ~ config.source.port.replace('!','') ~ ' }' %}
-{% else %}
-{% set src_port = 'sport { ' ~ config.source.port ~ ' }' if config.source.port is vyos_defined %}
-{% endif %}
-{# negated port groups need special treatment, move != in front of { } group #}
-{% if config.destination.port is vyos_defined and config.destination.port.startswith('!') %}
-{% set dst_port = 'dport != { ' ~ config.destination.port.replace('!','') ~ ' }' %}
-{% else %}
-{% set dst_port = 'dport { ' ~ config.destination.port ~ ' }' if config.destination.port is vyos_defined %}
-{% endif %}
-{% if chain is vyos_defined('PREROUTING') %}
-{% set comment = 'DST-NAT-' ~ rule %}
-{% set base_log = '[NAT-DST-' ~ rule %}
-{% set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
-{% if config.translation.address is vyos_defined %}
-{# support 1:1 network translation #}
-{% if config.translation.address | is_ip_network %}
-{% set trns_addr = 'dnat ip prefix to ip daddr map { ' ~ config.destination.address ~ ' : ' ~ config.translation.address ~ ' }' %}
-{# we can now clear out the dst_addr part as it's already covered in aboves map #}
-{% set dst_addr = '' %}
-{% else %}
-{% set trns_addr = 'dnat to ' ~ config.translation.address %}
-{% endif %}
-{% endif %}
-{% elif chain is vyos_defined('POSTROUTING') %}
-{% set comment = 'SRC-NAT-' ~ rule %}
-{% set base_log = '[NAT-SRC-' ~ rule %}
-{% set interface = ' oifname "' ~ config.outbound_interface ~ '"' if config.outbound_interface is vyos_defined and config.outbound_interface is not vyos_defined('any') else '' %}
-{% if config.translation.address is vyos_defined %}
-{% if config.translation.address is vyos_defined('masquerade') %}
-{% set trns_addr = config.translation.address %}
-{% if config.translation.port is vyos_defined %}
-{% set trns_addr = trns_addr ~ ' to ' %}
-{% endif %}
-{# support 1:1 network translation #}
-{% elif config.translation.address | is_ip_network %}
-{% set trns_addr = 'snat ip prefix to ip saddr map { ' ~ config.source.address ~ ' : ' ~ config.translation.address ~ ' }' %}
-{# we can now clear out the src_addr part as it's already covered in aboves map #}
-{% set src_addr = '' %}
-{% else %}
-{% set trns_addr = 'snat to ' ~ config.translation.address %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% set trns_port = ':' ~ config.translation.port if config.translation.port is vyos_defined %}
-{# protocol has a default value thus it is always present #}
-{% if config.protocol is vyos_defined('tcp_udp') %}
-{% set protocol = 'tcp' %}
-{% set comment = comment ~ ' tcp_udp' %}
-{% else %}
-{% set protocol = config.protocol %}
-{% endif %}
-{% if config.log is vyos_defined %}
-{% if config.exclude is vyos_defined %}
-{% set log = base_log ~ '-EXCL]' %}
-{% elif config.translation.address is vyos_defined('masquerade') %}
-{% set log = base_log ~ '-MASQ]' %}
-{% else %}
-{% set log = base_log ~ ']' %}
-{% endif %}
-{% endif %}
-{% if config.exclude is vyos_defined %}
-{# rule has been marked as 'exclude' thus we simply return here #}
-{% set trns_addr = 'return' %}
-{% set trns_port = '' %}
-{% endif %}
-{# T1083: NAT address and port translation options #}
-{% if config.translation.options is vyos_defined %}
-{% if config.translation.options.address_mapping is vyos_defined('persistent') %}
-{% set trns_opts_addr = 'persistent' %}
-{% endif %}
-{% if config.translation.options.port_mapping is vyos_defined('random') %}
-{% set trns_opts_port = 'random' %}
-{% elif config.translation.options.port_mapping is vyos_defined('fully-random') %}
-{% set trns_opts_port = 'fully-random' %}
-{% endif %}
-{% endif %}
-{% if trns_opts_addr is vyos_defined and trns_opts_port is vyos_defined %}
-{% set trns_opts = trns_opts_addr ~ ',' ~ trns_opts_port %}
-{% elif trns_opts_addr is vyos_defined %}
-{% set trns_opts = trns_opts_addr %}
-{% elif trns_opts_port is vyos_defined %}
-{% set trns_opts = trns_opts_port %}
-{% endif %}
-{% set output = 'add rule ip nat ' ~ chain ~ interface %}
-{% if protocol is not vyos_defined('all') %}
-{% set output = output ~ ' ip protocol ' ~ protocol %}
-{% endif %}
-{% if src_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ src_addr %}
-{% endif %}
-{% if src_port is vyos_defined %}
-{% set output = output ~ ' ' ~ protocol ~ ' ' ~ src_port %}
-{% endif %}
-{% if dst_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ dst_addr %}
-{% endif %}
-{% if dst_port is vyos_defined %}
-{% set output = output ~ ' ' ~ protocol ~ ' ' ~ dst_port %}
-{% endif %}
-{# Count packets #}
-{% set output = output ~ ' counter' %}
-{# Special handling of log option, we must repeat the entire rule before the #}
-{# NAT translation options are added, this is essential #}
-{% if log is vyos_defined %}
-{% set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %}
-{% endif %}
-{% if trns_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ trns_addr %}
-{% endif %}
-{% if trns_port is vyos_defined %}
-{# Do not add a whitespace here, translation port must be directly added after IP address #}
-{# e.g. 192.0.2.10:3389 #}
-{% set output = output ~ trns_port %}
-{% endif %}
-{% if trns_opts is vyos_defined %}
-{% set output = output ~ ' ' ~ trns_opts %}
-{% endif %}
-{% if comment is vyos_defined %}
-{% set output = output ~ ' comment "' ~ comment ~ '"' %}
-{% endif %}
-{{ log_output if log_output is vyos_defined }}
-{{ output }}
-{# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #}
-{% if config.protocol is vyos_defined('tcp_udp') %}
-{# Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #}
-{{ log_output | replace('tcp ', 'udp ') if log_output is vyos_defined }}
-{{ output | replace('tcp ', 'udp ') }}
-{% endif %}
-{% endmacro %}
-
-# Start with clean SNAT and DNAT chains
-flush chain ip nat PREROUTING
-flush chain ip nat POSTROUTING
{% if helper_functions is vyos_defined('remove') %}
{# NAT if going to be disabled - remove rules and targets from nftables #}
{% set base_command = 'delete rule ip raw' %}
@@ -162,21 +21,41 @@ add rule ip raw NAT_CONNTRACK counter accept
{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
{% endif %}
-#
-# Destination NAT rules build up here
-#
-add rule ip nat PREROUTING counter jump VYOS_PRE_DNAT_HOOK
+{% if first_install is not vyos_defined %}
+delete table ip vyos_nat
+{% endif %}
+table ip vyos_nat {
+ #
+ # Destination NAT rules build up here
+ #
+ chain PREROUTING {
+ type nat hook prerouting priority -100; policy accept;
+ counter jump VYOS_PRE_DNAT_HOOK
{% if destination.rule is vyos_defined %}
{% for rule, config in destination.rule.items() if config.disable is not vyos_defined %}
-{{ nat_rule(rule, config, 'PREROUTING') }}
+ {{ config | nat_rule(rule, 'destination') }}
{% endfor %}
{% endif %}
-#
-# Source NAT rules build up here
-#
-add rule ip nat POSTROUTING counter jump VYOS_PRE_SNAT_HOOK
+ }
+
+ #
+ # Source NAT rules build up here
+ #
+ chain POSTROUTING {
+ type nat hook postrouting priority 100; policy accept;
+ counter jump VYOS_PRE_SNAT_HOOK
{% if source.rule is vyos_defined %}
{% for rule, config in source.rule.items() if config.disable is not vyos_defined %}
-{{ nat_rule(rule, config, 'POSTROUTING') }}
+ {{ config | nat_rule(rule, 'source') }}
{% endfor %}
{% endif %}
+ }
+
+ chain VYOS_PRE_DNAT_HOOK {
+ return
+ }
+
+ chain VYOS_PRE_SNAT_HOOK {
+ return
+ }
+}
diff --git a/data/templates/firewall/nftables-nat66.j2 b/data/templates/firewall/nftables-nat66.j2
index 28714c7a7..27b3eec88 100644
--- a/data/templates/firewall/nftables-nat66.j2
+++ b/data/templates/firewall/nftables-nat66.j2
@@ -1,125 +1,5 @@
#!/usr/sbin/nft -f
-{% macro nptv6_rule(rule,config, chain) %}
-{% set comment = '' %}
-{% set base_log = '' %}
-{% set dst_prefix = 'ip6 daddr ' ~ config.destination.prefix.replace('!','!= ') if config.destination.prefix is vyos_defined %}
-{% set src_prefix = 'ip6 saddr ' ~ config.source.prefix.replace('!','!= ') if config.source.prefix is vyos_defined %}
-{% set source_address = 'ip6 saddr ' ~ config.source.address.replace('!','!= ') if config.source.address is vyos_defined %}
-{% set dest_address = 'ip6 daddr ' ~ config.destination.address.replace('!','!= ') if config.destination.address is vyos_defined %}
-{# Port #}
-{% if config.source.port is vyos_defined and config.source.port.startswith('!') %}
-{% set src_port = 'sport != { ' ~ config.source.port.replace('!','') ~ ' }' %}
-{% else %}
-{% set src_port = 'sport { ' ~ config.source.port ~ ' }' if config.source.port is vyos_defined %}
-{% endif %}
-{% if config.destination.port is vyos_defined and config.destination.port.startswith('!') %}
-{% set dst_port = 'dport != { ' ~ config.destination.port.replace('!','') ~ ' }' %}
-{% else %}
-{% set dst_port = 'dport { ' ~ config.destination.port ~ ' }' if config.destination.port is vyos_defined %}
-{% endif %}
-{% if chain is vyos_defined('PREROUTING') %}
-{% set comment = 'DST-NAT66-' ~ rule %}
-{% set base_log = '[NAT66-DST-' ~ rule %}
-{% set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
-{% if config.translation.address | is_ip_network %}
-{# support 1:1 network translation #}
-{% set dnat_type = 'dnat prefix to ' %}
-{% else %}
-{% set dnat_type = 'dnat to ' %}
-{% endif %}
-{% set trns_address = dnat_type ~ config.translation.address if config.translation.address is vyos_defined %}
-{% elif chain is vyos_defined('POSTROUTING') %}
-{% set comment = 'SRC-NAT66-' ~ rule %}
-{% set base_log = '[NAT66-SRC-' ~ rule %}
-{% if config.translation.address is vyos_defined %}
-{% if config.translation.address is vyos_defined('masquerade') %}
-{% set trns_address = config.translation.address %}
-{% else %}
-{% if config.translation.address | is_ip_network %}
-{# support 1:1 network translation #}
-{% set snat_type = 'snat prefix to ' %}
-{% else %}
-{% set snat_type = 'snat to ' %}
-{% endif %}
-{% set trns_address = snat_type ~ config.translation.address %}
-{% endif %}
-{% endif %}
-{% set interface = ' oifname "' ~ config.outbound_interface ~ '"' if config.outbound_interface is vyos_defined else '' %}
-{% endif %}
-{% set trns_port = ':' ~ config.translation.port if config.translation.port is vyos_defined %}
-{# protocol has a default value thus it is always present #}
-{% if config.protocol is vyos_defined('tcp_udp') %}
-{% set protocol = 'tcp' %}
-{% set comment = comment ~ ' tcp_udp' %}
-{% else %}
-{% set protocol = config.protocol %}
-{% endif %}
-{% if config.log is vyos_defined %}
-{% if config.translation.address is vyos_defined('masquerade') %}
-{% set log = base_log ~ '-MASQ]' %}
-{% else %}
-{% set log = base_log ~ ']' %}
-{% endif %}
-{% endif %}
-{% if config.exclude is vyos_defined %}
-{# rule has been marked as 'exclude' thus we simply return here #}
-{% set trns_addr = 'return' %}
-{% set trns_port = '' %}
-{% endif %}
-{% set output = 'add rule ip6 nat ' ~ chain ~ interface %}
-{# Count packets #}
-{% set output = output ~ ' counter' %}
-{# Special handling of log option, we must repeat the entire rule before the #}
-{# NAT translation options are added, this is essential #}
-{% if log is vyos_defined %}
-{% set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %}
-{% endif %}
-{% if src_prefix is vyos_defined %}
-{% set output = output ~ ' ' ~ src_prefix %}
-{% endif %}
-{% if dst_port is vyos_defined %}
-{% set output = output ~ ' ' ~ protocol ~ ' ' ~ dst_port %}
-{% endif %}
-{% if dst_prefix is vyos_defined %}
-{% set output = output ~ ' ' ~ dst_prefix %}
-{% endif %}
-{% if source_address is vyos_defined %}
-{% set output = output ~ ' ' ~ source_address %}
-{% endif %}
-{% if src_port is vyos_defined %}
-{% set output = output ~ ' ' ~ protocol ~ ' ' ~ src_port %}
-{% endif %}
-{% if dest_address is vyos_defined %}
-{% set output = output ~ ' ' ~ dest_address %}
-{% endif %}
-{% if config.exclude is vyos_defined %}
-{# rule has been marked as 'exclude' thus we simply return here #}
-{% set trns_address = 'return' %}
-{% endif %}
-{% if trns_address is vyos_defined %}
-{% set output = output ~ ' ' ~ trns_address %}
-{% endif %}
-{% if trns_port is vyos_defined %}
-{# Do not add a whitespace here, translation port must be directly added after IP address #}
-{# e.g. 2001:db8::1:3389 #}
-{% set output = output ~ trns_port %}
-{% endif %}
-{% if comment is vyos_defined %}
-{% set output = output ~ ' comment "' ~ comment ~ '"' %}
-{% endif %}
-{{ log_output if log_output is vyos_defined }}
-{{ output }}
-{# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #}
-{% if config.protocol is vyos_defined('tcp_udp') %}
-{# Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #}
-{{ log_output | replace('tcp ', 'udp ') if log_output is vyos_defined }}
-{{ output | replace('tcp ', 'udp ') }}
-{% endif %}
-{% endmacro %}
-
-# Start with clean NAT table
-flush table ip6 nat
{% if helper_functions is vyos_defined('remove') %}
{# NAT if going to be disabled - remove rules and targets from nftables #}
{% set base_command = 'delete rule ip6 raw' %}
@@ -137,19 +17,41 @@ add rule ip6 raw NAT_CONNTRACK counter accept
{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
{% endif %}
-#
-# Destination NAT66 rules build up here
-#
+{% if first_install is not vyos_defined %}
+delete table ip6 vyos_nat
+{% endif %}
+table ip6 vyos_nat {
+ #
+ # Destination NAT66 rules build up here
+ #
+ chain PREROUTING {
+ type nat hook prerouting priority -100; policy accept;
+ counter jump VYOS_DNPT_HOOK
{% if destination.rule is vyos_defined %}
{% for rule, config in destination.rule.items() if config.disable is not vyos_defined %}
-{{ nptv6_rule(rule, config, 'PREROUTING') }}
+ {{ config | nat_rule(rule, 'destination', ipv6=True) }}
{% endfor %}
{% endif %}
-#
-# Source NAT66 rules build up here
-#
+ }
+
+ #
+ # Source NAT66 rules build up here
+ #
+ chain POSTROUTING {
+ type nat hook postrouting priority 100; policy accept;
+ counter jump VYOS_SNPT_HOOK
{% if source.rule is vyos_defined %}
{% for rule, config in source.rule.items() if config.disable is not vyos_defined %}
-{{ nptv6_rule(rule, config, 'POSTROUTING') }}
+ {{ config | nat_rule(rule, 'source', ipv6=True) }}
{% endfor %}
{% endif %}
+ }
+
+ chain VYOS_DNPT_HOOK {
+ return
+ }
+
+ chain VYOS_SNPT_HOOK {
+ return
+ }
+}
diff --git a/data/templates/firewall/nftables-static-nat.j2 b/data/templates/firewall/nftables-static-nat.j2
index d3c43858f..790c33ce9 100644
--- a/data/templates/firewall/nftables-static-nat.j2
+++ b/data/templates/firewall/nftables-static-nat.j2
@@ -1,115 +1,31 @@
#!/usr/sbin/nft -f
-{% macro nat_rule(rule, config, chain) %}
-{% set comment = '' %}
-{% set base_log = '' %}
-
-{% if chain is vyos_defined('PREROUTING') %}
-{% set comment = 'STATIC-NAT-' ~ rule %}
-{% set base_log = '[NAT-DST-' ~ rule %}
-{% set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
-{% if config.translation.address is vyos_defined %}
-{# support 1:1 network translation #}
-{% if config.translation.address | is_ip_network %}
-{% set trns_addr = 'dnat ip prefix to ip daddr map { ' ~ config.destination.address ~ ' : ' ~ config.translation.address ~ ' }' %}
-{# we can now clear out the dst_addr part as it's already covered in aboves map #}
-{% else %}
-{% set dst_addr = 'ip daddr ' ~ config.destination.address if config.destination.address is vyos_defined %}
-{% set trns_addr = 'dnat to ' ~ config.translation.address %}
-{% endif %}
-{% endif %}
-{% elif chain is vyos_defined('POSTROUTING') %}
-{% set comment = 'STATIC-NAT-' ~ rule %}
-{% set base_log = '[NAT-SRC-' ~ rule %}
-{% set interface = ' oifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
-{% if config.translation.address is vyos_defined %}
-{# support 1:1 network translation #}
-{% if config.translation.address | is_ip_network %}
-{% set trns_addr = 'snat ip prefix to ip saddr map { ' ~ config.translation.address ~ ' : ' ~ config.destination.address ~ ' }' %}
-{# we can now clear out the src_addr part as it's already covered in aboves map #}
-{% else %}
-{% set src_addr = 'ip saddr ' ~ config.translation.address if config.translation.address is vyos_defined %}
-{% set trns_addr = 'snat to ' ~ config.destination.address %}
-{% endif %}
-{% endif %}
-{% endif %}
-
-{% if config.exclude is vyos_defined %}
-{# rule has been marked as 'exclude' thus we simply return here #}
-{% set trns_addr = 'return' %}
-{% set trns_port = '' %}
-{% endif %}
-
-{% if config.translation.options is vyos_defined %}
-{% if config.translation.options.address_mapping is vyos_defined('persistent') %}
-{% set trns_opts_addr = 'persistent' %}
-{% endif %}
-{% if config.translation.options.port_mapping is vyos_defined('random') %}
-{% set trns_opts_port = 'random' %}
-{% elif config.translation.options.port_mapping is vyos_defined('fully-random') %}
-{% set trns_opts_port = 'fully-random' %}
-{% endif %}
-{% endif %}
-
-{% if trns_opts_addr is vyos_defined and trns_opts_port is vyos_defined %}
-{% set trns_opts = trns_opts_addr ~ ',' ~ trns_opts_port %}
-{% elif trns_opts_addr is vyos_defined %}
-{% set trns_opts = trns_opts_addr %}
-{% elif trns_opts_port is vyos_defined %}
-{% set trns_opts = trns_opts_port %}
-{% endif %}
-
-{% set output = 'add rule ip vyos_static_nat ' ~ chain ~ interface %}
-
-{% if dst_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ dst_addr %}
+{% if first_install is not vyos_defined %}
+delete table ip vyos_static_nat
{% endif %}
-{% if src_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ src_addr %}
-{% endif %}
-
-{# Count packets #}
-{% set output = output ~ ' counter' %}
-{# Special handling of log option, we must repeat the entire rule before the #}
-{# NAT translation options are added, this is essential #}
-{% if log is vyos_defined %}
-{% set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %}
-{% endif %}
-{% if trns_addr is vyos_defined %}
-{% set output = output ~ ' ' ~ trns_addr %}
-{% endif %}
-
-{% if trns_opts is vyos_defined %}
-{% set output = output ~ ' ' ~ trns_opts %}
-{% endif %}
-{% if comment is vyos_defined %}
-{% set output = output ~ ' comment "' ~ comment ~ '"' %}
-{% endif %}
-{{ log_output if log_output is vyos_defined }}
-{{ output }}
-{% endmacro %}
-
-# Start with clean STATIC NAT chains
-flush chain ip vyos_static_nat PREROUTING
-flush chain ip vyos_static_nat POSTROUTING
+table ip vyos_static_nat {
+ #
+ # Destination NAT rules build up here
+ #
-{# NAT if enabled - add targets to nftables #}
-
-#
-# Destination NAT rules build up here
-#
-add rule ip vyos_static_nat PREROUTING counter jump VYOS_PRE_DNAT_HOOK
+ chain PREROUTING {
+ type nat hook prerouting priority -100; policy accept;
{% if static.rule is vyos_defined %}
{% for rule, config in static.rule.items() if config.disable is not vyos_defined %}
-{{ nat_rule(rule, config, 'PREROUTING') }}
+ {{ config | nat_static_rule(rule, 'destination') }}
{% endfor %}
{% endif %}
-#
-# Source NAT rules build up here
-#
-add rule ip vyos_static_nat POSTROUTING counter jump VYOS_PRE_SNAT_HOOK
+ }
+
+ #
+ # Source NAT rules build up here
+ #
+ chain POSTROUTING {
+ type nat hook postrouting priority 100; policy accept;
{% if static.rule is vyos_defined %}
{% for rule, config in static.rule.items() if config.disable is not vyos_defined %}
-{{ nat_rule(rule, config, 'POSTROUTING') }}
+ {{ config | nat_static_rule(rule, 'source') }}
{% endfor %}
{% endif %}
+ }
+}
diff --git a/data/templates/firewall/nftables-zone.j2 b/data/templates/firewall/nftables-zone.j2
new file mode 100644
index 000000000..17ef5101d
--- /dev/null
+++ b/data/templates/firewall/nftables-zone.j2
@@ -0,0 +1,78 @@
+
+{% macro zone_chains(zone, state_policy=False, ipv6=False) %}
+{% set fw_name = 'ipv6_name' if ipv6 else 'name' %}
+{% set suffix = '6' if ipv6 else '' %}
+ chain VYOS_ZONE_FORWARD {
+ type filter hook forward priority 1; policy accept;
+{% if state_policy %}
+ jump VYOS_STATE_POLICY{{ suffix }}
+{% endif %}
+{% for zone_name, zone_conf in zone.items() %}
+{% if 'local_zone' not in zone_conf %}
+ oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }}
+{% endif %}
+{% endfor %}
+ }
+ chain VYOS_ZONE_LOCAL {
+ type filter hook input priority 1; policy accept;
+{% if state_policy %}
+ jump VYOS_STATE_POLICY{{ suffix }}
+{% endif %}
+{% for zone_name, zone_conf in zone.items() %}
+{% if 'local_zone' in zone_conf %}
+ counter jump VZONE_{{ zone_name }}_IN
+{% endif %}
+{% endfor %}
+ }
+ chain VYOS_ZONE_OUTPUT {
+ type filter hook output priority 1; policy accept;
+{% if state_policy %}
+ jump VYOS_STATE_POLICY{{ suffix }}
+{% endif %}
+{% for zone_name, zone_conf in zone.items() %}
+{% if 'local_zone' in zone_conf %}
+ counter jump VZONE_{{ zone_name }}_OUT
+{% endif %}
+{% endfor %}
+ }
+{% for zone_name, zone_conf in zone.items() %}
+{% if zone_conf.local_zone is vyos_defined %}
+ chain VZONE_{{ zone_name }}_IN {
+ iifname lo counter return
+{% if zone_conf.from is vyos_defined %}
+{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter return
+{% endfor %}
+{% endif %}
+ {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
+ }
+ chain VZONE_{{ zone_name }}_OUT {
+ oifname lo counter return
+{% if zone_conf.from_local is vyos_defined %}
+{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall[fw_name] is vyos_defined %}
+ oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
+ oifname { {{ zone[from_zone].interface | join(",") }} } counter return
+{% endfor %}
+{% endif %}
+ {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
+ }
+{% else %}
+ chain VZONE_{{ zone_name }} {
+ iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }}
+{% if zone_conf.intra_zone_filtering is vyos_defined %}
+ iifname { {{ zone_conf.interface | join(",") }} } counter return
+{% endif %}
+{% if zone_conf.from is vyos_defined %}
+{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
+{% if zone[from_zone].local_zone is not defined %}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
+ iifname { {{ zone[from_zone].interface | join(",") }} } counter return
+{% endif %}
+{% endfor %}
+{% endif %}
+ {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
+ }
+{% endif %}
+{% endfor %}
+{% endmacro %}
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 5971e1bbc..a0f0b8c11 100644
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -1,25 +1,48 @@
#!/usr/sbin/nft -f
{% import 'firewall/nftables-defines.j2' as group_tmpl %}
+{% import 'firewall/nftables-zone.j2' as zone_tmpl %}
-{% if cleanup_commands is vyos_defined %}
-{% for command in cleanup_commands %}
-{{ command }}
-{% endfor %}
+{% if first_install is not vyos_defined %}
+delete table ip vyos_filter
{% endif %}
-
-table ip filter {
-{% if first_install is vyos_defined %}
+table ip vyos_filter {
chain VYOS_FW_FORWARD {
type filter hook forward priority 0; policy accept;
+{% if state_policy is vyos_defined %}
+ jump VYOS_STATE_POLICY
+{% endif %}
+{% if interface is vyos_defined %}
+{% for ifname, ifconf in interface.items() %}
+{% if ifconf.in is vyos_defined and ifconf.in.name is vyos_defined %}
+ iifname {{ ifname }} counter jump NAME_{{ ifconf.in.name }}
+{% endif %}
+{% if ifconf.out is vyos_defined and ifconf.out.name is vyos_defined %}
+ oifname {{ ifname }} counter jump NAME_{{ ifconf.out.name }}
+{% endif %}
+{% endfor %}
+{% endif %}
jump VYOS_POST_FW
}
chain VYOS_FW_LOCAL {
type filter hook input priority 0; policy accept;
+{% if state_policy is vyos_defined %}
+ jump VYOS_STATE_POLICY
+{% endif %}
+{% if interface is vyos_defined %}
+{% for ifname, ifconf in interface.items() %}
+{% if ifconf.local is vyos_defined and ifconf.local.name is vyos_defined %}
+ iifname {{ ifname }} counter jump NAME_{{ ifconf.local.name }}
+{% endif %}
+{% endfor %}
+{% endif %}
jump VYOS_POST_FW
}
chain VYOS_FW_OUTPUT {
type filter hook output priority 0; policy accept;
+{% if state_policy is vyos_defined %}
+ jump VYOS_STATE_POLICY
+{% endif %}
jump VYOS_POST_FW
}
chain VYOS_POST_FW {
@@ -29,7 +52,6 @@ table ip filter {
type filter hook prerouting priority -450; policy accept;
ip frag-off & 0x3fff != 0 meta mark set 0xffff1 return
}
-{% endif %}
{% if name is vyos_defined %}
{% set ns = namespace(sets=[]) %}
{% for name_text, conf in name.items() %}
@@ -72,6 +94,10 @@ table ip filter {
{{ group_tmpl.groups(group, False) }}
+{% if zone is vyos_defined %}
+{{ zone_tmpl.zone_chains(zone, state_policy is vyos_defined, False) }}
+{% endif %}
+
{% if state_policy is vyos_defined %}
chain VYOS_STATE_POLICY {
{% if state_policy.established is vyos_defined %}
@@ -88,18 +114,46 @@ table ip filter {
{% endif %}
}
-table ip6 filter {
-{% if first_install is vyos_defined %}
+{% if first_install is not vyos_defined %}
+delete table ip6 vyos_filter
+{% endif %}
+table ip6 vyos_filter {
chain VYOS_FW6_FORWARD {
type filter hook forward priority 0; policy accept;
+{% if state_policy is vyos_defined %}
+ jump VYOS_STATE_POLICY6
+{% endif %}
+{% if interface is vyos_defined %}
+{% for ifname, ifconf in interface.items() %}
+{% if ifconf.in is vyos_defined and ifconf.in.ipv6_name is vyos_defined %}
+ iifname {{ ifname }} counter jump NAME6_{{ ifconf.in.ipv6_name }}
+{% endif %}
+{% if ifconf.out is vyos_defined and ifconf.out.ipv6_name is vyos_defined %}
+ oifname {{ ifname }} counter jump NAME6_{{ ifconf.out.ipv6_name }}
+{% endif %}
+{% endfor %}
+{% endif %}
jump VYOS_POST_FW6
}
chain VYOS_FW6_LOCAL {
type filter hook input priority 0; policy accept;
+{% if state_policy is vyos_defined %}
+ jump VYOS_STATE_POLICY6
+{% endif %}
+{% if interface is vyos_defined %}
+{% for ifname, ifconf in interface.items() %}
+{% if ifconf.local is vyos_defined and ifconf.local.ipv6_name is vyos_defined %}
+ iifname {{ ifname }} counter jump NAME6_{{ ifconf.local.ipv6_name }}
+{% endif %}
+{% endfor %}
+{% endif %}
jump VYOS_POST_FW6
}
chain VYOS_FW6_OUTPUT {
type filter hook output priority 0; policy accept;
+{% if state_policy is vyos_defined %}
+ jump VYOS_STATE_POLICY6
+{% endif %}
jump VYOS_POST_FW6
}
chain VYOS_POST_FW6 {
@@ -109,7 +163,6 @@ table ip6 filter {
type filter hook prerouting priority -450; policy accept;
exthdr frag exists meta mark set 0xffff1 return
}
-{% endif %}
{% if ipv6_name is vyos_defined %}
{% set ns = namespace(sets=[]) %}
{% for name_text, conf in ipv6_name.items() %}
@@ -122,7 +175,7 @@ table ip6 filter {
{% endif %}
{% endfor %}
{% endif %}
- {{ conf | nft_default_rule(name_text) }}
+ {{ conf | nft_default_rule(name_text, ipv6=True) }}
}
{% endfor %}
{% for set_name in ns.sets %}
@@ -144,181 +197,22 @@ table ip6 filter {
{{ group_tmpl.groups(group, True) }}
+{% if zone is vyos_defined %}
+{{ zone_tmpl.zone_chains(zone, state_policy is vyos_defined, True) }}
+{% endif %}
+
{% if state_policy is vyos_defined %}
chain VYOS_STATE_POLICY6 {
{% if state_policy.established is vyos_defined %}
- {{ state_policy.established | nft_state_policy('established', ipv6=True) }}
+ {{ state_policy.established | nft_state_policy('established') }}
{% endif %}
{% if state_policy.invalid is vyos_defined %}
- {{ state_policy.invalid | nft_state_policy('invalid', ipv6=True) }}
+ {{ state_policy.invalid | nft_state_policy('invalid') }}
{% endif %}
{% if state_policy.related is vyos_defined %}
- {{ state_policy.related | nft_state_policy('related', ipv6=True) }}
+ {{ state_policy.related | nft_state_policy('related') }}
{% endif %}
return
}
{% endif %}
}
-
-{% if first_install is vyos_defined %}
-table ip nat {
- chain PREROUTING {
- type nat hook prerouting priority -100; policy accept;
- counter jump VYOS_PRE_DNAT_HOOK
- }
-
- chain POSTROUTING {
- type nat hook postrouting priority 100; policy accept;
- counter jump VYOS_PRE_SNAT_HOOK
- }
-
- chain VYOS_PRE_DNAT_HOOK {
- return
- }
-
- chain VYOS_PRE_SNAT_HOOK {
- return
- }
-}
-
-table ip vyos_static_nat {
- chain PREROUTING {
- type nat hook prerouting priority -100; policy accept;
- counter jump VYOS_PRE_DNAT_HOOK
- }
-
- chain POSTROUTING {
- type nat hook postrouting priority 100; policy accept;
- counter jump VYOS_PRE_SNAT_HOOK
- }
-
- chain VYOS_PRE_DNAT_HOOK {
- return
- }
-
- chain VYOS_PRE_SNAT_HOOK {
- return
- }
-}
-
-table ip6 nat {
- chain PREROUTING {
- type nat hook prerouting priority -100; policy accept;
- counter jump VYOS_DNPT_HOOK
- }
-
- chain POSTROUTING {
- type nat hook postrouting priority 100; policy accept;
- counter jump VYOS_SNPT_HOOK
- }
-
- chain VYOS_DNPT_HOOK {
- return
- }
-
- chain VYOS_SNPT_HOOK {
- return
- }
-}
-
-table inet mangle {
- chain FORWARD {
- type filter hook forward priority -150; policy accept;
- }
-}
-
-table raw {
- chain VYOS_TCP_MSS {
- type filter hook forward priority -300; policy accept;
- }
-
- chain PREROUTING {
- type filter hook prerouting priority -200; policy accept;
- counter jump VYOS_CT_IGNORE
- counter jump VYOS_CT_TIMEOUT
- counter jump VYOS_CT_PREROUTING_HOOK
- counter jump FW_CONNTRACK
- notrack
- }
-
- chain OUTPUT {
- type filter hook output priority -200; policy accept;
- counter jump VYOS_CT_IGNORE
- counter jump VYOS_CT_TIMEOUT
- counter jump VYOS_CT_OUTPUT_HOOK
- counter jump FW_CONNTRACK
- notrack
- }
-
- ct helper rpc_tcp {
- type "rpc" protocol tcp;
- }
-
- ct helper rpc_udp {
- type "rpc" protocol udp;
- }
-
- ct helper tns_tcp {
- type "tns" protocol tcp;
- }
-
- chain VYOS_CT_HELPER {
- ct helper set "rpc_tcp" tcp dport {111} return
- ct helper set "rpc_udp" udp dport {111} return
- ct helper set "tns_tcp" tcp dport {1521,1525,1536} return
- return
- }
-
- chain VYOS_CT_IGNORE {
- return
- }
-
- chain VYOS_CT_TIMEOUT {
- return
- }
-
- chain VYOS_CT_PREROUTING_HOOK {
- return
- }
-
- chain VYOS_CT_OUTPUT_HOOK {
- return
- }
-
- chain FW_CONNTRACK {
- accept
- }
-}
-
-table ip6 raw {
- chain VYOS_TCP_MSS {
- type filter hook forward priority -300; policy accept;
- }
-
- chain PREROUTING {
- type filter hook prerouting priority -300; policy accept;
- counter jump VYOS_CT_PREROUTING_HOOK
- counter jump FW_CONNTRACK
- notrack
- }
-
- chain OUTPUT {
- type filter hook output priority -300; policy accept;
- counter jump VYOS_CT_OUTPUT_HOOK
- counter jump FW_CONNTRACK
- notrack
- }
-
- chain VYOS_CT_PREROUTING_HOOK {
- return
- }
-
- chain VYOS_CT_OUTPUT_HOOK {
- return
- }
-
- chain FW_CONNTRACK {
- accept
- }
-}
-{% endif %}
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index 808e9dbe7..e8d135c78 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -378,14 +378,19 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% endif %}
{% endif %}
{% if afi_config.route_target.both is vyos_defined %}
- route-target both {{ afi_config.route_target.both }}
-{% else %}
-{% if afi_config.route_target.export is vyos_defined %}
- route-target export {{ afi_config.route_target.export }}
-{% endif %}
-{% if afi_config.route_target.import is vyos_defined %}
- route-target import {{ afi_config.route_target.import }}
-{% endif %}
+{% for route_target in afi_config.route_target.both %}
+ route-target both {{ route_target }}
+{% endfor %}
+{% endif %}
+{% if afi_config.route_target.export is vyos_defined %}
+{% for route_target in afi_config.route_target.export %}
+ route-target export {{ route_target }}
+{% endfor %}
+{% endif %}
+{% if afi_config.route_target.import is vyos_defined %}
+{% for route_target in afi_config.route_target.import %}
+ route-target import {{ route_target }}
+{% endfor %}
{% endif %}
{% if afi_config.route_map.vpn.export is vyos_defined %}
route-map vpn export {{ afi_config.route_map.vpn.export }}
@@ -461,6 +466,9 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if parameters.bestpath.med is vyos_defined %}
bgp bestpath med {{ 'confed' if parameters.bestpath.med.confed is vyos_defined }} {{ 'missing-as-worst' if parameters.bestpath.med.missing_as_worst is vyos_defined }}
{% endif %}
+{% if parameters.bestpath.peer_type is vyos_defined %}
+ bgp bestpath peer-type {{ 'multipath-relax' if parameters.bestpath.peer_type.multipath_relax is vyos_defined }}
+{% endif %}
{% if parameters.cluster_id is vyos_defined %}
bgp cluster-id {{ parameters.cluster_id }}
{% endif %}
diff --git a/data/templates/frr/isisd.frr.j2 b/data/templates/frr/isisd.frr.j2
index 8e95348bc..194dbcb07 100644
--- a/data/templates/frr/isisd.frr.j2
+++ b/data/templates/frr/isisd.frr.j2
@@ -107,9 +107,6 @@ router isis VyOS {{ 'vrf ' + vrf if vrf is vyos_defined }}
mpls-te inter-as{{ level }}
{% endif %}
{% if segment_routing is vyos_defined %}
-{% if segment_routing.enable is vyos_defined %}
- segment-routing on
-{% endif %}
{% if segment_routing.maximum_label_depth is vyos_defined %}
segment-routing node-msd {{ segment_routing.maximum_label_depth }}
{% endif %}
@@ -124,28 +121,27 @@ router isis VyOS {{ 'vrf ' + vrf if vrf is vyos_defined }}
{% for prefix, prefix_config in segment_routing.prefix.items() %}
{% if prefix_config.absolute is vyos_defined %}
{% if prefix_config.absolute.value is vyos_defined %}
- segment-routing prefix {{ prefixes }} absolute {{ prefix_config.absolute.value }}
+ segment-routing prefix {{ prefix }} absolute {{ prefix_config.absolute.value }}
{% if prefix_config.absolute.explicit_null is vyos_defined %}
- segment-routing prefix {{ prefixes }} absolute {{ prefix_config.absolute.value }} explicit-null
-{% endif %}
-{% if prefix_config.absolute.no_php_flag is vyos_defined %}
- segment-routing prefix {{ prefixes }} absolute {{ prefix_config.absolute.value }} no-php-flag
+ segment-routing prefix {{ prefix }} absolute {{ prefix_config.absolute.value }} explicit-null
+{% elif prefix_config.absolute.no_php_flag is vyos_defined %}
+ segment-routing prefix {{ prefix }} absolute {{ prefix_config.absolute.value }} no-php-flag
{% endif %}
{% endif %}
-{% if prefix_config.index is vyos_defined %}
-{% if prefix_config.index.value is vyos_defined %}
- segment-routing prefix {{ prefixes }} index {{ prefix_config.index.value }}
-{% if prefix_config.index.explicit_null is vyos_defined %}
- segment-routing prefix {{ prefixes }} index {{ prefix_config.index.value }} explicit-null
-{% endif %}
-{% if prefix_config.index.no_php_flag is vyos_defined %}
- segment-routing prefix {{ prefixes }} index {{ prefix_config.index.value }} no-php-flag
-{% endif %}
+{% endif %}
+{% if prefix_config.index is vyos_defined %}
+{% if prefix_config.index.value is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }}
+{% if prefix_config.index.explicit_null is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} explicit-null
+{% elif prefix_config.index.no_php_flag is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} no-php-flag
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
+ segment-routing on
{% endif %}
{% if spf_delay_ietf.init_delay is vyos_defined %}
spf-delay-ietf init-delay {{ spf_delay_ietf.init_delay }} short-delay {{ spf_delay_ietf.short_delay }} long-delay {{ spf_delay_ietf.long_delay }} holddown {{ spf_delay_ietf.holddown }} time-to-learn {{ spf_delay_ietf.time_to_learn }}
diff --git a/data/templates/frr/ospfd.frr.j2 b/data/templates/frr/ospfd.frr.j2
index 427fc8be7..9cd9b03dc 100644
--- a/data/templates/frr/ospfd.frr.j2
+++ b/data/templates/frr/ospfd.frr.j2
@@ -181,6 +181,33 @@ router ospf {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if refresh.timers is vyos_defined %}
refresh timer {{ refresh.timers }}
{% endif %}
+{% if segment_routing is vyos_defined %}
+{% if segment_routing.maximum_label_depth is vyos_defined %}
+ segment-routing node-msd {{ segment_routing.maximum_label_depth }}
+{% endif %}
+{% if segment_routing.global_block is vyos_defined %}
+{% if segment_routing.local_block is vyos_defined %}
+ segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }} local-block {{ segment_routing.local_block.low_label_value }} {{ segment_routing.local_block.high_label_value }}
+{% else %}
+ segment-routing global-block {{ segment_routing.global_block.low_label_value }} {{ segment_routing.global_block.high_label_value }}
+{% endif %}
+{% endif %}
+{% if segment_routing.prefix is vyos_defined %}
+{% for prefix, prefix_config in segment_routing.prefix.items() %}
+{% if prefix_config.index is vyos_defined %}
+{% if prefix_config.index.value is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }}
+{% if prefix_config.index.explicit_null is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} explicit-null
+{% elif prefix_config.index.no_php_flag is vyos_defined %}
+ segment-routing prefix {{ prefix }} index {{ prefix_config.index.value }} no-php-flag
+{% endif %}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ segment-routing on
+{% endif %}
{% if timers.throttle.spf.delay is vyos_defined and timers.throttle.spf.initial_holdtime is vyos_defined and timers.throttle.spf.max_holdtime is vyos_defined %}
{# Timer values have default values #}
timers throttle spf {{ timers.throttle.spf.delay }} {{ timers.throttle.spf.initial_holdtime }} {{ timers.throttle.spf.max_holdtime }}
diff --git a/data/templates/frr/policy.frr.j2 b/data/templates/frr/policy.frr.j2
index 33df17770..5ad4bd28c 100644
--- a/data/templates/frr/policy.frr.j2
+++ b/data/templates/frr/policy.frr.j2
@@ -274,11 +274,17 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }}
{% if rule_config.set.atomic_aggregate is vyos_defined %}
set atomic-aggregate
{% endif %}
-{% if rule_config.set.comm_list.comm_list is vyos_defined %}
- set comm-list {{ rule_config.set.comm_list.comm_list }} {{ 'delete' if rule_config.set.comm_list.delete is vyos_defined }}
+{% if rule_config.set.community.delete is vyos_defined %}
+ set comm-list {{ rule_config.set.community.delete }} delete
{% endif %}
-{% if rule_config.set.community is vyos_defined %}
- set community {{ rule_config.set.community }}
+{% if rule_config.set.community.replace is vyos_defined %}
+ set community {{ rule_config.set.community.replace | join(' ') | replace("local-as" , "local-AS") }}
+{% endif %}
+{% if rule_config.set.community.add is vyos_defined %}
+ set community {{ rule_config.set.community.add | join(' ') | replace("local-as" , "local-AS") }} additive
+{% endif %}
+{% if rule_config.set.community.none is vyos_defined %}
+ set community none
{% endif %}
{% if rule_config.set.distance is vyos_defined %}
set distance {{ rule_config.set.distance }}
@@ -290,13 +296,16 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }}
set evpn gateway-ip ipv6 {{ rule_config.set.evpn.gateway.ipv6 }}
{% endif %}
{% if rule_config.set.extcommunity.bandwidth is vyos_defined %}
- set extcommunity bandwidth {{ rule_config.set.extcommunity.bandwidth }}
+ set extcommunity bandwidth {{ rule_config.set.extcommunity.bandwidth }} {{ 'non-transitive' if rule_config.set.extcommunity.bandwidth_non_transitive is vyos_defined }}
{% endif %}
{% if rule_config.set.extcommunity.rt is vyos_defined %}
- set extcommunity rt {{ rule_config.set.extcommunity.rt }}
+ set extcommunity rt {{ rule_config.set.extcommunity.rt | join(' ') }}
{% endif %}
{% if rule_config.set.extcommunity.soo is vyos_defined %}
- set extcommunity soo {{ rule_config.set.extcommunity.soo }}
+ set extcommunity soo {{ rule_config.set.extcommunity.soo | join(' ') }}
+{% endif %}
+{% if rule_config.set.extcommunity.none is vyos_defined %}
+ set extcommunity none
{% endif %}
{% if rule_config.set.ip_next_hop is vyos_defined %}
set ip next-hop {{ rule_config.set.ip_next_hop }}
@@ -313,11 +322,17 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }}
{% if rule_config.set.ipv6_next_hop.prefer_global is vyos_defined %}
set ipv6 next-hop prefer-global
{% endif %}
-{% if rule_config.set.large_community is vyos_defined %}
- set large-community {{ rule_config.set.large_community }}
+{% if rule_config.set.large_community.replace is vyos_defined %}
+ set large-community {{ rule_config.set.large_community.replace | join(' ') }}
+{% endif %}
+{% if rule_config.set.large_community.add is vyos_defined %}
+ set large-community {{ rule_config.set.large_community.add | join(' ') }} additive
+{% endif %}
+{% if rule_config.set.large_community.none is vyos_defined %}
+ set large-community none
{% endif %}
-{% if rule_config.set.large_comm_list_delete is vyos_defined %}
- set large-comm-list {{ rule_config.set.large_comm_list_delete }} delete
+{% if rule_config.set.large_community.delete is vyos_defined %}
+ set large-comm-list {{ rule_config.set.large_community.delete }} delete
{% endif %}
{% if rule_config.set.local_preference is vyos_defined %}
set local-preference {{ rule_config.set.local_preference }}
diff --git a/data/templates/high-availability/keepalived.conf.j2 b/data/templates/high-availability/keepalived.conf.j2
index 6684dbc2c..706e1c5ae 100644
--- a/data/templates/high-availability/keepalived.conf.j2
+++ b/data/templates/high-availability/keepalived.conf.j2
@@ -47,10 +47,10 @@ vrrp_instance {{ name }} {
{% endif %}
{% endif %}
{% if group_config.rfc3768_compatibility is vyos_defined and group_config.peer_address is vyos_defined %}
- use_vmac {{ group_config.interface }}v{{ group_config.vrid }}
+ use_vmac {{ group_config.interface }}v{{ group_config.vrid }}v{{ '4' if group_config['address'] | first | is_ipv4 else '6' }}
vmac_xmit_base
{% elif group_config.rfc3768_compatibility is vyos_defined %}
- use_vmac {{ group_config.interface }}v{{ group_config.vrid }}
+ use_vmac {{ group_config.interface }}v{{ group_config.vrid }}v{{ '4' if group_config['address'] | first | is_ipv4 else '6' }}
{% endif %}
{% if group_config.authentication is vyos_defined %}
authentication {
diff --git a/data/templates/ids/fastnetmon.j2 b/data/templates/ids/fastnetmon.j2
index b9f77a257..0340d3c92 100644
--- a/data/templates/ids/fastnetmon.j2
+++ b/data/templates/ids/fastnetmon.j2
@@ -37,18 +37,70 @@ process_incoming_traffic = {{ 'on' if direction is vyos_defined and 'in' in dire
process_outgoing_traffic = {{ 'on' if direction is vyos_defined and 'out' in direction else 'off' }}
{% if threshold is vyos_defined %}
-{% for thr, thr_value in threshold.items() %}
-{% if thr is vyos_defined('fps') %}
+{% if threshold.general is vyos_defined %}
+# General threshold
+{% for thr, thr_value in threshold.general.items() %}
+{% if thr is vyos_defined('fps') %}
ban_for_flows = on
threshold_flows = {{ thr_value }}
-{% elif thr is vyos_defined('mbps') %}
+{% elif thr is vyos_defined('mbps') %}
ban_for_bandwidth = on
threshold_mbps = {{ thr_value }}
-{% elif thr is vyos_defined('pps') %}
+{% elif thr is vyos_defined('pps') %}
ban_for_pps = on
threshold_pps = {{ thr_value }}
-{% endif %}
-{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% if threshold.tcp is vyos_defined %}
+# TCP threshold
+{% for thr, thr_value in threshold.tcp.items() %}
+{% if thr is vyos_defined('fps') %}
+ban_for_tcp_flows = on
+threshold_tcp_flows = {{ thr_value }}
+{% elif thr is vyos_defined('mbps') %}
+ban_for_tcp_bandwidth = on
+threshold_tcp_mbps = {{ thr_value }}
+{% elif thr is vyos_defined('pps') %}
+ban_for_tcp_pps = on
+threshold_tcp_pps = {{ thr_value }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% if threshold.udp is vyos_defined %}
+# UDP threshold
+{% for thr, thr_value in threshold.udp.items() %}
+{% if thr is vyos_defined('fps') %}
+ban_for_udp_flows = on
+threshold_udp_flows = {{ thr_value }}
+{% elif thr is vyos_defined('mbps') %}
+ban_for_udp_bandwidth = on
+threshold_udp_mbps = {{ thr_value }}
+{% elif thr is vyos_defined('pps') %}
+ban_for_udp_pps = on
+threshold_udp_pps = {{ thr_value }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% if threshold.icmp is vyos_defined %}
+# ICMP threshold
+{% for thr, thr_value in threshold.icmp.items() %}
+{% if thr is vyos_defined('fps') %}
+ban_for_icmp_flows = on
+threshold_icmp_flows = {{ thr_value }}
+{% elif thr is vyos_defined('mbps') %}
+ban_for_icmp_bandwidth = on
+threshold_icmp_mbps = {{ thr_value }}
+{% elif thr is vyos_defined('pps') %}
+ban_for_icmp_pps = on
+threshold_icmp_pps = {{ thr_value }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
{% endif %}
{% if listen_interface is vyos_defined %}
diff --git a/data/templates/ipsec/ios_profile.j2 b/data/templates/ipsec/ios_profile.j2
index c8e17729a..eb74924b8 100644
--- a/data/templates/ipsec/ios_profile.j2
+++ b/data/templates/ipsec/ios_profile.j2
@@ -41,7 +41,7 @@
<!-- Remote identity, can be a FQDN, a userFQDN, an IP or (theoretically) a certificate's subject DN. Can't be empty.
IMPORTANT: DNs are currently not handled correctly, they are always sent as identities of type FQDN -->
<key>RemoteIdentifier</key>
- <string>{{ authentication.id if authentication.id is vyos_defined else 'VyOS' }}</string>
+ <string>{{ authentication.local_id if authentication.local_id is vyos_defined else 'VyOS' }}</string>
<!-- Local IKE identity, same restrictions as above. If it is empty the client's IP address will be used -->
<key>LocalIdentifier</key>
<string></string>
diff --git a/data/templates/ipsec/swanctl.conf.j2 b/data/templates/ipsec/swanctl.conf.j2
index bf6b8259c..38d7981c6 100644
--- a/data/templates/ipsec/swanctl.conf.j2
+++ b/data/templates/ipsec/swanctl.conf.j2
@@ -63,9 +63,11 @@ secrets {
{% if peer_conf.local_address is vyos_defined %}
id-local = {{ peer_conf.local_address }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }}
{% endif %}
- id-remote = {{ peer }}
-{% if peer_conf.authentication.id is vyos_defined %}
- id-localid = {{ peer_conf.authentication.id }}
+{% for address in peer_conf.remote_address %}
+ id-remote_{{ address | dot_colon_to_dash }} = {{ address }}
+{% endfor %}
+{% if peer_conf.authentication.local_id is vyos_defined %}
+ id-localid = {{ peer_conf.authentication.local_id }}
{% endif %}
{% if peer_conf.authentication.remote_id is vyos_defined %}
id-remoteid = {{ peer_conf.authentication.remote_id }}
@@ -93,8 +95,8 @@ secrets {
{% for ra, ra_conf in remote_access.connection.items() if ra_conf.disable is not vyos_defined %}
{% if ra_conf.authentication.server_mode is vyos_defined('pre-shared-secret') %}
ike_{{ ra }} {
-{% if ra_conf.authentication.id is vyos_defined %}
- id = "{{ ra_conf.authentication.id }}"
+{% if ra_conf.authentication.local_id is vyos_defined %}
+ id = "{{ ra_conf.authentication.local_id }}"
{% elif ra_conf.local_address is vyos_defined %}
id = "{{ ra_conf.local_address }}"
{% endif %}
diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2
index 90d2c774f..d097a04fc 100644
--- a/data/templates/ipsec/swanctl/peer.j2
+++ b/data/templates/ipsec/swanctl/peer.j2
@@ -2,14 +2,14 @@
{% set name = peer.replace("@", "") | dot_colon_to_dash %}
{# peer needs to reference the global IKE configuration for certain values #}
{% set ike = ike_group[peer_conf.ike_group] %}
- peer_{{ name }} {
+ {{ name }} {
proposals = {{ ike | get_esp_ike_cipher | join(',') }}
version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }}
{% if peer_conf.virtual_address is vyos_defined %}
vips = {{ peer_conf.virtual_address | join(', ') }}
{% endif %}
- local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '0.0.0.0/0' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }}
- remote_addrs = {{ peer if peer not in ['any', '0.0.0.0'] and peer[0:1] != '@' else '0.0.0.0/0' }}
+ local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '%any' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }}
+ remote_addrs = {{ peer_conf.remote_address | join(",") if peer_conf.remote_address is vyos_defined and 'any' not in peer_conf.remote_address else '%any' }}
{% if peer_conf.authentication.mode is vyos_defined('x509') %}
send_cert = always
{% endif %}
@@ -21,7 +21,7 @@
aggressive = yes
{% endif %}
rekey_time = {{ ike.lifetime }}s
- mobike = {{ "yes" if ike.mobike is not defined or ike.mobike == "enable" else "no" }}
+ mobike = {{ "no" if ike.disable_mobike is defined else "yes" }}
{% if peer[0:1] == '@' %}
keyingtries = 0
reauth_time = 0
@@ -30,12 +30,12 @@
{% elif peer_conf.connection_type is vyos_defined('respond') %}
keyingtries = 1
{% endif %}
-{% if peer_conf.force_encapsulation is vyos_defined('enable') %}
+{% if peer_conf.force_udp_encapsulation is vyos_defined %}
encap = yes
{% endif %}
local {
-{% if peer_conf.authentication.id is vyos_defined %}
- id = "{{ peer_conf.authentication.id }}"
+{% if peer_conf.authentication.local_id is vyos_defined %}
+ id = "{{ peer_conf.authentication.local_id }}"
{% endif %}
auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }}
{% if peer_conf.authentication.mode == 'x509' %}
@@ -58,7 +58,7 @@
children {
{% if peer_conf.vti.bind is vyos_defined and peer_conf.tunnel is not vyos_defined %}
{% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is vyos_defined else esp_group[ peer_conf.default_esp_group ] %}
- peer_{{ name }}_vti {
+ {{ name }}-vti {
esp_proposals = {{ vti_esp | get_esp_ike_cipher(ike) | join(',') }}
{% if vti_esp.life_bytes is vyos_defined %}
life_bytes = {{ vti_esp.life_bytes }}
@@ -75,7 +75,7 @@
{% set if_id = peer_conf.vti.bind | replace('vti', '') | int + 1 %}
if_id_in = {{ if_id }}
if_id_out = {{ if_id }}
- ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined('enable') else 'no' }}
+ ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined else 'no' }}
mode = {{ vti_esp.mode }}
{% if peer[0:1] == '@' %}
start_action = none
@@ -101,7 +101,7 @@
{% set local_suffix = '[{0}/{1}]'.format(proto, local_port) if proto or local_port else '' %}
{% set remote_port = tunnel_conf.remote.port if tunnel_conf.remote.port is vyos_defined else '' %}
{% set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %}
- peer_{{ name }}_tunnel_{{ tunnel_id }} {
+ {{ name }}-tunnel-{{ tunnel_id }} {
esp_proposals = {{ tunnel_esp | get_esp_ike_cipher(ike) | join(',') }}
{% if tunnel_esp.life_bytes is vyos_defined %}
life_bytes = {{ tunnel_esp.life_bytes }}
@@ -126,7 +126,7 @@
local_ts = {{ peer_conf.local_address }}{{ local_suffix }}
remote_ts = {{ peer }}{{ remote_suffix }}
{% endif %}
- ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined('enable') else 'no' }}
+ ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined else 'no' }}
mode = {{ tunnel_esp.mode }}
{% if peer[0:1] == '@' %}
start_action = none
@@ -152,7 +152,7 @@
{% endif %}
}
{% if tunnel_conf.passthrough is vyos_defined %}
- peer_{{ name }}_tunnel_{{ tunnel_id }}_passthrough {
+ {{ name }}-tunnel-{{ tunnel_id }}-passthrough {
local_ts = {{ tunnel_conf.passthrough | join(",") }}
remote_ts = {{ tunnel_conf.passthrough | join(",") }}
start_action = trap
diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2
index d2760ec1f..60d2d1807 100644
--- a/data/templates/ipsec/swanctl/remote_access.j2
+++ b/data/templates/ipsec/swanctl/remote_access.j2
@@ -17,9 +17,9 @@
pools = {{ rw_conf.pool | join(',') }}
{% endif %}
local {
-{% if rw_conf.authentication.id is vyos_defined and rw_conf.authentication.use_x509_id is not vyos_defined %}
+{% if rw_conf.authentication.local_id is vyos_defined and rw_conf.authentication.use_x509_id is not vyos_defined %}
{# please use " quotes - else Apple iOS goes crazy #}
- id = "{{ rw_conf.authentication.id }}"
+ id = "{{ rw_conf.authentication.local_id }}"
{% endif %}
{% if rw_conf.authentication.server_mode == 'x509' %}
auth = pubkey
diff --git a/data/templates/login/autologout.j2 b/data/templates/login/autologout.j2
new file mode 100644
index 000000000..dc94eecc3
--- /dev/null
+++ b/data/templates/login/autologout.j2
@@ -0,0 +1,5 @@
+{% if timeout is vyos_defined %}
+TMOUT={{ timeout }}
+readonly TMOUT
+export TMOUT
+{% endif %}
diff --git a/data/templates/login/pam_radius_auth.conf.j2 b/data/templates/login/pam_radius_auth.conf.j2
index 1105b60e5..c61154753 100644
--- a/data/templates/login/pam_radius_auth.conf.j2
+++ b/data/templates/login/pam_radius_auth.conf.j2
@@ -16,7 +16,7 @@
{% if radius.server is vyos_defined %}
# server[:port] shared_secret timeout source_ip
{# .items() returns a tuple of two elements: key and value. 1 relates to the 2nd element i.e. the value and .priority relates to the key from the internal dict #}
-{% for server, options in radius.server.items() | sort(attribute='1.priority') if not options.disabled %}
+{% for server, options in radius.server.items() | sort(attribute='1.priority') if not 'disable' in options %}
{# RADIUS IPv6 servers must be specified in [] notation #}
{% if server | is_ipv4 %}
{{ server }}:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv4 if source_address.ipv4 is vyos_defined }}
@@ -33,4 +33,3 @@ mapped_priv_user radius_priv_user
vrf-name {{ radius.vrf }}
{% endif %}
{% endif %}
-
diff --git a/data/templates/nhrp/nftables.conf.j2 b/data/templates/nhrp/nftables.conf.j2
new file mode 100644
index 000000000..a0d1f6d4c
--- /dev/null
+++ b/data/templates/nhrp/nftables.conf.j2
@@ -0,0 +1,17 @@
+#!/usr/sbin/nft -f
+
+{% if first_install is not vyos_defined %}
+delete table ip vyos_nhrp_filter
+{% endif %}
+table ip vyos_nhrp_filter {
+ chain VYOS_NHRP_OUTPUT {
+ type filter hook output priority 10; policy accept;
+{% if tunnel is vyos_defined %}
+{% for tun, tunnel_conf in tunnel.items() %}
+{% if if_tunnel[tun].source_address is vyos_defined %}
+ ip protocol gre ip saddr {{ if_tunnel[tun].source_address }} ip daddr 224.0.0.0/4 counter drop comment "VYOS_NHRP_{{ tun }}"
+{% endif %}
+{% endfor %}
+{% endif %}
+ }
+}
diff --git a/data/templates/ocserv/ocserv_config.j2 b/data/templates/ocserv/ocserv_config.j2
index e0cad5181..3194354e6 100644
--- a/data/templates/ocserv/ocserv_config.j2
+++ b/data/templates/ocserv/ocserv_config.j2
@@ -1,5 +1,9 @@
### generated by vpn_openconnect.py ###
+{% if listen_address is vyos_defined %}
+listen-host = {{ listen_address }}
+{% endif %}
+
tcp-port = {{ listen_ports.tcp }}
udp-port = {{ listen_ports.udp }}
@@ -7,7 +11,7 @@ run-as-user = nobody
run-as-group = daemon
{% if "radius" in authentication.mode %}
-auth = "radius [config=/run/ocserv/radiusclient.conf]"
+auth = "radius [config=/run/ocserv/radiusclient.conf{{ ',groupconfig=true' if authentication.radius.groupconfig is vyos_defined else '' }}]"
{% elif "local" in authentication.mode %}
{% if authentication.mode.local == "password-otp" %}
auth = "plain[passwd=/run/ocserv/ocpasswd,otp=/run/ocserv/users.oath]"
@@ -62,6 +66,13 @@ device = sslvpn
dns = {{ dns }}
{% endfor %}
{% endif %}
+{% if network_settings.tunnel_all_dns is vyos_defined %}
+{% if "yes" in network_settings.tunnel_all_dns %}
+tunnel-all-dns = true
+{% else %}
+tunnel-all-dns = false
+{% endif %}
+{% endif %}
# IPv4 network pool
{% if network_settings.client_ip_settings.subnet is vyos_defined %}
@@ -85,3 +96,10 @@ route = {{ route }}
split-dns = {{ tmp }}
{% endfor %}
{% endif %}
+
+{% if authentication.group is vyos_defined %}
+# Group settings
+{% for grp in authentication.group %}
+select-group = {{ grp }}
+{% endfor %}
+{% endif %} \ No newline at end of file
diff --git a/data/templates/ssh/sshd_config.j2 b/data/templates/ssh/sshd_config.j2
index 93c6735dd..5bbfdeb88 100644
--- a/data/templates/ssh/sshd_config.j2
+++ b/data/templates/ssh/sshd_config.j2
@@ -96,3 +96,7 @@ DenyGroups {{ access_control.deny.group | join(' ') }}
# sshd(8) will send a message through the encrypted channel to request a response from the client
ClientAliveInterval {{ client_keepalive_interval }}
{% endif %}
+
+{% if rekey.data is vyos_defined %}
+RekeyLimit {{ rekey.data }}M {{ rekey.time + 'M' if rekey.time is vyos_defined }}
+{% endif %}
diff --git a/data/templates/telegraf/override.conf.j2 b/data/templates/telegraf/override.conf.j2
index d30bb19de..7e3e4aaf5 100644
--- a/data/templates/telegraf/override.conf.j2
+++ b/data/templates/telegraf/override.conf.j2
@@ -12,4 +12,5 @@ EnvironmentFile=
Environment=INFLUX_TOKEN={{ influxdb.authentication.token }}
CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN CAP_BPF CAP_DAC_OVERRIDE
AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN
-
+Restart=always
+RestartSec=10
diff --git a/data/templates/telegraf/telegraf.j2 b/data/templates/telegraf/telegraf.j2
index 6b395692b..36571ce98 100644
--- a/data/templates/telegraf/telegraf.j2
+++ b/data/templates/telegraf/telegraf.j2
@@ -45,7 +45,7 @@
### Prometheus ###
[[outputs.prometheus_client]]
## Address to listen on
- listen = "{{ prometheus_client.listen_address if prometheus_client.listen_address is vyos_defined else '' }}:{{ prometheus_client.port }}"
+ listen = "{{ prometheus_client.listen_address | bracketize_ipv6 if prometheus_client.listen_address is vyos_defined else '' }}:{{ prometheus_client.port }}"
metric_version = {{ prometheus_client.metric_version }}
{% if prometheus_client.authentication.username is vyos_defined and prometheus_client.authentication.password is vyos_defined %}
## Use HTTP Basic Authentication
@@ -110,7 +110,7 @@
server = "unixgram:///run/telegraf/telegraf_syslog.sock"
best_effort = true
syslog_standard = "RFC3164"
-{% if influxdb_configured is vyos_defined %}
+{% if influxdb is vyos_defined %}
[[inputs.exec]]
commands = [
"{{ custom_scripts_dir }}/show_firewall_input_filter.py",
diff --git a/data/templates/zone_policy/nftables.j2 b/data/templates/zone_policy/nftables.j2
deleted file mode 100644
index fe941f9f8..000000000
--- a/data/templates/zone_policy/nftables.j2
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/sbin/nft -f
-
-{% if cleanup_commands is vyos_defined %}
-{% for command in cleanup_commands %}
-{{ command }}
-{% endfor %}
-{% endif %}
-
-{% if zone is vyos_defined %}
-table ip filter {
-{% for zone_name, zone_conf in zone.items() if zone_conf.ipv4 %}
-{% if zone_conf.local_zone is vyos_defined %}
- chain VZONE_{{ zone_name }}_IN {
- iifname lo counter return
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is vyos_defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
- {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
- }
- chain VZONE_{{ zone_name }}_OUT {
- oifname lo counter return
-{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.name is vyos_defined %}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
- {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
- }
-{% else %}
- chain VZONE_{{ zone_name }} {
- iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=False) }}
-{% if zone_conf.intra_zone_filtering is vyos_defined %}
- iifname { {{ zone_conf.interface | join(",") }} } counter return
-{% endif %}
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is vyos_defined %}
-{% if zone[from_zone].local_zone is not defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endif %}
-{% endfor %}
- {{ zone_conf | nft_default_rule('zone_' + zone_name) }}
- }
-{% endif %}
-{% endfor %}
-}
-
-table ip6 filter {
-{% for zone_name, zone_conf in zone.items() if zone_conf.ipv6 %}
-{% if zone_conf.local_zone is vyos_defined %}
- chain VZONE6_{{ zone_name }}_IN {
- iifname lo counter return
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is vyos_defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
- {{ zone_conf | nft_default_rule('zone6_' + zone_name) }}
- }
- chain VZONE6_{{ zone_name }}_OUT {
- oifname lo counter return
-{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.ipv6_name is vyos_defined %}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}
- oifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endfor %}
- {{ zone_conf | nft_default_rule('zone6_' + zone_name) }}
- }
-{% else %}
- chain VZONE6_{{ zone_name }} {
- iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=True) }}
-{% if zone_conf.intra_zone_filtering is vyos_defined %}
- iifname { {{ zone_conf.interface | join(",") }} } counter return
-{% endif %}
-{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is vyos_defined %}
-{% if zone[from_zone].local_zone is not defined %}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}
- iifname { {{ zone[from_zone].interface | join(",") }} } counter return
-{% endif %}
-{% endfor %}
- {{ zone_conf | nft_default_rule('zone6_' + zone_name) }}
- }
-{% endif %}
-{% endfor %}
-}
-
-{% for zone_name, zone_conf in zone.items() %}
-{% if zone_conf.ipv4 %}
-{% if 'local_zone' in zone_conf %}
-insert rule ip filter VYOS_FW_LOCAL counter jump VZONE_{{ zone_name }}_IN
-insert rule ip filter VYOS_FW_OUTPUT counter jump VZONE_{{ zone_name }}_OUT
-{% else %}
-insert rule ip filter VYOS_FW_FORWARD oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }}
-{% endif %}
-{% endif %}
-{% if zone_conf.ipv6 %}
-{% if 'local_zone' in zone_conf %}
-insert rule ip6 filter VYOS_FW6_LOCAL counter jump VZONE6_{{ zone_name }}_IN
-insert rule ip6 filter VYOS_FW6_OUTPUT counter jump VZONE6_{{ zone_name }}_OUT
-{% else %}
-insert rule ip6 filter VYOS_FW6_FORWARD oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE6_{{ zone_name }}
-{% endif %}
-{% endif %}
-{% endfor %}
-
-{# Ensure that state-policy rule is first in the chain #}
-{% if firewall.state_policy is vyos_defined %}
-{% for chain in ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'] %}
-insert rule ip filter {{ chain }} jump VYOS_STATE_POLICY
-{% endfor %}
-{% for chain in ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL'] %}
-insert rule ip6 filter {{ chain }} jump VYOS_STATE_POLICY6
-{% endfor %}
-{% endif %}
-
-{% endif %}
diff --git a/data/vyos-firewall-init.conf b/data/vyos-firewall-init.conf
new file mode 100644
index 000000000..11a5bc7bf
--- /dev/null
+++ b/data/vyos-firewall-init.conf
@@ -0,0 +1,110 @@
+#!/usr/sbin/nft -f
+
+# Required by wanloadbalance
+table ip nat {
+ chain VYOS_PRE_SNAT_HOOK {
+ type nat hook postrouting priority 99; policy accept;
+ return
+ }
+}
+
+table inet mangle {
+ chain FORWARD {
+ type filter hook forward priority -150; policy accept;
+ }
+}
+
+table raw {
+ chain VYOS_TCP_MSS {
+ type filter hook forward priority -300; policy accept;
+ }
+
+ chain PREROUTING {
+ type filter hook prerouting priority -200; policy accept;
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump VYOS_CT_PREROUTING_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ chain OUTPUT {
+ type filter hook output priority -200; policy accept;
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump VYOS_CT_OUTPUT_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ ct helper rpc_tcp {
+ type "rpc" protocol tcp;
+ }
+
+ ct helper rpc_udp {
+ type "rpc" protocol udp;
+ }
+
+ ct helper tns_tcp {
+ type "tns" protocol tcp;
+ }
+
+ chain VYOS_CT_HELPER {
+ ct helper set "rpc_tcp" tcp dport {111} return
+ ct helper set "rpc_udp" udp dport {111} return
+ ct helper set "tns_tcp" tcp dport {1521,1525,1536} return
+ return
+ }
+
+ chain VYOS_CT_IGNORE {
+ return
+ }
+
+ chain VYOS_CT_TIMEOUT {
+ return
+ }
+
+ chain VYOS_CT_PREROUTING_HOOK {
+ return
+ }
+
+ chain VYOS_CT_OUTPUT_HOOK {
+ return
+ }
+
+ chain FW_CONNTRACK {
+ accept
+ }
+}
+
+table ip6 raw {
+ chain VYOS_TCP_MSS {
+ type filter hook forward priority -300; policy accept;
+ }
+
+ chain PREROUTING {
+ type filter hook prerouting priority -300; policy accept;
+ counter jump VYOS_CT_PREROUTING_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ chain OUTPUT {
+ type filter hook output priority -300; policy accept;
+ counter jump VYOS_CT_OUTPUT_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ chain VYOS_CT_PREROUTING_HOOK {
+ return
+ }
+
+ chain VYOS_CT_OUTPUT_HOOK {
+ return
+ }
+
+ chain FW_CONNTRACK {
+ accept
+ }
+}
diff --git a/debian/control b/debian/control
index 0db098be6..1f2151284 100644
--- a/debian/control
+++ b/debian/control
@@ -58,6 +58,7 @@ Depends:
frr-pythontools,
frr-rpki-rtrlib,
frr-snmp,
+ libpam-google-authenticator,
grc,
hostapd,
hvinfo,
diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst
index 71750b3a1..213a23d9e 100644
--- a/debian/vyos-1x.preinst
+++ b/debian/vyos-1x.preinst
@@ -2,3 +2,4 @@ dpkg-divert --package vyos-1x --add --rename /etc/securetty
dpkg-divert --package vyos-1x --add --rename /etc/security/capability.conf
dpkg-divert --package vyos-1x --add --rename /lib/systemd/system/lcdproc.service
dpkg-divert --package vyos-1x --add --rename /etc/logrotate.d/conntrackd
+dpkg-divert --package vyos-1x --add --rename /usr/share/pam-configs/radius
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index c2d652278..673461036 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -314,6 +314,40 @@
</tagNode>
</children>
</node>
+ <tagNode name="interface">
+ <properties>
+ <help>Interface name to apply firewall configuration</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="in">
+ <properties>
+ <help>Forwarded packets on inbound interface</help>
+ </properties>
+ <children>
+ #include <include/firewall/name.xml.i>
+ </children>
+ </node>
+ <node name="out">
+ <properties>
+ <help>Forwarded packets on outbound interface</help>
+ </properties>
+ <children>
+ #include <include/firewall/name.xml.i>
+ </children>
+ </node>
+ <node name="local">
+ <properties>
+ <help>Packets destined for this router</help>
+ </properties>
+ <children>
+ #include <include/firewall/name.xml.i>
+ </children>
+ </node>
+ </children>
+ </tagNode>
<leafNode name="ip-src-route">
<properties>
<help>Policy for handling IPv4 packets with source route option</help>
@@ -345,6 +379,14 @@
#include <include/firewall/default-action.xml.i>
#include <include/firewall/enable-default-log.xml.i>
#include <include/generic-description.xml.i>
+ <leafNode name="default-jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv6-name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Firewall rule number (IPv6)</help>
@@ -383,17 +425,9 @@
</children>
</node>
#include <include/firewall/common-rule.xml.i>
+ #include <include/firewall/dscp.xml.i>
#include <include/firewall/packet-length.xml.i>
- <node name="hop-limit">
- <properties>
- <help>Hop Limit</help>
- </properties>
- <children>
- #include <include/firewall/eq.xml.i>
- #include <include/firewall/gt.xml.i>
- #include <include/firewall/lt.xml.i>
- </children>
- </node>
+ #include <include/firewall/hop-limit.xml.i>
<node name="icmpv6">
<properties>
<help>ICMPv6 type and code information</help>
@@ -426,6 +460,14 @@
#include <include/firewall/icmpv6-type-name.xml.i>
</children>
</node>
+ <leafNode name="jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall ipv6-name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
</children>
</tagNode>
</children>
@@ -501,6 +543,14 @@
#include <include/firewall/default-action.xml.i>
#include <include/firewall/enable-default-log.xml.i>
#include <include/generic-description.xml.i>
+ <leafNode name="default-jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined in default-action to use this setting</help>
+ <completionHelp>
+ <path>firewall name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
<tagNode name="rule">
<properties>
<help>Firewall rule number (IPv4)</help>
@@ -539,6 +589,7 @@
</children>
</node>
#include <include/firewall/common-rule.xml.i>
+ #include <include/firewall/dscp.xml.i>
#include <include/firewall/packet-length.xml.i>
<node name="icmp">
<properties>
@@ -572,16 +623,15 @@
#include <include/firewall/icmp-type-name.xml.i>
</children>
</node>
- <node name="ttl">
+ <leafNode name="jump-target">
<properties>
- <help>Time to live limit</help>
+ <help>Set jump target. Action jump must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall name</path>
+ </completionHelp>
</properties>
- <children>
- #include <include/firewall/eq.xml.i>
- #include <include/firewall/gt.xml.i>
- #include <include/firewall/lt.xml.i>
- </children>
- </node>
+ </leafNode>
+ #include <include/firewall/ttl.xml.i>
</children>
</tagNode>
</children>
@@ -661,6 +711,7 @@
</properties>
<children>
#include <include/firewall/action-accept-drop-reject.xml.i>
+ #include <include/firewall/log.xml.i>
#include <include/firewall/rule-log-level.xml.i>
</children>
</node>
@@ -670,6 +721,7 @@
</properties>
<children>
#include <include/firewall/action-accept-drop-reject.xml.i>
+ #include <include/firewall/log.xml.i>
#include <include/firewall/rule-log-level.xml.i>
</children>
</node>
@@ -679,6 +731,7 @@
</properties>
<children>
#include <include/firewall/action-accept-drop-reject.xml.i>
+ #include <include/firewall/log.xml.i>
#include <include/firewall/rule-log-level.xml.i>
</children>
</node>
@@ -724,6 +777,143 @@
</properties>
<defaultValue>disable</defaultValue>
</leafNode>
+ <tagNode name="zone">
+ <properties>
+ <help>Zone-policy</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Zone name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/firewall/enable-default-log.xml.i>
+ <leafNode name="default-action">
+ <properties>
+ <help>Default-action for traffic coming into this zone</help>
+ <completionHelp>
+ <list>drop reject</list>
+ </completionHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop silently</description>
+ </valueHelp>
+ <valueHelp>
+ <format>reject</format>
+ <description>Drop and notify source</description>
+ </valueHelp>
+ <constraint>
+ <regex>(drop|reject)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>drop</defaultValue>
+ </leafNode>
+ <tagNode name="from">
+ <properties>
+ <help>Zone from which to filter traffic</help>
+ <completionHelp>
+ <path>zone-policy zone</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="firewall">
+ <properties>
+ <help>Firewall options</help>
+ </properties>
+ <children>
+ <leafNode name="ipv6-name">
+ <properties>
+ <help>IPv6 firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv6-name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="name">
+ <properties>
+ <help>IPv4 firewall ruleset</help>
+ <completionHelp>
+ <path>firewall name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ <leafNode name="interface">
+ <properties>
+ <help>Interface associated with zone</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface associated with zone</description>
+ </valueHelp>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ <node name="intra-zone-filtering">
+ <properties>
+ <help>Intra-zone filtering</help>
+ </properties>
+ <children>
+ <leafNode name="action">
+ <properties>
+ <help>Action for intra-zone traffic</help>
+ <completionHelp>
+ <list>accept drop</list>
+ </completionHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept traffic</description>
+ </valueHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop silently</description>
+ </valueHelp>
+ <constraint>
+ <regex>(accept|drop)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="firewall">
+ <properties>
+ <help>Use the specified firewall chain</help>
+ </properties>
+ <children>
+ <leafNode name="ipv6-name">
+ <properties>
+ <help>IPv6 firewall ruleset</help>
+ <completionHelp>
+ <path>firewall ipv6-name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="name">
+ <properties>
+ <help>IPv4 firewall ruleset</help>
+ <completionHelp>
+ <path>firewall name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <leafNode name="local-zone">
+ <properties>
+ <help>Zone to be local-zone</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i b/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
index 01cf0e040..774741a5e 100644
--- a/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
+++ b/interface-definitions/include/accel-ppp/client-ipv6-pool.xml.i
@@ -16,19 +16,19 @@
</constraint>
</properties>
<children>
- <leafNode name="mask">
- <properties>
- <help>Prefix length used for individual client</help>
- <valueHelp>
- <format>u32:48-128</format>
- <description>Client prefix length</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 48-128"/>
- </constraint>
- </properties>
- <defaultValue>64</defaultValue>
- </leafNode>
+ <leafNode name="mask">
+ <properties>
+ <help>Prefix length used for individual client</help>
+ <valueHelp>
+ <format>u32:48-128</format>
+ <description>Client prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 48-128"/>
+ </constraint>
+ </properties>
+ <defaultValue>64</defaultValue>
+ </leafNode>
</children>
</tagNode>
<tagNode name="delegate">
diff --git a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
index f44920c3f..b8dbe73b2 100644
--- a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i
@@ -6,18 +6,24 @@
<children>
<leafNode name="attribute">
<properties>
- <help>Specifies which radius attribute contains rate information. (default is Filter-Id)</help>
+ <help>RADIUS attribute that contains rate information</help>
</properties>
<defaultValue>Filter-Id</defaultValue>
</leafNode>
<leafNode name="vendor">
<properties>
- <help>Specifies the vendor dictionary. (dictionary needs to be in /usr/share/accel-ppp/radius)</help>
+ <help>Vendor dictionary</help>
+ <completionHelp>
+ <list>alcatel cisco microsoft mikrotik</list>
+ </completionHelp>
+ <constraint>
+ <validator name="accel-radius-dictionary" />
+ </constraint>
</properties>
</leafNode>
<leafNode name="enable">
<properties>
- <help>Enables Bandwidth shaping via RADIUS</help>
+ <help>Enable bandwidth shaping via RADIUS</help>
<valueless />
</properties>
</leafNode>
diff --git a/interface-definitions/include/accel-ppp/vlan.xml.i b/interface-definitions/include/accel-ppp/vlan.xml.i
new file mode 100644
index 000000000..5ef4de633
--- /dev/null
+++ b/interface-definitions/include/accel-ppp/vlan.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from accel-ppp/vlan.xml.i -->
+<leafNode name="vlan">
+ <properties>
+ <help>VLAN monitor for automatic creation of VLAN interfaces</help>
+ <valueHelp>
+ <format>u32:1-4094</format>
+ <description>VLAN for automatic creation</description>
+ </valueHelp>
+ <valueHelp>
+ <format>start-end</format>
+ <description>VLAN range for automatic creation (e.g. 1-4094)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 1-4094"/>
+ </constraint>
+ <constraintErrorMessage>VLAN IDs need to be in range 1-4094</constraintErrorMessage>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i
index d586635c8..fef3daf3b 100644
--- a/interface-definitions/include/bgp/afi-l2vpn-common.xml.i
+++ b/interface-definitions/include/bgp/afi-l2vpn-common.xml.i
@@ -27,6 +27,7 @@
<constraint>
<validator name="bgp-rd-rt" argument="--route-target"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="import">
@@ -39,6 +40,7 @@
<constraint>
<validator name="bgp-rd-rt" argument="--route-target"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
<leafNode name="export">
@@ -51,6 +53,7 @@
<constraint>
<validator name="bgp-rd-rt" argument="--route-target"/>
</constraint>
+ <multi/>
</properties>
</leafNode>
</children>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index d2bcea62a..70176144d 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1135,6 +1135,19 @@
</leafNode>
</children>
</node>
+ <node name="peer-type">
+ <properties>
+ <help>Peer type</help>
+ </properties>
+ <children>
+ <leafNode name="multipath-relax">
+ <properties>
+ <help>Allow load sharing across routes learned from different peer types</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
</children>
</node>
<leafNode name="cluster-id">
diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i
index 512cc23bd..468340cbb 100644
--- a/interface-definitions/include/firewall/action.xml.i
+++ b/interface-definitions/include/firewall/action.xml.i
@@ -3,22 +3,30 @@
<properties>
<help>Rule action</help>
<completionHelp>
- <list>accept reject drop</list>
+ <list>accept jump reject return drop</list>
</completionHelp>
<valueHelp>
<format>accept</format>
<description>Accept matching entries</description>
</valueHelp>
<valueHelp>
+ <format>jump</format>
+ <description>Jump to another chain</description>
+ </valueHelp>
+ <valueHelp>
<format>reject</format>
<description>Reject matching entries</description>
</valueHelp>
<valueHelp>
+ <format>return</format>
+ <description>Return from the current chain and continue at the next rule of the last chain</description>
+ </valueHelp>
+ <valueHelp>
<format>drop</format>
<description>Drop matching entries</description>
</valueHelp>
<constraint>
- <regex>(accept|reject|drop)</regex>
+ <regex>(accept|jump|reject|return|drop)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i
index 079864122..a4f66f5cb 100644
--- a/interface-definitions/include/firewall/common-rule.xml.i
+++ b/interface-definitions/include/firewall/common-rule.xml.i
@@ -26,6 +26,14 @@
</leafNode>
</children>
</node>
+<leafNode name="inbound-interface">
+ <properties>
+ <help>Match inbound-interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+</leafNode>
<node name="ipsec">
<properties>
<help>Inbound IPsec packets</help>
@@ -122,6 +130,14 @@
</leafNode>
</children>
</node>
+<leafNode name="outbound-interface">
+ <properties>
+ <help>Match outbound-interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+</leafNode>
<leafNode name="protocol">
<properties>
<help>Protocol to match (protocol name, number, or "all")</help>
diff --git a/interface-definitions/include/firewall/default-action.xml.i b/interface-definitions/include/firewall/default-action.xml.i
index 92a2fcaaf..80efaf335 100644
--- a/interface-definitions/include/firewall/default-action.xml.i
+++ b/interface-definitions/include/firewall/default-action.xml.i
@@ -3,22 +3,30 @@
<properties>
<help>Default-action for rule-set</help>
<completionHelp>
- <list>drop reject accept</list>
+ <list>drop jump reject return accept</list>
</completionHelp>
<valueHelp>
<format>drop</format>
<description>Drop if no prior rules are hit</description>
</valueHelp>
<valueHelp>
+ <format>jump</format>
+ <description>Jump to another chain if no prior rules are hit</description>
+ </valueHelp>
+ <valueHelp>
<format>reject</format>
<description>Drop and notify source if no prior rules are hit</description>
</valueHelp>
<valueHelp>
+ <format>return</format>
+ <description>Return from the current chain and continue at the next rule of the last chain</description>
+ </valueHelp>
+ <valueHelp>
<format>accept</format>
<description>Accept if no prior rules are hit</description>
</valueHelp>
<constraint>
- <regex>(drop|reject|accept)</regex>
+ <regex>(drop|jump|reject|return|accept)</regex>
</constraint>
</properties>
<defaultValue>drop</defaultValue>
diff --git a/interface-definitions/include/firewall/dscp.xml.i b/interface-definitions/include/firewall/dscp.xml.i
new file mode 100644
index 000000000..dd4da4894
--- /dev/null
+++ b/interface-definitions/include/firewall/dscp.xml.i
@@ -0,0 +1,36 @@
+<!-- include start from firewall/dscp.xml.i -->
+<leafNode name="dscp">
+ <properties>
+ <help>DSCP value</help>
+ <valueHelp>
+ <format>u32:0-63</format>
+ <description>DSCP value to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;start-end&gt;</format>
+ <description>DSCP range to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-63"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<leafNode name="dscp-exclude">
+ <properties>
+ <help>DSCP value not to match</help>
+ <valueHelp>
+ <format>u32:0-63</format>
+ <description>DSCP value not to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;start-end&gt;</format>
+ <description>DSCP range not to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-63"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/hop-limit.xml.i b/interface-definitions/include/firewall/hop-limit.xml.i
new file mode 100644
index 000000000..d375dc985
--- /dev/null
+++ b/interface-definitions/include/firewall/hop-limit.xml.i
@@ -0,0 +1,12 @@
+<!-- include start from firewall/hop-limit.xml.i -->
+<node name="hop-limit">
+ <properties>
+ <help>Hop limit</help>
+ </properties>
+ <children>
+ #include <include/firewall/eq.xml.i>
+ #include <include/firewall/gt.xml.i>
+ #include <include/firewall/lt.xml.i>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/name.xml.i b/interface-definitions/include/firewall/name.xml.i
new file mode 100644
index 000000000..231b9b144
--- /dev/null
+++ b/interface-definitions/include/firewall/name.xml.i
@@ -0,0 +1,18 @@
+<!-- include start from firewall/name.xml.i -->
+<leafNode name="name">
+ <properties>
+ <help>Local IPv4 firewall ruleset name for interface</help>
+ <completionHelp>
+ <path>firewall name</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<leafNode name="ipv6-name">
+ <properties>
+ <help>Local IPv6 firewall ruleset name for interface</help>
+ <completionHelp>
+ <path>firewall ipv6-name</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<!-- include end from firewall/name.xml.i --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/packet-length.xml.i b/interface-definitions/include/firewall/packet-length.xml.i
index 043f56d16..fd2eb67b0 100644
--- a/interface-definitions/include/firewall/packet-length.xml.i
+++ b/interface-definitions/include/firewall/packet-length.xml.i
@@ -11,8 +11,7 @@
<description>Packet length range to match</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- <validator name="range" argument="--min=1 --max=65535"/>
+ <validator name="numeric" argument="--allow-range --range 1-65535"/>
</constraint>
<multi/>
</properties>
@@ -29,8 +28,7 @@
<description>Packet length range not to match</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- <validator name="range" argument="--min=1 --max=65535"/>
+ <validator name="numeric" argument="--allow-range --range 1-65535"/>
</constraint>
<multi/>
</properties>
diff --git a/interface-definitions/include/firewall/tcp-flags.xml.i b/interface-definitions/include/firewall/tcp-flags.xml.i
index 5a7b5a8d3..e2ce7b9fd 100644
--- a/interface-definitions/include/firewall/tcp-flags.xml.i
+++ b/interface-definitions/include/firewall/tcp-flags.xml.i
@@ -126,8 +126,7 @@
<description>TCP MSS range (use '-' as delimiter)</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-16384"/>
- <validator name="range" argument="--min=1 --max=16384"/>
+ <validator name="numeric" argument="--allow-range --range 1-16384"/>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/ttl.xml.i b/interface-definitions/include/firewall/ttl.xml.i
new file mode 100644
index 000000000..9c782a9a5
--- /dev/null
+++ b/interface-definitions/include/firewall/ttl.xml.i
@@ -0,0 +1,12 @@
+<!-- include start from firewall/ttl.xml.i -->
+<node name="ttl">
+ <properties>
+ <help>Time to live limit</help>
+ </properties>
+ <children>
+ #include <include/firewall/eq.xml.i>
+ #include <include/firewall/gt.xml.i>
+ #include <include/firewall/lt.xml.i>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/ids/threshold.xml.i b/interface-definitions/include/ids/threshold.xml.i
new file mode 100644
index 000000000..e21e3a005
--- /dev/null
+++ b/interface-definitions/include/ids/threshold.xml.i
@@ -0,0 +1,38 @@
+<!-- include start from ids/threshold.xml.i -->
+<leafNode name="fps">
+ <properties>
+ <help>Flows per second</help>
+ <valueHelp>
+ <format>u32:0-4294967294</format>
+ <description>Flows per second</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967294"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="mbps">
+ <properties>
+ <help>Megabits per second</help>
+ <valueHelp>
+ <format>u32:0-4294967294</format>
+ <description>Megabits per second</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967294"/>
+ </constraint>
+ </properties>
+</leafNode>
+<leafNode name="pps">
+ <properties>
+ <help>Packets per second</help>
+ <valueHelp>
+ <format>u32:0-4294967294</format>
+ <description>Packets per second</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4294967294"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/adjust-mss.xml.i b/interface-definitions/include/interface/adjust-mss.xml.i
index 41140ffe1..2b184a05e 100644
--- a/interface-definitions/include/interface/adjust-mss.xml.i
+++ b/interface-definitions/include/interface/adjust-mss.xml.i
@@ -11,11 +11,11 @@
<description>Automatically sets the MSS to the proper value</description>
</valueHelp>
<valueHelp>
- <format>u32:500-65535</format>
+ <format>u32:536-65535</format>
<description>TCP Maximum segment size in bytes</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 500-65535"/>
+ <validator name="numeric" argument="--range 536-65535"/>
<regex>(clamp-mss-to-pmtu)</regex>
</constraint>
</properties>
diff --git a/interface-definitions/include/interface/interface-firewall-vif-c.xml.i b/interface-definitions/include/interface/interface-firewall-vif-c.xml.i
deleted file mode 100644
index 1bc235fcb..000000000
--- a/interface-definitions/include/interface/interface-firewall-vif-c.xml.i
+++ /dev/null
@@ -1,79 +0,0 @@
-<!-- include start from interface/interface-firewall-vif-c.xml.i -->
-<node name="firewall" owner="${vyos_conf_scripts_dir}/firewall-interface.py $VAR(../../../@).$VAR(../../@).$VAR(../@)">
- <properties>
- <priority>615</priority>
- <help>Firewall options</help>
- </properties>
- <children>
- <node name="in">
- <properties>
- <help>forwarded packets on inbound interface</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Inbound IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Inbound IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="out">
- <properties>
- <help>forwarded packets on outbound interface</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Outbound IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Outbound IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="local">
- <properties>
- <help>packets destined for this router</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Local IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Local IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
-</node>
-<!-- include end -->
diff --git a/interface-definitions/include/interface/interface-firewall-vif.xml.i b/interface-definitions/include/interface/interface-firewall-vif.xml.i
deleted file mode 100644
index a37ac5c4a..000000000
--- a/interface-definitions/include/interface/interface-firewall-vif.xml.i
+++ /dev/null
@@ -1,79 +0,0 @@
-<!-- include start from interface/interface-firewall-vif.xml.i -->
-<node name="firewall" owner="${vyos_conf_scripts_dir}/firewall-interface.py $VAR(../../@).$VAR(../@)">
- <properties>
- <priority>615</priority>
- <help>Firewall options</help>
- </properties>
- <children>
- <node name="in">
- <properties>
- <help>forwarded packets on inbound interface</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Inbound IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Inbound IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="out">
- <properties>
- <help>forwarded packets on outbound interface</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Outbound IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Outbound IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="local">
- <properties>
- <help>packets destined for this router</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Local IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Local IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
-</node>
-<!-- include end -->
diff --git a/interface-definitions/include/interface/interface-firewall.xml.i b/interface-definitions/include/interface/interface-firewall.xml.i
deleted file mode 100644
index b3f20c3bf..000000000
--- a/interface-definitions/include/interface/interface-firewall.xml.i
+++ /dev/null
@@ -1,79 +0,0 @@
-<!-- include start from interface/interface-firewall.xml.i -->
-<node name="firewall" owner="${vyos_conf_scripts_dir}/firewall-interface.py $VAR(../@)">
- <properties>
- <priority>615</priority>
- <help>Firewall options</help>
- </properties>
- <children>
- <node name="in">
- <properties>
- <help>forwarded packets on inbound interface</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Inbound IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Inbound IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="out">
- <properties>
- <help>forwarded packets on outbound interface</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Outbound IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Outbound IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- <node name="local">
- <properties>
- <help>packets destined for this router</help>
- </properties>
- <children>
- <leafNode name="name">
- <properties>
- <help>Local IPv4 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="ipv6-name">
- <properties>
- <help>Local IPv6 firewall ruleset name for interface</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
-</node>
-<!-- include end -->
diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i
index c1af9f9e3..916349ade 100644
--- a/interface-definitions/include/interface/vif-s.xml.i
+++ b/interface-definitions/include/interface/vif-s.xml.i
@@ -18,7 +18,6 @@
#include <include/interface/dhcpv6-options.xml.i>
#include <include/interface/disable-link-detect.xml.i>
#include <include/interface/disable.xml.i>
- #include <include/interface/interface-firewall-vif.xml.i>
#include <include/interface/interface-policy-vif.xml.i>
<leafNode name="protocol">
<properties>
@@ -68,7 +67,6 @@
#include <include/interface/mtu-68-16000.xml.i>
#include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
- #include <include/interface/interface-firewall-vif-c.xml.i>
#include <include/interface/interface-policy-vif-c.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/include/interface/vif.xml.i b/interface-definitions/include/interface/vif.xml.i
index 57ef8d64c..73a8c98ff 100644
--- a/interface-definitions/include/interface/vif.xml.i
+++ b/interface-definitions/include/interface/vif.xml.i
@@ -18,7 +18,6 @@
#include <include/interface/dhcpv6-options.xml.i>
#include <include/interface/disable-link-detect.xml.i>
#include <include/interface/disable.xml.i>
- #include <include/interface/interface-firewall-vif.xml.i>
#include <include/interface/interface-policy-vif.xml.i>
<leafNode name="egress-qos">
<properties>
diff --git a/interface-definitions/include/ipsec/authentication-id.xml.i b/interface-definitions/include/ipsec/authentication-id.xml.i
index 4967782ec..4e0b848c3 100644
--- a/interface-definitions/include/ipsec/authentication-id.xml.i
+++ b/interface-definitions/include/ipsec/authentication-id.xml.i
@@ -1,10 +1,10 @@
<!-- include start from ipsec/authentication-id.xml.i -->
-<leafNode name="id">
+<leafNode name="local-id">
<properties>
- <help>ID for peer authentication</help>
+ <help>Local ID for peer authentication</help>
<valueHelp>
<format>txt</format>
- <description>ID used for peer authentication</description>
+ <description>Local ID used for peer authentication</description>
</valueHelp>
</properties>
</leafNode>
diff --git a/interface-definitions/include/ipsec/remote-address.xml.i b/interface-definitions/include/ipsec/remote-address.xml.i
new file mode 100644
index 000000000..ba96290d0
--- /dev/null
+++ b/interface-definitions/include/ipsec/remote-address.xml.i
@@ -0,0 +1,30 @@
+<!-- include start from ipsec/remote-address.xml.i -->
+<leafNode name="remote-address">
+ <properties>
+ <help>IPv4 or IPv6 address of the remote peer</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address of the remote peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of the remote peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hostname</format>
+ <description>Fully qualified domain name of the remote peer</description>
+ </valueHelp>
+ <valueHelp>
+ <format>any</format>
+ <description>Allow any IP address of the remote peer</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ <validator name="fqdn"/>
+ <regex>(any)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/isis/high-low-label-value.xml.i b/interface-definitions/include/isis/high-low-label-value.xml.i
index adc28417d..f30b5af3a 100644
--- a/interface-definitions/include/isis/high-low-label-value.xml.i
+++ b/interface-definitions/include/isis/high-low-label-value.xml.i
@@ -4,7 +4,7 @@
<help>MPLS label lower bound</help>
<valueHelp>
<format>u32:16-1048575</format>
- <description>Label value</description>
+ <description>Label value (recommended minimum value: 100)</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 16-1048575"/>
diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i
index 75a0355d4..57ee19300 100644
--- a/interface-definitions/include/isis/protocol-common-config.xml.i
+++ b/interface-definitions/include/isis/protocol-common-config.xml.i
@@ -233,12 +233,6 @@
<help>Segment-Routing (SPRING) settings</help>
</properties>
<children>
- <leafNode name="enable">
- <properties>
- <help>Enable segment-routing functionality</help>
- <valueless/>
- </properties>
- </leafNode>
<node name="global-block">
<properties>
<help>Segment Routing Global Block label range</help>
diff --git a/interface-definitions/include/listen-address-single.xml.i b/interface-definitions/include/listen-address-single.xml.i
new file mode 100644
index 000000000..b5841cabb
--- /dev/null
+++ b/interface-definitions/include/listen-address-single.xml.i
@@ -0,0 +1,22 @@
+<leafNode name="listen-address">
+ <properties>
+ <help>Local IP addresses to listen on</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 address to listen for incoming connections</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address to listen for incoming connections</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ <validator name="ipv6-link-local"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- 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 791bbc0f8..28e3b473b 100644
--- a/interface-definitions/include/ospf/protocol-common-config.xml.i
+++ b/interface-definitions/include/ospf/protocol-common-config.xml.i
@@ -621,6 +621,86 @@
</constraint>
</properties>
</leafNode>
+<node name="segment-routing">
+ <properties>
+ <help>Segment-Routing (SPRING) settings</help>
+ </properties>
+ <children>
+ <node name="global-block">
+ <properties>
+ <help>Segment Routing Global Block label range</help>
+ </properties>
+ <children>
+ #include <include/isis/high-low-label-value.xml.i>
+ </children>
+ </node>
+ <node name="local-block">
+ <properties>
+ <help>Segment Routing Local Block label range</help>
+ </properties>
+ <children>
+ #include <include/isis/high-low-label-value.xml.i>
+ </children>
+ </node>
+ <leafNode name="maximum-label-depth">
+ <properties>
+ <help>Maximum MPLS labels allowed for this router</help>
+ <valueHelp>
+ <format>u32:1-16</format>
+ <description>MPLS label depth</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-16"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="prefix">
+ <properties>
+ <help>Static IPv4 prefix segment/label mapping</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 prefix segment</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-prefix"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="index">
+ <properties>
+ <help>Specify the index value of prefix segment/label ID</help>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>Specify the index value of prefix segment/label ID</help>
+ <valueHelp>
+ <format>u32:0-65535</format>
+ <description>The index segment/label ID value</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="explicit-null">
+ <properties>
+ <help>Request upstream neighbor to replace segment/label with explicit null label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="no-php-flag">
+ <properties>
+ <help>Do not request penultimate hop popping for segment/label</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+</node>
<node name="redistribute">
<properties>
<help>Redistribute information from another routing protocol</help>
diff --git a/interface-definitions/include/policy/community-clear.xml.i b/interface-definitions/include/policy/community-clear.xml.i
new file mode 100644
index 000000000..0fd57cdf0
--- /dev/null
+++ b/interface-definitions/include/policy/community-clear.xml.i
@@ -0,0 +1,8 @@
+<!-- include start from policy/community-clear.xml.i -->
+<leafNode name="none">
+ <properties>
+ <help>Completely remove communities attribute from a prefix</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/policy/community-value-list.xml.i b/interface-definitions/include/policy/community-value-list.xml.i
new file mode 100644
index 000000000..8c665c5f0
--- /dev/null
+++ b/interface-definitions/include/policy/community-value-list.xml.i
@@ -0,0 +1,90 @@
+<!-- include start from policy/community-value-list.xml.i -->
+<completionHelp>
+ <list>
+ local-as
+ no-advertise
+ no-export
+ internet
+ graceful-shutdown
+ accept-own
+ route-filter-translated-v4
+ route-filter-v4
+ route-filter-translated-v6
+ route-filter-v6
+ llgr-stale
+ no-llgr
+ accept-own-nexthop
+ blackhole
+ no-peer
+ </list>
+</completionHelp>
+<valueHelp>
+ <format>&lt;AS:VAL&gt;</format>
+ <description>Community number in &lt;0-65535:0-65535&gt; format</description>
+</valueHelp>
+<valueHelp>
+ <format>local-as</format>
+ <description>Well-known communities value NO_EXPORT_SUBCONFED 0xFFFFFF03</description>
+</valueHelp>
+<valueHelp>
+ <format>no-advertise</format>
+ <description>Well-known communities value NO_ADVERTISE 0xFFFFFF02</description>
+</valueHelp>
+<valueHelp>
+ <format>no-export</format>
+ <description>Well-known communities value NO_EXPORT 0xFFFFFF01</description>
+</valueHelp>
+<valueHelp>
+ <format>internet</format>
+ <description>Well-known communities value 0</description>
+</valueHelp>
+<valueHelp>
+ <format>graceful-shutdown</format>
+ <description>Well-known communities value GRACEFUL_SHUTDOWN 0xFFFF0000</description>
+</valueHelp>
+<valueHelp>
+ <format>accept-own</format>
+ <description>Well-known communities value ACCEPT_OWN 0xFFFF0001</description>
+</valueHelp>
+<valueHelp>
+ <format>route-filter-translated-v4</format>
+ <description>Well-known communities value ROUTE_FILTER_TRANSLATED_v4 0xFFFF0002</description>
+</valueHelp>
+<valueHelp>
+ <format>route-filter-v4</format>
+ <description>Well-known communities value ROUTE_FILTER_v4 0xFFFF0003</description>
+</valueHelp>
+<valueHelp>
+ <format>route-filter-translated-v6</format>
+ <description>Well-known communities value ROUTE_FILTER_TRANSLATED_v6 0xFFFF0004</description>
+</valueHelp>
+<valueHelp>
+ <format>route-filter-v6</format>
+ <description>Well-known communities value ROUTE_FILTER_v6 0xFFFF0005</description>
+</valueHelp>
+<valueHelp>
+ <format>llgr-stale</format>
+ <description>Well-known communities value LLGR_STALE 0xFFFF0006</description>
+</valueHelp>
+<valueHelp>
+ <format>no-llgr</format>
+ <description>Well-known communities value NO_LLGR 0xFFFF0007</description>
+</valueHelp>
+<valueHelp>
+ <format>accept-own-nexthop</format>
+ <description>Well-known communities value accept-own-nexthop 0xFFFF0008</description>
+</valueHelp>
+<valueHelp>
+ <format>blackhole</format>
+ <description>Well-known communities value BLACKHOLE 0xFFFF029A</description>
+</valueHelp>
+<valueHelp>
+ <format>no-peer</format>
+ <description>Well-known communities value NOPEER 0xFFFFFF04</description>
+</valueHelp>
+<multi/>
+<constraint>
+ <regex>local-as|no-advertise|no-export|internet|graceful-shutdown|accept-own|route-filter-translated-v4|route-filter-v4|route-filter-translated-v6|route-filter-v6|llgr-stale|no-llgr|accept-own-nexthop|blackhole|no-peer</regex>
+ <validator name="bgp-regular-community"/>
+</constraint>
+ <!-- include end -->
diff --git a/interface-definitions/include/policy/extended-community-value-list.xml.i b/interface-definitions/include/policy/extended-community-value-list.xml.i
new file mode 100644
index 000000000..c79f78c67
--- /dev/null
+++ b/interface-definitions/include/policy/extended-community-value-list.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from policy/community-value-list.xml.i -->
+<valueHelp>
+ <format>ASN:NN</format>
+ <description>based on autonomous system number in format &lt;0-65535:0-4294967295&gt;</description>
+</valueHelp>
+<valueHelp>
+ <format>IP:NN</format>
+ <description>Based on a router-id IP address in format &lt;IP:0-65535&gt;</description>
+</valueHelp>
+<constraint>
+ <validator name="bgp-extended-community"/>
+</constraint>
+<constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage>
+<multi/>
+ <!-- include end -->
diff --git a/interface-definitions/include/policy/large-community-value-list.xml.i b/interface-definitions/include/policy/large-community-value-list.xml.i
new file mode 100644
index 000000000..33b1f13a2
--- /dev/null
+++ b/interface-definitions/include/policy/large-community-value-list.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from policy/community-value-list.xml.i -->
+<valueHelp>
+ <description>Community in format &lt;0-4294967295:0-4294967295:0-4294967295&gt;</description>
+ <format>&lt;GA:LDP1:LDP2&gt;</format>
+</valueHelp>
+<multi/>
+<constraint>
+ <validator name="bgp-large-community"/>
+</constraint>
+ <!-- include end -->
diff --git a/interface-definitions/include/policy/route-common-rule-ipv6.xml.i b/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
index cfeba1a6c..662206336 100644
--- a/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
+++ b/interface-definitions/include/policy/route-common-rule-ipv6.xml.i
@@ -198,6 +198,10 @@
<validator name="numeric" argument="--range 1-200"/>
<regex>(main)</regex>
</constraint>
+ <completionHelp>
+ <list>main</list>
+ <path>protocols static table</path>
+ </completionHelp>
</properties>
</leafNode>
<leafNode name="tcp-mss">
diff --git a/interface-definitions/include/policy/route-common-rule.xml.i b/interface-definitions/include/policy/route-common-rule.xml.i
index 5a17dbc95..35fccca50 100644
--- a/interface-definitions/include/policy/route-common-rule.xml.i
+++ b/interface-definitions/include/policy/route-common-rule.xml.i
@@ -198,6 +198,10 @@
<validator name="numeric" argument="--range 1-200"/>
<regex>(main)</regex>
</constraint>
+ <completionHelp>
+ <list>main</list>
+ <path>protocols static table</path>
+ </completionHelp>
</properties>
</leafNode>
<leafNode name="tcp-mss">
diff --git a/interface-definitions/include/qos/limiter-actions.xml.i b/interface-definitions/include/qos/limiter-actions.xml.i
new file mode 100644
index 000000000..a993423aa
--- /dev/null
+++ b/interface-definitions/include/qos/limiter-actions.xml.i
@@ -0,0 +1,66 @@
+<!-- include start from qos/limiter-actions.xml.i -->
+<leafNode name="exceed-action">
+ <properties>
+ <help>Default action for packets exceeding the limiter (default: drop)</help>
+ <completionHelp>
+ <list>continue drop ok reclassify pipe</list>
+ </completionHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Don't do anything, just continue with the next action in line</description>
+ </valueHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop the packet immediately</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ok</format>
+ <description>Accept the packet</description>
+ </valueHelp>
+ <valueHelp>
+ <format>reclassify</format>
+ <description>Treat the packet as non-matching to the filter this action is attached to and continue with the next filter in line (if any)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>pipe</format>
+ <description>Pass the packet to the next action in line</description>
+ </valueHelp>
+ <constraint>
+ <regex>(continue|drop|ok|reclassify|pipe)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>drop</defaultValue>
+</leafNode>
+<leafNode name="notexceed-action">
+ <properties>
+ <help>Default action for packets not exceeding the limiter (default: ok)</help>
+ <completionHelp>
+ <list>continue drop ok reclassify pipe</list>
+ </completionHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Don't do anything, just continue with the next action in line</description>
+ </valueHelp>
+ <valueHelp>
+ <format>drop</format>
+ <description>Drop the packet immediately</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ok</format>
+ <description>Accept the packet</description>
+ </valueHelp>
+ <valueHelp>
+ <format>reclassify</format>
+ <description>Treat the packet as non-matching to the filter this action is attached to and continue with the next filter in line (if any)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>pipe</format>
+ <description>Pass the packet to the next action in line</description>
+ </valueHelp>
+ <constraint>
+ <regex>(continue|drop|ok|reclassify|pipe)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>ok</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/url.xml.i b/interface-definitions/include/url.xml.i
new file mode 100644
index 000000000..caa6f67bd
--- /dev/null
+++ b/interface-definitions/include/url.xml.i
@@ -0,0 +1,15 @@
+<!-- include start from url.xml.i -->
+<leafNode name="url">
+ <properties>
+ <help>Remote URL</help>
+ <valueHelp>
+ <format>url</format>
+ <description>Remote URL</description>
+ </valueHelp>
+ <constraint>
+ <regex>^https?:\/\/?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*(\:[0-9]+)*(\/.*)?</regex>
+ </constraint>
+ <constraintErrorMessage>Incorrect URL format</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i
index 059a89f24..065925319 100644
--- a/interface-definitions/include/version/firewall-version.xml.i
+++ b/interface-definitions/include/version/firewall-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/firewall-version.xml.i -->
-<syntaxVersion component='firewall' version='7'></syntaxVersion>
+<syntaxVersion component='firewall' version='8'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/ids-version.xml.i b/interface-definitions/include/version/ids-version.xml.i
new file mode 100644
index 000000000..9133be02b
--- /dev/null
+++ b/interface-definitions/include/version/ids-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/ids-version.xml.i -->
+<syntaxVersion component='ids' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/include/version/ipsec-version.xml.i b/interface-definitions/include/version/ipsec-version.xml.i
index 59295cc91..1c978e8e6 100644
--- a/interface-definitions/include/version/ipsec-version.xml.i
+++ b/interface-definitions/include/version/ipsec-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/ipsec-version.xml.i -->
-<syntaxVersion component='ipsec' version='9'></syntaxVersion>
+<syntaxVersion component='ipsec' version='10'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/isis-version.xml.i b/interface-definitions/include/version/isis-version.xml.i
index 4a8fef39c..7bf12e81a 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='1'></syntaxVersion>
+<syntaxVersion component='isis' version='2'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/policy-version.xml.i b/interface-definitions/include/version/policy-version.xml.i
index 426173a19..89bde20c7 100644
--- a/interface-definitions/include/version/policy-version.xml.i
+++ b/interface-definitions/include/version/policy-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/policy-version.xml.i -->
-<syntaxVersion component='policy' version='3'></syntaxVersion>
+<syntaxVersion component='policy' version='4'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/pppoe-server-version.xml.i b/interface-definitions/include/version/pppoe-server-version.xml.i
index ec81487f8..6bdd8d75c 100644
--- a/interface-definitions/include/version/pppoe-server-version.xml.i
+++ b/interface-definitions/include/version/pppoe-server-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/pppoe-server-version.xml.i -->
-<syntaxVersion component='pppoe-server' version='5'></syntaxVersion>
+<syntaxVersion component='pppoe-server' version='6'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/interfaces-bonding.xml.in b/interface-definitions/interfaces-bonding.xml.in
index 8b6c6ef62..41e4a68a8 100644
--- a/interface-definitions/interfaces-bonding.xml.in
+++ b/interface-definitions/interfaces-bonding.xml.in
@@ -56,7 +56,6 @@
#include <include/interface/disable.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/interface/mirror.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="hash-policy">
<properties>
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index 48ee1efbc..1e11cd4c6 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -41,7 +41,6 @@
#include <include/interface/disable.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/interface/mtu-68-16000.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="forwarding-delay">
<properties>
diff --git a/interface-definitions/interfaces-dummy.xml.in b/interface-definitions/interfaces-dummy.xml.in
index 01438de31..fb36741f7 100644
--- a/interface-definitions/interfaces-dummy.xml.in
+++ b/interface-definitions/interfaces-dummy.xml.in
@@ -19,7 +19,6 @@
#include <include/interface/address-ipv4-ipv6.xml.i>
#include <include/interface/description.xml.i>
#include <include/interface/disable.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<node name="ip">
<properties>
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index c821f04b2..77f130e1c 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -31,7 +31,6 @@
</leafNode>
#include <include/interface/disable-link-detect.xml.i>
#include <include/interface/disable.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="duplex">
<properties>
@@ -94,6 +93,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="rfs">
+ <properties>
+ <help>Enable Receive Flow Steering</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="sg">
<properties>
<help>Enable Scatter-Gather</help>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index 6e8a8fee2..b959c787d 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -23,7 +23,6 @@
#include <include/interface/ipv6-options.xml.i>
#include <include/interface/mac.xml.i>
#include <include/interface/mtu-1450-16000.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<node name="parameters">
<properties>
diff --git a/interface-definitions/interfaces-input.xml.in b/interface-definitions/interfaces-input.xml.in
index 2164bfa4e..d01c760f8 100644
--- a/interface-definitions/interfaces-input.xml.in
+++ b/interface-definitions/interfaces-input.xml.in
@@ -19,7 +19,6 @@
<children>
#include <include/interface/description.xml.i>
#include <include/interface/disable.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
#include <include/interface/redirect.xml.i>
</children>
diff --git a/interface-definitions/interfaces-l2tpv3.xml.in b/interface-definitions/interfaces-l2tpv3.xml.in
index 6a85064cd..bde68dd5a 100644
--- a/interface-definitions/interfaces-l2tpv3.xml.in
+++ b/interface-definitions/interfaces-l2tpv3.xml.in
@@ -32,7 +32,6 @@
<defaultValue>5000</defaultValue>
</leafNode>
#include <include/interface/disable.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="encapsulation">
<properties>
diff --git a/interface-definitions/interfaces-macsec.xml.in b/interface-definitions/interfaces-macsec.xml.in
index adb48813f..5c9f4cd76 100644
--- a/interface-definitions/interfaces-macsec.xml.in
+++ b/interface-definitions/interfaces-macsec.xml.in
@@ -21,7 +21,6 @@
#include <include/interface/dhcpv6-options.xml.i>
#include <include/interface/ipv4-options.xml.i>
#include <include/interface/ipv6-options.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
#include <include/interface/mirror.xml.i>
<node name="security">
diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in
index 6cbd91ff4..3876e31da 100644
--- a/interface-definitions/interfaces-openvpn.xml.in
+++ b/interface-definitions/interfaces-openvpn.xml.in
@@ -34,7 +34,6 @@
</children>
</node>
#include <include/interface/description.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="device-type">
<properties>
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index 9674cfc0e..84f76a7ee 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -19,7 +19,6 @@
#include <include/pppoe-access-concentrator.xml.i>
#include <include/interface/authentication.xml.i>
#include <include/interface/dial-on-demand.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
#include <include/interface/no-default-route.xml.i>
#include <include/interface/default-route-distance.xml.i>
diff --git a/interface-definitions/interfaces-pseudo-ethernet.xml.in b/interface-definitions/interfaces-pseudo-ethernet.xml.in
index 53e6445fa..4eb9bf111 100644
--- a/interface-definitions/interfaces-pseudo-ethernet.xml.in
+++ b/interface-definitions/interfaces-pseudo-ethernet.xml.in
@@ -28,7 +28,6 @@
#include <include/source-interface-ethernet.xml.i>
#include <include/interface/mac.xml.i>
#include <include/interface/mirror.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="mode">
<properties>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index 98ff878ba..fe49d337a 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -29,7 +29,6 @@
#include <include/source-address-ipv4-ipv6.xml.i>
#include <include/interface/tunnel-remote.xml.i>
#include <include/source-interface.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="6rd-prefix">
<properties>
diff --git a/interface-definitions/interfaces-vti.xml.in b/interface-definitions/interfaces-vti.xml.in
index aa83a04b2..eeaea0dc3 100644
--- a/interface-definitions/interfaces-vti.xml.in
+++ b/interface-definitions/interfaces-vti.xml.in
@@ -25,7 +25,6 @@
#include <include/interface/mirror.xml.i>
#include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index faa3dd5e0..4902ff36d 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -54,7 +54,6 @@
#include <include/interface/mac.xml.i>
#include <include/interface/mtu-1200-16000.xml.i>
#include <include/interface/mirror.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<leafNode name="mtu">
<defaultValue>1450</defaultValue>
diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in
index 4a1b4ac68..23f50d146 100644
--- a/interface-definitions/interfaces-wireguard.xml.in
+++ b/interface-definitions/interfaces-wireguard.xml.in
@@ -21,7 +21,6 @@
#include <include/interface/disable.xml.i>
#include <include/port-number.xml.i>
#include <include/interface/mtu-68-16000.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
#include <include/interface/mirror.xml.i>
<leafNode name="mtu">
diff --git a/interface-definitions/interfaces-wireless.xml.in b/interface-definitions/interfaces-wireless.xml.in
index daee770a9..9e7fc29bc 100644
--- a/interface-definitions/interfaces-wireless.xml.in
+++ b/interface-definitions/interfaces-wireless.xml.in
@@ -20,7 +20,6 @@
</properties>
<children>
#include <include/interface/address-ipv4-ipv6-dhcp.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
<node name="capabilities">
<properties>
diff --git a/interface-definitions/interfaces-wwan.xml.in b/interface-definitions/interfaces-wwan.xml.in
index 3071e6091..b0b8367dc 100644
--- a/interface-definitions/interfaces-wwan.xml.in
+++ b/interface-definitions/interfaces-wwan.xml.in
@@ -39,7 +39,6 @@
#include <include/interface/ipv4-options.xml.i>
#include <include/interface/ipv6-options.xml.i>
#include <include/interface/dial-on-demand.xml.i>
- #include <include/interface/interface-firewall.xml.i>
#include <include/interface/interface-policy.xml.i>
#include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in
index d969613b1..8619e839e 100644
--- a/interface-definitions/policy-local-route.xml.in
+++ b/interface-definitions/policy-local-route.xml.in
@@ -6,6 +6,7 @@
<node name="local-route" owner="${vyos_conf_scripts_dir}/policy-local-route.py">
<properties>
<help>IPv4 policy route of local traffic</help>
+ <priority>500</priority>
</properties>
<children>
<tagNode name="rule">
@@ -96,6 +97,7 @@
<node name="local-route6" owner="${vyos_conf_scripts_dir}/policy-local-route.py">
<properties>
<help>IPv6 policy route of local traffic</help>
+ <priority>500</priority>
</properties>
<children>
<tagNode name="rule">
diff --git a/interface-definitions/policy-route.xml.in b/interface-definitions/policy-route.xml.in
index c2a9a8d94..f480f3bd5 100644
--- a/interface-definitions/policy-route.xml.in
+++ b/interface-definitions/policy-route.xml.in
@@ -47,6 +47,9 @@
</children>
</node>
#include <include/policy/route-common-rule-ipv6.xml.i>
+ #include <include/firewall/dscp.xml.i>
+ #include <include/firewall/packet-length.xml.i>
+ #include <include/firewall/hop-limit.xml.i>
</children>
</tagNode>
</children>
@@ -96,6 +99,9 @@
</children>
</node>
#include <include/policy/route-common-rule.xml.i>
+ #include <include/firewall/dscp.xml.i>
+ #include <include/firewall/packet-length.xml.i>
+ #include <include/firewall/ttl.xml.i>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in
index e794c4b90..6c60276d5 100644
--- a/interface-definitions/policy.xml.in
+++ b/interface-definitions/policy.xml.in
@@ -1118,67 +1118,120 @@
<valueless/>
</properties>
</leafNode>
- <node name="comm-list">
+ <node name="community">
<properties>
- <help>BGP communities matching a community-list</help>
+ <help>BGP community attribute</help>
</properties>
<children>
- <leafNode name="comm-list">
+ <leafNode name="add">
+ <properties>
+ <help>Add communities to a prefix</help>
+ #include <include/policy/community-value-list.xml.i>
+ </properties>
+ </leafNode>
+ <leafNode name="replace">
+ <properties>
+ <help>Set communities for a prefix</help>
+ #include <include/policy/community-value-list.xml.i>
+ </properties>
+ </leafNode>
+ #include <include/policy/community-clear.xml.i>
+ <leafNode name="delete">
<properties>
- <help>BGP communities with a community-list</help>
+ <help>Remove communities defined in a list from a prefix</help>
<completionHelp>
<path>policy community-list</path>
</completionHelp>
<valueHelp>
+ <description>Community-list</description>
<format>txt</format>
- <description>BGP communities with a community-list</description>
</valueHelp>
</properties>
</leafNode>
+ </children>
+ </node>
+ <node name="large-community">
+ <properties>
+ <help>BGP large community attribute</help>
+ </properties>
+ <children>
+ <leafNode name="add">
+ <properties>
+ <help>Add large communities to a prefix ;</help>
+ #include <include/policy/large-community-value-list.xml.i>
+ </properties>
+ </leafNode>
+ <leafNode name="replace">
+ <properties>
+ <help>Set large communities for a prefix</help>
+ #include <include/policy/large-community-value-list.xml.i>
+ </properties>
+ </leafNode>
+ #include <include/policy/community-clear.xml.i>
<leafNode name="delete">
<properties>
- <help>Delete BGP communities matching the community-list</help>
- <valueless/>
+ <help>Remove communities defined in a list from a prefix</help>
+ <completionHelp>
+ <path>policy large-community-list</path>
+ </completionHelp>
+ <valueHelp>
+ <description>Community-list</description>
+ <format>txt</format>
+ </valueHelp>
</properties>
</leafNode>
</children>
</node>
- <leafNode name="community">
+ <node name="extcommunity">
<properties>
- <help>Border Gateway Protocl (BGP) community attribute</help>
- <completionHelp>
- <list>local-AS no-advertise no-export internet additive none</list>
- </completionHelp>
- <valueHelp>
- <format>&lt;aa:nn&gt;</format>
- <description>Community number in AA:NN format</description>
- </valueHelp>
- <valueHelp>
- <format>local-AS</format>
- <description>Well-known communities value NO_EXPORT_SUBCONFED 0xFFFFFF03</description>
- </valueHelp>
- <valueHelp>
- <format>no-advertise</format>
- <description>Well-known communities value NO_ADVERTISE 0xFFFFFF02</description>
- </valueHelp>
- <valueHelp>
- <format>no-export</format>
- <description>Well-known communities value NO_EXPORT 0xFFFFFF01</description>
- </valueHelp>
- <valueHelp>
- <format>internet</format>
- <description>Well-known communities value 0</description>
- </valueHelp>
- <valueHelp>
- <format>additive</format>
- <description>New value is appended to the existing value</description>
- </valueHelp>
- <valueHelp>
- <format>none</format>
- <description>No community attribute</description>
- </valueHelp>
+ <help>BGP extended community attribute</help>
</properties>
- </leafNode>
+ <children>
+ <leafNode name="bandwidth">
+ <properties>
+ <help>Bandwidth value in Mbps</help>
+ <completionHelp>
+ <list>cumulative num-multipaths</list>
+ </completionHelp>
+ <valueHelp>
+ <format>u32:1-25600</format>
+ <description>Bandwidth value in Mbps</description>
+ </valueHelp>
+ <valueHelp>
+ <format>cumulative</format>
+ <description>Cumulative bandwidth of all multipaths (outbound-only)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>num-multipaths</format>
+ <description>Internally computed bandwidth based on number of multipaths (outbound-only)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-25600"/>
+ <regex>(cumulative|num-multipaths)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="bandwidth-non-transitive">
+ <properties>
+ <help>The link bandwidth extended community is encoded as non-transitive</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="rt">
+ <properties>
+ <help>Set route target value</help>
+ #include <include/policy/extended-community-value-list.xml.i>
+ </properties>
+ </leafNode>
+ <leafNode name="soo">
+ <properties>
+ <help>Set Site of Origin value</help>
+ #include <include/policy/extended-community-value-list.xml.i>
+ </properties>
+ </leafNode>
+ #include <include/policy/community-clear.xml.i>
+ </children>
+ </node>
<leafNode name="distance">
<properties>
<help>Locally significant administrative distance</help>
@@ -1229,71 +1282,6 @@
</node>
</children>
</node>
- <node name="extcommunity">
- <properties>
- <help>BGP extended community attribute</help>
- </properties>
- <children>
- <leafNode name="bandwidth">
- <properties>
- <help>Bandwidth value in Mbps</help>
- <completionHelp>
- <list>cumulative num-multipaths</list>
- </completionHelp>
- <valueHelp>
- <format>u32:1-25600</format>
- <description>Bandwidth value in Mbps</description>
- </valueHelp>
- <valueHelp>
- <format>cumulative</format>
- <description>Cumulative bandwidth of all multipaths (outbound-only)</description>
- </valueHelp>
- <valueHelp>
- <format>num-multipaths</format>
- <description>Internally computed bandwidth based on number of multipaths (outbound-only)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-25600"/>
- <regex>(cumulative|num-multipaths)</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="rt">
- <properties>
- <help>Set route target value</help>
- <valueHelp>
- <format>ASN:NN</format>
- <description>based on autonomous system number</description>
- </valueHelp>
- <valueHelp>
- <format>IP:NN</format>
- <description>Based on a router-id IP address</description>
- </valueHelp>
- <constraint>
- <regex>(((\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b)|(\d+)):(\d+) ?)+</regex>
- </constraint>
- <constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage>
- </properties>
- </leafNode>
- <leafNode name="soo">
- <properties>
- <help>Set Site of Origin value</help>
- <valueHelp>
- <format>ASN:NN</format>
- <description>based on autonomous system number</description>
- </valueHelp>
- <valueHelp>
- <format>IP:NN</format>
- <description>Based on a router-id IP address</description>
- </valueHelp>
- <constraint>
- <regex>((?:[0-9]{1,3}\.){3}[0-9]{1,3}|\d+):\d+</regex>
- </constraint>
- <constraintErrorMessage>Should be in form: ASN:NN or IPADDR:NN where ASN is autonomous system number</constraintErrorMessage>
- </properties>
- </leafNode>
- </children>
- </node>
<leafNode name="ip-next-hop">
<properties>
<help>Nexthop IP address</help>
@@ -1368,30 +1356,6 @@
</leafNode>
</children>
</node>
- <leafNode name="large-community">
- <properties>
- <help>Set BGP large community value</help>
- <valueHelp>
- <format>txt</format>
- <description>ASN:nn:mm BGP large community</description>
- </valueHelp>
- <completionHelp>
- <path>policy large-community-list</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="large-comm-list-delete">
- <properties>
- <help>Delete BGP communities matching the large community-list</help>
- <completionHelp>
- <path>policy large-community-list</path>
- </completionHelp>
- <valueHelp>
- <format>txt</format>
- <description>BGP large community-list</description>
- </valueHelp>
- </properties>
- </leafNode>
<leafNode name="local-preference">
<properties>
<help>BGP local preference attribute</help>
diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in
index e8f575a1e..e2dbcbeef 100644
--- a/interface-definitions/qos.xml.in
+++ b/interface-definitions/qos.xml.in
@@ -188,6 +188,7 @@
#include <include/qos/burst.xml.i>
#include <include/generic-description.xml.i>
#include <include/qos/match.xml.i>
+ #include <include/qos/limiter-actions.xml.i>
<leafNode name="priority">
<properties>
<help>Priority for rule evaluation</help>
@@ -211,6 +212,7 @@
<children>
#include <include/qos/bandwidth.xml.i>
#include <include/qos/burst.xml.i>
+ #include <include/qos/limiter-actions.xml.i>
</children>
</node>
#include <include/generic-description.xml.i>
diff --git a/interface-definitions/service-console-server.xml.in b/interface-definitions/service-console-server.xml.in
index e9591ad87..fb71538dd 100644
--- a/interface-definitions/service-console-server.xml.in
+++ b/interface-definitions/service-console-server.xml.in
@@ -28,6 +28,14 @@
</properties>
<children>
#include <include/interface/description.xml.i>
+ <leafNode name="alias">
+ <properties>
+ <help>Human-readable name for this console</help>
+ <constraint>
+ <regex>[-_a-zA-Z0-9.]{1,128}</regex>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="speed">
<properties>
<help>Serial port baud rate</help>
diff --git a/interface-definitions/service-ids-ddos-protection.xml.in b/interface-definitions/service-ids-ddos-protection.xml.in
index 86fc4dffa..a661b845d 100644
--- a/interface-definitions/service-ids-ddos-protection.xml.in
+++ b/interface-definitions/service-ids-ddos-protection.xml.in
@@ -107,42 +107,38 @@
<help>Attack limits thresholds</help>
</properties>
<children>
- <leafNode name="fps">
+ <node name="general">
<properties>
- <help>Flows per second</help>
- <valueHelp>
- <format>u32:0-4294967294</format>
- <description>Flows per second</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-4294967294"/>
- </constraint>
+ <help>General threshold</help>
</properties>
- </leafNode>
- <leafNode name="mbps">
+ <children>
+ #include <include/ids/threshold.xml.i>
+ </children>
+ </node>
+ <node name="tcp">
<properties>
- <help>Megabits per second</help>
- <valueHelp>
- <format>u32:0-4294967294</format>
- <description>Megabits per second</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-4294967294"/>
- </constraint>
+ <help>TCP threshold</help>
</properties>
- </leafNode>
- <leafNode name="pps">
+ <children>
+ #include <include/ids/threshold.xml.i>
+ </children>
+ </node>
+ <node name="udp">
<properties>
- <help>Packets per second</help>
- <valueHelp>
- <format>u32:0-4294967294</format>
- <description>Packets per second</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 0-4294967294"/>
- </constraint>
+ <help>UDP threshold</help>
</properties>
- </leafNode>
+ <children>
+ #include <include/ids/threshold.xml.i>
+ </children>
+ </node>
+ <node name="icmp">
+ <properties>
+ <help>ICMP threshold</help>
+ </properties>
+ <children>
+ #include <include/ids/threshold.xml.i>
+ </children>
+ </node>
</children>
</node>
</children>
diff --git a/interface-definitions/service-ipoe-server.xml.in b/interface-definitions/service-ipoe-server.xml.in
index cd3aa3638..ef8569437 100644
--- a/interface-definitions/service-ipoe-server.xml.in
+++ b/interface-definitions/service-ipoe-server.xml.in
@@ -10,30 +10,31 @@
<children>
<tagNode name="interface">
<properties>
- <help>Network interface to server IPoE</help>
+ <help>Interface to listen dhcp or unclassified packets</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
</properties>
<children>
- <leafNode name="network-mode">
+ <leafNode name="mode">
<properties>
- <help>Network Layer IPoE serves on</help>
+ <help>Client connectivity mode</help>
<completionHelp>
- <list>L2 L3</list>
+ <list>l2 l3</list>
</completionHelp>
- <constraint>
- <regex>(L2|L3)</regex>
- </constraint>
<valueHelp>
- <format>L2</format>
- <description>client share the same subnet</description>
+ <format>l2</format>
+ <description>Client located on same interface as server</description>
</valueHelp>
<valueHelp>
- <format>L3</format>
- <description>clients are behind this router</description>
+ <format>l3</format>
+ <description>Client located behind a router</description>
</valueHelp>
+ <constraint>
+ <regex>(l2|l3)</regex>
+ </constraint>
</properties>
+ <defaultValue>l2</defaultValue>
</leafNode>
<leafNode name="network">
<properties>
@@ -53,6 +54,7 @@
<description>One VLAN per client</description>
</valueHelp>
</properties>
+ <defaultValue>shared</defaultValue>
</leafNode>
<leafNode name="client-subnet">
<properties>
@@ -85,30 +87,19 @@
</leafNode>
<leafNode name="giaddr">
<properties>
- <help>address of the relay agent (Relay Agent IP Address)</help>
+ <help>Relay Agent IPv4 Address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Gateway IP address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
</properties>
</leafNode>
</children>
</node>
- <leafNode name="vlan-id">
- <properties>
- <help>VLAN monitor for the automatic creation of vlans (user per vlan)</help>
- <constraint>
- <validator name="numeric" argument="--range 1-4096"/>
- </constraint>
- <constraintErrorMessage>VLAN ID needs to be between 1 and 4096</constraintErrorMessage>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="vlan-range">
- <properties>
- <help>VLAN monitor for the automatic creation of vlans (user per vlan)</help>
- <constraint>
- <regex>(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2})-(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2})</regex>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/accel-ppp/vlan.xml.i>
</children>
</tagNode>
#include <include/name-server-ipv4-ipv6.xml.i>
@@ -120,6 +111,13 @@
<tagNode name="name">
<properties>
<help>Pool name</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Name of IP pool</description>
+ </valueHelp>
+ <constraint>
+ <regex>[-_a-zA-Z0-9.]+</regex>
+ </constraint>
</properties>
<children>
#include <include/accel-ppp/gateway-address.xml.i>
@@ -159,15 +157,15 @@
</leafNode>
<tagNode name="interface">
<properties>
- <help>Network interface the client mac will appear on</help>
+ <help>Network interface for client MAC addresses</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces.py</script>
</completionHelp>
</properties>
<children>
- <tagNode name="mac-address">
+ <tagNode name="mac">
<properties>
- <help>Client mac address allowed to receive an IP address</help>
+ <help>Media Access Control (MAC) address</help>
<valueHelp>
<format>macaddr</format>
<description>Hardware (MAC) address</description>
@@ -200,13 +198,17 @@
</leafNode>
</children>
</node>
- <leafNode name="vlan-id">
+ <leafNode name="vlan">
<properties>
- <help>VLAN-ID of the client network</help>
+ <help>VLAN monitor for automatic creation of VLAN interfaces</help>
+ <valueHelp>
+ <format>u32:1-4094</format>
+ <description>Client VLAN id</description>
+ </valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-4096"/>
+ <validator name="numeric" argument="--range 1-4094"/>
</constraint>
- <constraintErrorMessage>VLAN ID needs to be between 1 and 4096</constraintErrorMessage>
+ <constraintErrorMessage>VLAN IDs need to be in range 1-4094</constraintErrorMessage>
</properties>
</leafNode>
</children>
diff --git a/interface-definitions/service-monitoring-telegraf.xml.in b/interface-definitions/service-monitoring-telegraf.xml.in
index 68215dba4..47f943d83 100644
--- a/interface-definitions/service-monitoring-telegraf.xml.in
+++ b/interface-definitions/service-monitoring-telegraf.xml.in
@@ -228,7 +228,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/listen-address.xml.i>
+ #include <include/listen-address-single.xml.i>
<leafNode name="metric-version">
<properties>
<help>Metric version control mapping from Telegraf to Prometheus format</help>
diff --git a/interface-definitions/service-pppoe-server.xml.in b/interface-definitions/service-pppoe-server.xml.in
index 50f42849b..b31109296 100644
--- a/interface-definitions/service-pppoe-server.xml.in
+++ b/interface-definitions/service-pppoe-server.xml.in
@@ -68,33 +68,7 @@
</completionHelp>
</properties>
<children>
- <leafNode name="vlan-id">
- <properties>
- <help>VLAN monitor for the automatic creation of single vlan</help>
- <valueHelp>
- <format>u32:1-4094</format>
- <description>VLAN monitor for the automatic creation of single vlan</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-4094"/>
- </constraint>
- <constraintErrorMessage>VLAN ID needs to be between 1 and 4094</constraintErrorMessage>
- <multi/>
- </properties>
- </leafNode>
- <leafNode name="vlan-range">
- <properties>
- <help>VLAN monitor for the automatic creation of vlans range</help>
- <valueHelp>
- <format>start-end</format>
- <description>VLAN monitor range for the automatic creation of vlans (e.g. 1-4094)</description>
- </valueHelp>
- <constraint>
- <validator name="range" argument="--min=1 --max=4094"/>
- </constraint>
- <multi/>
- </properties>
- </leafNode>
+ #include <include/accel-ppp/vlan.xml.i>
</children>
</tagNode>
#include <include/accel-ppp/gateway-address.xml.i>
diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in
index 126183162..f3c731fe5 100644
--- a/interface-definitions/ssh.xml.in
+++ b/interface-definitions/ssh.xml.in
@@ -206,6 +206,37 @@
</properties>
<defaultValue>22</defaultValue>
</leafNode>
+ <node name="rekey">
+ <properties>
+ <help>SSH session rekey limit</help>
+ </properties>
+ <children>
+ <leafNode name="data">
+ <properties>
+ <help>Threshold data in megabytes</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Megabytes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="time">
+ <properties>
+ <help>Threshold time in minutes</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Minutes</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<leafNode name="client-keepalive-interval">
<properties>
<help>Enable transmission of keepalives from server to client</help>
diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in
index 14f12b569..5810a97c6 100644
--- a/interface-definitions/system-conntrack.xml.in
+++ b/interface-definitions/system-conntrack.xml.in
@@ -259,13 +259,13 @@
</leafNode>
<leafNode name="max-retrans">
<properties>
- <help>TCP maximum retransmit attempts</help>
+ <help>Maximum number of packets that can be retransmitted without received an ACK</help>
<valueHelp>
- <format>u32:1-2147483647</format>
- <description>Generic connection timeout in seconds</description>
+ <format>u32:1-255</format>
+ <description>Number of packets to be retransmitted</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-2147483647"/>
+ <validator name="numeric" argument="--range 1-255"/>
</constraint>
</properties>
<defaultValue>3</defaultValue>
diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in
index 79c7c4791..7dd045e6c 100644
--- a/interface-definitions/system-login.xml.in
+++ b/interface-definitions/system-login.xml.in
@@ -227,6 +227,19 @@
#include <include/interface/vrf.xml.i>
</children>
</node>
+ <leafNode name="timeout">
+ <properties>
+ <help>Session timeout</help>
+ <valueHelp>
+ <format>u32:5-604800</format>
+ <description>Session timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 5-604800"/>
+ </constraint>
+ <constraintErrorMessage>Timeout must be between 5 and 604800 seconds</constraintErrorMessage>
+ </properties>
+ </leafNode>
</children>
</node>
</children>
diff --git a/interface-definitions/system-option.xml.in b/interface-definitions/system-option.xml.in
index 8cd25799b..a9fed81fe 100644
--- a/interface-definitions/system-option.xml.in
+++ b/interface-definitions/system-option.xml.in
@@ -36,7 +36,7 @@
<properties>
<help>System keyboard layout, type ISO2</help>
<completionHelp>
- <list>us fr de fi no dk dvorak</list>
+ <list>us fr de es fi jp106 no dk dvorak</list>
</completionHelp>
<valueHelp>
<format>us</format>
@@ -51,10 +51,18 @@
<description>Germany</description>
</valueHelp>
<valueHelp>
+ <format>es</format>
+ <description>Spain</description>
+ </valueHelp>
+ <valueHelp>
<format>fi</format>
<description>Finland</description>
</valueHelp>
<valueHelp>
+ <format>jp106</format>
+ <description>Japan</description>
+ </valueHelp>
+ <valueHelp>
<format>no</format>
<description>Norway</description>
</valueHelp>
@@ -66,6 +74,10 @@
<format>dvorak</format>
<description>Dvorak</description>
</valueHelp>
+ <constraint>
+ <regex>(us|fr|de|es|fi|jp106|no|dk|dvorak)</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid keyboard layout</constraintErrorMessage>
</properties>
<defaultValue>us</defaultValue>
</leafNode>
diff --git a/interface-definitions/system-update-check.xml.in b/interface-definitions/system-update-check.xml.in
new file mode 100644
index 000000000..e4d7041ec
--- /dev/null
+++ b/interface-definitions/system-update-check.xml.in
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interfaceDefinition>
+ <node name="system">
+ <children>
+ <node name="update-check" owner="${vyos_conf_scripts_dir}/system_update_check.py">
+ <properties>
+ <help>Check available update images</help>
+ <priority>9999</priority>
+ </properties>
+ <children>
+ <leafNode name="auto-check">
+ <properties>
+ <help>Enable auto check for new images</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ #include <include/url.xml.i>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/vpn-ipsec.xml.in b/interface-definitions/vpn-ipsec.xml.in
index d36fbb024..4776c53dc 100644
--- a/interface-definitions/vpn-ipsec.xml.in
+++ b/interface-definitions/vpn-ipsec.xml.in
@@ -24,23 +24,9 @@
<children>
<leafNode name="compression">
<properties>
- <help>ESP compression</help>
- <completionHelp>
- <list>disable enable</list>
- </completionHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable ESP compression</description>
- </valueHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable ESP compression</description>
- </valueHelp>
- <constraint>
- <regex>(disable|enable)</regex>
- </constraint>
+ <help>Enable ESP compression</help>
+ <valueless/>
</properties>
- <defaultValue>disable</defaultValue>
</leafNode>
<leafNode name="lifetime">
<properties>
@@ -309,20 +295,7 @@
<leafNode name="ikev2-reauth">
<properties>
<help>Re-authentication of the remote peer during an IKE re-key (IKEv2 only)</help>
- <completionHelp>
- <list>yes no</list>
- </completionHelp>
- <valueHelp>
- <format>yes</format>
- <description>Enable remote host re-authentication during an IKE rekey (currently broken due to a strongswan bug)</description>
- </valueHelp>
- <valueHelp>
- <format>no</format>
- <description>Disable remote host re-authenticaton during an IKE rekey</description>
- </valueHelp>
- <constraint>
- <regex>(yes|no)</regex>
- </constraint>
+ <valueless/>
</properties>
</leafNode>
<leafNode name="key-exchange">
@@ -357,25 +330,11 @@
</properties>
<defaultValue>28800</defaultValue>
</leafNode>
- <leafNode name="mobike">
+ <leafNode name="disable-mobike">
<properties>
- <help>Enable MOBIKE Support (IKEv2 only)</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Enable MOBIKE</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Disable MOBIKE</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
+ <help>Disable MOBIKE Support (IKEv2 only)</help>
+ <valueless/>
</properties>
- <defaultValue>enable</defaultValue>
</leafNode>
<leafNode name="mode">
<properties>
@@ -664,6 +623,14 @@
<tagNode name="profile">
<properties>
<help>VPN IPsec profile</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Profile name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[a-zA-Z][0-9a-zA-Z_-]+</regex>
+ </constraint>
+ <constraintErrorMessage>Profile name must be alphanumeric and can contain hyphen(s) and underscore(s)</constraintErrorMessage>
</properties>
<children>
#include <include/generic-disable-node.xml.i>
@@ -719,6 +686,14 @@
<tagNode name="connection">
<properties>
<help>IKEv2 VPN connection name</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Connection name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[a-zA-Z][0-9a-zA-Z_-]+</regex>
+ </constraint>
+ <constraintErrorMessage>Profile name must be alphanumeric and can contain hyphen(s) and underscore(s)</constraintErrorMessage>
</properties>
<children>
<node name="authentication">
@@ -929,23 +904,15 @@
<children>
<tagNode name="peer">
<properties>
- <help>VPN peer</help>
- <valueHelp>
- <format>ipv4</format>
- <description>IPv4 address of the peer</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6</format>
- <description>IPv6 address of the peer</description>
- </valueHelp>
+ <help>Connection name of the peer</help>
<valueHelp>
<format>txt</format>
- <description>Hostname of the peer</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;@text&gt;</format>
- <description>ID of the peer</description>
+ <description>Connection name of the peer</description>
</valueHelp>
+ <constraint>
+ <regex>[-_a-zA-Z0-9|@]+</regex>
+ </constraint>
+ <constraintErrorMessage>Peer connection name must be alphanumeric and can contain hyphen and underscores</constraintErrorMessage>
</properties>
<children>
#include <include/generic-disable-node.xml.i>
@@ -1031,23 +998,10 @@
</leafNode>
#include <include/generic-description.xml.i>
#include <include/dhcp-interface.xml.i>
- <leafNode name="force-encapsulation">
+ <leafNode name="force-udp-encapsulation">
<properties>
- <help>Force UDP Encapsulation for ESP payloads</help>
- <completionHelp>
- <list>enable disable</list>
- </completionHelp>
- <valueHelp>
- <format>enable</format>
- <description>Force UDP encapsulation</description>
- </valueHelp>
- <valueHelp>
- <format>disable</format>
- <description>Do not force UDP encapsulation</description>
- </valueHelp>
- <constraint>
- <regex>(enable|disable)</regex>
- </constraint>
+ <help>Force UDP encapsulation</help>
+ <valueless/>
</properties>
</leafNode>
#include <include/ipsec/ike-group.xml.i>
@@ -1075,6 +1029,7 @@
</properties>
</leafNode>
#include <include/ipsec/local-address.xml.i>
+ #include <include/ipsec/remote-address.xml.i>
<tagNode name="tunnel">
<properties>
<help>Peer tunnel</help>
diff --git a/interface-definitions/vpn-l2tp.xml.in b/interface-definitions/vpn-l2tp.xml.in
index fd70a76dc..cb5900e0d 100644
--- a/interface-definitions/vpn-l2tp.xml.in
+++ b/interface-definitions/vpn-l2tp.xml.in
@@ -238,29 +238,7 @@
</leafNode>
</children>
</node>
- <node name="rate-limit">
- <properties>
- <help>Upload/Download speed limits</help>
- </properties>
- <children>
- <leafNode name="attribute">
- <properties>
- <help>Specifies which radius attribute contains rate information</help>
- </properties>
- </leafNode>
- <leafNode name="vendor">
- <properties>
- <help>Specifies the vendor dictionary. (dictionary needs to be in /usr/share/accel-ppp/radius)</help>
- </properties>
- </leafNode>
- <leafNode name="enable">
- <properties>
- <help>Enables Bandwidth shaping via RADIUS</help>
- <valueless />
- </properties>
- </leafNode>
- </children>
- </node>
+ #include <include/accel-ppp/radius-additions-rate-limit.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/vpn-openconnect.xml.in b/interface-definitions/vpn-openconnect.xml.in
index 6309863c5..3b3a83bd4 100644
--- a/interface-definitions/vpn-openconnect.xml.in
+++ b/interface-definitions/vpn-openconnect.xml.in
@@ -50,6 +50,16 @@
</leafNode>
</children>
</node>
+ <leafNode name="group">
+ <properties>
+ <help>Group that a client is allowed to select (from a list). Maps to RADIUS Class attribute.</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Group string. The group may be followed by a user-friendly name in brackets: group1[First Group]</description>
+ </valueHelp>
+ <multi/>
+ </properties>
+ </leafNode>
#include <include/auth-local-users.xml.i>
<node name="local-users">
<children>
@@ -144,10 +154,19 @@
</properties>
<defaultValue>2</defaultValue>
</leafNode>
+ <leafNode name="groupconfig">
+ <properties>
+ <help>If the groupconfig option is set, then config-per-user will be overriden, and all configuration will be read from RADIUS.</help>
+ </properties>
+ </leafNode>
</children>
</node>
</children>
</node>
+ #include <include/listen-address-ipv4.xml.i>
+ <leafNode name="listen-address">
+ <defaultValue>0.0.0.0</defaultValue>
+ </leafNode>
<node name="listen-ports">
<properties>
<help>Specify custom ports to use for client connections</help>
@@ -278,6 +297,26 @@
<multi/>
</properties>
</leafNode>
+ <leafNode name="tunnel-all-dns">
+ <properties>
+ <help>If the tunnel-all-dns option is set to yes, tunnel all DNS queries via the VPN. This is the default when a default route is set.</help>
+ <completionHelp>
+ <list>yes no</list>
+ </completionHelp>
+ <valueHelp>
+ <format>yes</format>
+ <description>Enable tunneling of all DNS traffic</description>
+ </valueHelp>
+ <valueHelp>
+ <format>no</format>
+ <description>Disable tunneling of all DNS traffic</description>
+ </valueHelp>
+ <constraint>
+ <regex>(yes|no)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>no</defaultValue>
+ </leafNode>
</children>
</node>
</children>
diff --git a/interface-definitions/vpn-pptp.xml.in b/interface-definitions/vpn-pptp.xml.in
index 28a53acb9..5e52965fd 100644
--- a/interface-definitions/vpn-pptp.xml.in
+++ b/interface-definitions/vpn-pptp.xml.in
@@ -110,6 +110,7 @@
</node>
#include <include/radius-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/xml-component-version.xml.in b/interface-definitions/xml-component-version.xml.in
index cf86f83d6..914e3bc69 100644
--- a/interface-definitions/xml-component-version.xml.in
+++ b/interface-definitions/xml-component-version.xml.in
@@ -14,6 +14,7 @@
#include <include/version/flow-accounting-version.xml.i>
#include <include/version/https-version.xml.i>
#include <include/version/interfaces-version.xml.i>
+ #include <include/version/ids-version.xml.i>
#include <include/version/ipoe-server-version.xml.i>
#include <include/version/ipsec-version.xml.i>
#include <include/version/isis-version.xml.i>
diff --git a/interface-definitions/zone-policy.xml.in b/interface-definitions/zone-policy.xml.in
deleted file mode 100644
index dc3408c3d..000000000
--- a/interface-definitions/zone-policy.xml.in
+++ /dev/null
@@ -1,148 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="zone-policy" owner="${vyos_conf_scripts_dir}/zone_policy.py">
- <properties>
- <help>Configure zone-policy</help>
- <priority>250</priority>
- </properties>
- <children>
- <tagNode name="zone">
- <properties>
- <help>Zone name</help>
- <valueHelp>
- <format>txt</format>
- <description>Zone name</description>
- </valueHelp>
- <constraint>
- <regex>[a-zA-Z0-9][\w\-\.]*</regex>
- </constraint>
- </properties>
- <children>
- #include <include/generic-description.xml.i>
- #include <include/firewall/enable-default-log.xml.i>
- <leafNode name="default-action">
- <properties>
- <help>Default-action for traffic coming into this zone</help>
- <completionHelp>
- <list>drop reject</list>
- </completionHelp>
- <valueHelp>
- <format>drop</format>
- <description>Drop silently</description>
- </valueHelp>
- <valueHelp>
- <format>reject</format>
- <description>Drop and notify source</description>
- </valueHelp>
- <constraint>
- <regex>(drop|reject)</regex>
- </constraint>
- </properties>
- <defaultValue>drop</defaultValue>
- </leafNode>
- <tagNode name="from">
- <properties>
- <help>Zone from which to filter traffic</help>
- <completionHelp>
- <path>zone-policy zone</path>
- </completionHelp>
- </properties>
- <children>
- <node name="firewall">
- <properties>
- <help>Firewall options</help>
- </properties>
- <children>
- <leafNode name="ipv6-name">
- <properties>
- <help>IPv6 firewall ruleset</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="name">
- <properties>
- <help>IPv4 firewall ruleset</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </tagNode>
- <leafNode name="interface">
- <properties>
- <help>Interface associated with zone</help>
- <valueHelp>
- <format>txt</format>
- <description>Interface associated with zone</description>
- </valueHelp>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- <multi/>
- </properties>
- </leafNode>
- <node name="intra-zone-filtering">
- <properties>
- <help>Intra-zone filtering</help>
- </properties>
- <children>
- <leafNode name="action">
- <properties>
- <help>Action for intra-zone traffic</help>
- <completionHelp>
- <list>accept drop</list>
- </completionHelp>
- <valueHelp>
- <format>accept</format>
- <description>Accept traffic</description>
- </valueHelp>
- <valueHelp>
- <format>drop</format>
- <description>Drop silently</description>
- </valueHelp>
- <constraint>
- <regex>(accept|drop)</regex>
- </constraint>
- </properties>
- </leafNode>
- <node name="firewall">
- <properties>
- <help>Use the specified firewall chain</help>
- </properties>
- <children>
- <leafNode name="ipv6-name">
- <properties>
- <help>IPv6 firewall ruleset</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="name">
- <properties>
- <help>IPv4 firewall ruleset</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
- <leafNode name="local-zone">
- <properties>
- <help>Zone to be local-zone</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </tagNode>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/op-mode-definitions/clear-session.xml.in b/op-mode-definitions/clear-session.xml.in
new file mode 100644
index 000000000..bfafe6312
--- /dev/null
+++ b/op-mode-definitions/clear-session.xml.in
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="clear">
+ <children>
+ <tagNode name="session">
+ <properties>
+ <help>Terminate TTY or PTS user session</help>
+ <completionHelp>
+ <script>who | awk '{print $2}'</script>
+ </completionHelp>
+ </properties>
+ <command>sudo pkill -9 -t $3</command>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/connect.xml.in b/op-mode-definitions/connect.xml.in
index 8f19eac70..d0c93195c 100644
--- a/op-mode-definitions/connect.xml.in
+++ b/op-mode-definitions/connect.xml.in
@@ -10,6 +10,7 @@
<help>Connect to device attached to serial console server</help>
<completionHelp>
<path>service console-server device</path>
+ <script>${vyos_completion_dir}/list_consoles.sh</script>
</completionHelp>
</properties>
<command>/usr/bin/console "$3"</command>
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 d2804e3b3..7dbc4fde5 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
@@ -153,7 +153,7 @@
<properties>
<help>Show BGP information for specified neighbor</help>
<completionHelp>
- <script>vtysh -c 'show bgp summary' | awk '{print $1'} | grep -e '^[0-9a-f]'</script>
+ <script>vtysh -c "$(IFS=$' '; echo "${COMP_WORDS[@]:0:${#COMP_WORDS[@]}-2} summary")" | awk '/^[0-9a-f]/ {print $1}'</script>
</completionHelp>
</properties>
<command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in
index 975d20465..01462ad8f 100644
--- a/op-mode-definitions/monitor-log.xml.in
+++ b/op-mode-definitions/monitor-log.xml.in
@@ -237,7 +237,7 @@
</leafNode>
<leafNode name="ipsec">
<properties>
- <help>Monitor last lines of IPSec</help>
+ <help>Monitor last lines of IPsec</help>
</properties>
<command>journalctl --no-hostname --boot --follow --unit strongswan-starter.service</command>
</leafNode>
diff --git a/op-mode-definitions/show-console-server.xml.in b/op-mode-definitions/show-console-server.xml.in
index 253d15498..eae6fd536 100644
--- a/op-mode-definitions/show-console-server.xml.in
+++ b/op-mode-definitions/show-console-server.xml.in
@@ -8,7 +8,7 @@
<properties>
<help>Show log for serial console server</help>
</properties>
- <command>/usr/bin/journalctl --unit conserver-server.service</command>
+ <command>journalctl --no-hostname --boot --follow --unit conserver-server.service</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/show-interfaces-pppoe.xml.in b/op-mode-definitions/show-interfaces-pppoe.xml.in
index 767836abf..09608bc04 100644
--- a/op-mode-definitions/show-interfaces-pppoe.xml.in
+++ b/op-mode-definitions/show-interfaces-pppoe.xml.in
@@ -17,7 +17,7 @@
<properties>
<help>Show specified PPPoE interface log</help>
</properties>
- <command>/usr/bin/journalctl --unit "ppp@$4".service</command>
+ <command>journalctl --no-hostname --boot --follow --unit "ppp@$4".service</command>
</leafNode>
<leafNode name="statistics">
<properties>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index ebd198215..8906d9ef3 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -384,7 +384,7 @@
</leafNode>
<leafNode name="ipsec">
<properties>
- <help>Show log for IPSec</help>
+ <help>Show log for IPsec</help>
</properties>
<command>journalctl --no-hostname --boot --unit strongswan-starter.service</command>
</leafNode>
diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in
index 60ed28b6f..4a0e6c3b2 100644
--- a/op-mode-definitions/show-system.xml.in
+++ b/op-mode-definitions/show-system.xml.in
@@ -142,7 +142,7 @@
<properties>
<help>Show summary of system processes</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_uptime.py</command>
+ <command>${vyos_op_scripts_dir}/uptime.py show</command>
</leafNode>
<leafNode name="tree">
<properties>
@@ -162,13 +162,19 @@
<properties>
<help>Show filesystem usage</help>
</properties>
- <command>df -h -x squashfs</command>
+ <command>${vyos_op_scripts_dir}/storage.py show</command>
+ </leafNode>
+ <leafNode name="updates">
+ <properties>
+ <help>Show system available updates</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/system.py show_update</command>
</leafNode>
<leafNode name="uptime">
<properties>
<help>Show system uptime and load averages</help>
</properties>
- <command>${vyos_op_scripts_dir}/show_uptime.py</command>
+ <command>${vyos_op_scripts_dir}/uptime.py show</command>
</leafNode>
</children>
</node>
diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in
index 8c9e76651..f1af65fcb 100644
--- a/op-mode-definitions/vpn-ipsec.xml.in
+++ b/op-mode-definitions/vpn-ipsec.xml.in
@@ -55,9 +55,9 @@
<children>
<node name="vpn">
<properties>
- <help>Restart IPSec VPN</help>
+ <help>Restart the IPsec VPN process</help>
</properties>
- <command>if pgrep charon >/dev/null ; then sudo ipsec restart ; sleep 3 ; sudo swanctl -q ; else echo "IPSec process not running" ; fi</command>
+ <command>if pgrep charon >/dev/null ; then sudo ipsec restart ; sleep 3 ; sudo swanctl -q ; else echo "IPsec process not running" ; fi</command>
</node>
</children>
</node>
@@ -134,7 +134,7 @@
</node>
<node name="ipsec">
<properties>
- <help>Show Internet Protocol Security (IPSec) information</help>
+ <help>Show Internet Protocol Security (IPsec) information</help>
</properties>
<children>
<node name="policy">
@@ -151,19 +151,19 @@
</leafNode>
<node name="sa">
<properties>
- <help>Show all active IPSec Security Associations (SA)</help>
+ <help>Show all active IPsec Security Associations (SA)</help>
</properties>
<children>
<!--
<node name="detail">
<properties>
- <help>Show Detail on all active IPSec Security Associations (SA)</help>
+ <help>Show Detail on all active IPsec Security Associations (SA)</help>
</properties>
<command></command>
</node>
<tagNode name="stats">
<properties>
- <help>Show statistics for all currently active IPSec Security Associations (SA)</help>
+ <help>Show statistics for all currently active IPsec Security Associations (SA)</help>
<valueHelp>
<format>txt</format>
<description>Show Statistics for SAs associated with a specific peer</description>
@@ -182,12 +182,12 @@
-->
<node name="verbose">
<properties>
- <help>Show Verbose Detail on all active IPSec Security Associations (SA)</help>
+ <help>Show Verbose Detail on all active IPsec Security Associations (SA)</help>
</properties>
- <command>if pgrep charon >/dev/null ; then sudo /usr/sbin/ipsec statusall ; else echo "IPSec process not running" ; fi</command>
+ <command>if pgrep charon >/dev/null ; then sudo /usr/sbin/ipsec statusall ; else echo "IPsec process not running" ; fi</command>
</node>
</children>
- <command>if pgrep charon >/dev/null ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_sa ; else echo "IPSec process not running" ; fi</command>
+ <command>if pgrep charon >/dev/null ; then sudo ${vyos_op_scripts_dir}/ipsec.py show_sa ; else echo "IPsec process not running" ; fi</command>
</node>
<node name="state">
<properties>
@@ -197,9 +197,9 @@
</node>
<node name="status">
<properties>
- <help>Show status of IPSec process</help>
+ <help>Show status of IPsec process</help>
</properties>
- <command>if pgrep charon >/dev/null ; then echo -e "IPSec Process Running: $(pgrep charon)\n$(sudo /usr/sbin/ipsec status)" ; else echo "IPSec process not running" ; fi</command>
+ <command>if pgrep charon >/dev/null ; then echo -e "IPsec Process Running: $(pgrep charon)\n$(sudo /usr/sbin/ipsec status)" ; else echo "IPsec process not running" ; fi</command>
</node>
</children>
</node>
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 912bc94f2..53decfbf5 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -643,7 +643,9 @@ def get_accel_dict(config, base, chap_secrets):
from vyos.util import get_half_cpus
from vyos.template import is_ipv4
- dict = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
+ dict = config.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
@@ -663,6 +665,18 @@ def get_accel_dict(config, base, chap_secrets):
# added to individual local users instead - so we can simply delete them
if dict_search('client_ipv6_pool.prefix.mask', default_values):
del default_values['client_ipv6_pool']['prefix']['mask']
+ # delete empty dicts
+ if len (default_values['client_ipv6_pool']['prefix']) == 0:
+ del default_values['client_ipv6_pool']['prefix']
+ if len (default_values['client_ipv6_pool']) == 0:
+ del default_values['client_ipv6_pool']
+
+ # T2665: IPoE only - it has an interface tag node
+ # added to individual local users instead - so we can simply delete them
+ if dict_search('authentication.interface', default_values):
+ del default_values['authentication']['interface']
+ if dict_search('interface', default_values):
+ del default_values['interface']
dict = dict_merge(default_values, dict)
@@ -684,11 +698,9 @@ def get_accel_dict(config, base, chap_secrets):
dict.update({'name_server_ipv4' : ns_v4, 'name_server_ipv6' : ns_v6})
del dict['name_server']
- # Add individual RADIUS server default values
+ # T2665: Add individual RADIUS server default values
if dict_search('authentication.radius.server', dict):
- # T2665
default_values = defaults(base + ['authentication', 'radius', 'server'])
-
for server in dict_search('authentication.radius.server', dict):
dict['authentication']['radius']['server'][server] = dict_merge(
default_values, dict['authentication']['radius']['server'][server])
@@ -698,22 +710,31 @@ def get_accel_dict(config, base, chap_secrets):
if 'disable_accounting' in dict['authentication']['radius']['server'][server]:
dict['authentication']['radius']['server'][server]['acct_port'] = '0'
- # Add individual local-user default values
+ # T2665: Add individual local-user default values
if dict_search('authentication.local_users.username', dict):
- # T2665
default_values = defaults(base + ['authentication', 'local-users', 'username'])
-
for username in dict_search('authentication.local_users.username', dict):
dict['authentication']['local_users']['username'][username] = dict_merge(
default_values, dict['authentication']['local_users']['username'][username])
- # Add individual IPv6 client-pool default mask if required
+ # T2665: Add individual IPv6 client-pool default mask if required
if dict_search('client_ipv6_pool.prefix', dict):
- # T2665
default_values = defaults(base + ['client-ipv6-pool', 'prefix'])
-
for prefix in dict_search('client_ipv6_pool.prefix', dict):
dict['client_ipv6_pool']['prefix'][prefix] = dict_merge(
default_values, dict['client_ipv6_pool']['prefix'][prefix])
+ # T2665: IPoE only - add individual local-user default values
+ if dict_search('authentication.interface', dict):
+ default_values = defaults(base + ['authentication', 'interface'])
+ for interface in dict_search('authentication.interface', dict):
+ dict['authentication']['interface'][interface] = dict_merge(
+ default_values, dict['authentication']['interface'][interface])
+
+ if dict_search('interface', dict):
+ default_values = defaults(base + ['interface'])
+ for interface in dict_search('interface', dict):
+ dict['interface'][interface] = dict_merge(default_values,
+ dict['interface'][interface])
+
return dict
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 447ec795c..afa0c5b33 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -381,14 +381,14 @@ def verify_vlan_config(config):
verify_mtu_parent(c_vlan, config)
verify_mtu_parent(c_vlan, s_vlan)
-def verify_accel_ppp_base_service(config):
+def verify_accel_ppp_base_service(config, local_users=True):
"""
Common helper function which must be used by all Accel-PPP services based
on get_config_dict()
"""
# vertify auth settings
- if dict_search('authentication.mode', config) == 'local':
- if not dict_search('authentication.local_users', config):
+ if local_users and dict_search('authentication.mode', config) == 'local':
+ if dict_search(f'authentication.local_users', config) == None:
raise ConfigError('Authentication mode local requires local users to be configured!')
for user in dict_search('authentication.local_users.username', config):
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 0bc5378db..4075e55b0 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -52,9 +52,9 @@ def get_ips_domains_dict(list_domains):
return ip_dict
-def nft_init_set(group_name, table="filter", family="ip"):
+def nft_init_set(group_name, table="vyos_filter", family="ip"):
"""
- table ip filter {
+ table ip vyos_filter {
set GROUP_NAME
type ipv4_addr
flags interval
@@ -63,9 +63,9 @@ def nft_init_set(group_name, table="filter", family="ip"):
return call(f'nft add set ip {table} {group_name} {{ type ipv4_addr\\; flags interval\\; }}')
-def nft_add_set_elements(group_name, elements, table="filter", family="ip"):
+def nft_add_set_elements(group_name, elements, table="vyos_filter", family="ip"):
"""
- table ip filter {
+ table ip vyos_filter {
set GROUP_NAME {
type ipv4_addr
flags interval
@@ -75,18 +75,18 @@ def nft_add_set_elements(group_name, elements, table="filter", family="ip"):
elements = ", ".join(elements)
return call(f'nft add element {family} {table} {group_name} {{ {elements} }} ')
-def nft_flush_set(group_name, table="filter", family="ip"):
+def nft_flush_set(group_name, table="vyos_filter", family="ip"):
"""
Flush elements of nft set
"""
return call(f'nft flush set {family} {table} {group_name}')
-def nft_update_set_elements(group_name, elements, table="filter", family="ip"):
+def nft_update_set_elements(group_name, elements, table="vyos_filter", family="ip"):
"""
Update elements of nft set
"""
- flush_set = nft_flush_set(group_name, table="filter", family="ip")
- nft_add_set = nft_add_set_elements(group_name, elements, table="filter", family="ip")
+ flush_set = nft_flush_set(group_name, table="vyos_filter", family="ip")
+ nft_add_set = nft_add_set_elements(group_name, elements, table="vyos_filter", family="ip")
return flush_set, nft_add_set
# END firewall group domain-group (sets)
@@ -248,6 +248,14 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
value = rule_conf['hop_limit'][op]
output.append(f'ip6 hoplimit {operator} {value}')
+ if 'inbound_interface' in rule_conf:
+ iiface = rule_conf['inbound_interface']
+ output.append(f'iifname {iiface}')
+
+ if 'outbound_interface' in rule_conf:
+ oiface = rule_conf['outbound_interface']
+ output.append(f'oifname {oiface}')
+
if 'ttl' in rule_conf:
operators = {'eq': '==', 'gt': '>', 'lt': '<'}
for op, operator in operators.items():
@@ -274,6 +282,13 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
negated_lengths_str = ','.join(rule_conf['packet_length_exclude'])
output.append(f'ip{def_suffix} length != {{{negated_lengths_str}}}')
+ if 'dscp' in rule_conf:
+ dscp_str = ','.join(rule_conf['dscp'])
+ output.append(f'ip{def_suffix} dscp {{{dscp_str}}}')
+
+ if 'dscp_exclude' in rule_conf:
+ negated_dscp_str = ','.join(rule_conf['dscp_exclude'])
+ output.append(f'ip{def_suffix} dscp != {{{negated_dscp_str}}}')
if 'ipsec' in rule_conf:
if 'match_ipsec' in rule_conf['ipsec']:
@@ -319,6 +334,10 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'action' in rule_conf:
output.append(nft_action(rule_conf['action']))
+ if 'jump' in rule_conf['action']:
+ target = rule_conf['jump_target']
+ output.append(f'NAME{def_suffix}_{target}')
+
else:
output.append('return')
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index b8deb3311..519cfc58c 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -16,6 +16,7 @@
import os
import re
+from glob import glob
from vyos.ethtool import Ethtool
from vyos.ifconfig.interface import Interface
from vyos.util import run
@@ -69,13 +70,6 @@ class EthernetIf(Interface):
},
}}
- _sysfs_set = {**Interface._sysfs_set, **{
- 'rps': {
- 'convert': lambda cpus: cpus if cpus else '0',
- 'location': '/sys/class/net/{ifname}/queues/rx-0/rps_cpus',
- },
- }}
-
def __init__(self, ifname, **kargs):
super().__init__(ifname, **kargs)
self.ethtool = Ethtool(ifname)
@@ -246,6 +240,7 @@ class EthernetIf(Interface):
raise ValueError('Value out of range')
rps_cpus = '0'
+ queues = len(glob(f'/sys/class/net/{self.ifname}/queues/rx-*'))
if state:
# Enable RPS on all available CPUs except CPU0 which we will not
# utilize so the system has one spare core when it's under high
@@ -255,8 +250,23 @@ class EthernetIf(Interface):
# Linux will clip that internally!
rps_cpus = 'ffffffff,ffffffff,ffffffff,fffffffe'
+ for i in range(0, queues):
+ self._write_sysfs(f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_cpus', rps_cpus)
+
# send bitmask representation as hex string without leading '0x'
- return self.set_interface('rps', rps_cpus)
+ return True
+
+ def set_rfs(self, state):
+ rfs_flow = 0
+ queues = len(glob(f'/sys/class/net/{self.ifname}/queues/rx-*'))
+ if state:
+ global_rfs_flow = 32768
+ rfs_flow = int(global_rfs_flow/queues)
+
+ for i in range(0, queues):
+ self._write_sysfs(f'/sys/class/net/{self.ifname}/queues/rx-{i}/rps_flow_cnt', rfs_flow)
+
+ return True
def set_sg(self, state):
"""
@@ -342,6 +352,9 @@ class EthernetIf(Interface):
# RPS - Receive Packet Steering
self.set_rps(dict_search('offload.rps', config) != None)
+ # RFS - Receive Flow Steering
+ self.set_rfs(dict_search('offload.rfs', config) != None)
+
# scatter-gather option
self.set_sg(dict_search('offload.sg', config) != None)
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 28b5e2991..fe5e9c519 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2022 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
@@ -17,11 +17,11 @@ import os
import time
from datetime import timedelta
+from tempfile import NamedTemporaryFile
from hurry.filesize import size
from hurry.filesize import alternative
-from vyos.config import Config
from vyos.ifconfig import Interface
from vyos.ifconfig import Operational
from vyos.template import is_ipv6
@@ -71,10 +71,11 @@ class WireGuardOperational(Operational):
return output
def show_interface(self):
- wgdump = self._dump().get(self.config['ifname'], None)
-
+ from vyos.config import Config
c = Config()
+ wgdump = self._dump().get(self.config['ifname'], None)
+
c.set_level(["interfaces", "wireguard", self.config['ifname']])
description = c.return_effective_value(["description"])
ips = c.return_effective_values(["address"])
@@ -167,64 +168,66 @@ class WireGuardIf(Interface):
# remove no longer associated peers first
if 'peer_remove' in config:
- for tmp in config['peer_remove']:
- peer = config['peer_remove'][tmp]
- peer['ifname'] = config['ifname']
+ for peer, public_key in config['peer_remove'].items():
+ self._cmd(f'wg set {self.ifname} peer {public_key} remove')
- cmd = 'wg set {ifname} peer {public_key} remove'
- self._cmd(cmd.format(**peer))
-
- config['private_key_file'] = '/tmp/tmp.wireguard.key'
- with open(config['private_key_file'], 'w') as f:
- f.write(config['private_key'])
+ tmp_file = NamedTemporaryFile('w')
+ tmp_file.write(config['private_key'])
+ tmp_file.flush()
# Wireguard base command is identical for every peer
- base_cmd = 'wg set {ifname} private-key {private_key_file}'
+ base_cmd = 'wg set {ifname}'
if 'port' in config:
base_cmd += ' listen-port {port}'
if 'fwmark' in config:
base_cmd += ' fwmark {fwmark}'
+ base_cmd += f' private-key {tmp_file.name}'
base_cmd = base_cmd.format(**config)
- for tmp in config['peer']:
- peer = config['peer'][tmp]
-
- # start of with a fresh 'wg' command
- cmd = base_cmd + ' peer {public_key}'
-
- # If no PSK is given remove it by using /dev/null - passing keys via
- # the shell (usually bash) is considered insecure, thus we use a file
- no_psk_file = '/dev/null'
- psk_file = no_psk_file
- if 'preshared_key' in peer:
- psk_file = '/tmp/tmp.wireguard.psk'
- with open(psk_file, 'w') as f:
- f.write(peer['preshared_key'])
- cmd += f' preshared-key {psk_file}'
-
- # Persistent keepalive is optional
- if 'persistent_keepalive'in peer:
- cmd += ' persistent-keepalive {persistent_keepalive}'
-
- # Multiple allowed-ip ranges can be defined - ensure we are always
- # dealing with a list
- if isinstance(peer['allowed_ips'], str):
- peer['allowed_ips'] = [peer['allowed_ips']]
- cmd += ' allowed-ips ' + ','.join(peer['allowed_ips'])
-
- # Endpoint configuration is optional
- if {'address', 'port'} <= set(peer):
- if is_ipv6(peer['address']):
- cmd += ' endpoint [{address}]:{port}'
- else:
- cmd += ' endpoint {address}:{port}'
+ if 'peer' in config:
+ for peer, peer_config in config['peer'].items():
+ # T4702: No need to configure this peer when it was explicitly
+ # marked as disabled - also active sessions are terminated as
+ # the public key was already removed when entering this method!
+ if 'disable' in peer_config:
+ continue
+
+ # start of with a fresh 'wg' command
+ cmd = base_cmd + ' peer {public_key}'
+
+ # If no PSK is given remove it by using /dev/null - passing keys via
+ # the shell (usually bash) is considered insecure, thus we use a file
+ no_psk_file = '/dev/null'
+ psk_file = no_psk_file
+ if 'preshared_key' in peer_config:
+ psk_file = '/tmp/tmp.wireguard.psk'
+ with open(psk_file, 'w') as f:
+ f.write(peer_config['preshared_key'])
+ cmd += f' preshared-key {psk_file}'
+
+ # Persistent keepalive is optional
+ if 'persistent_keepalive'in peer_config:
+ cmd += ' persistent-keepalive {persistent_keepalive}'
+
+ # Multiple allowed-ip ranges can be defined - ensure we are always
+ # dealing with a list
+ if isinstance(peer_config['allowed_ips'], str):
+ peer_config['allowed_ips'] = [peer_config['allowed_ips']]
+ cmd += ' allowed-ips ' + ','.join(peer_config['allowed_ips'])
+
+ # Endpoint configuration is optional
+ if {'address', 'port'} <= set(peer_config):
+ if is_ipv6(peer_config['address']):
+ cmd += ' endpoint [{address}]:{port}'
+ else:
+ cmd += ' endpoint {address}:{port}'
- self._cmd(cmd.format(**peer))
+ self._cmd(cmd.format(**peer_config))
- # PSK key file is not required to be stored persistently as its backed by CLI
- if psk_file != no_psk_file and os.path.exists(psk_file):
- os.remove(psk_file)
+ # PSK key file is not required to be stored persistently as its backed by CLI
+ if psk_file != no_psk_file and os.path.exists(psk_file):
+ os.remove(psk_file)
# call base class
super().update(config)
diff --git a/python/vyos/nat.py b/python/vyos/nat.py
new file mode 100644
index 000000000..31bbdc386
--- /dev/null
+++ b/python/vyos/nat.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 vyos.template import is_ip_network
+from vyos.util import dict_search_args
+
+def parse_nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
+ output = []
+ ip_prefix = 'ip6' if ipv6 else 'ip'
+ log_prefix = ('DST' if nat_type == 'destination' else 'SRC') + f'-NAT-{rule_id}'
+ log_suffix = ''
+
+ if ipv6:
+ log_prefix = log_prefix.replace("NAT-", "NAT66-")
+
+ ignore_type_addr = False
+ translation_str = ''
+
+ if 'inbound_interface' in rule_conf:
+ ifname = rule_conf['inbound_interface']
+ if ifname != 'any':
+ output.append(f'iifname "{ifname}"')
+
+ if 'outbound_interface' in rule_conf:
+ ifname = rule_conf['outbound_interface']
+ if ifname != 'any':
+ output.append(f'oifname "{ifname}"')
+
+ if 'protocol' in rule_conf and rule_conf['protocol'] != 'all':
+ protocol = rule_conf['protocol']
+ if protocol == 'tcp_udp':
+ protocol = '{ tcp, udp }'
+ output.append(f'meta l4proto {protocol}')
+
+ if 'exclude' in rule_conf:
+ translation_str = 'return'
+ log_suffix = '-EXCL'
+ elif 'translation' in rule_conf:
+ translation_prefix = nat_type[:1]
+ translation_output = [f'{translation_prefix}nat']
+ addr = dict_search_args(rule_conf, 'translation', 'address')
+ port = dict_search_args(rule_conf, 'translation', 'port')
+
+ if addr and is_ip_network(addr):
+ if not ipv6:
+ map_addr = dict_search_args(rule_conf, nat_type, 'address')
+ translation_output.append(f'{ip_prefix} prefix to {ip_prefix} {translation_prefix}addr map {{ {map_addr} : {addr} }}')
+ ignore_type_addr = True
+ else:
+ translation_output.append(f'prefix to {addr}')
+ elif addr == 'masquerade':
+ if port:
+ addr = f'{addr} to '
+ translation_output = [addr]
+ log_suffix = '-MASQ'
+ else:
+ translation_output.append('to')
+ if addr:
+ translation_output.append(addr)
+
+ options = []
+ addr_mapping = dict_search_args(rule_conf, 'translation', 'options', 'address_mapping')
+ port_mapping = dict_search_args(rule_conf, 'translation', 'options', 'port_mapping')
+ if addr_mapping == 'persistent':
+ options.append('persistent')
+ if port_mapping and port_mapping != 'none':
+ options.append(port_mapping)
+
+ translation_str = " ".join(translation_output) + (f':{port}' if port else '')
+
+ if options:
+ translation_str += f' {",".join(options)}'
+
+ for target in ['source', 'destination']:
+ prefix = target[:1]
+ addr = dict_search_args(rule_conf, target, 'address')
+ if addr and not (ignore_type_addr and target == nat_type):
+ operator = ''
+ if addr[:1] == '!':
+ operator = '!='
+ addr = addr[1:]
+ output.append(f'{ip_prefix} {prefix}addr {operator} {addr}')
+
+ addr_prefix = dict_search_args(rule_conf, target, 'prefix')
+ if addr_prefix and ipv6:
+ operator = ''
+ if addr_prefix[:1] == '!':
+ operator = '!='
+ addr_prefix = addr[1:]
+ output.append(f'ip6 {prefix}addr {operator} {addr_prefix}')
+
+ port = dict_search_args(rule_conf, target, 'port')
+ if port:
+ protocol = rule_conf['protocol']
+ if protocol == 'tcp_udp':
+ protocol = 'th'
+ operator = ''
+ if port[:1] == '!':
+ operator = '!='
+ port = port[1:]
+ output.append(f'{protocol} {prefix}port {operator} {{ {port} }}')
+
+ output.append('counter')
+
+ if 'log' in rule_conf:
+ output.append(f'log prefix "[{log_prefix}{log_suffix}]"')
+
+ if translation_str:
+ output.append(translation_str)
+
+ output.append(f'comment "{log_prefix}"')
+
+ return " ".join(output)
+
+def parse_nat_static_rule(rule_conf, rule_id, nat_type):
+ output = []
+ log_prefix = ('STATIC-DST' if nat_type == 'destination' else 'STATIC-SRC') + f'-NAT-{rule_id}'
+ log_suffix = ''
+
+ ignore_type_addr = False
+ translation_str = ''
+
+ if 'inbound_interface' in rule_conf:
+ ifname = rule_conf['inbound_interface']
+ ifprefix = 'i' if nat_type == 'destination' else 'o'
+ if ifname != 'any':
+ output.append(f'{ifprefix}ifname "{ifname}"')
+
+ if 'exclude' in rule_conf:
+ translation_str = 'return'
+ log_suffix = '-EXCL'
+ elif 'translation' in rule_conf:
+ translation_prefix = nat_type[:1]
+ translation_output = [f'{translation_prefix}nat']
+ addr = dict_search_args(rule_conf, 'translation', 'address')
+ map_addr = dict_search_args(rule_conf, 'destination', 'address')
+
+ if nat_type == 'source':
+ addr, map_addr = map_addr, addr # Swap
+
+ if addr and is_ip_network(addr):
+ translation_output.append(f'ip prefix to ip {translation_prefix}addr map {{ {map_addr} : {addr} }}')
+ ignore_type_addr = True
+ elif addr:
+ translation_output.append(f'to {addr}')
+
+ options = []
+ addr_mapping = dict_search_args(rule_conf, 'translation', 'options', 'address_mapping')
+ port_mapping = dict_search_args(rule_conf, 'translation', 'options', 'port_mapping')
+ if addr_mapping == 'persistent':
+ options.append('persistent')
+ if port_mapping and port_mapping != 'none':
+ options.append(port_mapping)
+
+ if options:
+ translation_output.append(",".join(options))
+
+ translation_str = " ".join(translation_output)
+
+ prefix = nat_type[:1]
+ addr = dict_search_args(rule_conf, 'translation' if nat_type == 'source' else nat_type, 'address')
+ if addr and not ignore_type_addr:
+ output.append(f'ip {prefix}addr {addr}')
+
+ output.append('counter')
+
+ if translation_str:
+ output.append(translation_str)
+
+ if 'log' in rule_conf:
+ output.append(f'log prefix "[{log_prefix}{log_suffix}]"')
+
+ output.append(f'comment "{log_prefix}"')
+
+ return " ".join(output)
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 9804308c1..2a4135f9e 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -548,7 +548,7 @@ def nft_rule(rule_conf, fw_name, rule_id, ip_name='ip'):
return parse_rule(rule_conf, fw_name, rule_id, ip_name)
@register_filter('nft_default_rule')
-def nft_default_rule(fw_conf, fw_name):
+def nft_default_rule(fw_conf, fw_name, ipv6=False):
output = ['counter']
default_action = fw_conf['default_action']
@@ -557,16 +557,26 @@ def nft_default_rule(fw_conf, fw_name):
output.append(f'log prefix "[{fw_name[:19]}-default-{action_suffix}]"')
output.append(nft_action(default_action))
+ if 'default_jump_target' in fw_conf:
+ target = fw_conf['default_jump_target']
+ def_suffix = '6' if ipv6 else ''
+ output.append(f'NAME{def_suffix}_{target}')
+
output.append(f'comment "{fw_name} default-action {default_action}"')
return " ".join(output)
@register_filter('nft_state_policy')
-def nft_state_policy(conf, state, ipv6=False):
+def nft_state_policy(conf, state):
out = [f'ct state {state}']
- if 'log' in conf:
- log_level = conf['log']
- out.append(f'log level {log_level}')
+ if 'log' in conf and 'enable' in conf['log']:
+ log_state = state[:3].upper()
+ log_action = (conf['action'] if 'action' in conf else 'accept')[:1].upper()
+ out.append(f'log prefix "[STATE-POLICY-{log_state}-{log_action}]"')
+
+ if 'log_level' in conf:
+ log_level = conf['log_level']
+ out.append(f'level {log_level}')
out.append('counter')
@@ -611,6 +621,25 @@ def nft_nested_group(out_list, includes, groups, key):
add_includes(name)
return out_list
+@register_filter('nat_rule')
+def nat_rule(rule_conf, rule_id, nat_type, ipv6=False):
+ from vyos.nat import parse_nat_rule
+ return parse_nat_rule(rule_conf, rule_id, nat_type, ipv6)
+
+@register_filter('nat_static_rule')
+def nat_static_rule(rule_conf, rule_id, nat_type):
+ from vyos.nat import parse_nat_static_rule
+ return parse_nat_static_rule(rule_conf, rule_id, nat_type)
+
+@register_filter('range_to_regex')
+def range_to_regex(num_range):
+ from vyos.range_regex import range_to_regex
+ if '-' not in num_range:
+ return num_range
+
+ regex = range_to_regex(num_range)
+ return f'({regex})'
+
@register_test('vyos_defined')
def vyos_defined(value, test_value=None, var_type=None):
"""
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 325b630bc..461df9a6e 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -1,4 +1,4 @@
-# Copyright 2020-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2020-2022 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
@@ -471,6 +471,12 @@ def process_named_running(name):
return p.pid
return None
+def is_list_equal(first: list, second: list) -> bool:
+ """ Check if 2 lists are equal and list not empty """
+ if len(first) != len(second) or len(first) == 0:
+ return False
+ return sorted(first) == sorted(second)
+
def is_listen_port_bind_service(port: int, service: str) -> bool:
"""Check if listen port bound to expected program name
:param port: Bind port
diff --git a/python/vyos/version.py b/python/vyos/version.py
index 871bb0f1b..fb706ad44 100644
--- a/python/vyos/version.py
+++ b/python/vyos/version.py
@@ -31,6 +31,7 @@ Example of the version data dict::
import os
import json
+import requests
import vyos.defaults
from vyos.util import read_file
@@ -105,3 +106,41 @@ def get_full_version_data(fname=version_file):
version_data['hardware_uuid'] = read_file(subsystem + '/product_uuid', 'Unknown')
return version_data
+
+def get_remote_version(url):
+ """
+ Get remote available JSON file from remote URL
+ An example of the image-version.json
+
+ [
+ {
+ "arch":"amd64",
+ "flavors":[
+ "generic"
+ ],
+ "image":"vyos-rolling-latest.iso",
+ "latest":true,
+ "lts":false,
+ "release_date":"2022-09-06",
+ "release_train":"sagitta",
+ "url":"http://xxx/rolling/current/vyos-rolling-latest.iso",
+ "version":"vyos-1.4-rolling-202209060217"
+ }
+ ]
+ """
+ headers = {}
+ try:
+ remote_data = requests.get(url=url, headers=headers)
+ remote_data.raise_for_status()
+ if remote_data.status_code != 200:
+ return False
+ return remote_data.json()
+ except requests.exceptions.HTTPError as errh:
+ print ("HTTP Error:", errh)
+ except requests.exceptions.ConnectionError as errc:
+ print ("Connecting error:", errc)
+ except requests.exceptions.Timeout as errt:
+ print ("Timeout error:", errt)
+ except requests.exceptions.RequestException as err:
+ print ("Unable to get remote data", err)
+ return False
diff --git a/smoketest/configs/ipoe-server b/smoketest/configs/ipoe-server
new file mode 100644
index 000000000..a375e91de
--- /dev/null
+++ b/smoketest/configs/ipoe-server
@@ -0,0 +1,119 @@
+interfaces {
+ ethernet eth0 {
+ address dhcp
+ }
+ ethernet eth1 {
+ address 192.168.0.1/24
+ }
+ ethernet eth2 {
+ }
+ loopback lo {
+ }
+}
+nat {
+ source {
+ rule 100 {
+ outbound-interface eth0
+ source {
+ address 192.168.0.0/24
+ }
+ translation {
+ address masquerade
+ }
+ }
+ }
+}
+service {
+ ipoe-server {
+ authentication {
+ interface eth1 {
+ mac-address 08:00:27:2f:d8:06 {
+ rate-limit {
+ download 1000
+ upload 500
+ }
+ vlan-id 100
+ }
+ }
+ interface eth2 {
+ mac-address 08:00:27:2f:d8:06 {
+ }
+ }
+ mode local
+ }
+ client-ip-pool {
+ name POOL1 {
+ gateway-address 192.0.2.1
+ subnet 192.0.2.0/24
+ }
+ }
+ client-ipv6-pool {
+ delegate 2001:db8:1::/48 {
+ delegation-prefix 56
+ }
+ prefix 2001:db8::/48 {
+ mask 64
+ }
+ }
+ interface eth1 {
+ client-subnet 192.168.0.0/24
+ network vlan
+ network-mode L3
+ vlan-id 100
+ vlan-id 200
+ vlan-range 1000-2000
+ vlan-range 2500-2700
+ }
+ interface eth2 {
+ client-subnet 192.168.1.0/24
+ }
+ name-server 10.10.1.1
+ name-server 10.10.1.2
+ name-server 2001:db8:aaa::
+ name-server 2001:db8:bbb::
+ }
+ ssh {
+ }
+}
+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 ""
+ }
+ }
+ }
+ ntp {
+ server 0.pool.ntp.org {
+ }
+ server 1.pool.ntp.org {
+ }
+ server 2.pool.ntp.org {
+ }
+ }
+ syslog {
+ global {
+ facility all {
+ level info
+ }
+ facility protocols {
+ level debug
+ }
+ }
+ }
+}
+
+
+// Warning: Do not remove the following line.
+// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@1:conntrack-sync@1:dhcp-relay@2:dhcp-server@5:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@13:ipoe-server@1:ipsec@5:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@6:salt@1:snmp@2:ssh@2:sstp@3:system@19:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webgui@1:webproxy@2:zone-policy@1"
+// Release version: 1.3.1
diff --git a/smoketest/configs/pppoe-server b/smoketest/configs/pppoe-server
index 7e4ccc80e..bfbef4a34 100644
--- a/smoketest/configs/pppoe-server
+++ b/smoketest/configs/pppoe-server
@@ -43,7 +43,13 @@ service {
stop 192.168.0.200
}
gateway-address 192.168.0.2
+ interface eth1 {
+ }
interface eth2 {
+ vlan-id 10
+ vlan-id 20
+ vlan-range 30-40
+ vlan-range 50-60
}
name-server 192.168.0.1
}
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index b2acb03cc..471bdaffb 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -27,6 +27,17 @@ from vyos.util import process_named_running
class BasicAccelPPPTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls._process_name = 'accel-pppd'
+
+ super(BasicAccelPPPTest.TestCase, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, cls._base_path)
+
def setUp(self):
self._gateway = '192.0.2.1'
# ensure we can also run this test on a live system - so lets clean
@@ -34,9 +45,15 @@ class BasicAccelPPPTest:
self.cli_delete(self._base_path)
def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(self._process_name))
+
self.cli_delete(self._base_path)
self.cli_commit()
+ # Check for running process
+ self.assertFalse(process_named_running(self._process_name))
+
def set(self, path):
self.cli_set(self._base_path + path)
@@ -113,9 +130,6 @@ class BasicAccelPPPTest:
tmp = re.findall(regex, tmp)
self.assertTrue(tmp)
- # Check for running process
- self.assertTrue(process_named_running(self._process_name))
-
# Check local-users default value(s)
self.delete(['authentication', 'local-users', 'username', user, 'static-ip'])
# commit changes
@@ -127,9 +141,6 @@ class BasicAccelPPPTest:
tmp = re.findall(regex, tmp)
self.assertTrue(tmp)
- # Check for running process
- self.assertTrue(process_named_running(self._process_name))
-
def test_accel_radius_authentication(self):
# Test configuration of RADIUS authentication for PPPoE server
self.basic_config()
@@ -186,9 +197,6 @@ class BasicAccelPPPTest:
self.assertEqual(f'req-limit=0', server[4])
self.assertEqual(f'fail-time=0', server[5])
- # Check for running process
- self.assertTrue(process_named_running(self._process_name))
-
#
# Disable Radius Accounting
#
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 1517180de..821925bcd 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -36,8 +36,6 @@ sysfs_config = {
'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337', 'default': '0', 'test_value': 'enable'}
}
-eth0_addr = '172.16.10.1/24'
-
class TestFirewall(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -47,15 +45,11 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
# out the current configuration :)
cls.cli_delete(cls, ['firewall'])
- cls.cli_set(cls, ['interfaces', 'ethernet', 'eth0', 'address', eth0_addr])
-
@classmethod
def tearDownClass(cls):
- cls.cli_delete(cls, ['interfaces', 'ethernet', 'eth0', 'address', eth0_addr])
super(TestFirewall, cls).tearDownClass()
def tearDown(self):
- self.cli_delete(['interfaces', 'ethernet', 'eth0', 'firewall'])
self.cli_delete(['firewall'])
self.cli_commit()
@@ -69,7 +63,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['chain NAME_smoketest']
]
- self.verify_nftables(nftables_search, 'ip filter', inverse=True)
+ self.verify_nftables(nftables_search, 'ip vyos_filter', inverse=True)
def verify_nftables(self, nftables_search, table, inverse=False, args=''):
nftables_output = cmd(f'sudo nft {args} list table {table}')
@@ -99,7 +93,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
]
# -t prevents 1000+ GeoIP elements being returned
- self.verify_nftables(nftables_search, 'ip filter', args='-t')
+ self.verify_nftables(nftables_search, 'ip vyos_filter', args='-t')
def test_groups(self):
hostmap_path = ['system', 'static-host-mapping', 'host-name']
@@ -128,7 +122,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept'])
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'source', 'group', 'domain-group', 'smoketest_domain'])
- self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])
+ self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
self.cli_commit()
nftables_search = [
@@ -143,7 +137,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['192.0.2.10, 192.0.2.11 }'],
['ip saddr @D_smoketest_domain', 'return']
]
- self.verify_nftables(nftables_search, 'ip filter')
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
self.cli_delete(['system', 'static-host-mapping'])
self.cli_commit()
@@ -160,7 +154,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port1'])
self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
- self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])
+ self.cli_set(['firewall', 'interface', 'eth0', 'in', 'name', 'smoketest'])
self.cli_commit()
@@ -178,7 +172,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['elements = { 53, 123 }']
]
- self.verify_nftables(nftables_search, 'ip filter')
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
def test_ipv4_basic_rules(self):
name = 'smoketest'
@@ -215,25 +209,31 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'name', name, 'rule', '5', 'protocol', 'tcp'])
self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'flags', 'syn'])
self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'mss', mss_range])
+ self.cli_set(['firewall', 'name', name, 'rule', '5', 'inbound-interface', interface])
+ self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'return'])
+ self.cli_set(['firewall', 'name', name, 'rule', '6', 'protocol', 'gre'])
+ self.cli_set(['firewall', 'name', name, 'rule', '6', 'outbound-interface', interface])
- self.cli_set(['interfaces', 'ethernet', interface, 'firewall', 'in', 'name', name])
+ self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
self.cli_commit()
nftables_search = [
[f'iifname "{interface}"', f'jump NAME_{name}'],
- ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" level debug', 'ip ttl 15','return'],
- ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'log prefix "[smoketest-2-R]" level err', 'ip ttl > 102', 'reject'],
- ['tcp dport { 22 }', 'limit rate 5/minute', 'return'],
+ ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" level debug', 'ip ttl 15', 'return'],
+ ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" level err', 'ip ttl > 102', 'reject'],
+ ['tcp dport 22', 'limit rate 5/minute', 'return'],
['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop'],
- ['tcp dport { 22 }', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'drop'],
- [f'tcp flags & syn == syn tcp option maxseg size {mss_range}'],
+ ['tcp dport 22', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'drop'],
+ ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface}"'],
+ ['meta l4proto gre', f'oifname "{interface}"', 'return']
]
- self.verify_nftables(nftables_search, 'ip filter')
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
- def test_ipv4_packet_length(self):
- name = 'smoketest-plen'
+ def test_ipv4_advanced(self):
+ name = 'smoketest-adv'
+ name2 = 'smoketest-adv2'
interface = 'eth0'
self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
@@ -243,23 +243,36 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '64'])
self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '512'])
self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '1024'])
+ self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '17'])
+ self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '52'])
self.cli_set(['firewall', 'name', name, 'rule', '7', 'action', 'accept'])
self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length', '1-30000'])
self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length-exclude', '60000-65535'])
+ self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp', '3-11'])
+ self.cli_set(['firewall', 'name', name, 'rule', '7', 'dscp-exclude', '21-25'])
+
+ self.cli_set(['firewall', 'name', name2, 'default-action', 'jump'])
+ self.cli_set(['firewall', 'name', name2, 'default-jump-target', name])
+ self.cli_set(['firewall', 'name', name2, 'enable-default-log'])
+ self.cli_set(['firewall', 'name', name2, 'rule', '1', 'source', 'address', '198.51.100.1'])
+ self.cli_set(['firewall', 'name', name2, 'rule', '1', 'action', 'jump'])
+ self.cli_set(['firewall', 'name', name2, 'rule', '1', 'jump-target', name])
- self.cli_set(['interfaces', 'ethernet', interface, 'firewall', 'in', 'name', name])
+ self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
self.cli_commit()
nftables_search = [
[f'iifname "{interface}"', f'jump NAME_{name}'],
- ['ip length { 64, 512, 1024 }', 'return'],
- ['ip length { 1-30000 }', 'ip length != { 60000-65535 }', 'return'],
- [f'log prefix "[{name}-default-D]" drop']
+ ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', 'return'],
+ ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'],
+ [f'log prefix "[{name}-default-D]"', 'drop'],
+ ['ip saddr 198.51.100.1', f'jump NAME_{name}'],
+ [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}']
]
- self.verify_nftables(nftables_search, 'ip filter')
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
def test_ipv6_basic_rules(self):
name = 'v6-smoketest'
@@ -277,22 +290,29 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'reject'])
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'protocol', 'tcp_udp'])
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'destination', 'port', '8888'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'inbound-interface', interface])
- self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'ipv6-name', name])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'return'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'protocol', 'gre'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'outbound-interface', interface])
+
+ self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
self.cli_commit()
nftables_search = [
[f'iifname "{interface}"', f'jump NAME6_{name}'],
['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" level crit', 'return'],
- ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'reject'],
+ ['meta l4proto { tcp, udp }', 'th dport 8888', f'iifname "{interface}"', 'reject'],
+ ['meta l4proto gre', f'oifname "{interface}"', 'return'],
['smoketest default-action', f'log prefix "[{name}-default-D]"', 'drop']
]
- self.verify_nftables(nftables_search, 'ip6 filter')
+ self.verify_nftables(nftables_search, 'ip6 vyos_filter')
- def test_ipv6_packet_length(self):
- name = 'v6-smoketest-plen'
+ def test_ipv6_advanced(self):
+ name = 'v6-smoketest-adv'
+ name2 = 'v6-smoketest-adv2'
interface = 'eth0'
self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
@@ -302,23 +322,36 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '65'])
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '513'])
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'packet-length', '1025'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '18'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'dscp', '53'])
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'action', 'accept'])
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length', '1-1999'])
self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'packet-length-exclude', '60000-65535'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp', '4-14'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '4', 'dscp-exclude', '31-35'])
+
+ self.cli_set(['firewall', 'ipv6-name', name2, 'default-action', 'jump'])
+ self.cli_set(['firewall', 'ipv6-name', name2, 'default-jump-target', name])
+ self.cli_set(['firewall', 'ipv6-name', name2, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'source', 'address', '2001:db8::/64'])
+ self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'action', 'jump'])
+ self.cli_set(['firewall', 'ipv6-name', name2, 'rule', '1', 'jump-target', name])
- self.cli_set(['interfaces', 'ethernet', interface, 'firewall', 'in', 'ipv6-name', name])
+ self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
self.cli_commit()
nftables_search = [
[f'iifname "{interface}"', f'jump NAME6_{name}'],
- ['ip6 length { 65, 513, 1025 }', 'return'],
- ['ip6 length { 1-1999 }', 'ip6 length != { 60000-65535 }', 'return'],
- [f'log prefix "[{name}-default-D]"', 'drop']
+ ['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'return'],
+ ['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'return'],
+ [f'log prefix "[{name}-default-D]"', 'drop'],
+ ['ip6 saddr 2001:db8::/64', f'jump NAME6_{name}'],
+ [f'log prefix "[{name2}-default-J]"', f'jump NAME6_{name}']
]
- self.verify_nftables(nftables_search, 'ip6 filter')
+ self.verify_nftables(nftables_search, 'ip6 vyos_filter')
def test_state_policy(self):
self.cli_set(['firewall', 'state-policy', 'established', 'action', 'accept'])
@@ -328,11 +361,11 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
chains = {
- 'ip filter': ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
- 'ip6 filter': ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
+ 'ip vyos_filter': ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
+ 'ip6 vyos_filter': ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
}
- for table in ['ip filter', 'ip6 filter']:
+ for table in ['ip vyos_filter', 'ip6 vyos_filter']:
for chain in chains[table]:
nftables_output = cmd(f'sudo nft list chain {table} {chain}')
self.assertTrue('jump VYOS_STATE_POLICY' in nftables_output)
@@ -356,20 +389,20 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'name', name, 'rule', '4', 'state', 'established', 'enable'])
self.cli_set(['firewall', 'name', name, 'rule', '4', 'connection-status', 'nat', 'source'])
- self.cli_set(['interfaces', 'ethernet', interface, 'firewall', 'in', 'name', name])
+ self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
self.cli_commit()
nftables_search = [
[f'iifname "{interface}"', f'jump NAME_{name}'],
['ct state { established, related }', 'return'],
- ['ct state { invalid }', 'reject'],
- ['ct state { new }', 'ct status { dnat }', 'return'],
- ['ct state { established, new }', 'ct status { snat }', 'return'],
+ ['ct state invalid', 'reject'],
+ ['ct state new', 'ct status dnat', 'return'],
+ ['ct state { established, new }', 'ct status snat', 'return'],
['drop', f'comment "{name} default-action drop"']
]
- self.verify_nftables(nftables_search, 'ip filter')
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
def test_sysfs(self):
for name, conf in sysfs_config.items():
@@ -388,5 +421,35 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with open(path, 'r') as f:
self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
+ def test_zone_basic(self):
+ self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
+ self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
+ self.cli_set(['firewall', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['chain VZONE_smoketest-eth0'],
+ ['chain VZONE_smoketest-local_IN'],
+ ['chain VZONE_smoketest-local_OUT'],
+ ['oifname "eth0"', 'jump VZONE_smoketest-eth0'],
+ ['jump VZONE_smoketest-local_IN'],
+ ['jump VZONE_smoketest-local_OUT'],
+ ['iifname "eth0"', 'jump NAME_smoketest'],
+ ['oifname "eth0"', 'jump NAME_smoketest']
+ ]
+
+ nftables_output = cmd('sudo nft list table ip vyos_filter')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index 05d2ae5f5..ed611062a 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -17,6 +17,7 @@
import os
import re
import unittest
+from glob import glob
from netifaces import AF_INET
from netifaces import AF_INET6
@@ -119,15 +120,13 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
cls._base_path = ['interfaces', 'ethernet']
cls._mirror_interfaces = ['dum21354']
- # we need to filter out VLAN interfaces identified by a dot (.)
- # in their name - just in case!
+ # We only test on physical interfaces and not VLAN (sub-)interfaces
if 'TEST_ETH' in os.environ:
tmp = os.environ['TEST_ETH'].split()
cls._interfaces = tmp
else:
- for tmp in Section.interfaces('ethernet'):
- if not '.' in tmp:
- cls._interfaces.append(tmp)
+ for tmp in Section.interfaces('ethernet', vlan=False):
+ cls._interfaces.append(tmp)
cls._macs = {}
for interface in cls._interfaces:
@@ -185,6 +184,39 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(f'{cpus:x}', f'{rps_cpus:x}')
+ def test_offloading_rfs(self):
+ global_rfs_flow = 32768
+ rfs_flow = global_rfs_flow
+
+ for interface in self._interfaces:
+ self.cli_set(self._base_path + [interface, 'offload', 'rfs'])
+
+ self.cli_commit()
+
+ for interface in self._interfaces:
+ queues = len(glob(f'/sys/class/net/{interface}/queues/rx-*'))
+ rfs_flow = int(global_rfs_flow/queues)
+ for i in range(0, queues):
+ tmp = read_file(f'/sys/class/net/{interface}/queues/rx-{i}/rps_flow_cnt')
+ self.assertEqual(int(tmp), rfs_flow)
+
+ tmp = read_file(f'/proc/sys/net/core/rps_sock_flow_entries')
+ self.assertEqual(int(tmp), global_rfs_flow)
+
+ # delete configuration of RFS and check all values returned to default "0"
+ for interface in self._interfaces:
+ self.cli_delete(self._base_path + [interface, 'offload', 'rfs'])
+
+ self.cli_commit()
+
+ for interface in self._interfaces:
+ queues = len(glob(f'/sys/class/net/{interface}/queues/rx-*'))
+ rfs_flow = int(global_rfs_flow/queues)
+ for i in range(0, queues):
+ tmp = read_file(f'/sys/class/net/{interface}/queues/rx-{i}/rps_flow_cnt')
+ self.assertEqual(int(tmp), 0)
+
+
def test_non_existing_interface(self):
unknonw_interface = self._base_path + ['eth667']
self.cli_set(unknonw_interface)
diff --git a/smoketest/scripts/cli/test_load_balancning_wan.py b/smoketest/scripts/cli/test_load_balancning_wan.py
index 303dece86..23020b9b1 100755
--- a/smoketest/scripts/cli/test_load_balancning_wan.py
+++ b/smoketest/scripts/cli/test_load_balancning_wan.py
@@ -177,6 +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;
counter jump WANLOADBALANCE
return
}
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 408facfb3..f824838c0 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -26,6 +26,7 @@ from vyos.util import dict_search
base_path = ['nat']
src_path = base_path + ['source']
dst_path = base_path + ['destination']
+static_path = base_path + ['static']
class TestNAT(VyOSUnitTestSHIM.TestCase):
@classmethod
@@ -40,10 +41,24 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
+ def verify_nftables(self, nftables_search, table, inverse=False, args=''):
+ nftables_output = cmd(f'sudo nft {args} list table {table}')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(not matched if inverse else matched, msg=search)
+
def test_snat(self):
rules = ['100', '110', '120', '130', '200', '210', '220', '230']
outbound_iface_100 = 'eth0'
outbound_iface_200 = 'eth1'
+
+ nftables_search = ['jump VYOS_PRE_SNAT_HOOK']
+
for rule in rules:
network = f'192.168.{rule}.0/24'
# depending of rule order we check either for source address for NAT
@@ -52,51 +67,16 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.cli_set(src_path + ['rule', rule, 'source', 'address', network])
self.cli_set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_100])
self.cli_set(src_path + ['rule', rule, 'translation', 'address', 'masquerade'])
+ nftables_search.append([f'saddr {network}', f'oifname "{outbound_iface_100}"', 'masquerade'])
else:
self.cli_set(src_path + ['rule', rule, 'destination', 'address', network])
self.cli_set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_200])
self.cli_set(src_path + ['rule', rule, 'exclude'])
+ nftables_search.append([f'daddr {network}', f'oifname "{outbound_iface_200}"', 'return'])
self.cli_commit()
- tmp = cmd('sudo nft -j list chain ip nat POSTROUTING')
- data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
-
- for idx in range(0, len(data_json)):
- data = data_json[idx]
- if idx == 0:
- self.assertEqual(data['chain'], 'POSTROUTING')
- self.assertEqual(data['family'], 'ip')
- self.assertEqual(data['table'], 'nat')
-
- jump_target = dict_search('jump.target', data['expr'][1])
- self.assertEqual(jump_target,'VYOS_PRE_SNAT_HOOK')
- else:
- rule = str(rules[idx - 1])
- network = f'192.168.{rule}.0/24'
-
- self.assertEqual(data['chain'], 'POSTROUTING')
- self.assertEqual(data['comment'], f'SRC-NAT-{rule}')
- self.assertEqual(data['family'], 'ip')
- self.assertEqual(data['table'], 'nat')
-
- iface = dict_search('match.right', data['expr'][0])
- direction = dict_search('match.left.payload.field', data['expr'][1])
- address = dict_search('match.right.prefix.addr', data['expr'][1])
- mask = dict_search('match.right.prefix.len', data['expr'][1])
-
- if int(rule) < 200:
- self.assertEqual(direction, 'saddr')
- self.assertEqual(iface, outbound_iface_100)
- # check for masquerade keyword
- self.assertIn('masquerade', data['expr'][3])
- else:
- self.assertEqual(direction, 'daddr')
- self.assertEqual(iface, outbound_iface_200)
- # check for return keyword due to 'exclude'
- self.assertIn('return', data['expr'][3])
-
- self.assertEqual(f'{address}/{mask}', network)
+ self.verify_nftables(nftables_search, 'ip vyos_nat')
def test_dnat(self):
rules = ['100', '110', '120', '130', '200', '210', '220', '230']
@@ -105,56 +85,29 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
inbound_proto_100 = 'udp'
inbound_proto_200 = 'tcp'
+ nftables_search = ['jump VYOS_PRE_DNAT_HOOK']
+
for rule in rules:
port = f'10{rule}'
self.cli_set(dst_path + ['rule', rule, 'source', 'port', port])
self.cli_set(dst_path + ['rule', rule, 'translation', 'address', '192.0.2.1'])
self.cli_set(dst_path + ['rule', rule, 'translation', 'port', port])
+ rule_search = [f'dnat to 192.0.2.1:{port}']
if int(rule) < 200:
self.cli_set(dst_path + ['rule', rule, 'protocol', inbound_proto_100])
self.cli_set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_100])
+ rule_search.append(f'{inbound_proto_100} sport {port}')
+ rule_search.append(f'iifname "{inbound_iface_100}"')
else:
self.cli_set(dst_path + ['rule', rule, 'protocol', inbound_proto_200])
self.cli_set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_200])
+ rule_search.append(f'iifname "{inbound_iface_200}"')
- self.cli_commit()
-
- tmp = cmd('sudo nft -j list chain ip nat PREROUTING')
- data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
-
- for idx in range(0, len(data_json)):
- data = data_json[idx]
- if idx == 0:
- self.assertEqual(data['chain'], 'PREROUTING')
- self.assertEqual(data['family'], 'ip')
- self.assertEqual(data['table'], 'nat')
+ nftables_search.append(rule_search)
- jump_target = dict_search('jump.target', data['expr'][1])
- self.assertEqual(jump_target,'VYOS_PRE_DNAT_HOOK')
- else:
+ self.cli_commit()
- rule = str(rules[idx - 1])
- port = int(f'10{rule}')
-
- self.assertEqual(data['chain'], 'PREROUTING')
- self.assertEqual(data['comment'].split()[0], f'DST-NAT-{rule}')
- self.assertEqual(data['family'], 'ip')
- self.assertEqual(data['table'], 'nat')
-
- iface = dict_search('match.right', data['expr'][0])
- direction = dict_search('match.left.payload.field', data['expr'][1])
- protocol = dict_search('match.left.payload.protocol', data['expr'][1])
- dnat_addr = dict_search('dnat.addr', data['expr'][3])
- dnat_port = dict_search('dnat.port', data['expr'][3])
-
- self.assertEqual(direction, 'sport')
- self.assertEqual(dnat_addr, '192.0.2.1')
- self.assertEqual(dnat_port, port)
- if int(rule) < 200:
- self.assertEqual(iface, inbound_iface_100)
- self.assertEqual(protocol, inbound_proto_100)
- else:
- self.assertEqual(iface, inbound_iface_200)
+ self.verify_nftables(nftables_search, 'ip vyos_nat')
def test_snat_required_translation_address(self):
# T2813: Ensure translation address is specified
@@ -193,8 +146,48 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
# without any rule
self.cli_set(src_path)
self.cli_set(dst_path)
+ self.cli_set(static_path)
+ self.cli_commit()
+
+ def test_dnat_without_translation_address(self):
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '1', 'destination', 'port', '443'])
+ self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp'])
+ self.cli_set(dst_path + ['rule', '1', 'translation', 'port', '443'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['iifname "eth1"', 'tcp dport 443', 'dnat to :443']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_nat')
+
+ def test_static_nat(self):
+ dst_addr_1 = '10.0.1.1'
+ translate_addr_1 = '192.168.1.1'
+ dst_addr_2 = '203.0.113.0/24'
+ translate_addr_2 = '192.0.2.0/24'
+ ifname = 'eth0'
+
+ self.cli_set(static_path + ['rule', '10', 'destination', 'address', dst_addr_1])
+ self.cli_set(static_path + ['rule', '10', 'inbound-interface', ifname])
+ self.cli_set(static_path + ['rule', '10', 'translation', 'address', translate_addr_1])
+
+ self.cli_set(static_path + ['rule', '20', 'destination', 'address', dst_addr_2])
+ self.cli_set(static_path + ['rule', '20', 'inbound-interface', ifname])
+ self.cli_set(static_path + ['rule', '20', 'translation', 'address', translate_addr_2])
+
self.cli_commit()
+ nftables_search = [
+ [f'iifname "{ifname}"', f'ip daddr {dst_addr_1}', f'dnat to {translate_addr_1}'],
+ [f'oifname "{ifname}"', f'ip saddr {translate_addr_1}', f'snat to {dst_addr_1}'],
+ [f'iifname "{ifname}"', f'dnat ip prefix to ip daddr map {{ {dst_addr_2} : {translate_addr_2} }}'],
+ [f'oifname "{ifname}"', f'snat ip prefix to ip saddr map {{ {translate_addr_2} : {dst_addr_2} }}']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_static_nat')
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index c5db066db..6cf7ca0a1 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -71,12 +71,12 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
nftables_search = [
- ['oifname "eth1"', 'ip6 saddr fc00::/64', 'snat prefix to fc01::/64'],
- ['oifname "eth1"', 'ip6 saddr fc00::/64', 'masquerade'],
- ['oifname "eth1"', 'ip6 saddr fc00::/64', 'return']
+ ['oifname "eth1"', f'ip6 saddr {source_prefix}', f'snat prefix to {translation_prefix}'],
+ ['oifname "eth1"', f'ip6 saddr {source_prefix}', 'masquerade'],
+ ['oifname "eth1"', f'ip6 saddr {source_prefix}', 'return']
]
- self.verify_nftables(nftables_search, 'ip6 nat')
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
def test_source_nat66_address(self):
source_prefix = 'fc00::/64'
@@ -88,25 +88,11 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
# check validate() - outbound-interface must be defined
self.cli_commit()
- tmp = cmd('sudo nft -j list table ip6 nat')
- data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
-
- for idx in range(0, len(data_json)):
- data = data_json[idx]
-
- self.assertEqual(data['chain'], 'POSTROUTING')
- self.assertEqual(data['family'], 'ip6')
- self.assertEqual(data['table'], 'nat')
-
- iface = dict_search('match.right', data['expr'][0])
- address = dict_search('match.right.prefix.addr', data['expr'][2])
- mask = dict_search('match.right.prefix.len', data['expr'][2])
- snat_address = dict_search('snat.addr', data['expr'][3])
+ nftables_search = [
+ ['oifname "eth1"', f'ip6 saddr {source_prefix}', f'snat to {translation_address}']
+ ]
- self.assertEqual(iface, 'eth1')
- # check for translation address
- self.assertEqual(snat_address, translation_address)
- self.assertEqual(f'{address}/{mask}', source_prefix)
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
def test_destination_nat66(self):
destination_address = 'fc00::1'
@@ -129,7 +115,7 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
['iifname "eth1"', 'ip6 saddr fc02::1', 'ip6 daddr fc00::1', 'return']
]
- self.verify_nftables(nftables_search, 'ip6 nat')
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
def test_destination_nat66_protocol(self):
translation_address = '2001:db8:1111::1'
@@ -150,10 +136,10 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
nftables_search = [
- ['iifname "eth1"', 'tcp dport { 4545 } ip6 saddr 2001:db8:2222::/64 tcp sport { 8080 } dnat to 2001:db8:1111::1:5555']
+ ['iifname "eth1"', 'tcp dport 4545', 'ip6 saddr 2001:db8:2222::/64', 'tcp sport 8080', 'dnat to 2001:db8:1111::1:5555']
]
- self.verify_nftables(nftables_search, 'ip6 nat')
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
def test_destination_nat66_prefix(self):
destination_prefix = 'fc00::/64'
@@ -165,22 +151,25 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
# check validate() - outbound-interface must be defined
self.cli_commit()
- tmp = cmd('sudo nft -j list table ip6 nat')
- data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
+ nftables_search = [
+ ['iifname "eth1"', f'ip6 daddr {destination_prefix}', f'dnat prefix to {translation_prefix}']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
- for idx in range(0, len(data_json)):
- data = data_json[idx]
+ def test_destination_nat66_without_translation_address(self):
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '1', 'destination', 'port', '443'])
+ self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp'])
+ self.cli_set(dst_path + ['rule', '1', 'translation', 'port', '443'])
- self.assertEqual(data['chain'], 'PREROUTING')
- self.assertEqual(data['family'], 'ip6')
- self.assertEqual(data['table'], 'nat')
+ self.cli_commit()
- iface = dict_search('match.right', data['expr'][0])
- translation_address = dict_search('dnat.addr.prefix.addr', data['expr'][3])
- translation_mask = dict_search('dnat.addr.prefix.len', data['expr'][3])
+ nftables_search = [
+ ['iifname "eth1"', 'tcp dport 443', 'dnat to :443']
+ ]
- self.assertEqual(f'{translation_address}/{translation_mask}', translation_prefix)
- self.assertEqual(iface, 'eth1')
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
def test_source_nat66_required_translation_prefix(self):
# T2813: Ensure translation address is specified
@@ -219,10 +208,10 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
nftables_search = [
- ['oifname "eth1"', 'ip6 saddr 2001:db8:2222::/64 tcp dport { 9999 } tcp sport { 8080 } snat to 2001:db8:1111::1:80']
+ ['oifname "eth1"', 'ip6 saddr 2001:db8:2222::/64', 'tcp dport 9999', 'tcp sport 8080', 'snat to 2001:db8:1111::1:80']
]
- self.verify_nftables(nftables_search, 'ip6 nat')
+ self.verify_nftables(nftables_search, 'ip6 vyos_nat')
def test_nat66_no_rules(self):
# T3206: deleting all rules but keep the direction 'destination' or
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index 3d37d22ae..2166e63ec 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -698,6 +698,184 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
for rule in test_range:
tmp = f'ip prefix-list {prefix_list} seq {rule} permit {prefix} le {rule}'
self.assertIn(tmp, config)
+ def test_route_map_community_set(self):
+ test_data = {
+ "community-configuration": {
+ "rule": {
+ "10": {
+ "action": "permit",
+ "set": {
+ "community": {
+ "replace": [
+ "65000:10",
+ "65001:11"
+ ]
+ },
+ "extcommunity": {
+ "bandwidth": "200",
+ "rt": [
+ "65000:10",
+ "192.168.0.1:11"
+ ],
+ "soo": [
+ "192.168.0.1:11",
+ "65000:10"
+ ]
+ },
+ "large-community": {
+ "replace": [
+ "65000:65000:10",
+ "65000:65000:11"
+ ]
+ }
+ }
+ },
+ "20": {
+ "action": "permit",
+ "set": {
+ "community": {
+ "add": [
+ "65000:10",
+ "65001:11"
+ ]
+ },
+ "extcommunity": {
+ "bandwidth": "200",
+ "bandwidth-non-transitive": {}
+ },
+ "large-community": {
+ "add": [
+ "65000:65000:10",
+ "65000:65000:11"
+ ]
+ }
+ }
+ },
+ "30": {
+ "action": "permit",
+ "set": {
+ "community": {
+ "none": {}
+ },
+ "extcommunity": {
+ "none": {}
+ },
+ "large-community": {
+ "none": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ for route_map, route_map_config in test_data.items():
+ path = base_path + ['route-map', route_map]
+ self.cli_set(path + ['description', f'VyOS ROUTE-MAP {route_map}'])
+ if 'rule' not in route_map_config:
+ continue
+
+ for rule, rule_config in route_map_config['rule'].items():
+ if 'action' in rule_config:
+ self.cli_set(path + ['rule', rule, 'action', rule_config['action']])
+ if 'set' in rule_config:
+
+ #Add community in configuration
+ if 'community' in rule_config['set']:
+ if 'none' in rule_config['set']['community']:
+ self.cli_set(path + ['rule', rule, 'set', 'community', 'none'])
+ else:
+ community_path = path + ['rule', rule, 'set', 'community']
+ if 'add' in rule_config['set']['community']:
+ for community_unit in rule_config['set']['community']['add']:
+ self.cli_set(community_path + ['add', community_unit])
+ if 'replace' in rule_config['set']['community']:
+ for community_unit in rule_config['set']['community']['replace']:
+ self.cli_set(community_path + ['replace', community_unit])
+
+ #Add large-community in configuration
+ if 'large-community' in rule_config['set']:
+ if 'none' in rule_config['set']['large-community']:
+ self.cli_set(path + ['rule', rule, 'set', 'large-community', 'none'])
+ else:
+ community_path = path + ['rule', rule, 'set', 'large-community']
+ if 'add' in rule_config['set']['large-community']:
+ for community_unit in rule_config['set']['large-community']['add']:
+ self.cli_set(community_path + ['add', community_unit])
+ if 'replace' in rule_config['set']['large-community']:
+ for community_unit in rule_config['set']['large-community']['replace']:
+ self.cli_set(community_path + ['replace', community_unit])
+
+ #Add extcommunity in configuration
+ if 'extcommunity' in rule_config['set']:
+ if 'none' in rule_config['set']['extcommunity']:
+ self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'none'])
+ else:
+ if 'bandwidth' in rule_config['set']['extcommunity']:
+ self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'bandwidth', rule_config['set']['extcommunity']['bandwidth']])
+ if 'bandwidth-non-transitive' in rule_config['set']['extcommunity']:
+ self.cli_set(path + ['rule', rule, 'set','extcommunity', 'bandwidth-non-transitive'])
+ if 'rt' in rule_config['set']['extcommunity']:
+ for community_unit in rule_config['set']['extcommunity']['rt']:
+ self.cli_set(path + ['rule', rule, 'set', 'extcommunity','rt',community_unit])
+ if 'soo' in rule_config['set']['extcommunity']:
+ for community_unit in rule_config['set']['extcommunity']['soo']:
+ self.cli_set(path + ['rule', rule, 'set', 'extcommunity','soo',community_unit])
+ self.cli_commit()
+
+ for route_map, route_map_config in test_data.items():
+ if 'rule' not in route_map_config:
+ continue
+ for rule, rule_config in route_map_config['rule'].items():
+ name = f'route-map {route_map} {rule_config["action"]} {rule}'
+ config = self.getFRRconfig(name)
+ self.assertIn(name, config)
+
+ if 'set' in rule_config:
+ #Check community
+ if 'community' in rule_config['set']:
+ if 'none' in rule_config['set']['community']:
+ tmp = f'set community none'
+ self.assertIn(tmp, config)
+ if 'replace' in rule_config['set']['community']:
+ values = ' '.join(rule_config['set']['community']['replace'])
+ tmp = f'set community {values}'
+ self.assertIn(tmp, config)
+ if 'add' in rule_config['set']['community']:
+ values = ' '.join(rule_config['set']['community']['add'])
+ tmp = f'set community {values} additive'
+ self.assertIn(tmp, config)
+ #Check large-community
+ if 'large-community' in rule_config['set']:
+ if 'none' in rule_config['set']['large-community']:
+ tmp = f'set large-community none'
+ self.assertIn(tmp, config)
+ if 'replace' in rule_config['set']['large-community']:
+ values = ' '.join(rule_config['set']['large-community']['replace'])
+ tmp = f'set large-community {values}'
+ self.assertIn(tmp, config)
+ if 'add' in rule_config['set']['large-community']:
+ values = ' '.join(rule_config['set']['large-community']['add'])
+ tmp = f'set large-community {values} additive'
+ self.assertIn(tmp, config)
+ #Check extcommunity
+ if 'extcommunity' in rule_config['set']:
+ if 'none' in rule_config['set']['extcommunity']:
+ tmp = 'set extcommunity none'
+ self.assertIn(tmp, config)
+ if 'bandwidth' in rule_config['set']['extcommunity']:
+ values = rule_config['set']['extcommunity']['bandwidth']
+ tmp = f'set extcommunity bandwidth {values}'
+ if 'bandwidth-non-transitive' in rule_config['set']['extcommunity']:
+ tmp = tmp + ' non-transitive'
+ self.assertIn(tmp, config)
+ if 'rt' in rule_config['set']['extcommunity']:
+ values = ' '.join(rule_config['set']['extcommunity']['rt'])
+ tmp = f'set extcommunity rt {values}'
+ self.assertIn(tmp, config)
+ if 'soo' in rule_config['set']['extcommunity']:
+ values = ' '.join(rule_config['set']['extcommunity']['soo'])
+ tmp = f'set extcommunity soo {values}'
+ self.assertIn(tmp, config)
def test_route_map(self):
access_list = '50'
@@ -845,13 +1023,9 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
'as-path-prepend-last-as' : '5',
'atomic-aggregate' : '',
'distance' : '110',
- 'extcommunity-bw' : '20000',
- 'extcommunity-rt' : '123:456',
- 'extcommunity-soo' : '456:789',
'ipv6-next-hop-global' : '2001::1',
'ipv6-next-hop-local' : 'fe80::1',
'ip-next-hop' : '192.168.1.1',
- 'large-community' : '100:200:300',
'local-preference' : '500',
'metric' : '150',
'metric-type' : 'type-1',
@@ -1049,20 +1223,12 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_set(path + ['rule', rule, 'set', 'atomic-aggregate'])
if 'distance' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'distance', rule_config['set']['distance']])
- if 'extcommunity-bw' in rule_config['set']:
- self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'bandwidth', rule_config['set']['extcommunity-bw']])
- if 'extcommunity-rt' in rule_config['set']:
- self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'rt', rule_config['set']['extcommunity-rt']])
- if 'extcommunity-soo' in rule_config['set']:
- self.cli_set(path + ['rule', rule, 'set', 'extcommunity', 'soo', rule_config['set']['extcommunity-soo']])
if 'ipv6-next-hop-global' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'ipv6-next-hop', 'global', rule_config['set']['ipv6-next-hop-global']])
if 'ipv6-next-hop-local' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'ipv6-next-hop', 'local', rule_config['set']['ipv6-next-hop-local']])
if 'ip-next-hop' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'ip-next-hop', rule_config['set']['ip-next-hop']])
- if 'large-community' in rule_config['set']:
- self.cli_set(path + ['rule', rule, 'set', 'large-community', rule_config['set']['large-community']])
if 'local-preference' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'local-preference', rule_config['set']['local-preference']])
if 'metric' in rule_config['set']:
@@ -1236,20 +1402,12 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
tmp += 'atomic-aggregate'
elif 'distance' in rule_config['set']:
tmp += 'distance ' + rule_config['set']['distance']
- elif 'extcommunity-bw' in rule_config['set']:
- tmp += 'extcommunity bandwidth' + rule_config['set']['extcommunity-bw']
- elif 'extcommunity-rt' in rule_config['set']:
- tmp += 'extcommunity rt' + rule_config['set']['extcommunity-rt']
- elif 'extcommunity-soo' in rule_config['set']:
- tmp += 'extcommunity rt' + rule_config['set']['extcommunity-soo']
elif 'ip-next-hop' in rule_config['set']:
tmp += 'ip next-hop ' + rule_config['set']['ip-next-hop']
elif 'ipv6-next-hop-global' in rule_config['set']:
tmp += 'ipv6 next-hop global ' + rule_config['set']['ipv6-next-hop-global']
elif 'ipv6-next-hop-local' in rule_config['set']:
tmp += 'ipv6 next-hop local ' + rule_config['set']['ipv6-next-hop-local']
- elif 'large-community' in rule_config['set']:
- tmp += 'large-community ' + rule_config['set']['large-community']
elif 'local-preference' in rule_config['set']:
tmp += 'local-preference ' + rule_config['set']['local-preference']
elif 'metric' in rule_config['set']:
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index 534cfb082..046e385bb 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -53,7 +53,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
['chain VYOS_PBR_smoketest']
]
- self.verify_nftables(nftables_search, 'ip filter', inverse=True)
+ self.verify_nftables(nftables_search, 'ip mangle', inverse=True)
def verify_nftables(self, nftables_search, table, inverse=False):
nftables_output = cmd(f'sudo nft list table {table}')
@@ -127,7 +127,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
nftables_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'],
- ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'meta mark set ' + mark_hex]
+ ['tcp flags syn / syn,ack', 'tcp dport 8888', 'meta mark set ' + mark_hex]
]
self.verify_nftables(nftables_search, 'ip mangle')
@@ -136,7 +136,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
nftables6_search = [
[f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'],
- ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'meta mark set ' + mark_hex]
+ ['meta l4proto { tcp, udp }', 'th dport 8888', 'meta mark set ' + mark_hex]
]
self.verify_nftables(nftables6_search, 'ip6 mangle')
@@ -158,5 +158,81 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
self.assertTrue(matched)
+ def test_pbr_matching_criteria(self):
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'udp'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'action', 'drop'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'protocol', 'tcp'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'tcp', 'flags', 'syn'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '2', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'source', 'address', '198.51.100.0/24'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'protocol', 'tcp'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'destination', 'port', '22'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'state', 'new', 'enable'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'ttl', 'gt', '2'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '3', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'protocol', 'icmp'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'icmp', 'type-name', 'echo-request'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '128'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '1024-2048'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'log', 'enable'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'dscp', '41'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'dscp', '57-59'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'set', 'table', table_id])
+
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'udp'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'action', 'drop'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'protocol', 'tcp'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'tcp', 'flags', 'syn'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'source', 'address', '2001:db8::0/64'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'protocol', 'tcp'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'destination', 'port', '22'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'state', 'new', 'enable'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'hop-limit', 'gt', '2'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'protocol', 'icmpv6'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'icmpv6', 'type', 'echo-request'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '128'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '1024-2048'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'log', 'enable'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '61'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '14-19'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'set', 'table', table_id])
+
+ self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])
+ self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route6', 'smoketest6'])
+
+ self.cli_commit()
+
+ mark_hex = "{0:#010x}".format(table_mark_offset - int(table_id))
+
+ # IPv4
+ nftables_search = [
+ [f'iifname "{interface}"', '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],
+ ['meta l4proto icmp', 'log prefix "[smoketest-4-A]"', 'icmp type echo-request', 'ip length { 128, 1024-2048 }', 'meta mark set ' + mark_hex],
+ ['ip dscp { 0x29, 0x39-0x3b }', 'meta mark set ' + mark_hex]
+ ]
+
+ self.verify_nftables(nftables_search, 'ip mangle')
+
+ # IPv6
+ nftables6_search = [
+ [f'iifname "{interface}"', '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],
+ ['meta l4proto ipv6-icmp', 'log prefix "[smoketest6-4-A]"', 'icmpv6 type echo-request', 'ip6 length != { 128, 1024-2048 }', 'meta mark set ' + mark_hex],
+ ['ip6 dscp != { 0x0e-0x13, 0x3d }', 'meta mark set ' + mark_hex]
+ ]
+
+ self.verify_nftables(nftables6_search, 'ip6 mangle')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 6196ffe60..d2dad8c1a 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -287,6 +287,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['parameters', 'bestpath', 'as-path', 'multipath-relax'])
self.cli_set(base_path + ['parameters', 'bestpath', 'bandwidth', 'default-weight-for-missing'])
self.cli_set(base_path + ['parameters', 'bestpath', 'compare-routerid'])
+ self.cli_set(base_path + ['parameters', 'bestpath', 'peer-type', 'multipath-relax'])
self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer])
self.cli_set(base_path + ['parameters', 'fast-convergence'])
@@ -318,6 +319,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig)
self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig)
self.assertIn(f' bgp bestpath compare-routerid', frrconfig)
+ self.assertIn(f' bgp bestpath peer-type multipath-relax', frrconfig)
self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig)
self.assertIn(f' bgp reject-as-sets', frrconfig)
self.assertIn(f' bgp shutdown', frrconfig)
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index ee4be0b37..c26028253 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -262,5 +262,51 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' isis bfd', tmp)
self.assertIn(f' isis bfd profile {bfd_profile}', tmp)
+ def test_isis_07_segment_routing_configuration(self):
+ global_block_low = "100"
+ global_block_high = "199"
+ local_block_low = "200"
+ local_block_high = "299"
+ interface = 'lo'
+ maximum_stack_size = '5'
+ prefix_one = '192.168.0.1/32'
+ prefix_two = '192.168.0.2/32'
+ prefix_three = '192.168.0.3/32'
+ prefix_four = '192.168.0.4/32'
+ prefix_one_value = '1'
+ prefix_two_value = '2'
+ prefix_three_value = '60000'
+ prefix_four_value = '65000'
+
+ self.cli_set(base_path + ['net', net])
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(base_path + ['segment-routing', 'maximum-label-depth', maximum_stack_size])
+ self.cli_set(base_path + ['segment-routing', 'global-block', 'low-label-value', global_block_low])
+ self.cli_set(base_path + ['segment-routing', 'global-block', 'high-label-value', global_block_high])
+ self.cli_set(base_path + ['segment-routing', 'local-block', 'low-label-value', local_block_low])
+ self.cli_set(base_path + ['segment-routing', 'local-block', 'high-label-value', local_block_high])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'value', prefix_one_value])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'explicit-null'])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'value', prefix_two_value])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'no-php-flag'])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_three, 'absolute', 'value', prefix_three_value])
+ 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()
+
+ # Verify all changes
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
+ self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' segment-routing on', tmp)
+ self.assertIn(f' segment-routing global-block {global_block_low} {global_block_high} local-block {local_block_low} {local_block_high}', tmp)
+ self.assertIn(f' segment-routing node-msd {maximum_stack_size}', tmp)
+ self.assertIn(f' segment-routing prefix {prefix_one} index {prefix_one_value} explicit-null', tmp)
+ self.assertIn(f' segment-routing prefix {prefix_two} index {prefix_two_value} no-php-flag', tmp)
+ 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)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_nhrp.py b/smoketest/scripts/cli/test_protocols_nhrp.py
index 40b19fec7..59252875b 100755
--- a/smoketest/scripts/cli/test_protocols_nhrp.py
+++ b/smoketest/scripts/cli/test_protocols_nhrp.py
@@ -26,65 +26,79 @@ nhrp_path = ['protocols', 'nhrp']
vpn_path = ['vpn', 'ipsec']
class TestProtocolsNHRP(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestProtocolsNHRP, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, nhrp_path)
+ cls.cli_delete(cls, tunnel_path)
+
def tearDown(self):
self.cli_delete(nhrp_path)
self.cli_delete(tunnel_path)
self.cli_commit()
def test_config(self):
- self.cli_delete(nhrp_path)
- self.cli_delete(tunnel_path)
+ tunnel_if = "tun100"
+ tunnel_source = "192.0.2.1"
+ tunnel_encapsulation = "gre"
+ esp_group = "ESP-HUB"
+ ike_group = "IKE-HUB"
+ nhrp_secret = "vyos123"
+ nhrp_profile = "NHRPVPN"
+ ipsec_secret = "secret"
# Tunnel
- self.cli_set(tunnel_path + ["tun100", "address", "172.16.253.134/29"])
- self.cli_set(tunnel_path + ["tun100", "encapsulation", "gre"])
- self.cli_set(tunnel_path + ["tun100", "source-address", "192.0.2.1"])
- self.cli_set(tunnel_path + ["tun100", "multicast", "enable"])
- self.cli_set(tunnel_path + ["tun100", "parameters", "ip", "key", "1"])
+ self.cli_set(tunnel_path + [tunnel_if, "address", "172.16.253.134/29"])
+ self.cli_set(tunnel_path + [tunnel_if, "encapsulation", tunnel_encapsulation])
+ self.cli_set(tunnel_path + [tunnel_if, "source-address", tunnel_source])
+ self.cli_set(tunnel_path + [tunnel_if, "multicast", "enable"])
+ self.cli_set(tunnel_path + [tunnel_if, "parameters", "ip", "key", "1"])
# NHRP
- self.cli_set(nhrp_path + ["tunnel", "tun100", "cisco-authentication", "secret"])
- self.cli_set(nhrp_path + ["tunnel", "tun100", "holding-time", "300"])
- self.cli_set(nhrp_path + ["tunnel", "tun100", "multicast", "dynamic"])
- self.cli_set(nhrp_path + ["tunnel", "tun100", "redirect"])
- self.cli_set(nhrp_path + ["tunnel", "tun100", "shortcut"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "cisco-authentication", nhrp_secret])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "holding-time", "300"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "multicast", "dynamic"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "redirect"])
+ self.cli_set(nhrp_path + ["tunnel", tunnel_if, "shortcut"])
# IKE/ESP Groups
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "compression", "disable"])
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "lifetime", "1800"])
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "mode", "transport"])
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "pfs", "dh-group2"])
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "proposal", "1", "encryption", "aes256"])
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "proposal", "1", "hash", "sha1"])
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "proposal", "2", "encryption", "3des"])
- self.cli_set(vpn_path + ["esp-group", "ESP-HUB", "proposal", "2", "hash", "md5"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "ikev2-reauth", "no"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "key-exchange", "ikev1"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "lifetime", "3600"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "proposal", "1", "dh-group", "2"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "proposal", "1", "encryption", "aes256"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "proposal", "1", "hash", "sha1"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "proposal", "2", "dh-group", "2"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "proposal", "2", "encryption", "aes128"])
- self.cli_set(vpn_path + ["ike-group", "IKE-HUB", "proposal", "2", "hash", "sha1"])
+ self.cli_set(vpn_path + ["esp-group", esp_group, "lifetime", "1800"])
+ self.cli_set(vpn_path + ["esp-group", esp_group, "mode", "transport"])
+ self.cli_set(vpn_path + ["esp-group", esp_group, "pfs", "dh-group2"])
+ self.cli_set(vpn_path + ["esp-group", esp_group, "proposal", "1", "encryption", "aes256"])
+ self.cli_set(vpn_path + ["esp-group", esp_group, "proposal", "1", "hash", "sha1"])
+ self.cli_set(vpn_path + ["esp-group", esp_group, "proposal", "2", "encryption", "3des"])
+ self.cli_set(vpn_path + ["esp-group", esp_group, "proposal", "2", "hash", "md5"])
+
+ self.cli_set(vpn_path + ["ike-group", ike_group, "key-exchange", "ikev1"])
+ self.cli_set(vpn_path + ["ike-group", ike_group, "lifetime", "3600"])
+ self.cli_set(vpn_path + ["ike-group", ike_group, "proposal", "1", "dh-group", "2"])
+ self.cli_set(vpn_path + ["ike-group", ike_group, "proposal", "1", "encryption", "aes256"])
+ self.cli_set(vpn_path + ["ike-group", ike_group, "proposal", "1", "hash", "sha1"])
+ self.cli_set(vpn_path + ["ike-group", ike_group, "proposal", "2", "dh-group", "2"])
+ self.cli_set(vpn_path + ["ike-group", ike_group, "proposal", "2", "encryption", "aes128"])
+ self.cli_set(vpn_path + ["ike-group", ike_group, "proposal", "2", "hash", "sha1"])
# Profile - Not doing full DMVPN checks here, just want to verify the profile name in the output
self.cli_set(vpn_path + ["interface", "eth0"])
- self.cli_set(vpn_path + ["profile", "NHRPVPN", "authentication", "mode", "pre-shared-secret"])
- self.cli_set(vpn_path + ["profile", "NHRPVPN", "authentication", "pre-shared-secret", "secret"])
- self.cli_set(vpn_path + ["profile", "NHRPVPN", "bind", "tunnel", "tun100"])
- self.cli_set(vpn_path + ["profile", "NHRPVPN", "esp-group", "ESP-HUB"])
- self.cli_set(vpn_path + ["profile", "NHRPVPN", "ike-group", "IKE-HUB"])
+ self.cli_set(vpn_path + ["profile", nhrp_profile, "authentication", "mode", "pre-shared-secret"])
+ self.cli_set(vpn_path + ["profile", nhrp_profile, "authentication", "pre-shared-secret", ipsec_secret])
+ self.cli_set(vpn_path + ["profile", nhrp_profile, "bind", "tunnel", tunnel_if])
+ self.cli_set(vpn_path + ["profile", nhrp_profile, "esp-group", esp_group])
+ self.cli_set(vpn_path + ["profile", nhrp_profile, "ike-group", ike_group])
self.cli_commit()
opennhrp_lines = [
- 'interface tun100 #hub NHRPVPN',
- 'cisco-authentication secret',
- 'holding-time 300',
- 'shortcut',
- 'multicast dynamic',
- 'redirect'
+ f'interface {tunnel_if} #hub {nhrp_profile}',
+ f'cisco-authentication {nhrp_secret}',
+ f'holding-time 300',
+ f'shortcut',
+ f'multicast dynamic',
+ f'redirect'
]
tmp_opennhrp_conf = read_file('/run/opennhrp/opennhrp.conf')
@@ -93,13 +107,13 @@ class TestProtocolsNHRP(VyOSUnitTestSHIM.TestCase):
self.assertIn(line, tmp_opennhrp_conf)
firewall_matches = [
- 'ip protocol gre',
- 'ip saddr 192.0.2.1',
- 'ip daddr 224.0.0.0/4',
- 'comment "VYOS_NHRP_tun100"'
+ f'ip protocol {tunnel_encapsulation}',
+ f'ip saddr {tunnel_source}',
+ f'ip daddr 224.0.0.0/4',
+ f'comment "VYOS_NHRP_{tunnel_if}"'
]
- self.assertTrue(find_nftables_rule('ip filter', 'VYOS_FW_OUTPUT', firewall_matches) is not None)
+ self.assertTrue(find_nftables_rule('ip vyos_nhrp_filter', 'VYOS_NHRP_OUTPUT', firewall_matches) is not None)
self.assertTrue(process_named_running('opennhrp'))
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index e15ea478b..93bb761c1 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.py
@@ -14,8 +14,6 @@
# 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 logging
-import sys
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
@@ -23,15 +21,12 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.util import process_named_running
-from vyos.util import cmd
PROCESS_NAME = 'ospfd'
base_path = ['protocols', 'ospf']
route_map = 'foo-bar-baz10'
-log = logging.getLogger('TestProtocolsOSPF')
-
class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -210,25 +205,14 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map])
self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type])
- # enable FRR debugging to find the root cause of failing testcases
- cmd('touch /tmp/vyos.frr.debug')
-
# commit changes
self.cli_commit()
- # disable FRR debugging
- cmd('rm -f /tmp/vyos.frr.debug')
-
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf')
- try:
- self.assertIn(f'router ospf', frrconfig)
- for protocol in redistribute:
- self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
- except:
- log.debug(frrconfig)
- log.debug(cmd('sudo cat /tmp/vyos-configd-script-stdout'))
- self.fail('Now we can hopefully see why OSPF fails!')
+ self.assertIn(f'router ospf', frrconfig)
+ for protocol in redistribute:
+ self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
def test_ospf_08_virtual_link(self):
networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
@@ -396,6 +380,44 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' network {network} area {area}', frrconfig)
self.assertIn(f' area {area} export-list {acl}', frrconfig)
+
+ def test_ospf_14_segment_routing_configuration(self):
+ global_block_low = "100"
+ global_block_high = "199"
+ local_block_low = "200"
+ local_block_high = "299"
+ interface = 'lo'
+ maximum_stack_size = '5'
+ prefix_one = '192.168.0.1/32'
+ prefix_two = '192.168.0.2/32'
+ prefix_one_value = '1'
+ prefix_two_value = '2'
+
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(base_path + ['segment-routing', 'maximum-label-depth', maximum_stack_size])
+ self.cli_set(base_path + ['segment-routing', 'global-block', 'low-label-value', global_block_low])
+ self.cli_set(base_path + ['segment-routing', 'global-block', 'high-label-value', global_block_high])
+ self.cli_set(base_path + ['segment-routing', 'local-block', 'low-label-value', local_block_low])
+ self.cli_set(base_path + ['segment-routing', 'local-block', 'high-label-value', local_block_high])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'value', prefix_one_value])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_one, 'index', 'explicit-null'])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'value', prefix_two_value])
+ self.cli_set(base_path + ['segment-routing', 'prefix', prefix_two, 'index', 'no-php-flag'])
+
+ # Commit all changes
+ self.cli_commit()
+
+ # Verify all changes
+
+ frrconfig = self.getFRRconfig('router ospf')
+ self.assertIn(f' segment-routing global-block {global_block_low} {global_block_high} local-block {local_block_low} {local_block_high}', frrconfig)
+ self.assertIn(f' segment-routing node-msd {maximum_stack_size}', frrconfig)
+ 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)
+
+ self.skipTest('https://github.com/FRRouting/frr/issues/12007')
+ self.assertIn(f' segment-routing on', frrconfig)
+
+
if __name__ == '__main__':
- logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ids.py b/smoketest/scripts/cli/test_service_ids.py
index d471eeaed..dcf2bcefe 100755
--- a/smoketest/scripts/cli/test_service_ids.py
+++ b/smoketest/scripts/cli/test_service_ids.py
@@ -77,9 +77,9 @@ class TestServiceIDS(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['listen-interface', tmp])
self.cli_set(base_path + ['direction', 'in'])
- self.cli_set(base_path + ['threshold', 'fps', fps])
- self.cli_set(base_path + ['threshold', 'pps', pps])
- self.cli_set(base_path + ['threshold', 'mbps', mbps])
+ self.cli_set(base_path + ['threshold', 'general', 'fps', fps])
+ self.cli_set(base_path + ['threshold', 'general', 'pps', pps])
+ self.cli_set(base_path + ['threshold', 'general', 'mbps', mbps])
# commit changes
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py
new file mode 100755
index 000000000..bdab35834
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_ipoe-server.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import unittest
+
+from base_accel_ppp_test import BasicAccelPPPTest
+from vyos.configsession import ConfigSessionError
+from vyos.util import cmd
+
+from configparser import ConfigParser
+
+ac_name = 'ACN'
+interface = 'eth0'
+
+class TestServiceIPoEServer(BasicAccelPPPTest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls._base_path = ['service', 'ipoe-server']
+ cls._config_file = '/run/accel-pppd/ipoe.conf'
+ cls._chap_secrets = '/run/accel-pppd/ipoe.chap-secrets'
+
+ # call base-classes classmethod
+ super(TestServiceIPoEServer, cls).setUpClass()
+
+ def verify(self, conf):
+ super().verify(conf)
+
+ # Validate configuration values
+ accel_modules = list(conf['modules'].keys())
+ self.assertIn('log_syslog', accel_modules)
+ self.assertIn('ipoe', accel_modules)
+ self.assertIn('shaper', accel_modules)
+ self.assertIn('ipv6pool', accel_modules)
+ self.assertIn('ipv6_nd', accel_modules)
+ self.assertIn('ipv6_dhcp', accel_modules)
+ self.assertIn('ippool', accel_modules)
+
+ def basic_config(self):
+ self.set(['interface', interface, 'client-subnet', '192.168.0.0/24'])
+
+ def test_accel_local_authentication(self):
+ mac_address = '08:00:27:2f:d8:06'
+ self.set(['authentication', 'interface', interface, 'mac', mac_address])
+ self.set(['authentication', 'mode', 'local'])
+
+ # No IPoE interface configured
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Test configuration of local authentication for PPPoE server
+ self.basic_config()
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+
+ # check proper path to chap-secrets file
+ self.assertEqual(conf['chap-secrets']['chap-secrets'], self._chap_secrets)
+
+ accel_modules = list(conf['modules'].keys())
+ self.assertIn('chap-secrets', accel_modules)
+
+ # basic verification
+ self.verify(conf)
+
+ # check local users
+ tmp = cmd(f'sudo cat {self._chap_secrets}')
+ regex = f'{interface}\s+\*\s+{mac_address}\s+\*'
+ tmp = re.findall(regex, tmp)
+ self.assertTrue(tmp)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
+
diff --git a/smoketest/scripts/cli/test_service_monitoring_telegraf.py b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
index c1c4044e6..ed486c3b9 100755
--- a/smoketest/scripts/cli/test_service_monitoring_telegraf.py
+++ b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
@@ -60,6 +60,7 @@ class TestMonitoringTelegraf(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' token = "$INFLUX_TOKEN"', config)
self.assertIn(f'urls = ["{url}:{port}"]', config)
self.assertIn(f'bucket = "{bucket}"', config)
+ self.assertIn(f'[[inputs.exec]]', config)
for input in inputs:
self.assertIn(input, config)
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 51cc098ef..7546c2e3d 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 020 VyOS maintainers and contributors
+# Copyright (C) 2022 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
@@ -14,27 +14,27 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
import unittest
from base_accel_ppp_test import BasicAccelPPPTest
from configparser import ConfigParser
-from vyos.configsession import ConfigSessionError
-from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.template import range_to_regex
local_if = ['interfaces', 'dummy', 'dum667']
ac_name = 'ACN'
interface = 'eth0'
class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
- def setUp(self):
- self._base_path = ['service', 'pppoe-server']
- self._process_name = 'accel-pppd'
- self._config_file = '/run/accel-pppd/pppoe.conf'
- self._chap_secrets = '/run/accel-pppd/pppoe.chap-secrets'
+ @classmethod
+ def setUpClass(cls):
+ cls._base_path = ['service', 'pppoe-server']
+ cls._config_file = '/run/accel-pppd/pppoe.conf'
+ cls._chap_secrets = '/run/accel-pppd/pppoe.chap-secrets'
- super().setUp()
+ # call base-classes classmethod
+ super(TestServicePPPoEServer, cls).setUpClass()
def tearDown(self):
self.cli_delete(local_if)
@@ -120,8 +120,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
# check interface-cache
self.assertEqual(conf['ppp']['unit-cache'], interface_cache)
- # Check for running process
- self.assertTrue(process_named_running(self._process_name))
def test_pppoe_server_authentication_protocols(self):
# Test configuration of local authentication for PPPoE server
@@ -139,8 +137,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
self.assertEqual(conf['modules']['auth_mschap_v2'], None)
- # Check for running process
- self.assertTrue(process_named_running(self._process_name))
def test_pppoe_server_client_ip_pool(self):
# Test configuration of IPv6 client pools
@@ -168,9 +164,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
self.assertEqual(conf['ip-pool'][start_stop], None)
self.assertEqual(conf['ip-pool']['gw-ip-address'], self._gateway)
- # Check for running process
- self.assertTrue(process_named_running(self._process_name))
-
def test_pppoe_server_client_ipv6_pool(self):
# Test configuration of IPv6 client pools
@@ -211,9 +204,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
self.assertEqual(conf['ipv6-pool'][client_prefix], None)
self.assertEqual(conf['ipv6-pool']['delegate'], f'{delegate_prefix},{delegate_mask}')
- # Check for running process
- self.assertTrue(process_named_running(self._process_name))
-
def test_accel_radius_authentication(self):
radius_called_sid = 'ifname:mac'
@@ -234,5 +224,27 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
self.assertEqual(conf['radius']['acct-interim-jitter'], radius_acct_interim_jitter)
+ def test_pppoe_server_vlan(self):
+
+ vlans = ['100', '200', '300-310']
+
+ # Test configuration of local authentication for PPPoE server
+ self.basic_config()
+
+ for vlan in vlans:
+ self.set(['interface', interface, 'vlan', vlan])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ config = read_file(self._config_file)
+ for vlan in vlans:
+ tmp = range_to_regex(vlan)
+ self.assertIn(f'interface=re:^{interface}\.{tmp}$', config)
+
+ tmp = ','.join(vlans)
+ self.assertIn(f'vlan-mon={interface},{tmp}', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py
index 95c2a6c55..fd16146b1 100755
--- a/smoketest/scripts/cli/test_system_conntrack.py
+++ b/smoketest/scripts/cli/test_system_conntrack.py
@@ -59,7 +59,7 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
},
'net.netfilter.nf_conntrack_tcp_max_retrans' :{
'cli' : ['tcp', 'max-retrans'],
- 'test_value' : '1024',
+ 'test_value' : '128',
'default_value' : '3',
},
'net.netfilter.nf_conntrack_icmp_timeout' :{
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index 8a6514d57..bd242104f 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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,7 @@ dhcp_waiting_file = '/tmp/ipsec_dhcp_waiting'
swanctl_file = '/etc/swanctl/swanctl.conf'
peer_ip = '203.0.113.45'
+connection_name = 'main-branch'
interface = 'eth1'
vif = '100'
esp_group = 'MyESPGroup'
@@ -150,7 +151,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(ethernet_path + [interface, 'vif', vif, 'address', 'dhcp']) # Use VLAN to avoid getting IP from qemu dhcp server
# Site to site
- peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+ peer_base_path = base_path + ['site-to-site', 'peer', connection_name]
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
self.cli_set(peer_base_path + ['ike-group', ike_group])
@@ -173,7 +174,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
priority = '20'
life_bytes = '100000'
life_packets = '2000000'
- peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+ peer_base_path = base_path + ['site-to-site', 'peer', connection_name]
self.cli_set(base_path + ['esp-group', esp_group, 'life-bytes', life_bytes])
self.cli_set(base_path + ['esp-group', esp_group, 'life-packets', life_packets])
@@ -183,6 +184,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(peer_base_path + ['ike-group', ike_group])
self.cli_set(peer_base_path + ['default-esp-group', esp_group])
self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['remote-address', peer_ip])
self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'tcp'])
self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.10.0/24'])
self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.11.0/24'])
@@ -211,11 +213,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'local_addrs = {local_address} # dhcp:no',
f'remote_addrs = {peer_ip}',
f'mode = tunnel',
- f'peer_{peer_ip.replace(".","-")}_tunnel_1',
+ f'{connection_name}-tunnel-1',
f'local_ts = 172.16.10.0/24[tcp/443],172.16.11.0/24[tcp/443]',
f'remote_ts = 172.17.10.0/24[tcp/443],172.17.11.0/24[tcp/443]',
f'mode = tunnel',
- f'peer_{peer_ip.replace(".","-")}_tunnel_2',
+ f'{connection_name}-tunnel-2',
f'local_ts = 10.1.0.0/16',
f'remote_ts = 10.2.0.0/16',
f'priority = {priority}',
@@ -226,7 +228,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
swanctl_secrets_lines = [
f'id-local = {local_address} # dhcp:no',
- f'id-remote = {peer_ip}',
+ f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}',
f'secret = "{secret}"'
]
for line in swanctl_secrets_lines:
@@ -236,18 +238,24 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
def test_03_site_to_site_vti(self):
local_address = '192.0.2.10'
vti = 'vti10'
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'disable-mobike'])
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'compression'])
# VTI interface
self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24'])
- self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
# Site to site
- peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+ peer_base_path = base_path + ['site-to-site', 'peer', connection_name]
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
self.cli_set(peer_base_path + ['connection-type', 'none'])
+ self.cli_set(peer_base_path + ['force-udp-encapsulation'])
self.cli_set(peer_base_path + ['ike-group', ike_group])
self.cli_set(peer_base_path + ['default-esp-group', esp_group])
self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['remote-address', peer_ip])
self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.10.0/24'])
self.cli_set(peer_base_path + ['tunnel', '1', 'local', 'prefix', '172.16.11.0/24'])
self.cli_set(peer_base_path + ['tunnel', '1', 'remote', 'prefix', '172.17.10.0/24'])
@@ -269,10 +277,12 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'proposals = aes128-sha1-modp1024',
f'esp_proposals = aes128-sha1-modp1024',
f'local_addrs = {local_address} # dhcp:no',
+ f'mobike = no',
f'remote_addrs = {peer_ip}',
f'mode = tunnel',
f'local_ts = 172.16.10.0/24,172.16.11.0/24',
f'remote_ts = 172.17.10.0/24,172.17.11.0/24',
+ f'ipcomp = yes',
f'start_action = none',
f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one
f'if_id_out = {if_id}',
@@ -283,7 +293,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
swanctl_secrets_lines = [
f'id-local = {local_address} # dhcp:no',
- f'id-remote = {peer_ip}',
+ f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}',
f'secret = "{secret}"'
]
for line in swanctl_secrets_lines:
@@ -311,7 +321,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(nhrp_path + ['tunnel', tunnel_if, 'shortcut'])
# IKE/ESP Groups
- self.cli_set(base_path + ['esp-group', esp_group, 'compression', 'disable'])
self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', esp_lifetime])
self.cli_set(base_path + ['esp-group', esp_group, 'mode', 'transport'])
self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'dh-group2'])
@@ -320,7 +329,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', '3des'])
self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'md5'])
- self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no'])
self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev1'])
self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])
self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '2'])
@@ -366,10 +374,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(vti_path + [vti, 'address', '192.168.0.1/31'])
peer_ip = '172.18.254.202'
+ connection_name = 'office'
local_address = '172.18.254.201'
- peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+ peer_base_path = base_path + ['site-to-site', 'peer', connection_name]
- self.cli_set(peer_base_path + ['authentication', 'id', peer_name])
+ self.cli_set(peer_base_path + ['authentication', 'local-id', peer_name])
self.cli_set(peer_base_path + ['authentication', 'mode', 'x509'])
self.cli_set(peer_base_path + ['authentication', 'remote-id', 'peer2'])
self.cli_set(peer_base_path + ['authentication', 'x509', 'ca-certificate', ca_name])
@@ -378,6 +387,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(peer_base_path + ['ike-group', ike_group])
self.cli_set(peer_base_path + ['ikev2-reauth', 'inherit'])
self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['remote-address', peer_ip])
self.cli_set(peer_base_path + ['vti', 'bind', vti])
self.cli_set(peer_base_path + ['vti', 'esp-group', esp_group])
@@ -391,7 +401,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# to also support a vti0 interface
if_id = str(int(if_id) +1)
swanctl_lines = [
- f'peer_{tmp}',
+ f'{connection_name}',
f'version = 0', # key-exchange not set - defaulting to 0 for ikev1 and ikev2
f'send_cert = always',
f'mobike = yes',
@@ -416,7 +426,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.assertIn(line, swanctl_conf)
swanctl_secrets_lines = [
- f'peer_{tmp}',
+ f'{connection_name}',
f'file = {peer_name}.pem',
]
for line in swanctl_secrets_lines:
@@ -430,7 +440,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
local_address = '192.0.2.5'
local_id = 'vyos-r1'
remote_id = 'vyos-r2'
- peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+ peer_base_path = base_path + ['site-to-site', 'peer', connection_name]
self.cli_set(tunnel_path + ['tun1', 'encapsulation', 'gre'])
self.cli_set(tunnel_path + ['tun1', 'source-address', local_address])
@@ -438,10 +448,9 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['interface', interface])
self.cli_set(base_path + ['options', 'flexvpn'])
self.cli_set(base_path + ['options', 'interface', 'tun1'])
- self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no'])
self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
- self.cli_set(peer_base_path + ['authentication', 'id', local_id])
+ self.cli_set(peer_base_path + ['authentication', 'local-id', local_id])
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
self.cli_set(peer_base_path + ['authentication', 'remote-id', remote_id])
@@ -449,6 +458,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(peer_base_path + ['ike-group', ike_group])
self.cli_set(peer_base_path + ['default-esp-group', esp_group])
self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['remote-address', peer_ip])
self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'gre'])
self.cli_set(peer_base_path + ['virtual-address', '203.0.113.55'])
@@ -464,7 +474,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'life_time = 3600s', # default value
f'local_addrs = {local_address} # dhcp:no',
f'remote_addrs = {peer_ip}',
- f'peer_{peer_ip.replace(".","-")}_tunnel_1',
+ f'{connection_name}-tunnel-1',
f'mode = tunnel',
]
@@ -473,7 +483,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
swanctl_secrets_lines = [
f'id-local = {local_address} # dhcp:no',
- f'id-remote = {peer_ip}',
+ f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}',
f'id-localid = {local_id}',
f'id-remoteid = {remote_id}',
f'secret = "{secret}"',
diff --git a/smoketest/scripts/cli/test_vpn_sstp.py b/smoketest/scripts/cli/test_vpn_sstp.py
index f58920b5b..434e3aa05 100755
--- a/smoketest/scripts/cli/test_vpn_sstp.py
+++ b/smoketest/scripts/cli/test_vpn_sstp.py
@@ -19,29 +19,49 @@ import unittest
from base_accel_ppp_test import BasicAccelPPPTest
from vyos.util import read_file
-
pki_path = ['pki']
-cert_data = 'MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIwWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIxMDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3LftzngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93+dm/LDnp7C0='
-key_data = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww'
+
+cert_data = """
+MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
+WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
+MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
+BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
+UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
+01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
+QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
+BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
++JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
+ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
++dm/LDnp7C0="""
+
+key_data = """
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
+2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
+u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+"""
class TestVPNSSTPServer(BasicAccelPPPTest.TestCase):
- def setUp(self):
- self._base_path = ['vpn', 'sstp']
- self._process_name = 'accel-pppd'
- self._config_file = '/run/accel-pppd/sstp.conf'
- self._chap_secrets = '/run/accel-pppd/sstp.chap-secrets'
- super().setUp()
+ @classmethod
+ def setUpClass(cls):
+ cls._base_path = ['vpn', 'sstp']
+ cls._config_file = '/run/accel-pppd/sstp.conf'
+ cls._chap_secrets = '/run/accel-pppd/sstp.chap-secrets'
- def tearDown(self):
- self.cli_delete(pki_path)
- super().tearDown()
+ # call base-classes classmethod
+ super(TestVPNSSTPServer, cls).setUpClass()
- def basic_config(self):
- self.cli_delete(pki_path)
- self.cli_set(pki_path + ['ca', 'sstp', 'certificate', cert_data])
- self.cli_set(pki_path + ['certificate', 'sstp', 'certificate', cert_data])
- self.cli_set(pki_path + ['certificate', 'sstp', 'private', 'key', key_data])
+ cls.cli_set(cls, pki_path + ['ca', 'sstp', 'certificate', cert_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['certificate', 'sstp', 'certificate', cert_data.replace('\n','')])
+ cls.cli_set(cls, pki_path + ['certificate', 'sstp', 'private', 'key', key_data.replace('\n','')])
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, pki_path)
+
+ super(TestVPNSSTPServer, cls).tearDownClass()
+
+ def basic_config(self):
# SSL is mandatory
self.set(['ssl', 'ca-certificate', 'sstp'])
self.set(['ssl', 'certificate', 'sstp'])
diff --git a/smoketest/scripts/cli/test_zone_policy.py b/smoketest/scripts/cli/test_zone_policy.py
deleted file mode 100755
index 2c580e2f1..000000000
--- a/smoketest/scripts/cli/test_zone_policy.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021-2022 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 unittest
-
-from base_vyostest_shim import VyOSUnitTestSHIM
-
-from vyos.util import cmd
-
-class TestZonePolicy(VyOSUnitTestSHIM.TestCase):
- @classmethod
- def setUpClass(cls):
- super(TestZonePolicy, cls).setUpClass()
- cls.cli_set(cls, ['firewall', 'name', 'smoketest', 'default-action', 'drop'])
-
- @classmethod
- def tearDownClass(cls):
- cls.cli_delete(cls, ['firewall'])
- super(TestZonePolicy, cls).tearDownClass()
-
- def tearDown(self):
- self.cli_delete(['zone-policy'])
- self.cli_commit()
-
- def test_basic_zone(self):
- self.cli_set(['zone-policy', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
- self.cli_set(['zone-policy', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
- self.cli_set(['zone-policy', 'zone', 'smoketest-local', 'local-zone'])
- self.cli_set(['zone-policy', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
-
- self.cli_commit()
-
- nftables_search = [
- ['chain VZONE_smoketest-eth0'],
- ['chain VZONE_smoketest-local_IN'],
- ['chain VZONE_smoketest-local_OUT'],
- ['oifname { "eth0" }', 'jump VZONE_smoketest-eth0'],
- ['jump VZONE_smoketest-local_IN'],
- ['jump VZONE_smoketest-local_OUT'],
- ['iifname { "eth0" }', 'jump NAME_smoketest'],
- ['oifname { "eth0" }', 'jump NAME_smoketest']
- ]
-
- nftables_output = cmd('sudo nft list table ip filter')
-
- for search in nftables_search:
- matched = False
- for line in nftables_output.split("\n"):
- if all(item in line for item in search):
- matched = True
- break
- self.assertTrue(matched)
-
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
diff --git a/src/completion/list_consoles.sh b/src/completion/list_consoles.sh
new file mode 100755
index 000000000..52278c4cb
--- /dev/null
+++ b/src/completion/list_consoles.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# For lines like `aliases "foo";`, regex matches everything between the quotes
+grep -oP '(?<=aliases ").+(?=";)' /run/conserver/conserver.cf \ No newline at end of file
diff --git a/src/conf_mode/firewall-interface.py b/src/conf_mode/firewall-interface.py
deleted file mode 100755
index ab1c69259..000000000
--- a/src/conf_mode/firewall-interface.py
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-import re
-
-from sys import argv
-from sys import exit
-
-from vyos.config import Config
-from vyos.configdict import leaf_node_changed
-from vyos.ifconfig import Section
-from vyos.template import render
-from vyos.util import cmd
-from vyos.util import dict_search_args
-from vyos.util import run
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-NAME_PREFIX = 'NAME_'
-NAME6_PREFIX = 'NAME6_'
-
-NFT_CHAINS = {
- 'in': 'VYOS_FW_FORWARD',
- 'out': 'VYOS_FW_FORWARD',
- 'local': 'VYOS_FW_LOCAL'
-}
-NFT6_CHAINS = {
- 'in': 'VYOS_FW6_FORWARD',
- 'out': 'VYOS_FW6_FORWARD',
- 'local': 'VYOS_FW6_LOCAL'
-}
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
-
- ifname = argv[1]
- ifpath = Section.get_config_path(ifname)
- if_firewall_path = f'interfaces {ifpath} firewall'
-
- if_firewall = conf.get_config_dict(if_firewall_path, key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
- if_firewall['ifname'] = ifname
- if_firewall['firewall'] = conf.get_config_dict(['firewall'], key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
- return if_firewall
-
-def verify_chain(table, chain):
- # Verify firewall applied
- code = run(f'nft list chain {table} {chain}')
- return code == 0
-
-def verify(if_firewall):
- # bail out early - looks like removal from running config
- if not if_firewall:
- return None
-
- for direction in ['in', 'out', 'local']:
- if direction in if_firewall:
- if 'name' in if_firewall[direction]:
- name = if_firewall[direction]['name']
-
- if 'name' not in if_firewall['firewall']:
- raise ConfigError('Firewall name not configured')
-
- if name not in if_firewall['firewall']['name']:
- raise ConfigError(f'Invalid firewall name "{name}"')
-
- if not verify_chain('ip filter', f'{NAME_PREFIX}{name}'):
- raise ConfigError('Firewall did not apply')
-
- if 'ipv6_name' in if_firewall[direction]:
- name = if_firewall[direction]['ipv6_name']
-
- if 'ipv6_name' not in if_firewall['firewall']:
- raise ConfigError('Firewall ipv6-name not configured')
-
- if name not in if_firewall['firewall']['ipv6_name']:
- raise ConfigError(f'Invalid firewall ipv6-name "{name}"')
-
- if not verify_chain('ip6 filter', f'{NAME6_PREFIX}{name}'):
- raise ConfigError('Firewall did not apply')
-
- return None
-
-def generate(if_firewall):
- return None
-
-def cleanup_rule(table, chain, prefix, ifname, new_name=None):
- results = cmd(f'nft -a list chain {table} {chain}').split("\n")
- retval = None
- for line in results:
- if f'{prefix}ifname "{ifname}"' in line:
- if new_name and f'jump {new_name}' in line:
- # new_name is used to clear rules for any previously referenced chains
- # returns true when rule exists and doesn't need to be created
- retval = True
- continue
-
- handle_search = re.search('handle (\d+)', line)
- if handle_search:
- run(f'nft delete rule {table} {chain} handle {handle_search[1]}')
- return retval
-
-def state_policy_handle(table, chain):
- # Find any state-policy rule to ensure interface rules are only inserted afterwards
- results = cmd(f'nft -a list chain {table} {chain}').split("\n")
- for line in results:
- if 'jump VYOS_STATE_POLICY' in line:
- handle_search = re.search('handle (\d+)', line)
- if handle_search:
- return handle_search[1]
- return None
-
-def apply(if_firewall):
- ifname = if_firewall['ifname']
-
- for direction in ['in', 'out', 'local']:
- chain = NFT_CHAINS[direction]
- ipv6_chain = NFT6_CHAINS[direction]
- if_prefix = 'i' if direction in ['in', 'local'] else 'o'
-
- name = dict_search_args(if_firewall, direction, 'name')
- if name:
- rule_exists = cleanup_rule('ip filter', chain, if_prefix, ifname, f'{NAME_PREFIX}{name}')
-
- if not rule_exists:
- rule_action = 'insert'
- rule_prefix = ''
-
- handle = state_policy_handle('ip filter', chain)
- if handle:
- rule_action = 'add'
- rule_prefix = f'position {handle}'
-
- run(f'nft {rule_action} rule ip filter {chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {NAME_PREFIX}{name}')
- else:
- cleanup_rule('ip filter', chain, if_prefix, ifname)
-
- ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name')
- if ipv6_name:
- rule_exists = cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname, f'{NAME6_PREFIX}{ipv6_name}')
-
- if not rule_exists:
- rule_action = 'insert'
- rule_prefix = ''
-
- handle = state_policy_handle('ip6 filter', ipv6_chain)
- if handle:
- rule_action = 'add'
- rule_prefix = f'position {handle}'
-
- run(f'nft {rule_action} rule ip6 filter {ipv6_chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {NAME6_PREFIX}{ipv6_name}')
- else:
- cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname)
-
- return None
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index f0ea1a1e5..cbd9cbe90 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -26,6 +26,7 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import node_changed
from vyos.configdiff import get_config_diff, Diff
+# from vyos.configverify import verify_interface_exists
from vyos.firewall import geoip_update
from vyos.firewall import get_ips_domains_dict
from vyos.firewall import nft_add_set_elements
@@ -38,7 +39,7 @@ from vyos.util import cmd
from vyos.util import dict_search_args
from vyos.util import dict_search_recursive
from vyos.util import process_named_running
-from vyos.util import run
+from vyos.util import rc_cmd
from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
@@ -47,7 +48,6 @@ airbag.enable()
policy_route_conf_script = '/usr/libexec/vyos/conf_mode/policy-route.py'
nftables_conf = '/run/nftables.conf'
-nftables_defines_conf = '/run/nftables_defines.conf'
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'enable': '0', 'disable': '1'},
@@ -63,28 +63,6 @@ sysfs_config = {
'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337'}
}
-NAME_PREFIX = 'NAME_'
-NAME6_PREFIX = 'NAME6_'
-
-preserve_chains = [
- 'INPUT',
- 'FORWARD',
- 'OUTPUT',
- 'VYOS_FW_FORWARD',
- 'VYOS_FW_LOCAL',
- 'VYOS_FW_OUTPUT',
- 'VYOS_POST_FW',
- 'VYOS_FRAG_MARK',
- 'VYOS_FW6_FORWARD',
- 'VYOS_FW6_LOCAL',
- 'VYOS_FW6_OUTPUT',
- 'VYOS_POST_FW6',
- 'VYOS_FRAG6_MARK'
-]
-
-nft_iface_chains = ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL']
-nft6_iface_chains = ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
-
valid_groups = [
'address_group',
'domain_group',
@@ -97,16 +75,6 @@ nested_group_types = [
'port_group', 'ipv6_address_group', 'ipv6_network_group'
]
-group_set_prefix = {
- 'A_': 'address_group',
- 'A6_': 'ipv6_address_group',
- 'D_': 'domain_group',
- 'M_': 'mac_group',
- 'N_': 'network_group',
- 'N6_': 'ipv6_network_group',
- 'P_': 'port_group'
-}
-
snmp_change_type = {
'unknown': 0,
'add': 1,
@@ -117,51 +85,6 @@ snmp_event_source = 1
snmp_trap_mib = 'VYATTA-TRAP-MIB'
snmp_trap_name = 'mgmtEventTrap'
-def get_firewall_interfaces(conf):
- out = {}
- interfaces = conf.get_config_dict(['interfaces'], key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
- def find_interfaces(iftype_conf, output={}, prefix=''):
- for ifname, if_conf in iftype_conf.items():
- if 'firewall' in if_conf:
- output[prefix + ifname] = if_conf['firewall']
- for vif in ['vif', 'vif_s', 'vif_c']:
- if vif in if_conf:
- output.update(find_interfaces(if_conf[vif], output, f'{prefix}{ifname}.'))
- return output
- for iftype, iftype_conf in interfaces.items():
- out.update(find_interfaces(iftype_conf))
- return out
-
-def get_firewall_zones(conf):
- used_v4 = []
- used_v6 = []
- zone_policy = conf.get_config_dict(['zone-policy'], key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
- if 'zone' in zone_policy:
- for zone, zone_conf in zone_policy['zone'].items():
- if 'from' in zone_conf:
- for from_zone, from_conf in zone_conf['from'].items():
- name = dict_search_args(from_conf, 'firewall', 'name')
- if name:
- used_v4.append(name)
-
- ipv6_name = dict_search_args(from_conf, 'firewall', 'ipv6_name')
- if ipv6_name:
- used_v6.append(ipv6_name)
-
- if 'intra_zone_filtering' in zone_conf:
- name = dict_search_args(zone_conf, 'intra_zone_filtering', 'firewall', 'name')
- if name:
- used_v4.append(name)
-
- ipv6_name = dict_search_args(zone_conf, 'intra_zone_filtering', 'firewall', 'ipv6_name')
- if ipv6_name:
- used_v6.append(ipv6_name)
-
- return {'name': used_v4, 'ipv6_name': used_v6}
-
def geoip_updated(conf, firewall):
diff = get_config_diff(conf)
node_diff = diff.get_child_nodes_diff(['firewall'], expand_nodes=Diff.DELETE, recursive=True)
@@ -215,6 +138,9 @@ def get_config(config=None):
if tmp in default_values:
del default_values[tmp]
+ if 'zone' in default_values:
+ del default_values['zone']
+
firewall = dict_merge(default_values, firewall)
# Merge in defaults for IPv4 ruleset
@@ -231,9 +157,12 @@ def get_config(config=None):
firewall['ipv6_name'][ipv6_name] = dict_merge(default_values,
firewall['ipv6_name'][ipv6_name])
+ if 'zone' in firewall:
+ default_values = defaults(base + ['zone'])
+ for zone in firewall['zone']:
+ firewall['zone'][zone] = dict_merge(default_values, firewall['zone'][zone])
+
firewall['policy_resync'] = bool('group' in firewall or node_changed(conf, base + ['group']))
- firewall['interfaces'] = get_firewall_interfaces(conf)
- firewall['zone_policy'] = get_firewall_zones(conf)
if 'config_trap' in firewall and firewall['config_trap'] == 'enable':
diff = get_config_diff(conf)
@@ -250,6 +179,20 @@ def verify_rule(firewall, rule_conf, ipv6):
if 'action' not in rule_conf:
raise ConfigError('Rule action must be defined')
+ if 'jump' in rule_conf['action'] and 'jump_target' not in rule_conf:
+ raise ConfigError('Action set to jump, but no jump-target specified')
+
+ if 'jump_target' in rule_conf:
+ if 'jump' not in rule_conf['action']:
+ raise ConfigError('jump-target defined, but action jump needed and it is not defined')
+ target = rule_conf['jump_target']
+ if not ipv6:
+ if target not in dict_search_args(firewall, 'name'):
+ raise ConfigError(f'Invalid jump-target. Firewall name {target} does not exist on the system')
+ else:
+ if target not in dict_search_args(firewall, 'ipv6_name'):
+ raise ConfigError(f'Invalid jump-target. Firewall ipv6-name {target} does not exist on the system')
+
if 'fragment' in rule_conf:
if {'match_frag', 'match_non_frag'} <= set(rule_conf['fragment']):
raise ConfigError('Cannot specify both "match-frag" and "match-non-frag"')
@@ -358,109 +301,111 @@ def verify(firewall):
for name in ['name', 'ipv6_name']:
if name in firewall:
for name_id, name_conf in firewall[name].items():
- if name_id in preserve_chains:
- raise ConfigError(f'Firewall name "{name_id}" is reserved for VyOS')
-
- if name_id.startswith("VZONE"):
- raise ConfigError(f'Firewall name "{name_id}" uses reserved prefix')
+ if 'jump' in name_conf['default_action'] and 'default_jump_target' not in name_conf:
+ raise ConfigError('default-action set to jump, but no default-jump-target specified')
+ if 'default_jump_target' in name_conf:
+ target = name_conf['default_jump_target']
+ if 'jump' not in name_conf['default_action']:
+ raise ConfigError('default-jump-target defined,but default-action jump needed and it is not defined')
+ if name_conf['default_jump_target'] == name_id:
+ raise ConfigError(f'Loop detected on default-jump-target.')
+ ## Now need to check that default-jump-target exists (other firewall chain/name)
+ if target not in dict_search_args(firewall, name):
+ raise ConfigError(f'Invalid jump-target. Firewall {name} {target} does not exist on the system')
if 'rule' in name_conf:
for rule_id, rule_conf in name_conf['rule'].items():
verify_rule(firewall, rule_conf, name == 'ipv6_name')
- for ifname, if_firewall in firewall['interfaces'].items():
- for direction in ['in', 'out', 'local']:
- name = dict_search_args(if_firewall, direction, 'name')
- ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name')
+ if 'interface' in firewall:
+ for ifname, if_firewall in firewall['interface'].items():
+ # verify ifname needs to be disabled, dynamic devices come up later
+ # verify_interface_exists(ifname)
- if name and dict_search_args(firewall, 'name', name) == None:
- raise ConfigError(f'Firewall name "{name}" is still referenced on interface {ifname}')
+ for direction in ['in', 'out', 'local']:
+ name = dict_search_args(if_firewall, direction, 'name')
+ ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name')
- if ipv6_name and dict_search_args(firewall, 'ipv6_name', ipv6_name) == None:
- raise ConfigError(f'Firewall ipv6-name "{ipv6_name}" is still referenced on interface {ifname}')
+ if name and dict_search_args(firewall, 'name', name) == None:
+ raise ConfigError(f'Invalid firewall name "{name}" referenced on interface {ifname}')
- for fw_name, used_names in firewall['zone_policy'].items():
- for name in used_names:
- if dict_search_args(firewall, fw_name, name) == None:
- raise ConfigError(f'Firewall {fw_name.replace("_", "-")} "{name}" is still referenced in zone-policy')
+ if ipv6_name and dict_search_args(firewall, 'ipv6_name', ipv6_name) == None:
+ raise ConfigError(f'Invalid firewall ipv6-name "{ipv6_name}" referenced on interface {ifname}')
- return None
+ local_zone = False
+ zone_interfaces = []
-def cleanup_commands(firewall):
- commands = []
- commands_chains = []
- commands_sets = []
- for table in ['ip filter', 'ip6 filter']:
- name_node = 'name' if table == 'ip filter' else 'ipv6_name'
- chain_prefix = NAME_PREFIX if table == 'ip filter' else NAME6_PREFIX
- state_chain = 'VYOS_STATE_POLICY' if table == 'ip filter' else 'VYOS_STATE_POLICY6'
- iface_chains = nft_iface_chains if table == 'ip filter' else nft6_iface_chains
-
- geoip_list = []
- if firewall['geoip_updated']:
- geoip_key = 'deleted_ipv6_name' if table == 'ip6 filter' else 'deleted_name'
- geoip_list = dict_search_args(firewall, 'geoip_updated', geoip_key) or []
-
- json_str = cmd(f'nft -t -j list table {table}')
- obj = loads(json_str)
-
- if 'nftables' not in obj:
- continue
-
- for item in obj['nftables']:
- if 'chain' in item:
- chain = item['chain']['name']
- if chain in preserve_chains or chain.startswith("VZONE"):
- continue
+ if 'zone' in firewall:
+ for zone, zone_conf in firewall['zone'].items():
+ if 'local_zone' not in zone_conf and 'interface' not in zone_conf:
+ raise ConfigError(f'Zone "{zone}" has no interfaces and is not the local zone')
+
+ if 'local_zone' in zone_conf:
+ if local_zone:
+ raise ConfigError('There cannot be multiple local zones')
+ if 'interface' in zone_conf:
+ raise ConfigError('Local zone cannot have interfaces assigned')
+ if 'intra_zone_filtering' in zone_conf:
+ raise ConfigError('Local zone cannot use intra-zone-filtering')
+ local_zone = True
- if chain == state_chain:
- command = 'delete' if 'state_policy' not in firewall else 'flush'
- commands_chains.append(f'{command} chain {table} {chain}')
- elif dict_search_args(firewall, name_node, chain.replace(chain_prefix, "", 1)) != None:
- commands.append(f'flush chain {table} {chain}')
- else:
- commands_chains.append(f'delete chain {table} {chain}')
+ if 'interface' in zone_conf:
+ found_duplicates = [intf for intf in zone_conf['interface'] if intf in zone_interfaces]
- if 'rule' in item:
- rule = item['rule']
- chain = rule['chain']
- handle = rule['handle']
+ if found_duplicates:
+ raise ConfigError(f'Interfaces cannot be assigned to multiple zones')
- if chain in iface_chains:
- target, _ = next(dict_search_recursive(rule['expr'], 'target'))
+ zone_interfaces += zone_conf['interface']
- if target == state_chain and 'state_policy' not in firewall:
- commands.append(f'delete rule {table} {chain} handle {handle}')
+ if 'intra_zone_filtering' in zone_conf:
+ intra_zone = zone_conf['intra_zone_filtering']
- if target.startswith(chain_prefix):
- if dict_search_args(firewall, name_node, target.replace(chain_prefix, "", 1)) == None:
- commands.append(f'delete rule {table} {chain} handle {handle}')
+ if len(intra_zone) > 1:
+ raise ConfigError('Only one intra-zone-filtering action must be specified')
- if 'set' in item:
- set_name = item['set']['name']
+ if 'firewall' in intra_zone:
+ v4_name = dict_search_args(intra_zone, 'firewall', 'name')
+ if v4_name and not dict_search_args(firewall, 'name', v4_name):
+ raise ConfigError(f'Firewall name "{v4_name}" does not exist')
- if set_name.startswith('GEOIP_CC_') and set_name in geoip_list:
- commands_sets.append(f'delete set {table} {set_name}')
- continue
+ v6_name = dict_search_args(intra_zone, 'firewall', 'ipv6_name')
+ if v6_name and not dict_search_args(firewall, 'ipv6_name', v6_name):
+ raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
- if set_name.startswith("RECENT_"):
- commands_sets.append(f'delete set {table} {set_name}')
- continue
+ if not v4_name and not v6_name:
+ raise ConfigError('No firewall names specified for intra-zone-filtering')
+
+ if 'from' in zone_conf:
+ for from_zone, from_conf in zone_conf['from'].items():
+ if from_zone not in firewall['zone']:
+ raise ConfigError(f'Zone "{zone}" refers to a non-existent or deleted zone "{from_zone}"')
+
+ v4_name = dict_search_args(from_conf, 'firewall', 'name')
+ if v4_name and not dict_search_args(firewall, 'name', v4_name):
+ raise ConfigError(f'Firewall name "{v4_name}" does not exist')
+
+ v6_name = dict_search_args(from_conf, 'firewall', 'ipv6_name')
+ if v6_name and not dict_search_args(firewall, 'ipv6_name', v6_name):
+ raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
- for prefix, group_type in group_set_prefix.items():
- if set_name.startswith(prefix):
- group_name = set_name.replace(prefix, "", 1)
- if dict_search_args(firewall, 'group', group_type, group_name) != None:
- commands_sets.append(f'flush set {table} {set_name}')
- else:
- commands_sets.append(f'delete set {table} {set_name}')
- return commands + commands_chains + commands_sets
+ return None
def generate(firewall):
if not os.path.exists(nftables_conf):
firewall['first_install'] = True
- else:
- firewall['cleanup_commands'] = cleanup_commands(firewall)
+
+ if 'zone' in firewall:
+ for local_zone, local_zone_conf in firewall['zone'].items():
+ if 'local_zone' not in local_zone_conf:
+ continue
+
+ local_zone_conf['from_local'] = {}
+
+ for zone, zone_conf in firewall['zone'].items():
+ if zone == local_zone or 'from' not in zone_conf:
+ continue
+ if local_zone in zone_conf['from']:
+ local_zone_conf['from_local'][zone] = zone_conf['from'][local_zone]
render(nftables_conf, 'firewall/nftables.j2', firewall)
return None
@@ -521,26 +466,16 @@ def post_apply_trap(firewall):
cmd(base_cmd + ' '.join(objects))
-def state_policy_rule_exists():
- # Determine if state policy rules already exist in nft
- search_str = cmd(f'nft list chain ip filter VYOS_FW_FORWARD')
- return 'VYOS_STATE_POLICY' in search_str
-
def resync_policy_route():
# Update policy route as firewall groups were updated
- tmp = run(policy_route_conf_script)
+ tmp, out = rc_cmd(policy_route_conf_script)
if tmp > 0:
- Warning('Failed to re-apply policy route configuration!')
+ Warning(f'Failed to re-apply policy route configuration! {out}')
def apply(firewall):
- if 'first_install' in firewall:
- run('nfct helper add rpc inet tcp')
- run('nfct helper add rpc inet udp')
- run('nfct helper add tns inet tcp')
-
- install_result = run(f'nft -f {nftables_conf}')
+ install_result, output = rc_cmd(f'nft -f {nftables_conf}')
if install_result == 1:
- raise ConfigError('Failed to apply firewall')
+ raise ConfigError(f'Failed to apply firewall: {output}')
# set firewall group domain-group xxx
if 'group' in firewall:
@@ -563,13 +498,6 @@ def apply(firewall):
else:
call('systemctl stop vyos-domain-group-resolve.service')
- if 'state_policy' in firewall and not state_policy_rule_exists():
- for chain in ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL']:
- cmd(f'nft insert rule ip filter {chain} jump VYOS_STATE_POLICY')
-
- for chain in ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']:
- cmd(f'nft insert rule ip6 filter {chain} jump VYOS_STATE_POLICY6')
-
apply_sysfs(firewall)
if firewall['policy_resync']:
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index ef745d737..8155f36c2 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -55,6 +55,7 @@ from vyos.util import chown
from vyos.util import cmd
from vyos.util import dict_search
from vyos.util import dict_search_args
+from vyos.util import is_list_equal
from vyos.util import makedir
from vyos.util import read_file
from vyos.util import write_file
@@ -274,7 +275,7 @@ def verify(openvpn):
elif v6remAddr and not v6loAddr:
raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"')
- if (v4loAddr == v4remAddr) or (v6remAddr == v4remAddr):
+ if is_list_equal(v4loAddr, v4remAddr) or is_list_equal(v6loAddr, v6remAddr):
raise ConfigError('"local-address" and "remote-address" cannot be the same')
if dict_search('local_host', openvpn) in dict_search('local_address', openvpn):
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index 61bab2feb..8d738f55e 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -14,16 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
-
from sys import exit
-from copy import deepcopy
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import get_interface_dict
-from vyos.configdict import node_changed
-from vyos.configdict import leaf_node_changed
+from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
@@ -50,17 +46,20 @@ def get_config(config=None):
ifname, wireguard = get_interface_dict(conf, base)
# Check if a port was changed
- wireguard['port_changed'] = leaf_node_changed(conf, base + [ifname, 'port'])
+ tmp = is_node_changed(conf, base + [ifname, 'port'])
+ if tmp: wireguard['port_changed'] = {}
# Determine which Wireguard peer has been removed.
# Peers can only be removed with their public key!
- dict = {}
- tmp = node_changed(conf, base + [ifname, 'peer'], key_mangling=('-', '_'))
- for peer in (tmp or []):
- public_key = leaf_node_changed(conf, base + [ifname, 'peer', peer, 'public_key'])
- if public_key:
- dict = dict_merge({'peer_remove' : {peer : {'public_key' : public_key[0]}}}, dict)
- wireguard.update(dict)
+ if 'peer' in wireguard:
+ peer_remove = {}
+ for peer, peer_config in wireguard['peer'].items():
+ # T4702: If anything on a peer changes we remove the peer first and re-add it
+ if is_node_changed(conf, base + [ifname, 'peer', peer]):
+ if 'public_key' in peer_config:
+ peer_remove = dict_merge({'peer_remove' : {peer : peer_config['public_key']}}, peer_remove)
+ if peer_remove:
+ wireguard.update(peer_remove)
return wireguard
@@ -81,12 +80,11 @@ def verify(wireguard):
if 'peer' not in wireguard:
raise ConfigError('At least one Wireguard peer is required!')
- if 'port' in wireguard and wireguard['port_changed']:
+ if 'port' in wireguard and 'port_changed' in wireguard:
listen_port = int(wireguard['port'])
if check_port_availability('0.0.0.0', listen_port, 'udp') is not True:
- raise ConfigError(
- f'The UDP port {listen_port} is busy or unavailable and cannot be used for the interface'
- )
+ raise ConfigError(f'UDP port {listen_port} is busy or unavailable and '
+ 'cannot be used for the interface!')
# run checks on individual configured WireGuard peer
for tmp in wireguard['peer']:
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py
index 97b3a6396..a14a992ae 100755
--- a/src/conf_mode/interfaces-wwan.py
+++ b/src/conf_mode/interfaces-wwan.py
@@ -116,7 +116,7 @@ def generate(wwan):
# disconnect - e.g. happens during RF signal loss. The script watches every
# WWAN interface - so there is only one instance.
if not os.path.exists(cron_script):
- write_file(cron_script, '*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py')
+ write_file(cron_script, '*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py\n')
return None
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index e75418ba5..8b1a5a720 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -147,14 +147,10 @@ def verify(nat):
Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
addr = dict_search('translation.address', config)
- if addr != None:
- if addr != 'masquerade' and not is_ip_network(addr):
- for ip in addr.split('-'):
- if not is_addr_assigned(ip):
- Warning(f'IP address {ip} does not exist on the system!')
- elif 'exclude' not in config:
- raise ConfigError(f'{err_msg}\n' \
- 'translation address not specified')
+ if addr != None and addr != 'masquerade' and not is_ip_network(addr):
+ for ip in addr.split('-'):
+ if not is_addr_assigned(ip):
+ Warning(f'IP address {ip} does not exist on the system!')
# common rule verification
verify_rule(config, err_msg)
@@ -167,14 +163,8 @@ def verify(nat):
if 'inbound_interface' not in config:
raise ConfigError(f'{err_msg}\n' \
'inbound-interface not specified')
- else:
- if config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces():
- Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')
-
-
- if dict_search('translation.address', config) == None and 'exclude' not in config:
- raise ConfigError(f'{err_msg}\n' \
- 'translation address not specified')
+ elif config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces():
+ Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')
# common rule verification
verify_rule(config, err_msg)
@@ -193,6 +183,9 @@ def verify(nat):
return None
def generate(nat):
+ if not os.path.exists(nftables_nat_config):
+ nat['first_install'] = True
+
render(nftables_nat_config, 'firewall/nftables-nat.j2', nat)
render(nftables_static_nat_conf, 'firewall/nftables-static-nat.j2', nat)
@@ -201,7 +194,9 @@ def generate(nat):
if tmp > 0:
raise ConfigError('Configuration file errors encountered!')
- tmp = run(f'nft -c -f {nftables_nat_config}')
+ tmp = run(f'nft -c -f {nftables_static_nat_conf}')
+ if tmp > 0:
+ raise ConfigError('Configuration file errors encountered!')
return None
diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py
index f64102d88..d8f913b0c 100755
--- a/src/conf_mode/nat66.py
+++ b/src/conf_mode/nat66.py
@@ -36,7 +36,7 @@ airbag.enable()
k_mod = ['nft_nat', 'nft_chain_nat']
-nftables_nat66_config = '/tmp/vyos-nat66-rules.nft'
+nftables_nat66_config = '/run/nftables_nat66.nft'
ndppd_config = '/run/ndppd/ndppd.conf'
def get_handler(json, chain, target):
@@ -147,6 +147,9 @@ def verify(nat):
return None
def generate(nat):
+ if not os.path.exists(nftables_nat66_config):
+ nat['first_install'] = True
+
render(nftables_nat66_config, 'firewall/nftables-nat66.j2', nat, permission=0o755)
render(ndppd_config, 'ndppd/ndppd.conf.j2', nat, permission=0o755)
return None
@@ -154,15 +157,15 @@ def generate(nat):
def apply(nat):
if not nat:
return None
- cmd(f'{nftables_nat66_config}')
+
+ cmd(f'nft -f {nftables_nat66_config}')
+
if 'deleted' in nat or not dict_search('source.rule', nat):
cmd('systemctl stop ndppd')
if os.path.isfile(ndppd_config):
os.unlink(ndppd_config)
else:
cmd('systemctl restart ndppd')
- if os.path.isfile(nftables_nat66_config):
- os.unlink(nftables_nat66_config)
return None
diff --git a/src/conf_mode/policy-route.py b/src/conf_mode/policy-route.py
index 9fddbd2c6..00539b9c7 100755
--- a/src/conf_mode/policy-route.py
+++ b/src/conf_mode/policy-route.py
@@ -92,7 +92,7 @@ def get_config(config=None):
return policy
-def verify_rule(policy, name, rule_conf, ipv6):
+def verify_rule(policy, name, rule_conf, ipv6, rule_id):
icmp = 'icmp' if not ipv6 else 'icmpv6'
if icmp in rule_conf:
icmp_defined = False
@@ -166,7 +166,7 @@ def verify(policy):
for name, pol_conf in policy[route].items():
if 'rule' in pol_conf:
for rule_id, rule_conf in pol_conf['rule'].items():
- verify_rule(policy, name, rule_conf, ipv6)
+ verify_rule(policy, name, rule_conf, ipv6, rule_id)
for ifname, if_policy in policy['interfaces'].items():
name = dict_search_args(if_policy, 'route')
diff --git a/src/conf_mode/policy.py b/src/conf_mode/policy.py
index 3008a20e0..a0d288e91 100755
--- a/src/conf_mode/policy.py
+++ b/src/conf_mode/policy.py
@@ -23,8 +23,42 @@ from vyos.util import dict_search
from vyos import ConfigError
from vyos import frr
from vyos import airbag
+
airbag.enable()
+
+def community_action_compatibility(actions: dict) -> bool:
+ """
+ Check compatibility of values in community and large community sections
+ :param actions: dictionary with community
+ :type actions: dict
+ :return: true if compatible, false if not
+ :rtype: bool
+ """
+ if ('none' in actions) and ('replace' in actions or 'add' in actions):
+ return False
+ if 'replace' in actions and 'add' in actions:
+ return False
+ if ('delete' in actions) and ('none' in actions or 'replace' in actions):
+ return False
+ return True
+
+
+def extcommunity_action_compatibility(actions: dict) -> bool:
+ """
+ Check compatibility of values in extended community sections
+ :param actions: dictionary with community
+ :type actions: dict
+ :return: true if compatible, false if not
+ :rtype: bool
+ """
+ if ('none' in actions) and (
+ 'rt' in actions or 'soo' in actions or 'bandwidth' in actions or 'bandwidth_non_transitive' in actions):
+ return False
+ if ('bandwidth_non_transitive' in actions) and ('bandwidth' not in actions):
+ return False
+ return True
+
def routing_policy_find(key, dictionary):
# Recursively traverse a dictionary and extract the value assigned to
# a given key as generator object. This is made for routing policies,
@@ -46,6 +80,7 @@ def routing_policy_find(key, dictionary):
for result in routing_policy_find(key, d):
yield result
+
def get_config(config=None):
if config:
conf = config
@@ -53,7 +88,8 @@ def get_config(config=None):
conf = Config()
base = ['policy']
- policy = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True,
+ policy = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
no_tag_node_value_mangle=True)
# We also need some additional information from the config, prefix-lists
@@ -67,12 +103,14 @@ def get_config(config=None):
policy = dict_merge(tmp, policy)
return policy
+
def verify(policy):
if not policy:
return None
for policy_type in ['access_list', 'access_list6', 'as_path_list',
- 'community_list', 'extcommunity_list', 'large_community_list',
+ 'community_list', 'extcommunity_list',
+ 'large_community_list',
'prefix_list', 'prefix_list6', 'route_map']:
# Bail out early and continue with next policy type
if policy_type not in policy:
@@ -97,15 +135,18 @@ def verify(policy):
if 'source' not in rule_config:
raise ConfigError(f'A source {mandatory_error}')
- if int(instance) in range(100, 200) or int(instance) in range(2000, 2700):
+ if int(instance) in range(100, 200) or int(
+ instance) in range(2000, 2700):
if 'destination' not in rule_config:
- raise ConfigError(f'A destination {mandatory_error}')
+ raise ConfigError(
+ f'A destination {mandatory_error}')
if policy_type == 'access_list6':
if 'source' not in rule_config:
raise ConfigError(f'A source {mandatory_error}')
- if policy_type in ['as_path_list', 'community_list', 'extcommunity_list',
+ if policy_type in ['as_path_list', 'community_list',
+ 'extcommunity_list',
'large_community_list']:
if 'regex' not in rule_config:
raise ConfigError(f'A regex {mandatory_error}')
@@ -115,10 +156,10 @@ def verify(policy):
raise ConfigError(f'A prefix {mandatory_error}')
if rule_config in entries:
- raise ConfigError(f'Rule "{rule}" contains a duplicate prefix definition!')
+ raise ConfigError(
+ f'Rule "{rule}" contains a duplicate prefix definition!')
entries.append(rule_config)
-
# route-maps tend to be a bit more complex so they get their own verify() section
if 'route_map' in policy:
for route_map, route_map_config in policy['route_map'].items():
@@ -127,19 +168,23 @@ def verify(policy):
for rule, rule_config in route_map_config['rule'].items():
# Specified community-list must exist
- tmp = dict_search('match.community.community_list', rule_config)
+ tmp = dict_search('match.community.community_list',
+ rule_config)
if tmp and tmp not in policy.get('community_list', []):
raise ConfigError(f'community-list {tmp} does not exist!')
# Specified extended community-list must exist
tmp = dict_search('match.extcommunity', rule_config)
if tmp and tmp not in policy.get('extcommunity_list', []):
- raise ConfigError(f'extcommunity-list {tmp} does not exist!')
+ raise ConfigError(
+ f'extcommunity-list {tmp} does not exist!')
# Specified large-community-list must exist
- tmp = dict_search('match.large_community.large_community_list', rule_config)
+ tmp = dict_search('match.large_community.large_community_list',
+ rule_config)
if tmp and tmp not in policy.get('large_community_list', []):
- raise ConfigError(f'large-community-list {tmp} does not exist!')
+ raise ConfigError(
+ f'large-community-list {tmp} does not exist!')
# Specified prefix-list must exist
tmp = dict_search('match.ip.address.prefix_list', rule_config)
@@ -147,49 +192,87 @@ def verify(policy):
raise ConfigError(f'prefix-list {tmp} does not exist!')
# Specified prefix-list must exist
- tmp = dict_search('match.ipv6.address.prefix_list', rule_config)
+ tmp = dict_search('match.ipv6.address.prefix_list',
+ rule_config)
if tmp and tmp not in policy.get('prefix_list6', []):
raise ConfigError(f'prefix-list6 {tmp} does not exist!')
-
+
# Specified access_list6 in nexthop must exist
- tmp = dict_search('match.ipv6.nexthop.access_list', rule_config)
+ tmp = dict_search('match.ipv6.nexthop.access_list',
+ rule_config)
if tmp and tmp not in policy.get('access_list6', []):
raise ConfigError(f'access_list6 {tmp} does not exist!')
# Specified prefix-list6 in nexthop must exist
- tmp = dict_search('match.ipv6.nexthop.prefix_list', rule_config)
+ tmp = dict_search('match.ipv6.nexthop.prefix_list',
+ rule_config)
if tmp and tmp not in policy.get('prefix_list6', []):
raise ConfigError(f'prefix-list6 {tmp} does not exist!')
+ tmp = dict_search('set.community.delete', rule_config)
+ if tmp and tmp not in policy.get('community_list', []):
+ raise ConfigError(f'community-list {tmp} does not exist!')
+
+ tmp = dict_search('set.large_community.delete',
+ rule_config)
+ if tmp and tmp not in policy.get('large_community_list', []):
+ raise ConfigError(
+ f'large-community-list {tmp} does not exist!')
+
+ if 'set' in rule_config:
+ rule_action = rule_config['set']
+ if 'community' in rule_action:
+ if not community_action_compatibility(
+ rule_action['community']):
+ raise ConfigError(
+ f'Unexpected combination between action replace, add, delete or none in community')
+ if 'large_community' in rule_action:
+ if not community_action_compatibility(
+ rule_action['large_community']):
+ raise ConfigError(
+ f'Unexpected combination between action replace, add, delete or none in large-community')
+ if 'extcommunity' in rule_action:
+ if not extcommunity_action_compatibility(
+ rule_action['extcommunity']):
+ raise ConfigError(
+ f'Unexpected combination between none, rt, soo, bandwidth, bandwidth-non-transitive in extended-community')
# When routing protocols are active some use prefix-lists, route-maps etc.
# to apply the systems routing policy to the learned or redistributed routes.
# When the "routing policy" changes and policies, route-maps etc. are deleted,
# it is our responsibility to verify that the policy can not be deleted if it
# is used by any routing protocol
if 'protocols' in policy:
- for policy_type in ['access_list', 'access_list6', 'as_path_list', 'community_list',
- 'extcommunity_list', 'large_community_list', 'prefix_list', 'route_map']:
+ for policy_type in ['access_list', 'access_list6', 'as_path_list',
+ 'community_list',
+ 'extcommunity_list', 'large_community_list',
+ 'prefix_list', 'route_map']:
if policy_type in policy:
- for policy_name in list(set(routing_policy_find(policy_type, policy['protocols']))):
+ for policy_name in list(set(routing_policy_find(policy_type,
+ policy[
+ 'protocols']))):
found = False
if policy_name in policy[policy_type]:
found = True
# BGP uses prefix-list for selecting both an IPv4 or IPv6 AFI related
# list - we need to go the extra mile here and check both prefix-lists
- if policy_type == 'prefix_list' and 'prefix_list6' in policy and policy_name in policy['prefix_list6']:
+ if policy_type == 'prefix_list' and 'prefix_list6' in policy and policy_name in \
+ policy['prefix_list6']:
found = True
if not found:
- tmp = policy_type.replace('_','-')
- raise ConfigError(f'Can not delete {tmp} "{policy_name}", still in use!')
+ tmp = policy_type.replace('_', '-')
+ raise ConfigError(
+ f'Can not delete {tmp} "{policy_name}", still in use!')
return None
+
def generate(policy):
if not policy:
return None
policy['new_frr_config'] = render_to_string('frr/policy.frr.j2', policy)
return None
+
def apply(policy):
bgp_daemon = 'bgpd'
zebra_daemon = 'zebra'
@@ -203,7 +286,8 @@ def apply(policy):
frr_cfg.modify_section(r'^bgp community-list .*')
frr_cfg.modify_section(r'^bgp extcommunity-list .*')
frr_cfg.modify_section(r'^bgp large-community-list .*')
- frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', remove_stop_mark=True)
+ frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit',
+ remove_stop_mark=True)
if 'new_frr_config' in policy:
frr_cfg.add_before(frr.default_add_before, policy['new_frr_config'])
frr_cfg.commit_configuration(bgp_daemon)
@@ -214,13 +298,15 @@ def apply(policy):
frr_cfg.modify_section(r'^ipv6 access-list .*')
frr_cfg.modify_section(r'^ip prefix-list .*')
frr_cfg.modify_section(r'^ipv6 prefix-list .*')
- frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit', remove_stop_mark=True)
+ frr_cfg.modify_section(r'^route-map .*', stop_pattern='^exit',
+ remove_stop_mark=True)
if 'new_frr_config' in policy:
frr_cfg.add_before(frr.default_add_before, policy['new_frr_config'])
frr_cfg.commit_configuration(zebra_daemon)
return None
+
if __name__ == '__main__':
try:
c = get_config()
diff --git a/src/conf_mode/protocols_isis.py b/src/conf_mode/protocols_isis.py
index 5dafd26d0..cb8ea3be4 100755
--- a/src/conf_mode/protocols_isis.py
+++ b/src/conf_mode/protocols_isis.py
@@ -203,6 +203,28 @@ 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():
+ if 'absolute' in prefix_config:
+ if prefix_config['absolute'].get('value') is None:
+ raise ConfigError(f'Segment routing prefix {prefix} absolute value cannot be blank.')
+ elif 'index' in prefix_config:
+ if prefix_config['index'].get('value') is None:
+ raise ConfigError(f'Segment routing prefix {prefix} index value cannot be blank.')
+
+ # Check for explicit-null and no-php-flag configured at the same time per prefix
+ 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']):
+ 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:
+ if ("explicit_null" in prefix_config['index']) and ("no_php_flag" in prefix_config['index']):
+ raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\
+ f'and no-php-flag configured at the same time.')
return None
diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py
index b247ce2ab..d28ced4fd 100755
--- a/src/conf_mode/protocols_nhrp.py
+++ b/src/conf_mode/protocols_nhrp.py
@@ -14,10 +14,10 @@
# 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 vyos.config import Config
from vyos.configdict import node_changed
-from vyos.firewall import find_nftables_rule
-from vyos.firewall import remove_nftables_rule
from vyos.template import render
from vyos.util import process_named_running
from vyos.util import run
@@ -26,6 +26,7 @@ from vyos import airbag
airbag.enable()
opennhrp_conf = '/run/opennhrp/opennhrp.conf'
+nhrp_nftables_conf = '/run/nftables_nhrp.conf'
def get_config(config=None):
if config:
@@ -84,28 +85,23 @@ def verify(nhrp):
return None
def generate(nhrp):
+ if not os.path.exists(nhrp_nftables_conf):
+ nhrp['first_install'] = True
+
render(opennhrp_conf, 'nhrp/opennhrp.conf.j2', nhrp)
+ render(nhrp_nftables_conf, 'nhrp/nftables.conf.j2', nhrp)
return None
def apply(nhrp):
- if 'tunnel' in nhrp:
- for tunnel, tunnel_conf in nhrp['tunnel'].items():
- if 'source_address' in nhrp['if_tunnel'][tunnel]:
- comment = f'VYOS_NHRP_{tunnel}'
- source_address = nhrp['if_tunnel'][tunnel]['source_address']
-
- rule_handle = find_nftables_rule('ip filter', 'VYOS_FW_OUTPUT', ['ip protocol gre', f'ip saddr {source_address}', 'ip daddr 224.0.0.0/4'])
- if not rule_handle:
- run(f'sudo nft insert rule ip filter VYOS_FW_OUTPUT ip protocol gre ip saddr {source_address} ip daddr 224.0.0.0/4 counter drop comment "{comment}"')
-
- for tunnel in nhrp['del_tunnels']:
- comment = f'VYOS_NHRP_{tunnel}'
- rule_handle = find_nftables_rule('ip filter', 'VYOS_FW_OUTPUT', [f'comment "{comment}"'])
- if rule_handle:
- remove_nftables_rule('ip filter', 'VYOS_FW_OUTPUT', rule_handle)
+ nft_rc = run(f'nft -f {nhrp_nftables_conf}')
+ if nft_rc != 0:
+ raise ConfigError('Failed to apply NHRP tunnel firewall rules')
action = 'restart' if nhrp and 'tunnel' in nhrp else 'stop'
- run(f'systemctl {action} opennhrp.service')
+ service_rc = run(f'systemctl {action} opennhrp.service')
+ if service_rc != 0:
+ raise ConfigError(f'Failed to {action} the NHRP service')
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_ospf.py b/src/conf_mode/protocols_ospf.py
index 5b4874ba2..0582d32be 100755
--- a/src/conf_mode/protocols_ospf.py
+++ b/src/conf_mode/protocols_ospf.py
@@ -198,6 +198,58 @@ def verify(ospf):
if 'master' not in tmp or tmp['master'] != vrf:
raise ConfigError(f'Interface {interface} is not a member of VRF {vrf}!')
+ # Segment routing checks
+ if dict_search('segment_routing.global_block', ospf):
+ g_high_label_value = dict_search('segment_routing.global_block.high_label_value', ospf)
+ g_low_label_value = dict_search('segment_routing.global_block.low_label_value', ospf)
+
+ # If segment routing global block high or low value is blank, throw error
+ if not (g_low_label_value or g_high_label_value):
+ raise ConfigError('Segment routing global-block requires both low and high value!')
+
+ # If segment routing global block low value is higher than the high value, throw error
+ if int(g_low_label_value) > int(g_high_label_value):
+ raise ConfigError('Segment routing global-block low value must be lower than high value')
+
+ if dict_search('segment_routing.local_block', ospf):
+ if dict_search('segment_routing.global_block', ospf) == None:
+ raise ConfigError('Segment routing local-block requires global-block to be configured!')
+
+ l_high_label_value = dict_search('segment_routing.local_block.high_label_value', ospf)
+ l_low_label_value = dict_search('segment_routing.local_block.low_label_value', ospf)
+
+ # If segment routing local-block high or low value is blank, throw error
+ if not (l_low_label_value or l_high_label_value):
+ raise ConfigError('Segment routing local-block requires both high and low value!')
+
+ # If segment routing local-block low value is higher than the high value, throw error
+ if int(l_low_label_value) > int(l_high_label_value):
+ raise ConfigError('Segment routing local-block low value must be lower than high value')
+
+ # local-block most live outside global block
+ global_range = range(int(g_low_label_value), int(g_high_label_value) +1)
+ local_range = range(int(l_low_label_value), int(l_high_label_value) +1)
+
+ # Check for overlapping ranges
+ 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', ospf):
+ for prefix, prefix_config in ospf['segment_routing']['prefix'].items():
+ if 'index' in prefix_config:
+ if prefix_config['index'].get('value') is None:
+ raise ConfigError(f'Segment routing prefix {prefix} index value cannot be blank.')
+
+ # Check for explicit-null and no-php-flag configured at the same time per prefix
+ if dict_search('segment_routing.prefix', ospf):
+ for prefix, prefix_config in ospf['segment_routing']['prefix'].items():
+ if 'index' in prefix_config:
+ if ("explicit_null" in prefix_config['index']) and ("no_php_flag" in prefix_config['index']):
+ raise ConfigError(f'Segment routing prefix {prefix} cannot have both explicit-null '\
+ f'and no-php-flag configured at the same time.')
+
return None
def generate(ospf):
diff --git a/src/conf_mode/service_console-server.py b/src/conf_mode/service_console-server.py
index a2e411e49..ee4fe42ab 100755
--- a/src/conf_mode/service_console-server.py
+++ b/src/conf_mode/service_console-server.py
@@ -61,6 +61,7 @@ def verify(proxy):
if not proxy:
return None
+ aliases = []
processes = process_iter(['name', 'cmdline'])
if 'device' in proxy:
for device, device_config in proxy['device'].items():
@@ -75,6 +76,12 @@ def verify(proxy):
if 'ssh' in device_config and 'port' not in device_config['ssh']:
raise ConfigError(f'Port "{device}" requires SSH port to be set!')
+ if 'alias' in device_config:
+ if device_config['alias'] in aliases:
+ raise ConfigError("Console aliases must be unique")
+ else:
+ aliases.append(device_config['alias'])
+
return None
def generate(proxy):
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index 61f484129..e9afd6a55 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -15,266 +15,34 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
-from copy import deepcopy
-from stat import S_IRUSR, S_IWUSR, S_IRGRP
from sys import exit
from vyos.config import Config
+from vyos.configdict import get_accel_dict
+from vyos.configverify import verify_accel_ppp_base_service
+from vyos.configverify import verify_interface_exists
from vyos.template import render
-from vyos.template import is_ipv4
-from vyos.template import is_ipv6
-from vyos.util import call, get_half_cpus
+from vyos.util import call
+from vyos.util import dict_search
from vyos import ConfigError
-
from vyos import airbag
airbag.enable()
ipoe_conf = '/run/accel-pppd/ipoe.conf'
ipoe_chap_secrets = '/run/accel-pppd/ipoe.chap-secrets'
-default_config_data = {
- 'auth_mode': 'local',
- 'auth_interfaces': [],
- 'chap_secrets_file': ipoe_chap_secrets, # used in Jinja2 template
- 'interfaces': [],
- 'dnsv4': [],
- 'dnsv6': [],
- 'client_named_ip_pool': [],
- 'client_ipv6_pool': [],
- 'client_ipv6_delegate_prefix': [],
- 'radius_server': [],
- 'radius_acct_inter_jitter': '',
- 'radius_acct_tmo': '3',
- 'radius_max_try': '3',
- 'radius_timeout': '3',
- 'radius_nas_id': '',
- 'radius_nas_ip': '',
- 'radius_source_address': '',
- 'radius_shaper_attr': '',
- 'radius_shaper_enable': False,
- 'radius_shaper_multiplier': '',
- 'radius_shaper_vendor': '',
- 'radius_dynamic_author': '',
- 'thread_cnt': get_half_cpus()
-}
-
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base_path = ['service', 'ipoe-server']
- if not conf.exists(base_path):
+ base = ['service', 'ipoe-server']
+ if not conf.exists(base):
return None
- conf.set_level(base_path)
- ipoe = deepcopy(default_config_data)
-
- for interface in conf.list_nodes(['interface']):
- tmp = {
- 'mode': 'L2',
- 'name': interface,
- 'shared': '1',
- # may need a config option, can be dhcpv4 or up for unclassified pkts
- 'sess_start': 'dhcpv4',
- 'range': None,
- 'ifcfg': '1',
- 'vlan_mon': []
- }
-
- conf.set_level(base_path + ['interface', interface])
-
- if conf.exists(['network-mode']):
- tmp['mode'] = conf.return_value(['network-mode'])
-
- if conf.exists(['network']):
- mode = conf.return_value(['network'])
- if mode == 'vlan':
- tmp['shared'] = '0'
-
- if conf.exists(['vlan-id']):
- tmp['vlan_mon'] += conf.return_values(['vlan-id'])
-
- if conf.exists(['vlan-range']):
- tmp['vlan_mon'] += conf.return_values(['vlan-range'])
-
- if conf.exists(['client-subnet']):
- tmp['range'] = conf.return_value(['client-subnet'])
-
- ipoe['interfaces'].append(tmp)
-
- conf.set_level(base_path)
-
- if conf.exists(['name-server']):
- for name_server in conf.return_values(['name-server']):
- if is_ipv4(name_server):
- ipoe['dnsv4'].append(name_server)
- else:
- ipoe['dnsv6'].append(name_server)
-
- if conf.exists(['authentication', 'mode']):
- ipoe['auth_mode'] = conf.return_value(['authentication', 'mode'])
-
- if conf.exists(['authentication', 'interface']):
- for interface in conf.list_nodes(['authentication', 'interface']):
- tmp = {
- 'name': interface,
- 'mac': []
- }
- for mac in conf.list_nodes(['authentication', 'interface', interface, 'mac-address']):
- client = {
- 'address': mac,
- 'rate_download': '',
- 'rate_upload': '',
- 'vlan_id': ''
- }
- conf.set_level(base_path + ['authentication', 'interface', interface, 'mac-address', mac])
-
- if conf.exists(['rate-limit', 'download']):
- client['rate_download'] = conf.return_value(['rate-limit', 'download'])
-
- if conf.exists(['rate-limit', 'upload']):
- client['rate_upload'] = conf.return_value(['rate-limit', 'upload'])
-
- if conf.exists(['vlan-id']):
- client['vlan'] = conf.return_value(['vlan-id'])
-
- tmp['mac'].append(client)
-
- ipoe['auth_interfaces'].append(tmp)
-
- conf.set_level(base_path)
-
- #
- # authentication mode radius servers and settings
- if conf.exists(['authentication', 'mode', 'radius']):
- for server in conf.list_nodes(['authentication', 'radius', 'server']):
- radius = {
- 'server' : server,
- 'key' : '',
- 'fail_time' : 0,
- 'port' : '1812',
- 'acct_port' : '1813'
- }
-
- conf.set_level(base_path + ['authentication', 'radius', 'server', server])
-
- if conf.exists(['fail-time']):
- radius['fail_time'] = conf.return_value(['fail-time'])
-
- if conf.exists(['port']):
- radius['port'] = conf.return_value(['port'])
-
- if conf.exists(['acct-port']):
- radius['acct_port'] = conf.return_value(['acct-port'])
-
- if conf.exists(['key']):
- radius['key'] = conf.return_value(['key'])
-
- if not conf.exists(['disable']):
- ipoe['radius_server'].append(radius)
-
- #
- # advanced radius-setting
- conf.set_level(base_path + ['authentication', 'radius'])
-
- if conf.exists(['acct-interim-jitter']):
- ipoe['radius_acct_inter_jitter'] = conf.return_value(['acct-interim-jitter'])
-
- if conf.exists(['acct-timeout']):
- ipoe['radius_acct_tmo'] = conf.return_value(['acct-timeout'])
-
- if conf.exists(['max-try']):
- ipoe['radius_max_try'] = conf.return_value(['max-try'])
-
- if conf.exists(['timeout']):
- ipoe['radius_timeout'] = conf.return_value(['timeout'])
-
- if conf.exists(['nas-identifier']):
- ipoe['radius_nas_id'] = conf.return_value(['nas-identifier'])
-
- if conf.exists(['nas-ip-address']):
- ipoe['radius_nas_ip'] = conf.return_value(['nas-ip-address'])
-
- if conf.exists(['rate-limit', 'attribute']):
- ipoe['radius_shaper_attr'] = conf.return_value(['rate-limit', 'attribute'])
-
- if conf.exists(['rate-limit', 'enable']):
- ipoe['radius_shaper_enable'] = True
-
- if conf.exists(['rate-limit', 'multiplier']):
- ipoe['radius_shaper_multiplier'] = conf.return_value(['rate-limit', 'multiplier'])
-
- if conf.exists(['rate-limit', 'vendor']):
- ipoe['radius_shaper_vendor'] = conf.return_value(['rate-limit', 'vendor'])
-
- if conf.exists(['source-address']):
- ipoe['radius_source_address'] = conf.return_value(['source-address'])
-
- # Dynamic Authorization Extensions (DOA)/Change Of Authentication (COA)
- if conf.exists(['dynamic-author']):
- dae = {
- 'port' : '',
- 'server' : '',
- 'key' : ''
- }
-
- if conf.exists(['dynamic-author', 'server']):
- dae['server'] = conf.return_value(['dynamic-author', 'server'])
-
- if conf.exists(['dynamic-author', 'port']):
- dae['port'] = conf.return_value(['dynamic-author', 'port'])
-
- if conf.exists(['dynamic-author', 'key']):
- dae['key'] = conf.return_value(['dynamic-author', 'key'])
-
- ipoe['radius_dynamic_author'] = dae
-
-
- conf.set_level(base_path)
- # Named client-ip-pool
- if conf.exists(['client-ip-pool', 'name']):
- for name in conf.list_nodes(['client-ip-pool', 'name']):
- tmp = {
- 'name': name,
- 'gateway_address': '',
- 'subnet': ''
- }
-
- if conf.exists(['client-ip-pool', 'name', name, 'gateway-address']):
- tmp['gateway_address'] += conf.return_value(['client-ip-pool', 'name', name, 'gateway-address'])
- if conf.exists(['client-ip-pool', 'name', name, 'subnet']):
- tmp['subnet'] += conf.return_value(['client-ip-pool', 'name', name, 'subnet'])
-
- ipoe['client_named_ip_pool'].append(tmp)
-
- if conf.exists(['client-ipv6-pool', 'prefix']):
- for prefix in conf.list_nodes(['client-ipv6-pool', 'prefix']):
- tmp = {
- 'prefix': prefix,
- 'mask': '64'
- }
-
- if conf.exists(['client-ipv6-pool', 'prefix', prefix, 'mask']):
- tmp['mask'] = conf.return_value(['client-ipv6-pool', 'prefix', prefix, 'mask'])
-
- ipoe['client_ipv6_pool'].append(tmp)
-
-
- if conf.exists(['client-ipv6-pool', 'delegate']):
- for prefix in conf.list_nodes(['client-ipv6-pool', 'delegate']):
- tmp = {
- 'prefix': prefix,
- 'mask': ''
- }
-
- if conf.exists(['client-ipv6-pool', 'delegate', prefix, 'delegation-prefix']):
- tmp['mask'] = conf.return_value(['client-ipv6-pool', 'delegate', prefix, 'delegation-prefix'])
-
- ipoe['client_ipv6_delegate_prefix'].append(tmp)
-
+ # retrieve common dictionary keys
+ ipoe = get_accel_dict(conf, base, ipoe_chap_secrets)
return ipoe
@@ -282,26 +50,17 @@ def verify(ipoe):
if not ipoe:
return None
- if not ipoe['interfaces']:
+ if 'interface' not in ipoe:
raise ConfigError('No IPoE interface configured')
- if len(ipoe['dnsv4']) > 2:
- raise ConfigError('Not more then two IPv4 DNS name-servers can be configured')
-
- if len(ipoe['dnsv6']) > 3:
- raise ConfigError('Not more then three IPv6 DNS name-servers can be configured')
-
- if ipoe['auth_mode'] == 'radius':
- if len(ipoe['radius_server']) == 0:
- raise ConfigError('RADIUS authentication requires at least one server')
+ for interface in ipoe['interface']:
+ verify_interface_exists(interface)
- for radius in ipoe['radius_server']:
- if not radius['key']:
- server = radius['server']
- raise ConfigError(f'Missing RADIUS secret key for server "{ server }"')
+ #verify_accel_ppp_base_service(ipoe, local_users=False)
- if ipoe['client_ipv6_delegate_prefix'] and not ipoe['client_ipv6_pool']:
- raise ConfigError('IPoE IPv6 deletate-prefix requires IPv6 prefix to be configured!')
+ if 'client_ipv6_pool' in ipoe:
+ if 'delegate' in ipoe['client_ipv6_pool'] and 'prefix' not in ipoe['client_ipv6_pool']:
+ raise ConfigError('IPoE IPv6 deletate-prefix requires IPv6 prefix to be configured!')
return None
@@ -312,27 +71,23 @@ def generate(ipoe):
render(ipoe_conf, 'accel-ppp/ipoe.config.j2', ipoe)
- if ipoe['auth_mode'] == 'local':
- render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.j2', ipoe)
- os.chmod(ipoe_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
-
- else:
- if os.path.exists(ipoe_chap_secrets):
- os.unlink(ipoe_chap_secrets)
-
+ if dict_search('authentication.mode', ipoe) == 'local':
+ render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.j2',
+ ipoe, permission=0o640)
return None
def apply(ipoe):
+ systemd_service = 'accel-ppp@ipoe.service'
if ipoe == None:
- call('systemctl stop accel-ppp@ipoe.service')
+ call(f'systemctl stop {systemd_service}')
for file in [ipoe_conf, ipoe_chap_secrets]:
if os.path.exists(file):
os.unlink(file)
return None
- call('systemctl restart accel-ppp@ipoe.service')
+ call(f'systemctl reload-or-restart {systemd_service}')
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py
index 53df006a4..427cb6911 100755
--- a/src/conf_mode/service_monitoring_telegraf.py
+++ b/src/conf_mode/service_monitoring_telegraf.py
@@ -42,7 +42,7 @@ systemd_override = '/etc/systemd/system/telegraf.service.d/10-override.conf'
def get_nft_filter_chains():
""" Get nft chains for table filter """
- nft = cmd('nft --json list table ip filter')
+ nft = cmd('nft --json list table ip vyos_filter')
nft = json.loads(nft)
chain_list = []
diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py
index 6086ef859..ba0249efd 100755
--- a/src/conf_mode/service_pppoe-server.py
+++ b/src/conf_mode/service_pppoe-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -21,14 +21,12 @@ from sys import exit
from vyos.config import Config
from vyos.configdict import get_accel_dict
from vyos.configverify import verify_accel_ppp_base_service
+from vyos.configverify import verify_interface_exists
from vyos.template import render
from vyos.util import call
from vyos.util import dict_search
-from vyos.util import get_interface_config
from vyos import ConfigError
from vyos import airbag
-from vyos.range_regex import range_to_regex
-
airbag.enable()
pppoe_conf = r'/run/accel-pppd/pppoe.conf'
@@ -54,15 +52,14 @@ def verify(pppoe):
verify_accel_ppp_base_service(pppoe)
if 'wins_server' in pppoe and len(pppoe['wins_server']) > 2:
- raise ConfigError('Not more then two IPv4 WINS name-servers can be configured')
+ raise ConfigError('Not more then two WINS name-servers can be configured')
if 'interface' not in pppoe:
raise ConfigError('At least one listen interface must be defined!')
# Check is interface exists in the system
- for iface in pppoe['interface']:
- if not get_interface_config(iface):
- raise ConfigError(f'Interface {iface} does not exist!')
+ for interface in pppoe['interface']:
+ verify_interface_exists(interface)
# local ippool and gateway settings config checks
if not (dict_search('client_ip_pool.subnet', pppoe) or
@@ -81,35 +78,24 @@ def generate(pppoe):
if not pppoe:
return None
- # Generate special regex for dynamic interfaces
- for iface in pppoe['interface']:
- if 'vlan_range' in pppoe['interface'][iface]:
- pppoe['interface'][iface]['regex'] = []
- for vlan_range in pppoe['interface'][iface]['vlan_range']:
- pppoe['interface'][iface]['regex'].append(range_to_regex(vlan_range))
-
render(pppoe_conf, 'accel-ppp/pppoe.config.j2', pppoe)
if dict_search('authentication.mode', pppoe) == 'local':
render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.j2',
pppoe, permission=0o640)
- else:
- if os.path.exists(pppoe_chap_secrets):
- os.unlink(pppoe_chap_secrets)
-
return None
def apply(pppoe):
+ systemd_service = 'accel-ppp@pppoe.service'
if not pppoe:
- call('systemctl stop accel-ppp@pppoe.service')
+ call(f'systemctl stop {systemd_service}')
for file in [pppoe_conf, pppoe_chap_secrets]:
if os.path.exists(file):
os.unlink(file)
-
return None
- call('systemctl restart accel-ppp@pppoe.service')
+ call(f'systemctl reload-or-restart {systemd_service}')
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py
index 2bbd7142a..8746cc701 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -73,6 +73,9 @@ def verify(ssh):
if not ssh:
return None
+ if 'rekey' in ssh and 'data' not in ssh['rekey']:
+ raise ConfigError(f'Rekey data is required!')
+
verify_vrf(ssh)
return None
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index fc2723ece..bd9cc3b89 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -40,6 +40,7 @@ from vyos import ConfigError
from vyos import airbag
airbag.enable()
+autologout_file = "/etc/profile.d/autologout.sh"
radius_config_file = "/etc/pam_radius_auth.conf"
def get_local_users():
@@ -203,6 +204,13 @@ def generate(login):
if os.path.isfile(radius_config_file):
os.unlink(radius_config_file)
+ if 'timeout' in login:
+ render(autologout_file, 'login/autologout.j2', login,
+ permission=0o755, user='root', group='root')
+ else:
+ if os.path.isfile(autologout_file):
+ os.unlink(autologout_file)
+
return None
diff --git a/src/conf_mode/system_update_check.py b/src/conf_mode/system_update_check.py
new file mode 100755
index 000000000..08ecfcb81
--- /dev/null
+++ b/src/conf_mode/system_update_check.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 json
+import jmespath
+
+from pathlib import Path
+from sys import exit
+
+from vyos.config import Config
+from vyos.util import call
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+
+base = ['system', 'update-check']
+service_name = 'vyos-system-update'
+service_conf = Path(f'/run/{service_name}.conf')
+motd_file = Path('/run/motd.d/10-vyos-update')
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ if not conf.exists(base):
+ return None
+
+ config = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True, no_tag_node_value_mangle=True)
+
+ return config
+
+
+def verify(config):
+ # bail out early - looks like removal from running config
+ if config is None:
+ return
+
+ if 'url' not in config:
+ raise ConfigError('URL is required!')
+
+
+def generate(config):
+ # bail out early - looks like removal from running config
+ if config is None:
+ # Remove old config and return
+ service_conf.unlink(missing_ok=True)
+ # MOTD used in /run/motd.d/10-update
+ motd_file.unlink(missing_ok=True)
+ return None
+
+ # Write configuration file
+ conf_json = json.dumps(config, indent=4)
+ service_conf.write_text(conf_json)
+
+ return None
+
+
+def apply(config):
+ if config:
+ if 'auto_check' in config:
+ call(f'systemctl restart {service_name}.service')
+ else:
+ call(f'systemctl stop {service_name}.service')
+
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index 5ca32d23e..77a425f8b 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 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
@@ -16,6 +16,7 @@
import ipaddress
import os
+import re
from sys import exit
from time import sleep
@@ -264,7 +265,7 @@ def verify(ipsec):
ike = ra_conf['ike_group']
if dict_search(f'ike_group.{ike}.key_exchange', ipsec) != 'ikev2':
- raise ConfigError('IPSec remote-access connections requires IKEv2!')
+ raise ConfigError('IPsec remote-access connections requires IKEv2!')
else:
raise ConfigError(f"Missing ike-group on {name} remote-access config")
@@ -307,10 +308,10 @@ def verify(ipsec):
for pool in ra_conf['pool']:
if pool == 'dhcp':
if dict_search('remote_access.dhcp.server', ipsec) == None:
- raise ConfigError('IPSec DHCP server is not configured!')
+ raise ConfigError('IPsec DHCP server is not configured!')
elif pool == 'radius':
if dict_search('remote_access.radius.server', ipsec) == None:
- raise ConfigError('IPSec RADIUS server is not configured!')
+ raise ConfigError('IPsec RADIUS server is not configured!')
if dict_search('authentication.client_mode', ra_conf) != 'eap-radius':
raise ConfigError('RADIUS IP pool requires eap-radius client authentication!')
@@ -348,6 +349,14 @@ def verify(ipsec):
if 'site_to_site' in ipsec and 'peer' in ipsec['site_to_site']:
for peer, peer_conf in ipsec['site_to_site']['peer'].items():
has_default_esp = False
+ # Peer name it is swanctl connection name and shouldn't contain dots or colons, T4118
+ if bool(re.search(':|\.', peer)):
+ raise ConfigError(f'Incorrect peer name "{peer}" '
+ f'Peer name can contain alpha-numeric letters, hyphen and underscore')
+
+ if 'remote_address' not in peer_conf:
+ print(f'You should set correct remote-address "peer {peer} remote-address x.x.x.x"\n')
+
if 'default_esp_group' in peer_conf:
has_default_esp = True
if 'esp_group' not in ipsec or peer_conf['default_esp_group'] not in ipsec['esp_group']:
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index 240546817..c050b796b 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -58,15 +58,16 @@ def get_config():
default_values = defaults(base)
ocserv = dict_merge(default_values, ocserv)
- # workaround a "know limitation" - https://phabricator.vyos.net/T2665
- del ocserv['authentication']['local_users']['username']['otp']
- if not ocserv["authentication"]["local_users"]["username"]:
- raise ConfigError('openconnect mode local required at least one user')
- default_ocserv_usr_values = default_values['authentication']['local_users']['username']['otp']
- for user, params in ocserv['authentication']['local_users']['username'].items():
- # Not every configuration requires OTP settings
- if ocserv['authentication']['local_users']['username'][user].get('otp'):
- ocserv['authentication']['local_users']['username'][user]['otp'] = dict_merge(default_ocserv_usr_values, ocserv['authentication']['local_users']['username'][user]['otp'])
+ if "local" in ocserv["authentication"]["mode"]:
+ # workaround a "know limitation" - https://phabricator.vyos.net/T2665
+ del ocserv['authentication']['local_users']['username']['otp']
+ if not ocserv["authentication"]["local_users"]["username"]:
+ raise ConfigError('openconnect mode local required at least one user')
+ default_ocserv_usr_values = default_values['authentication']['local_users']['username']['otp']
+ for user, params in ocserv['authentication']['local_users']['username'].items():
+ # Not every configuration requires OTP settings
+ if ocserv['authentication']['local_users']['username'][user].get('otp'):
+ ocserv['authentication']['local_users']['username'][user]['otp'] = dict_merge(default_ocserv_usr_values, ocserv['authentication']['local_users']['username'][user]['otp'])
if ocserv:
ocserv['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
@@ -80,9 +81,10 @@ def verify(ocserv):
# Check if listen-ports not binded other services
# It can be only listen by 'ocserv-main'
for proto, port in ocserv.get('listen_ports').items():
- if check_port_availability('0.0.0.0', int(port), proto) is not True and \
+ if check_port_availability(ocserv['listen_address'], int(port), proto) is not True and \
not is_listen_port_bind_service(int(port), 'ocserv-main'):
raise ConfigError(f'"{proto}" port "{port}" is used by another service')
+
# Check authentication
if "authentication" in ocserv:
if "mode" in ocserv["authentication"]:
diff --git a/src/conf_mode/zone_policy.py b/src/conf_mode/zone_policy.py
deleted file mode 100755
index a52c52706..000000000
--- a/src/conf_mode/zone_policy.py
+++ /dev/null
@@ -1,213 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021-2022 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 json import loads
-from sys import exit
-
-from vyos.config import Config
-from vyos.configdict import dict_merge
-from vyos.template import render
-from vyos.util import cmd
-from vyos.util import dict_search_args
-from vyos.util import run
-from vyos.xml import defaults
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-nftables_conf = '/run/nftables_zone.conf'
-
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
- base = ['zone-policy']
- zone_policy = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- zone_policy['firewall'] = conf.get_config_dict(['firewall'],
- key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- if 'zone' in zone_policy:
- # We have gathered the dict representation of the CLI, but there are default
- # options which we need to update into the dictionary retrived.
- default_values = defaults(base + ['zone'])
- for zone in zone_policy['zone']:
- zone_policy['zone'][zone] = dict_merge(default_values,
- zone_policy['zone'][zone])
-
- return zone_policy
-
-def verify(zone_policy):
- # bail out early - looks like removal from running config
- if not zone_policy:
- return None
-
- local_zone = False
- interfaces = []
-
- if 'zone' in zone_policy:
- for zone, zone_conf in zone_policy['zone'].items():
- if 'local_zone' not in zone_conf and 'interface' not in zone_conf:
- raise ConfigError(f'Zone "{zone}" has no interfaces and is not the local zone')
-
- if 'local_zone' in zone_conf:
- if local_zone:
- raise ConfigError('There cannot be multiple local zones')
- if 'interface' in zone_conf:
- raise ConfigError('Local zone cannot have interfaces assigned')
- if 'intra_zone_filtering' in zone_conf:
- raise ConfigError('Local zone cannot use intra-zone-filtering')
- local_zone = True
-
- if 'interface' in zone_conf:
- found_duplicates = [intf for intf in zone_conf['interface'] if intf in interfaces]
-
- if found_duplicates:
- raise ConfigError(f'Interfaces cannot be assigned to multiple zones')
-
- interfaces += zone_conf['interface']
-
- if 'intra_zone_filtering' in zone_conf:
- intra_zone = zone_conf['intra_zone_filtering']
-
- if len(intra_zone) > 1:
- raise ConfigError('Only one intra-zone-filtering action must be specified')
-
- if 'firewall' in intra_zone:
- v4_name = dict_search_args(intra_zone, 'firewall', 'name')
- if v4_name and not dict_search_args(zone_policy, 'firewall', 'name', v4_name):
- raise ConfigError(f'Firewall name "{v4_name}" does not exist')
-
- v6_name = dict_search_args(intra_zone, 'firewall', 'ipv6-name')
- if v6_name and not dict_search_args(zone_policy, 'firewall', 'ipv6-name', v6_name):
- raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
-
- if not v4_name and not v6_name:
- raise ConfigError('No firewall names specified for intra-zone-filtering')
-
- if 'from' in zone_conf:
- for from_zone, from_conf in zone_conf['from'].items():
- if from_zone not in zone_policy['zone']:
- raise ConfigError(f'Zone "{zone}" refers to a non-existent or deleted zone "{from_zone}"')
-
- v4_name = dict_search_args(from_conf, 'firewall', 'name')
- if v4_name:
- if 'name' not in zone_policy['firewall']:
- raise ConfigError(f'Firewall name "{v4_name}" does not exist')
-
- if not dict_search_args(zone_policy, 'firewall', 'name', v4_name):
- raise ConfigError(f'Firewall name "{v4_name}" does not exist')
-
- v6_name = dict_search_args(from_conf, 'firewall', 'v6_name')
- if v6_name:
- if 'ipv6_name' not in zone_policy['firewall']:
- raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
-
- if not dict_search_args(zone_policy, 'firewall', 'ipv6_name', v6_name):
- raise ConfigError(f'Firewall ipv6-name "{v6_name}" does not exist')
-
- return None
-
-def has_ipv4_fw(zone_conf):
- if 'from' not in zone_conf:
- return False
- zone_from = zone_conf['from']
- return any([True for fz in zone_from if dict_search_args(zone_from, fz, 'firewall', 'name')])
-
-def has_ipv6_fw(zone_conf):
- if 'from' not in zone_conf:
- return False
- zone_from = zone_conf['from']
- return any([True for fz in zone_from if dict_search_args(zone_from, fz, 'firewall', 'ipv6_name')])
-
-def get_local_from(zone_policy, local_zone_name):
- # Get all zone firewall names from the local zone
- out = {}
- for zone, zone_conf in zone_policy['zone'].items():
- if zone == local_zone_name:
- continue
- if 'from' not in zone_conf:
- continue
- if local_zone_name in zone_conf['from']:
- out[zone] = zone_conf['from'][local_zone_name]
- return out
-
-def cleanup_commands():
- commands = []
- for table in ['ip filter', 'ip6 filter']:
- json_str = cmd(f'nft -t -j list table {table}')
- obj = loads(json_str)
- if 'nftables' not in obj:
- continue
- for item in obj['nftables']:
- if 'rule' in item:
- chain = item['rule']['chain']
- handle = item['rule']['handle']
- if 'expr' not in item['rule']:
- continue
- for expr in item['rule']['expr']:
- target = dict_search_args(expr, 'jump', 'target')
- if not target:
- continue
- if target.startswith("VZONE") or target.startswith("VYOS_STATE_POLICY"):
- commands.append(f'delete rule {table} {chain} handle {handle}')
- for item in obj['nftables']:
- if 'chain' in item:
- if item['chain']['name'].startswith("VZONE"):
- chain = item['chain']['name']
- commands.append(f'delete chain {table} {chain}')
- return commands
-
-def generate(zone_policy):
- data = zone_policy or {}
-
- if os.path.exists(nftables_conf): # Check to see if we've run before
- data['cleanup_commands'] = cleanup_commands()
-
- if 'zone' in data:
- for zone, zone_conf in data['zone'].items():
- zone_conf['ipv4'] = has_ipv4_fw(zone_conf)
- zone_conf['ipv6'] = has_ipv6_fw(zone_conf)
-
- if 'local_zone' in zone_conf:
- zone_conf['from_local'] = get_local_from(data, zone)
-
- render(nftables_conf, 'zone_policy/nftables.j2', data)
- return None
-
-def apply(zone_policy):
- install_result = run(f'nft -f {nftables_conf}')
- if install_result != 0:
- raise ConfigError('Failed to apply zone-policy')
-
- 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/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf
index 4feb7e09a..411429510 100644
--- a/src/etc/sysctl.d/30-vyos-router.conf
+++ b/src/etc/sysctl.d/30-vyos-router.conf
@@ -109,3 +109,7 @@ net.ipv4.neigh.default.gc_thresh3 = 8192
net.ipv6.neigh.default.gc_thresh1 = 1024
net.ipv6.neigh.default.gc_thresh2 = 4096
net.ipv6.neigh.default.gc_thresh3 = 8192
+
+# Enable global RFS (Receive Flow Steering) configuration. RFS is inactive
+# until explicitly configured at the interface level
+net.core.rps_sock_flow_entries = 32768
diff --git a/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
index bf4bfd05d..cbc2bfe6b 100755
--- a/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
+++ b/src/etc/telegraf/custom_scripts/show_firewall_input_filter.py
@@ -11,7 +11,7 @@ def get_nft_filter_chains():
"""
Get list of nft chains for table filter
"""
- nft = cmd('/usr/sbin/nft --json list table ip filter')
+ nft = cmd('/usr/sbin/nft --json list table ip vyos_filter')
nft = json.loads(nft)
chain_list = []
@@ -27,7 +27,7 @@ def get_nftables_details(name):
"""
Get dict, counters packets and bytes for chain
"""
- command = f'/usr/sbin/nft list chain ip filter {name}'
+ command = f'/usr/sbin/nft list chain ip vyos_filter {name}'
try:
results = cmd(command)
except:
@@ -60,7 +60,7 @@ def get_nft_telegraf(name):
Get data for telegraf in influxDB format
"""
for rule, rule_config in get_nftables_details(name).items():
- print(f'nftables,table=filter,chain={name},'
+ print(f'nftables,table=vyos_filter,chain={name},'
f'ruleid={rule} '
f'pkts={rule_config["packets"]}i,'
f'bytes={rule_config["bytes"]}i '
diff --git a/src/migration-scripts/firewall/7-to-8 b/src/migration-scripts/firewall/7-to-8
new file mode 100755
index 000000000..ce527acf5
--- /dev/null
+++ b/src/migration-scripts/firewall/7-to-8
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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/>.
+
+# T2199: Migrate interface firewall nodes to firewall interfaces <ifname> <direction> name/ipv6-name <name>
+# T2199: Migrate zone-policy to firewall node
+
+import re
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+from vyos.ifconfig import Section
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['firewall']
+zone_base = ['zone-policy']
+config = ConfigTree(config_file)
+
+if not config.exists(base) and not config.exists(zone_base):
+ # Nothing to do
+ exit(0)
+
+def migrate_interface(config, iftype, ifname, vif=None, vifs=None, vifc=None):
+ if_path = ['interfaces', iftype, ifname]
+ ifname_full = ifname
+
+ if vif:
+ if_path += ['vif', vif]
+ ifname_full = f'{ifname}.{vif}'
+ elif vifs:
+ if_path += ['vif-s', vifs]
+ ifname_full = f'{ifname}.{vifs}'
+ if vifc:
+ if_path += ['vif-c', vifc]
+ ifname_full = f'{ifname}.{vifs}.{vifc}'
+
+ if not config.exists(if_path + ['firewall']):
+ return
+
+ if not config.exists(['firewall', 'interface']):
+ config.set(['firewall', 'interface'])
+ config.set_tag(['firewall', 'interface'])
+
+ config.copy(if_path + ['firewall'], ['firewall', 'interface', ifname_full])
+ config.delete(if_path + ['firewall'])
+
+for iftype in config.list_nodes(['interfaces']):
+ for ifname in config.list_nodes(['interfaces', iftype]):
+ migrate_interface(config, iftype, ifname)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif']):
+ for vif in config.list_nodes(['interfaces', iftype, ifname, 'vif']):
+ migrate_interface(config, iftype, ifname, vif=vif)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s']):
+ for vifs in config.list_nodes(['interfaces', iftype, ifname, 'vif-s']):
+ migrate_interface(config, iftype, ifname, vifs=vifs)
+
+ if config.exists(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ for vifc in config.list_nodes(['interfaces', iftype, ifname, 'vif-s', vifs, 'vif-c']):
+ migrate_interface(config, iftype, ifname, vifs=vifs, vifc=vifc)
+
+if config.exists(zone_base + ['zone']):
+ config.set(['firewall', 'zone'])
+ config.set_tag(['firewall', 'zone'])
+
+ for zone in config.list_nodes(zone_base + ['zone']):
+ config.copy(zone_base + ['zone', zone], ['firewall', 'zone', zone])
+ config.delete(zone_base)
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/migration-scripts/ids/0-to-1 b/src/migration-scripts/ids/0-to-1
new file mode 100755
index 000000000..9f08f7dc7
--- /dev/null
+++ b/src/migration-scripts/ids/0-to-1
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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.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()
+
+base = ['service', 'ids', 'ddos-protection']
+config = ConfigTree(config_file)
+
+if not config.exists(base + ['threshold']):
+ # Nothing to do
+ exit(0)
+else:
+ if config.exists(base + ['threshold', 'fps']):
+ tmp = config.return_value(base + ['threshold', 'fps'])
+ config.delete(base + ['threshold', 'fps'])
+ config.set(base + ['threshold', 'general', 'fps'], value=tmp)
+ if config.exists(base + ['threshold', 'mbps']):
+ tmp = config.return_value(base + ['threshold', 'mbps'])
+ config.delete(base + ['threshold', 'mbps'])
+ config.set(base + ['threshold', 'general', 'mbps'], value=tmp)
+ if config.exists(base + ['threshold', 'pps']):
+ tmp = config.return_value(base + ['threshold', 'pps'])
+ config.delete(base + ['threshold', 'pps'])
+ config.set(base + ['threshold', 'general', 'pps'], value=tmp)
+
+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/ipoe-server/0-to-1 b/src/migration-scripts/ipoe-server/0-to-1
index f328ebced..d768758ba 100755
--- a/src/migration-scripts/ipoe-server/0-to-1
+++ b/src/migration-scripts/ipoe-server/0-to-1
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2022 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
@@ -14,8 +14,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# - remove primary/secondary identifier from nameserver
-# - Unifi RADIUS configuration by placing it all under "authentication radius" node
+# - T4703: merge vlan-id and vlan-range to vlan CLI node
+
+# L2|L3 -> l2|l3
+# mac-address -> mac
+# network-mode -> mode
import os
import sys
@@ -37,97 +40,35 @@ base = ['service', 'ipoe-server']
if not config.exists(base):
# Nothing to do
exit(0)
-else:
-
- # Migrate IPv4 DNS servers
- dns_base = base + ['dns-server']
- if config.exists(dns_base):
- for server in ['server-1', 'server-2']:
- if config.exists(dns_base + [server]):
- dns = config.return_value(dns_base + [server])
- config.set(base + ['name-server'], value=dns, replace=False)
-
- config.delete(dns_base)
-
- # Migrate IPv6 DNS servers
- dns_base = base + ['dnsv6-server']
- if config.exists(dns_base):
- for server in ['server-1', 'server-2', 'server-3']:
- if config.exists(dns_base + [server]):
- dns = config.return_value(dns_base + [server])
- config.set(base + ['name-server'], value=dns, replace=False)
-
- config.delete(dns_base)
-
- # Migrate radius-settings node to RADIUS and use this as base for the
- # later migration of the RADIUS servers - this will save a lot of code
- radius_settings = base + ['authentication', 'radius-settings']
- if config.exists(radius_settings):
- config.rename(radius_settings, 'radius')
-
- # Migrate RADIUS dynamic author / change of authorisation server
- dae_old = base + ['authentication', 'radius', 'dae-server']
- if config.exists(dae_old):
- config.rename(dae_old, 'dynamic-author')
- dae_new = base + ['authentication', 'radius', 'dynamic-author']
-
- if config.exists(dae_new + ['ip-address']):
- config.rename(dae_new + ['ip-address'], 'server')
-
- if config.exists(dae_new + ['secret']):
- config.rename(dae_new + ['secret'], 'key')
- # Migrate RADIUS server
- radius_server = base + ['authentication', 'radius-server']
- if config.exists(radius_server):
- new_base = base + ['authentication', 'radius', 'server']
- config.set(new_base)
- config.set_tag(new_base)
- for server in config.list_nodes(radius_server):
- old_base = radius_server + [server]
- config.copy(old_base, new_base + [server])
-
- # migrate key
- if config.exists(new_base + [server, 'secret']):
- config.rename(new_base + [server, 'secret'], 'key')
-
- # remove old req-limit node
- if config.exists(new_base + [server, 'req-limit']):
- config.delete(new_base + [server, 'req-limit'])
-
- config.delete(radius_server)
-
- # Migrate IPv6 prefixes
- ipv6_base = base + ['client-ipv6-pool']
- if config.exists(ipv6_base + ['prefix']):
- prefix_old = config.return_values(ipv6_base + ['prefix'])
- # delete old prefix CLI nodes
- config.delete(ipv6_base + ['prefix'])
- # create ned prefix tag node
- config.set(ipv6_base + ['prefix'])
- config.set_tag(ipv6_base + ['prefix'])
-
- for p in prefix_old:
- prefix = p.split(',')[0]
- mask = p.split(',')[1]
- config.set(ipv6_base + ['prefix', prefix, 'mask'], value=mask)
-
- if config.exists(ipv6_base + ['delegate-prefix']):
- prefix_old = config.return_values(ipv6_base + ['delegate-prefix'])
- # delete old delegate prefix CLI nodes
- config.delete(ipv6_base + ['delegate-prefix'])
- # create ned delegation tag node
- config.set(ipv6_base + ['delegate'])
- config.set_tag(ipv6_base + ['delegate'])
-
- for p in prefix_old:
- prefix = p.split(',')[0]
- mask = p.split(',')[1]
- config.set(ipv6_base + ['delegate', prefix, 'delegation-prefix'], value=mask)
-
- try:
- with open(file_name, 'w') as f:
- f.write(config.to_string())
- except OSError as e:
- print("Failed to save the modified config: {}".format(e))
- exit(1)
+if config.exists(base + ['authentication', 'interface']):
+ for interface in config.list_nodes(base + ['authentication', 'interface']):
+ config.rename(base + ['authentication', 'interface', interface, 'mac-address'], 'mac')
+
+ mac_base = base + ['authentication', 'interface', interface, 'mac']
+ for mac in config.list_nodes(mac_base):
+ vlan_config = mac_base + [mac, 'vlan-id']
+ if config.exists(vlan_config):
+ config.rename(vlan_config, 'vlan')
+
+for interface in config.list_nodes(base + ['interface']):
+ base_path = base + ['interface', interface]
+ for vlan in ['vlan-id', 'vlan-range']:
+ if config.exists(base_path + [vlan]):
+ print(interface, vlan)
+ for tmp in config.return_values(base_path + [vlan]):
+ config.set(base_path + ['vlan'], value=tmp, replace=False)
+ config.delete(base_path + [vlan])
+
+ if config.exists(base_path + ['network-mode']):
+ tmp = config.return_value(base_path + ['network-mode'])
+ config.delete(base_path + ['network-mode'])
+ # Change L2|L3 to lower case l2|l3
+ config.set(base_path + ['mode'], value=tmp.lower())
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/migration-scripts/ipsec/9-to-10 b/src/migration-scripts/ipsec/9-to-10
new file mode 100755
index 000000000..1254104cb
--- /dev/null
+++ b/src/migration-scripts/ipsec/9-to-10
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+from vyos.template import is_ipv4
+from vyos.template import is_ipv6
+
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['vpn', 'ipsec']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+# IKE changes, T4118:
+if config.exists(base + ['ike-group']):
+ for ike_group in config.list_nodes(base + ['ike-group']):
+ # replace 'ipsec ike-group <tag> mobike disable'
+ # => 'ipsec ike-group <tag> disable-mobike'
+ mobike = base + ['ike-group', ike_group, 'mobike']
+ if config.exists(mobike):
+ if config.return_value(mobike) == 'disable':
+ config.set(base + ['ike-group', ike_group, 'disable-mobike'])
+ config.delete(mobike)
+
+ # replace 'ipsec ike-group <tag> ikev2-reauth yes'
+ # => 'ipsec ike-group <tag> ikev2-reauth'
+ reauth = base + ['ike-group', ike_group, 'ikev2-reauth']
+ if config.exists(reauth):
+ if config.return_value(reauth) == 'yes':
+ config.delete(reauth)
+ config.set(reauth)
+ else:
+ config.delete(reauth)
+
+# ESP changes
+# replace 'ipsec esp-group <tag> compression enable'
+# => 'ipsec esp-group <tag> compression'
+if config.exists(base + ['esp-group']):
+ for esp_group in config.list_nodes(base + ['esp-group']):
+ compression = base + ['esp-group', esp_group, 'compression']
+ if config.exists(compression):
+ if config.return_value(compression) == 'enable':
+ config.delete(compression)
+ config.set(compression)
+ else:
+ config.delete(compression)
+
+# PEER changes
+if config.exists(base + ['site-to-site', 'peer']):
+ for peer in config.list_nodes(base + ['site-to-site', 'peer']):
+ peer_base = base + ['site-to-site', 'peer', peer]
+
+ # replace: 'peer <tag> id x'
+ # => 'peer <tag> local-id x'
+ if config.exists(peer_base + ['authentication', 'id']):
+ config.rename(peer_base + ['authentication', 'id'], 'local-id')
+
+ # For the peer '@foo' set remote-id 'foo' if remote-id is not defined
+ if peer.startswith('@'):
+ if not config.exists(peer_base + ['authentication', 'remote-id']):
+ tmp = peer.replace('@', '')
+ config.set(peer_base + ['authentication', 'remote-id'], value=tmp)
+
+ # replace: 'peer <tag> force-encapsulation enable'
+ # => 'peer <tag> force-udp-encapsulation'
+ force_enc = peer_base + ['force-encapsulation']
+ if config.exists(force_enc):
+ if config.return_value(force_enc) == 'enable':
+ config.delete(force_enc)
+ config.set(peer_base + ['force-udp-encapsulation'])
+ else:
+ config.delete(force_enc)
+
+ # add option: 'peer <tag> remote-address x.x.x.x'
+ remote_address = peer
+ if peer.startswith('@'):
+ remote_address = 'any'
+ config.set(peer_base + ['remote-address'], value=remote_address)
+ # Peer name it is swanctl connection name and shouldn't contain dots or colons
+ # rename peer:
+ # peer 192.0.2.1 => peer peer_192-0-2-1
+ # peer 2001:db8::2 => peer peer_2001-db8--2
+ # peer @foo => peer peer_foo
+ re_peer_name = re.sub(':|\.', '-', peer)
+ if re_peer_name.startswith('@'):
+ re_peer_name = re.sub('@', '', re_peer_name)
+ new_peer_name = f'peer_{re_peer_name}'
+
+ config.rename(peer_base, new_peer_name)
+
+# remote-access/road-warrior changes
+if config.exists(base + ['remote-access', 'connection']):
+ for connection in config.list_nodes(base + ['remote-access', 'connection']):
+ ra_base = base + ['remote-access', 'connection', connection]
+ # replace: 'remote-access connection <tag> authentication id x'
+ # => 'remote-access connection <tag> authentication local-id x'
+ if config.exists(ra_base + ['authentication', 'id']):
+ config.rename(ra_base + ['authentication', 'id'], 'local-id')
+
+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/1-to-2 b/src/migration-scripts/isis/1-to-2
new file mode 100755
index 000000000..f914ea995
--- /dev/null
+++ b/src/migration-scripts/isis/1-to-2
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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/>.
+
+# T4739 refactor, and remove "on" from segment routing from the configuration
+
+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)
+
+# Check if ISIS segment routing is configured. Then check if segment routing "on" exists, then delete the "on" as it is no longer needed. This is for global configuration.
+if config.exists(['protocols', 'isis']):
+ if config.exists(['protocols', 'isis', 'segment-routing']):
+ if config.exists(['protocols', 'isis', 'segment-routing', 'enable']):
+ config.delete(['protocols', 'isis', 'segment-routing', 'enable'])
+
+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/policy/3-to-4 b/src/migration-scripts/policy/3-to-4
new file mode 100755
index 000000000..bae30cffc
--- /dev/null
+++ b/src/migration-scripts/policy/3-to-4
@@ -0,0 +1,162 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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/>.
+
+# T4660: change cli
+# from: set policy route-map FOO rule 10 set community 'TEXT'
+# Multiple value
+# to: set policy route-map FOO rule 10 set community replace <community>
+# Multiple value
+# to: set policy route-map FOO rule 10 set community add <community>
+# to: set policy route-map FOO rule 10 set community none
+#
+# from: set policy route-map FOO rule 10 set large-community 'TEXT'
+# Multiple value
+# to: set policy route-map FOO rule 10 set large-community replace <community>
+# Multiple value
+# to: set policy route-map FOO rule 10 set large-community add <community>
+# to: set policy route-map FOO rule 10 set large-community none
+#
+# from: set policy route-map FOO rule 10 set extecommunity [rt|soo] 'TEXT'
+# Multiple value
+# to: set policy route-map FOO rule 10 set extcommunity [rt|soo] <community>
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+
+# Migration function for large and regular communities
+def community_migrate(config: ConfigTree, rule: list[str]) -> bool:
+ """
+
+ :param config: configuration object
+ :type config: ConfigTree
+ :param rule: Path to variable
+ :type rule: list[str]
+ :return: True if additive presents in community string
+ :rtype: bool
+ """
+ community_list = list((config.return_value(rule)).split(" "))
+ config.delete(rule)
+ if 'none' in community_list:
+ config.set(rule + ['none'])
+ return False
+ else:
+ community_action: str = 'replace'
+ if 'additive' in community_list:
+ community_action = 'add'
+ community_list.remove('additive')
+ for community in community_list:
+ config.set(rule + [community_action], value=community,
+ replace=False)
+ if community_action == 'replace':
+ return False
+ else:
+ return True
+
+
+# Migration function for extcommunities
+def extcommunity_migrate(config: ConfigTree, rule: list[str]) -> None:
+ """
+
+ :param config: configuration object
+ :type config: ConfigTree
+ :param rule: Path to variable
+ :type rule: list[str]
+ """
+ # if config.exists(rule + ['bandwidth']):
+ # bandwidth: str = config.return_value(rule + ['bandwidth'])
+ # config.delete(rule + ['bandwidth'])
+ # config.set(rule + ['bandwidth'], value=bandwidth)
+
+ if config.exists(rule + ['rt']):
+ community_list = list((config.return_value(rule + ['rt'])).split(" "))
+ config.delete(rule + ['rt'])
+ for community in community_list:
+ config.set(rule + ['rt'], value=community, replace=False)
+
+ if config.exists(rule + ['soo']):
+ community_list = list((config.return_value(rule + ['soo'])).split(" "))
+ config.delete(rule + ['soo'])
+ for community in community_list:
+ config.set(rule + ['soo'], value=community, replace=False)
+
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name: str = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base: list[str] = ['policy', 'route-map']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+for route_map in config.list_nodes(base):
+ if not config.exists(base + [route_map, 'rule']):
+ continue
+ for rule in config.list_nodes(base + [route_map, 'rule']):
+ base_rule: list[str] = base + [route_map, 'rule', rule, 'set']
+
+ # IF additive presents in coummunity then comm-list is redundant
+ isAdditive: bool = True
+ #### Change Set community ########
+ if config.exists(base_rule + ['community']):
+ isAdditive = community_migrate(config,
+ base_rule + ['community'])
+
+ #### Change Set community-list delete migrate ########
+ if config.exists(base_rule + ['comm-list', 'comm-list']):
+ if isAdditive:
+ tmp = config.return_value(
+ base_rule + ['comm-list', 'comm-list'])
+ config.delete(base_rule + ['comm-list'])
+ config.set(base_rule + ['community', 'delete'], value=tmp)
+ else:
+ config.delete(base_rule + ['comm-list'])
+
+ isAdditive = False
+ #### Change Set large-community ########
+ if config.exists(base_rule + ['large-community']):
+ isAdditive = community_migrate(config,
+ base_rule + ['large-community'])
+
+ #### Change Set large-community delete by List ########
+ if config.exists(base_rule + ['large-comm-list-delete']):
+ if isAdditive:
+ tmp = config.return_value(
+ base_rule + ['large-comm-list-delete'])
+ config.delete(base_rule + ['large-comm-list-delete'])
+ config.set(base_rule + ['large-community', 'delete'],
+ value=tmp)
+ else:
+ config.delete(base_rule + ['large-comm-list-delete'])
+
+ #### Change Set extcommunity ########
+ extcommunity_migrate(config, base_rule + ['extcommunity'])
+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/pppoe-server/5-to-6 b/src/migration-scripts/pppoe-server/5-to-6
new file mode 100755
index 000000000..e4888f4db
--- /dev/null
+++ b/src/migration-scripts/pppoe-server/5-to-6
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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/>.
+
+# - T4703: merge vlan-id and vlan-range to vlan CLI node
+
+from vyos.configtree import ConfigTree
+from sys import argv
+from sys import exit
+
+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)
+base_path = ['service', 'pppoe-server', 'interface']
+if not config.exists(base_path):
+ # Nothing to do
+ exit(0)
+
+for interface in config.list_nodes(base_path):
+ for vlan in ['vlan-id', 'vlan-range']:
+ if config.exists(base_path + [interface, vlan]):
+ print(interface, vlan)
+ for tmp in config.return_values(base_path + [interface, vlan]):
+ config.set(base_path + [interface, 'vlan'], value=tmp, replace=False)
+ config.delete(base_path + [interface, vlan])
+
+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/bridge.py b/src/op_mode/bridge.py
index fe8dadd70..5a821a287 100755
--- a/src/op_mode/bridge.py
+++ b/src/op_mode/bridge.py
@@ -22,7 +22,7 @@ import typing
from sys import exit
from tabulate import tabulate
-from vyos.util import cmd
+from vyos.util import cmd, rc_cmd
from vyos.util import dict_search
import vyos.opmode
@@ -57,7 +57,11 @@ def _get_raw_data_fdb(bridge):
"""Get MAC-address for the bridge brX
:returns list
"""
- json_data = cmd(f'sudo bridge --json fdb show br {bridge}')
+ code, json_data = rc_cmd(f'sudo bridge --json fdb show br {bridge}')
+ # From iproute2 fdb.c, fdb_show() will only exit(-1) in case of
+ # non-existent bridge device; raise error.
+ if code == 255:
+ raise vyos.opmode.UnconfiguredSubsystem(f"no such bridge device {bridge}")
data_dict = json.loads(json_data)
return data_dict
diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py
index b27aa6060..fff537936 100755
--- a/src/op_mode/conntrack.py
+++ b/src/op_mode/conntrack.py
@@ -48,6 +48,14 @@ def _get_raw_data(family):
Return: dictionary
"""
xml = _get_xml_data(family)
+ if len(xml) == 0:
+ output = {'conntrack':
+ {
+ 'error': True,
+ 'reason': 'entries not found'
+ }
+ }
+ return output
return _xml_to_dict(xml)
@@ -72,7 +80,8 @@ def get_formatted_output(dict_data):
:return: formatted output
"""
data_entries = []
- #dict_data = _get_raw_data(family)
+ if 'error' in dict_data['conntrack']:
+ return 'Entries not found'
for entry in dict_data['conntrack']['flow']:
orig_src, orig_dst, orig_sport, orig_dport = {}, {}, {}, {}
reply_src, reply_dst, reply_sport, reply_dport = {}, {}, {}, {}
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index 0aea17b3a..950feb625 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -24,43 +24,33 @@ from vyos.config import Config
from vyos.util import cmd
from vyos.util import dict_search_args
-def get_firewall_interfaces(conf, firewall, name=None, ipv6=False):
- interfaces = conf.get_config_dict(['interfaces'], key_mangling=('-', '_'),
- get_first_key=True, no_tag_node_value_mangle=True)
-
+def get_firewall_interfaces(firewall, name=None, ipv6=False):
directions = ['in', 'out', 'local']
- def parse_if(ifname, if_conf):
- if 'firewall' in if_conf:
+ if 'interface' in firewall:
+ for ifname, if_conf in firewall['interface'].items():
for direction in directions:
- if direction in if_conf['firewall']:
- fw_conf = if_conf['firewall'][direction]
- name_str = f'({ifname},{direction})'
-
- if 'name' in fw_conf:
- fw_name = fw_conf['name']
+ if direction not in if_conf:
+ continue
- if not name:
- firewall['name'][fw_name]['interface'].append(name_str)
- elif not ipv6 and name == fw_name:
- firewall['interface'].append(name_str)
+ fw_conf = if_conf[direction]
+ name_str = f'({ifname},{direction})'
- if 'ipv6_name' in fw_conf:
- fw_name = fw_conf['ipv6_name']
+ if 'name' in fw_conf:
+ fw_name = fw_conf['name']
- if not name:
- firewall['ipv6_name'][fw_name]['interface'].append(name_str)
- elif ipv6 and name == fw_name:
- firewall['interface'].append(name_str)
+ if not name:
+ firewall['name'][fw_name]['interface'].append(name_str)
+ elif not ipv6 and name == fw_name:
+ firewall['interface'].append(name_str)
- for iftype in ['vif', 'vif_s', 'vif_c']:
- if iftype in if_conf:
- for vifname, vif_conf in if_conf[iftype].items():
- parse_if(f'{ifname}.{vifname}', vif_conf)
+ if 'ipv6_name' in fw_conf:
+ fw_name = fw_conf['ipv6_name']
- for iftype, iftype_conf in interfaces.items():
- for ifname, if_conf in iftype_conf.items():
- parse_if(ifname, if_conf)
+ if not name:
+ firewall['ipv6_name'][fw_name]['interface'].append(name_str)
+ elif ipv6 and name == fw_name:
+ firewall['interface'].append(name_str)
return firewall
@@ -83,13 +73,13 @@ def get_config_firewall(conf, name=None, ipv6=False, interfaces=True):
for fw_name, name_conf in firewall['ipv6_name'].items():
name_conf['interface'] = []
- get_firewall_interfaces(conf, firewall, name, ipv6)
+ get_firewall_interfaces(firewall, name, ipv6)
return firewall
def get_nftables_details(name, ipv6=False):
suffix = '6' if ipv6 else ''
name_prefix = 'NAME6_' if ipv6 else 'NAME_'
- command = f'sudo nft list chain ip{suffix} filter {name_prefix}{name}'
+ command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{name}'
try:
results = cmd(command)
except:
diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py
index 21561d16f..a22f04c45 100755
--- a/src/op_mode/ikev2_profile_generator.py
+++ b/src/op_mode/ikev2_profile_generator.py
@@ -119,7 +119,7 @@ config_base = ipsec_base + ['remote-access', 'connection']
pki_base = ['pki']
conf = ConfigTreeQuery()
if not conf.exists(config_base):
- exit('IPSec remote-access is not configured!')
+ exit('IPsec remote-access is not configured!')
profile_name = 'VyOS IKEv2 Profile'
if args.profile:
@@ -131,7 +131,7 @@ if args.name:
conn_base = config_base + [args.connection]
if not conf.exists(conn_base):
- exit(f'IPSec remote-access connection "{args.connection}" does not exist!')
+ exit(f'IPsec remote-access connection "{args.connection}" does not exist!')
data = conf.get_config_dict(conn_base, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
@@ -178,7 +178,7 @@ for _, proposal in ike_proposal.items():
proposal['hash'] in set(vyos2client_integrity) and
proposal['dh_group'] in set(supported_dh_groups)):
- # We 're-code' from the VyOS IPSec proposals to the Apple naming scheme
+ # We 're-code' from the VyOS IPsec proposals to the Apple naming scheme
proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
@@ -191,7 +191,7 @@ count = 1
for _, proposal in esp_proposals.items():
if {'encryption', 'hash'} <= set(proposal):
if proposal['encryption'] in set(vyos2client_cipher) and proposal['hash'] in set(vyos2client_integrity):
- # We 're-code' from the VyOS IPSec proposals to the Apple naming scheme
+ # We 're-code' from the VyOS IPsec proposals to the Apple naming scheme
proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
diff --git a/src/op_mode/nat.py b/src/op_mode/nat.py
index 1339d5b92..845dbbb2c 100755
--- a/src/op_mode/nat.py
+++ b/src/op_mode/nat.py
@@ -60,7 +60,7 @@ def _get_json_data(direction, family):
if direction == 'destination':
chain = 'PREROUTING'
family = 'ip6' if family == 'inet6' else 'ip'
- return cmd(f'sudo nft --json list chain {family} nat {chain}')
+ return cmd(f'sudo nft --json list chain {family} vyos_nat {chain}')
def _get_raw_data_rules(direction, family):
@@ -109,7 +109,7 @@ def _get_formatted_output_rules(data, direction, family):
if jmespath.search('rule.expr[*].match.left.meta', rule) else 'any'
for index, match in enumerate(jmespath.search('rule.expr[*].match', rule)):
if 'payload' in match['left']:
- if 'prefix' in match['right'] or 'set' in match['right']:
+ if isinstance(match['right'], dict) and ('prefix' in match['right'] or 'set' in match['right']):
# Merge dict src/dst l3_l4 parameters
my_dict = {**match['left']['payload'], **match['right']}
my_dict['op'] = match['op']
@@ -136,10 +136,15 @@ def _get_formatted_output_rules(data, direction, family):
dport = my_dict.get('set')
dport = ','.join(map(str, dport))
else:
- if jmespath.search('left.payload.field', match) == 'saddr':
+ field = jmespath.search('left.payload.field', match)
+ if field == 'saddr':
saddr = match.get('right')
- if jmespath.search('left.payload.field', match) == 'daddr':
+ elif field == 'daddr':
daddr = match.get('right')
+ elif field == 'sport':
+ sport = match.get('right')
+ elif field == 'dport':
+ dport = match.get('right')
else:
saddr = '::/0' if family == 'inet6' else '0.0.0.0/0'
daddr = '::/0' if family == 'inet6' else '0.0.0.0/0'
diff --git a/src/op_mode/route.py b/src/op_mode/route.py
index e1eee5bbf..e1eee5bbf 100644..100755
--- a/src/op_mode/route.py
+++ b/src/op_mode/route.py
diff --git a/src/op_mode/show_nat66_statistics.py b/src/op_mode/show_nat66_statistics.py
index bc81692ae..cb10aed9f 100755
--- a/src/op_mode/show_nat66_statistics.py
+++ b/src/op_mode/show_nat66_statistics.py
@@ -44,7 +44,7 @@ group.add_argument("--destination", help="Show statistics for configured destina
args = parser.parse_args()
if args.source or args.destination:
- tmp = cmd('sudo nft -j list table ip6 nat')
+ tmp = cmd('sudo nft -j list table ip6 vyos_nat')
tmp = json.loads(tmp)
source = r"nftables[?rule.chain=='POSTROUTING'].rule.{chain: chain, handle: handle, comment: comment, counter: expr[].counter | [0], interface: expr[].match.right | [0] }"
diff --git a/src/op_mode/show_nat_statistics.py b/src/op_mode/show_nat_statistics.py
index c568c8305..be41e083b 100755
--- a/src/op_mode/show_nat_statistics.py
+++ b/src/op_mode/show_nat_statistics.py
@@ -44,7 +44,7 @@ group.add_argument("--destination", help="Show statistics for configured destina
args = parser.parse_args()
if args.source or args.destination:
- tmp = cmd('sudo nft -j list table ip nat')
+ tmp = cmd('sudo nft -j list table ip vyos_nat')
tmp = json.loads(tmp)
source = r"nftables[?rule.chain=='POSTROUTING'].rule.{chain: chain, handle: handle, comment: comment, counter: expr[].counter | [0], interface: expr[].match.right | [0] }"
diff --git a/src/op_mode/storage.py b/src/op_mode/storage.py
new file mode 100755
index 000000000..75964c493
--- /dev/null
+++ b/src/op_mode/storage.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sys
+
+import vyos.opmode
+from vyos.util import cmd
+
+
+def _get_system_storage(only_persistent=False):
+ if not only_persistent:
+ cmd_str = 'df -h -x squashf'
+ else:
+ cmd_str = 'df -h -t ext4 --output=source,size,used,avail,pcent'
+
+ res = cmd(cmd_str)
+
+ return res
+
+def _get_raw_data():
+ out = _get_system_storage(only_persistent=True)
+ lines = out.splitlines()
+ lists = [l.split() for l in lines]
+ res = {lists[0][i]: lists[1][i] for i in range(len(lists[0]))}
+
+ return res
+
+def _get_formatted_output():
+ return _get_system_storage()
+
+def show(raw: bool):
+ if raw:
+ return _get_raw_data()
+
+ return _get_formatted_output()
+
+
+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/system.py b/src/op_mode/system.py
new file mode 100755
index 000000000..11a3a8730
--- /dev/null
+++ b/src/op_mode/system.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import jmespath
+import json
+import sys
+import requests
+import typing
+
+from sys import exit
+
+from vyos.configquery import ConfigTreeQuery
+
+import vyos.opmode
+import vyos.version
+
+config = ConfigTreeQuery()
+base = ['system', 'update-check']
+
+
+def _compare_version_raw():
+ url = config.value(base + ['url'])
+ local_data = vyos.version.get_full_version_data()
+ remote_data = vyos.version.get_remote_version(url)
+ if not remote_data:
+ return {"error": True,
+ "reason": "Unable to get remote version"}
+ if local_data.get('version') and remote_data:
+ local_version = local_data.get('version')
+ remote_version = jmespath.search('[0].version', remote_data)
+ image_url = jmespath.search('[0].url', remote_data)
+ if local_data.get('version') != remote_version:
+ return {"error": False,
+ "update_available": True,
+ "local_version": local_version,
+ "remote_version": remote_version,
+ "url": image_url}
+ return {"update_available": False,
+ "local_version": local_version,
+ "remote_version": remote_version}
+
+
+def _formatted_compare_version(data):
+ local_version = data.get('local_version')
+ remote_version = data.get('remote_version')
+ url = data.get('url')
+ if {'update_available','local_version', 'remote_version', 'url'} <= set(data):
+ return f'Current version: {local_version}\n\nUpdate available: {remote_version}\nUpdate URL: {url}'
+ elif local_version == remote_version and remote_version is not None:
+ return f'No available updates for your system \n' \
+ f'current version: {local_version}\nremote version: {remote_version}'
+ else:
+ return 'Update not found'
+
+
+def _verify():
+ if not config.exists(base):
+ return False
+ return True
+
+
+def show_update(raw: bool):
+ if not _verify():
+ raise vyos.opmode.UnconfiguredSubsystem("system update-check not configured")
+ data = _compare_version_raw()
+ if raw:
+ return data
+ else:
+ return _formatted_compare_version(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_uptime.py b/src/op_mode/uptime.py
index b70c60cf8..2ebe6783b 100755
--- a/src/op_mode/show_uptime.py
+++ b/src/op_mode/uptime.py
@@ -14,7 +14,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-def get_uptime_seconds():
+import sys
+
+import vyos.opmode
+
+def _get_uptime_seconds():
from re import search
from vyos.util import read_file
@@ -23,7 +27,7 @@ def get_uptime_seconds():
return int(float(seconds))
-def get_load_averages():
+def _get_load_averages():
from re import search
from vyos.util import cmd
from vyos.cpu import get_core_count
@@ -40,19 +44,17 @@ def get_load_averages():
return res
-def get_raw_data():
+def _get_raw_data():
from vyos.util import seconds_to_human
res = {}
- res["uptime_seconds"] = get_uptime_seconds()
- res["uptime"] = seconds_to_human(get_uptime_seconds())
- res["load_average"] = get_load_averages()
+ res["uptime_seconds"] = _get_uptime_seconds()
+ res["uptime"] = seconds_to_human(_get_uptime_seconds())
+ res["load_average"] = _get_load_averages()
return res
-def get_formatted_output():
- data = get_raw_data()
-
+def _get_formatted_output(data):
out = "Uptime: {}\n\n".format(data["uptime"])
avgs = data["load_average"]
out += "Load averages:\n"
@@ -62,5 +64,19 @@ def get_formatted_output():
return out
+def show(raw: bool):
+ uptime_data = _get_raw_data()
+
+ if raw:
+ return uptime_data
+ else:
+ return _get_formatted_output(uptime_data)
+
if __name__ == '__main__':
- print(get_formatted_output())
+ 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/vpn_ike_sa.py b/src/op_mode/vpn_ike_sa.py
index 00f34564a..4b44c5c15 100755
--- a/src/op_mode/vpn_ike_sa.py
+++ b/src/op_mode/vpn_ike_sa.py
@@ -71,7 +71,7 @@ if __name__ == '__main__':
args = parser.parse_args()
if not process_named_running('charon'):
- print("IPSec Process NOT Running")
+ print("IPsec Process NOT Running")
sys.exit(0)
ike_sa(args.peer, args.nat)
diff --git a/src/services/api/graphql/graphql/directives.py b/src/services/api/graphql/graphql/directives.py
index d8ceefae6..d75d72582 100644
--- a/src/services/api/graphql/graphql/directives.py
+++ b/src/services/api/graphql/graphql/directives.py
@@ -31,54 +31,21 @@ class VyosDirective(SchemaDirectiveVisitor):
field.resolve = func
return field
-
-class ConfigureDirective(VyosDirective):
- """
- Class providing implementation of 'configure' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_configure_resolver)
-
-class ShowConfigDirective(VyosDirective):
- """
- Class providing implementation of 'show' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_show_config_resolver)
-
-class SystemStatusDirective(VyosDirective):
- """
- Class providing implementation of 'system_status' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_system_status_resolver)
-
-class ConfigFileDirective(VyosDirective):
- """
- Class providing implementation of 'configfile' directive in schema.
- """
- def visit_field_definition(self, field, object_type):
- super().visit_field_definition(field, object_type,
- make_resolver=make_config_file_resolver)
-
-class ShowDirective(VyosDirective):
+class ConfigSessionQueryDirective(VyosDirective):
"""
- Class providing implementation of 'show' directive in schema.
+ Class providing implementation of 'configsessionquery' directive in schema.
"""
def visit_field_definition(self, field, object_type):
super().visit_field_definition(field, object_type,
- make_resolver=make_show_resolver)
+ make_resolver=make_config_session_query_resolver)
-class ImageDirective(VyosDirective):
+class ConfigSessionMutationDirective(VyosDirective):
"""
- Class providing implementation of 'image' directive in schema.
+ Class providing implementation of 'configsessionmutation' directive in schema.
"""
def visit_field_definition(self, field, object_type):
super().visit_field_definition(field, object_type,
- make_resolver=make_image_resolver)
+ make_resolver=make_config_session_mutation_resolver)
class GenOpQueryDirective(VyosDirective):
"""
@@ -96,11 +63,16 @@ class GenOpMutationDirective(VyosDirective):
super().visit_field_definition(field, object_type,
make_resolver=make_gen_op_mutation_resolver)
-directives_dict = {"configure": ConfigureDirective,
- "showconfig": ShowConfigDirective,
- "systemstatus": SystemStatusDirective,
- "configfile": ConfigFileDirective,
- "show": ShowDirective,
- "image": ImageDirective,
+class SystemStatusDirective(VyosDirective):
+ """
+ Class providing implementation of 'system_status' directive in schema.
+ """
+ def visit_field_definition(self, field, object_type):
+ super().visit_field_definition(field, object_type,
+ make_resolver=make_system_status_resolver)
+
+directives_dict = {"configsessionquery": ConfigSessionQueryDirective,
+ "configsessionmutation": ConfigSessionMutationDirective,
"genopquery": GenOpQueryDirective,
- "genopmutation": GenOpMutationDirective}
+ "genopmutation": GenOpMutationDirective,
+ "systemstatus": SystemStatusDirective}
diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py
index 1b77cff87..f7d285a77 100644
--- a/src/services/api/graphql/graphql/mutations.py
+++ b/src/services/api/graphql/graphql/mutations.py
@@ -90,11 +90,12 @@ def make_mutation_resolver(mutation_name, class_name, session_func):
}
except OpModeError as e:
typename = type(e).__name__
+ msg = str(e)
return {
"success": False,
"errore": ['op_mode_error'],
"op_mode_error": {"name": f"{typename}",
- "message": op_mode_err_msg.get(typename, "Unknown"),
+ "message": msg if msg else op_mode_err_msg.get(typename, "Unknown"),
"vyos_code": op_mode_err_code.get(typename, 9999)}
}
except Exception as error:
@@ -105,24 +106,9 @@ def make_mutation_resolver(mutation_name, class_name, session_func):
return func_impl
-def make_prefix_resolver(mutation_name, prefix=[]):
- for pre in prefix:
- Pre = pre.capitalize()
- if Pre in mutation_name:
- class_name = mutation_name.replace(Pre, '', 1)
- return make_mutation_resolver(mutation_name, class_name, pre)
- raise Exception
-
-def make_configure_resolver(mutation_name):
- class_name = mutation_name
- return make_mutation_resolver(mutation_name, class_name, 'configure')
-
-def make_config_file_resolver(mutation_name):
- return make_prefix_resolver(mutation_name, prefix=['save', 'load'])
-
-def make_image_resolver(mutation_name):
- return make_prefix_resolver(mutation_name, prefix=['add', 'delete'])
+def make_config_session_mutation_resolver(mutation_name):
+ return make_mutation_resolver(mutation_name, mutation_name,
+ convert_camel_case_to_snake(mutation_name))
def make_gen_op_mutation_resolver(mutation_name):
- class_name = mutation_name
- return make_mutation_resolver(mutation_name, class_name, 'gen_op_mutation')
+ return make_mutation_resolver(mutation_name, mutation_name, 'gen_op_mutation')
diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py
index 8ae61b704..5f3a7d005 100644
--- a/src/services/api/graphql/graphql/queries.py
+++ b/src/services/api/graphql/graphql/queries.py
@@ -90,11 +90,12 @@ def make_query_resolver(query_name, class_name, session_func):
}
except OpModeError as e:
typename = type(e).__name__
+ msg = str(e)
return {
"success": False,
"errors": ['op_mode_error'],
"op_mode_error": {"name": f"{typename}",
- "message": op_mode_err_msg.get(typename, "Unknown"),
+ "message": msg if msg else op_mode_err_msg.get(typename, "Unknown"),
"vyos_code": op_mode_err_code.get(typename, 9999)}
}
except Exception as error:
@@ -105,18 +106,12 @@ def make_query_resolver(query_name, class_name, session_func):
return func_impl
-def make_show_config_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'show_config')
-
-def make_system_status_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'system_status')
-
-def make_show_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'show')
+def make_config_session_query_resolver(query_name):
+ return make_query_resolver(query_name, query_name,
+ convert_camel_case_to_snake(query_name))
def make_gen_op_query_resolver(query_name):
- class_name = query_name
- return make_query_resolver(query_name, class_name, 'gen_op_query')
+ return make_query_resolver(query_name, query_name, 'gen_op_query')
+
+def make_system_status_resolver(query_name):
+ return make_query_resolver(query_name, query_name, 'system_status')
diff --git a/src/services/api/graphql/graphql/schema/config_file.graphql b/src/services/api/graphql/graphql/schema/config_file.graphql
deleted file mode 100644
index a7263114b..000000000
--- a/src/services/api/graphql/graphql/schema/config_file.graphql
+++ /dev/null
@@ -1,29 +0,0 @@
-input SaveConfigFileInput {
- key: String!
- fileName: String
-}
-
-type SaveConfigFile {
- fileName: String
-}
-
-type SaveConfigFileResult {
- data: SaveConfigFile
- success: Boolean!
- errors: [String]
-}
-
-input LoadConfigFileInput {
- key: String!
- fileName: String!
-}
-
-type LoadConfigFile {
- fileName: String!
-}
-
-type LoadConfigFileResult {
- data: LoadConfigFile
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/configsession.graphql b/src/services/api/graphql/graphql/schema/configsession.graphql
new file mode 100644
index 000000000..b1deac4b3
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/configsession.graphql
@@ -0,0 +1,115 @@
+
+input ShowConfigInput {
+ key: String!
+ path: [String!]!
+ configFormat: String = null
+}
+
+type ShowConfig {
+ result: Generic
+}
+
+type ShowConfigResult {
+ data: ShowConfig
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Query {
+ ShowConfig(data: ShowConfigInput) : ShowConfigResult @configsessionquery
+}
+
+input ShowInput {
+ key: String!
+ path: [String!]!
+}
+
+type Show {
+ result: Generic
+}
+
+type ShowResult {
+ data: Show
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Query {
+ Show(data: ShowInput) : ShowResult @configsessionquery
+}
+
+input SaveConfigFileInput {
+ key: String!
+ fileName: String = null
+}
+
+type SaveConfigFile {
+ result: Generic
+}
+
+type SaveConfigFileResult {
+ data: SaveConfigFile
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ SaveConfigFile(data: SaveConfigFileInput) : SaveConfigFileResult @configsessionmutation
+}
+
+input LoadConfigFileInput {
+ key: String!
+ fileName: String!
+}
+
+type LoadConfigFile {
+ result: Generic
+}
+
+type LoadConfigFileResult {
+ data: LoadConfigFile
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ LoadConfigFile(data: LoadConfigFileInput) : LoadConfigFileResult @configsessionmutation
+}
+
+input AddSystemImageInput {
+ key: String!
+ location: String!
+}
+
+type AddSystemImage {
+ result: Generic
+}
+
+type AddSystemImageResult {
+ data: AddSystemImage
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ AddSystemImage(data: AddSystemImageInput) : AddSystemImageResult @configsessionmutation
+}
+
+input DeleteSystemImageInput {
+ key: String!
+ name: String!
+}
+
+type DeleteSystemImage {
+ result: Generic
+}
+
+type DeleteSystemImageResult {
+ data: DeleteSystemImage
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ DeleteSystemImage(data: DeleteSystemImageInput) : DeleteSystemImageResult @configsessionmutation
+} \ No newline at end of file
diff --git a/src/services/api/graphql/graphql/schema/dhcp_server.graphql b/src/services/api/graphql/graphql/schema/dhcp_server.graphql
deleted file mode 100644
index 345c349ac..000000000
--- a/src/services/api/graphql/graphql/schema/dhcp_server.graphql
+++ /dev/null
@@ -1,36 +0,0 @@
-input DhcpServerConfigInput {
- key: String!
- sharedNetworkName: String
- subnet: String
- defaultRouter: String
- nameServer: String
- domainName: String
- lease: Int
- range: Int
- start: String
- stop: String
- dnsForwardingAllowFrom: String
- dnsForwardingCacheSize: Int
- dnsForwardingListenAddress: String
-}
-
-type DhcpServerConfig {
- sharedNetworkName: String
- subnet: String
- defaultRouter: String
- nameServer: String
- domainName: String
- lease: Int
- range: Int
- start: String
- stop: String
- dnsForwardingAllowFrom: String
- dnsForwardingCacheSize: Int
- dnsForwardingListenAddress: String
-}
-
-type CreateDhcpServerResult {
- data: DhcpServerConfig
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/firewall_group.graphql b/src/services/api/graphql/graphql/schema/firewall_group.graphql
deleted file mode 100644
index 9454d2997..000000000
--- a/src/services/api/graphql/graphql/schema/firewall_group.graphql
+++ /dev/null
@@ -1,101 +0,0 @@
-input CreateFirewallAddressGroupInput {
- key: String!
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressGroup {
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressGroupResult {
- data: CreateFirewallAddressGroup
- success: Boolean!
- errors: [String]
-}
-
-input UpdateFirewallAddressGroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressGroupMembers {
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressGroupMembersResult {
- data: UpdateFirewallAddressGroupMembers
- success: Boolean!
- errors: [String]
-}
-
-input RemoveFirewallAddressGroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressGroupMembers {
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressGroupMembersResult {
- data: RemoveFirewallAddressGroupMembers
- success: Boolean!
- errors: [String]
-}
-
-input CreateFirewallAddressIpv6GroupInput {
- key: String!
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressIpv6Group {
- name: String!
- address: [String]
-}
-
-type CreateFirewallAddressIpv6GroupResult {
- data: CreateFirewallAddressIpv6Group
- success: Boolean!
- errors: [String]
-}
-
-input UpdateFirewallAddressIpv6GroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressIpv6GroupMembers {
- name: String!
- address: [String!]!
-}
-
-type UpdateFirewallAddressIpv6GroupMembersResult {
- data: UpdateFirewallAddressIpv6GroupMembers
- success: Boolean!
- errors: [String]
-}
-
-input RemoveFirewallAddressIpv6GroupMembersInput {
- key: String!
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressIpv6GroupMembers {
- name: String!
- address: [String!]!
-}
-
-type RemoveFirewallAddressIpv6GroupMembersResult {
- data: RemoveFirewallAddressIpv6GroupMembers
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/image.graphql b/src/services/api/graphql/graphql/schema/image.graphql
deleted file mode 100644
index 485033875..000000000
--- a/src/services/api/graphql/graphql/schema/image.graphql
+++ /dev/null
@@ -1,31 +0,0 @@
-input AddSystemImageInput {
- key: String!
- location: String!
-}
-
-type AddSystemImage {
- location: String
- result: String
-}
-
-type AddSystemImageResult {
- data: AddSystemImage
- success: Boolean!
- errors: [String]
-}
-
-input DeleteSystemImageInput {
- key: String!
- name: String!
-}
-
-type DeleteSystemImage {
- name: String
- result: String
-}
-
-type DeleteSystemImageResult {
- data: DeleteSystemImage
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/interface_ethernet.graphql b/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
deleted file mode 100644
index 8a17d919f..000000000
--- a/src/services/api/graphql/graphql/schema/interface_ethernet.graphql
+++ /dev/null
@@ -1,19 +0,0 @@
-input InterfaceEthernetConfigInput {
- key: String!
- interface: String
- address: String
- replace: Boolean = true
- description: String
-}
-
-type InterfaceEthernetConfig {
- interface: String
- address: String
- description: String
-}
-
-type CreateInterfaceEthernetResult {
- data: InterfaceEthernetConfig
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/schema.graphql b/src/services/api/graphql/graphql/schema/schema.graphql
index 624be2620..2acecade4 100644
--- a/src/services/api/graphql/graphql/schema/schema.graphql
+++ b/src/services/api/graphql/graphql/schema/schema.graphql
@@ -3,34 +3,16 @@ schema {
mutation: Mutation
}
-directive @configure on FIELD_DEFINITION
-directive @configfile on FIELD_DEFINITION
-directive @show on FIELD_DEFINITION
-directive @showconfig on FIELD_DEFINITION
directive @systemstatus on FIELD_DEFINITION
-directive @image on FIELD_DEFINITION
+directive @configsessionquery on FIELD_DEFINITION
+directive @configsessionmutation on FIELD_DEFINITION
directive @genopquery on FIELD_DEFINITION
directive @genopmutation on FIELD_DEFINITION
scalar Generic
type Query {
- Show(data: ShowInput) : ShowResult @show
- ShowConfig(data: ShowConfigInput) : ShowConfigResult @showconfig
SystemStatus(data: SystemStatusInput) : SystemStatusResult @systemstatus
}
-type Mutation {
- CreateDhcpServer(data: DhcpServerConfigInput) : CreateDhcpServerResult @configure
- CreateInterfaceEthernet(data: InterfaceEthernetConfigInput) : CreateInterfaceEthernetResult @configure
- CreateFirewallAddressGroup(data: CreateFirewallAddressGroupInput) : CreateFirewallAddressGroupResult @configure
- UpdateFirewallAddressGroupMembers(data: UpdateFirewallAddressGroupMembersInput) : UpdateFirewallAddressGroupMembersResult @configure
- RemoveFirewallAddressGroupMembers(data: RemoveFirewallAddressGroupMembersInput) : RemoveFirewallAddressGroupMembersResult @configure
- CreateFirewallAddressIpv6Group(data: CreateFirewallAddressIpv6GroupInput) : CreateFirewallAddressIpv6GroupResult @configure
- UpdateFirewallAddressIpv6GroupMembers(data: UpdateFirewallAddressIpv6GroupMembersInput) : UpdateFirewallAddressIpv6GroupMembersResult @configure
- RemoveFirewallAddressIpv6GroupMembers(data: RemoveFirewallAddressIpv6GroupMembersInput) : RemoveFirewallAddressIpv6GroupMembersResult @configure
- SaveConfigFile(data: SaveConfigFileInput) : SaveConfigFileResult @configfile
- LoadConfigFile(data: LoadConfigFileInput) : LoadConfigFileResult @configfile
- AddSystemImage(data: AddSystemImageInput) : AddSystemImageResult @image
- DeleteSystemImage(data: DeleteSystemImageInput) : DeleteSystemImageResult @image
-}
+type Mutation
diff --git a/src/services/api/graphql/graphql/schema/show.graphql b/src/services/api/graphql/graphql/schema/show.graphql
deleted file mode 100644
index 278ed536b..000000000
--- a/src/services/api/graphql/graphql/schema/show.graphql
+++ /dev/null
@@ -1,15 +0,0 @@
-input ShowInput {
- key: String!
- path: [String!]!
-}
-
-type Show {
- path: [String]
- result: String
-}
-
-type ShowResult {
- data: Show
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/graphql/schema/show_config.graphql b/src/services/api/graphql/graphql/schema/show_config.graphql
deleted file mode 100644
index 5a1fe43da..000000000
--- a/src/services/api/graphql/graphql/schema/show_config.graphql
+++ /dev/null
@@ -1,21 +0,0 @@
-"""
-Use 'scalar Generic' for show config output, to avoid attempts to
-JSON-serialize in case of JSON output.
-"""
-
-input ShowConfigInput {
- key: String!
- path: [String!]!
- configFormat: String
-}
-
-type ShowConfig {
- path: [String]
- result: Generic
-}
-
-type ShowConfigResult {
- data: ShowConfig
- success: Boolean!
- errors: [String]
-}
diff --git a/src/services/api/graphql/session/composite/system_status.py b/src/services/api/graphql/session/composite/system_status.py
index 8dadcc9f3..3c1a3d45b 100755
--- a/src/services/api/graphql/session/composite/system_status.py
+++ b/src/services/api/graphql/session/composite/system_status.py
@@ -30,8 +30,8 @@ def get_system_version() -> dict:
return show_version.show(raw=True, funny=False)
def get_system_uptime() -> dict:
- show_uptime = load_op_mode_as_module('show_uptime.py')
- return show_uptime.get_raw_data()
+ show_uptime = load_op_mode_as_module('uptime.py')
+ return show_uptime._get_raw_data()
def get_system_ram_usage() -> dict:
show_ram = load_op_mode_as_module('memory.py')
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index 93e1c328e..f990e63d0 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -45,40 +45,6 @@ class Session:
except Exception:
self._op_mode_list = None
- def configure(self):
- session = self._session
- data = self._data
- func_base_name = self._name
-
- tmpl_file = f'{func_base_name}.tmpl'
- cmd_file = f'/tmp/{func_base_name}.cmds'
- tmpl_dir = directories['api_templates']
-
- try:
- render(cmd_file, tmpl_file, data, location=tmpl_dir)
- commands = []
- with open(cmd_file) as f:
- lines = f.readlines()
- for line in lines:
- commands.append(line.split())
- for cmd in commands:
- if cmd[0] == 'set':
- session.set(cmd[1:])
- elif cmd[0] == 'delete':
- session.delete(cmd[1:])
- else:
- raise ValueError('Operation must be "set" or "delete"')
- session.commit()
- except Exception as error:
- raise error
-
- def delete_path_if_childless(self, path):
- session = self._session
- config = Config(session.get_session_env())
- if not config.list_nodes(path):
- session.delete(path)
- session.commit()
-
def show_config(self):
session = self._session
data = self._data
@@ -87,14 +53,14 @@ class Session:
try:
out = session.show_config(data['path'])
if data.get('config_format', '') == 'json':
- config_tree = vyos.configtree.ConfigTree(out)
+ config_tree = ConfigTree(out)
out = json.loads(config_tree.to_json())
except Exception as error:
raise error
return out
- def save(self):
+ def save_config_file(self):
session = self._session
data = self._data
if 'file_name' not in data or not data['file_name']:
@@ -105,7 +71,7 @@ class Session:
except Exception as error:
raise error
- def load(self):
+ def load_config_file(self):
session = self._session
data = self._data
@@ -127,7 +93,7 @@ class Session:
return out
- def add(self):
+ def add_system_image(self):
session = self._session
data = self._data
@@ -138,7 +104,7 @@ class Session:
return res
- def delete(self):
+ def delete_system_image(self):
session = self._session
data = self._data
diff --git a/src/services/api/graphql/utils/config_session_function.py b/src/services/api/graphql/utils/config_session_function.py
new file mode 100644
index 000000000..fc0dd7a87
--- /dev/null
+++ b/src/services/api/graphql/utils/config_session_function.py
@@ -0,0 +1,28 @@
+# typing information for native configsession functions; used to generate
+# schema definition files
+import typing
+
+def show_config(path: list[str], configFormat: typing.Optional[str]):
+ pass
+
+def show(path: list[str]):
+ pass
+
+queries = {'show_config': show_config,
+ 'show': show}
+
+def save_config_file(fileName: typing.Optional[str]):
+ pass
+def load_config_file(fileName: str):
+ pass
+def add_system_image(location: str):
+ pass
+def delete_system_image(name: str):
+ pass
+
+mutations = {'save_config_file': save_config_file,
+ 'load_config_file': load_config_file,
+ 'add_system_image': add_system_image,
+ 'delete_system_image': delete_system_image}
+
+
diff --git a/src/services/api/graphql/utils/schema_from_config_session.py b/src/services/api/graphql/utils/schema_from_config_session.py
new file mode 100755
index 000000000..ea78aaf88
--- /dev/null
+++ b/src/services/api/graphql/utils/schema_from_config_session.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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/>.
+#
+#
+# A utility to generate GraphQL schema defintions from typing information of
+# (wrappers of) native configsession functions.
+
+import os
+import json
+from inspect import signature, getmembers, isfunction, isclass, getmro
+from jinja2 import Template
+
+if __package__ is None or __package__ == '':
+ from util import snake_to_pascal_case, map_type_name
+else:
+ from . util import snake_to_pascal_case, map_type_name
+
+# this will be run locally before the build
+SCHEMA_PATH = '../graphql/schema'
+
+schema_data: dict = {'schema_name': '',
+ 'schema_fields': []}
+
+query_template = """
+input {{ schema_name }}Input {
+ key: String!
+ {%- for field_entry in schema_fields %}
+ {{ field_entry }}
+ {%- endfor %}
+}
+
+type {{ schema_name }} {
+ result: Generic
+}
+
+type {{ schema_name }}Result {
+ data: {{ schema_name }}
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Query {
+ {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @configsessionquery
+}
+"""
+
+mutation_template = """
+input {{ schema_name }}Input {
+ key: String!
+ {%- for field_entry in schema_fields %}
+ {{ field_entry }}
+ {%- endfor %}
+}
+
+type {{ schema_name }} {
+ result: Generic
+}
+
+type {{ schema_name }}Result {
+ data: {{ schema_name }}
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @configsessionmutation
+}
+"""
+
+def create_schema(func_name: str, func: callable, template: str) -> str:
+ sig = signature(func)
+
+ field_dict = {}
+ for k in sig.parameters:
+ field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation)
+
+ schema_fields = []
+ for k,v in field_dict.items():
+ schema_fields.append(k+': '+v)
+
+ schema_data['schema_name'] = snake_to_pascal_case(func_name)
+ schema_data['schema_fields'] = schema_fields
+
+ j2_template = Template(template)
+ res = j2_template.render(schema_data)
+
+ return res
+
+def generate_config_session_definitions():
+ from config_session_function import queries, mutations
+
+ results = []
+ for name,func in queries.items():
+ res = create_schema(name, func, query_template)
+ results.append(res)
+
+ for name,func in mutations.items():
+ res = create_schema(name, func, mutation_template)
+ results.append(res)
+
+ out = '\n'.join(results)
+ with open(f'{SCHEMA_PATH}/configsession.graphql', 'w') as f:
+ f.write(out)
+
+if __name__ == '__main__':
+ generate_config_session_definitions()
diff --git a/src/services/api/graphql/utils/schema_from_op_mode.py b/src/services/api/graphql/utils/schema_from_op_mode.py
index 379d15250..57d63628b 100755
--- a/src/services/api/graphql/utils/schema_from_op_mode.py
+++ b/src/services/api/graphql/utils/schema_from_op_mode.py
@@ -20,15 +20,16 @@
import os
import json
-import typing
from inspect import signature, getmembers, isfunction, isclass, getmro
from jinja2 import Template
from vyos.defaults import directories
if __package__ is None or __package__ == '':
from util import load_as_module, is_op_mode_function_name, is_show_function_name
+ from util import snake_to_pascal_case, map_type_name
else:
from . util import load_as_module, is_op_mode_function_name, is_show_function_name
+ from . util import snake_to_pascal_case, map_type_name
OP_MODE_PATH = directories['op_mode']
SCHEMA_PATH = directories['api_schema']
@@ -103,35 +104,12 @@ type {{ name }} implements OpModeError {
{%- endfor %}
"""
-def _snake_to_pascal_case(name: str) -> str:
- res = ''.join(map(str.title, name.split('_')))
- return res
-
-def _map_type_name(type_name: type, optional: bool = False) -> str:
- if type_name == str:
- return 'String!' if not optional else 'String = null'
- if type_name == int:
- return 'Int!' if not optional else 'Int = null'
- if type_name == bool:
- return 'Boolean!' if not optional else 'Boolean = false'
- if typing.get_origin(type_name) == list:
- if not optional:
- return f'[{_map_type_name(typing.get_args(type_name)[0])}]!'
- return f'[{_map_type_name(typing.get_args(type_name)[0])}]'
- # typing.Optional is typing.Union[_, NoneType]
- if (typing.get_origin(type_name) is typing.Union and
- typing.get_args(type_name)[1] == type(None)):
- return f'{_map_type_name(typing.get_args(type_name)[0], optional=True)}'
-
- # scalar 'Generic' is defined in schema.graphql
- return 'Generic'
-
def create_schema(func_name: str, base_name: str, func: callable) -> str:
sig = signature(func)
field_dict = {}
for k in sig.parameters:
- field_dict[sig.parameters[k].name] = _map_type_name(sig.parameters[k].annotation)
+ field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation)
# It is assumed that if one is generating a schema for a 'show_*'
# function, that 'get_raw_data' is present and 'raw' is desired.
@@ -142,7 +120,7 @@ def create_schema(func_name: str, base_name: str, func: callable) -> str:
for k,v in field_dict.items():
schema_fields.append(k+': '+v)
- schema_data['schema_name'] = _snake_to_pascal_case(func_name + '_' + base_name)
+ schema_data['schema_name'] = snake_to_pascal_case(func_name + '_' + base_name)
schema_data['schema_fields'] = schema_fields
if is_show_function_name(func_name):
diff --git a/src/services/api/graphql/utils/util.py b/src/services/api/graphql/utils/util.py
index 073126853..da2bcdb5b 100644
--- a/src/services/api/graphql/utils/util.py
+++ b/src/services/api/graphql/utils/util.py
@@ -15,6 +15,7 @@
import os
import re
+import typing
import importlib.util
from vyos.defaults import directories
@@ -74,3 +75,26 @@ def split_compound_op_mode_name(name: str, files: list):
pair = (pair[0], f[0])
return pair
return (name, '')
+
+def snake_to_pascal_case(name: str) -> str:
+ res = ''.join(map(str.title, name.split('_')))
+ return res
+
+def map_type_name(type_name: type, optional: bool = False) -> str:
+ if type_name == str:
+ return 'String!' if not optional else 'String = null'
+ if type_name == int:
+ return 'Int!' if not optional else 'Int = null'
+ if type_name == bool:
+ return 'Boolean!' if not optional else 'Boolean = false'
+ if typing.get_origin(type_name) == list:
+ if not optional:
+ return f'[{map_type_name(typing.get_args(type_name)[0])}]!'
+ return f'[{map_type_name(typing.get_args(type_name)[0])}]'
+ # typing.Optional is typing.Union[_, NoneType]
+ if (typing.get_origin(type_name) is typing.Union and
+ typing.get_args(type_name)[1] == type(None)):
+ return f'{map_type_name(typing.get_args(type_name)[0], optional=True)}'
+
+ # scalar 'Generic' is defined in schema.graphql
+ return 'Generic'
diff --git a/src/system/vyos-system-update-check.py b/src/system/vyos-system-update-check.py
new file mode 100755
index 000000000..c9597721b
--- /dev/null
+++ b/src/system/vyos-system-update-check.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 argparse
+import json
+import jmespath
+
+from pathlib import Path
+from sys import exit
+from time import sleep
+
+from vyos.util import call
+
+import vyos.version
+
+motd_file = Path('/run/motd.d/10-vyos-update')
+
+
+if __name__ == '__main__':
+ # Parse command arguments and get config
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-c',
+ '--config',
+ action='store',
+ help='Path to system-update-check configuration',
+ required=True,
+ type=Path)
+
+ args = parser.parse_args()
+ try:
+ config_path = Path(args.config)
+ config = json.loads(config_path.read_text())
+ except Exception as err:
+ print(
+ f'Configuration file "{config_path}" does not exist or malformed: {err}'
+ )
+ exit(1)
+
+ url_json = config.get('url')
+ local_data = vyos.version.get_full_version_data()
+ local_version = local_data.get('version')
+
+ while True:
+ remote_data = vyos.version.get_remote_version(url_json)
+ if remote_data:
+ url = jmespath.search('[0].url', remote_data)
+ remote_version = jmespath.search('[0].version', remote_data)
+ if local_version != remote_version and remote_version:
+ call(f'wall -n "Update available: {remote_version} \nUpdate URL: {url}"')
+ # MOTD used in /run/motd.d/10-update
+ motd_file.parent.mkdir(exist_ok=True)
+ motd_file.write_text(f'---\n'
+ f'Current version: {local_version}\n'
+ f'Update available: \033[1;34m{remote_version}\033[0m\n'
+ f'---\n')
+ # Check every 12 hours
+ sleep(43200)
diff --git a/src/systemd/vyos-system-update.service b/src/systemd/vyos-system-update.service
new file mode 100644
index 000000000..032e5a14c
--- /dev/null
+++ b/src/systemd/vyos-system-update.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=VyOS system udpate-check service
+After=network.target vyos-router.service
+
+[Service]
+Type=simple
+Restart=always
+ExecStart=/usr/bin/python3 /usr/libexec/vyos/system/vyos-system-update-check.py --config /run/vyos-system-update.conf
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/validators/accel-radius-dictionary b/src/validators/accel-radius-dictionary
new file mode 100755
index 000000000..05287e770
--- /dev/null
+++ b/src/validators/accel-radius-dictionary
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+DICT_PATH=/usr/share/accel-ppp/radius
+NAME=$1
+
+if [ -n "$NAME" -a -e $DICT_PATH/dictionary.$NAME ]; then
+ exit 0
+else
+ echo "$NAME is not a valid RADIUS dictionary name"
+ echo "Please make sure that $DICT_PATH/dictionary.$NAME file exists"
+ exit 1
+fi
+
diff --git a/src/validators/bgp-extended-community b/src/validators/bgp-extended-community
new file mode 100755
index 000000000..b69ae3449
--- /dev/null
+++ b/src/validators/bgp-extended-community
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+
+# Copyright 2019-2022 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/>.
+
+from argparse import ArgumentParser
+from sys import exit
+
+from vyos.template import is_ipv4
+
+COMM_MAX_2_OCTET: int = 65535
+COMM_MAX_4_OCTET: int = 4294967295
+
+if __name__ == '__main__':
+ # add an argument with community
+ parser: ArgumentParser = ArgumentParser()
+ parser.add_argument('community', type=str)
+ args = parser.parse_args()
+ community: str = args.community
+ if community.count(':') != 1:
+ print("Invalid community format")
+ exit(1)
+ try:
+ # try to extract community parts from an argument
+ comm_left: str = community.split(':')[0]
+ comm_right: int = int(community.split(':')[1])
+
+ # check if left part is an IPv4 address
+ if is_ipv4(comm_left) and 0 <= comm_right <= COMM_MAX_2_OCTET:
+ exit()
+ # check if a left part is a number
+ if 0 <= int(comm_left) <= COMM_MAX_2_OCTET \
+ and 0 <= comm_right <= COMM_MAX_4_OCTET:
+ exit()
+
+ except Exception:
+ # fail if something was wrong
+ print("Invalid community format")
+ exit(1)
+
+ # fail if none of validators catched the value
+ print("Invalid community format")
+ exit(1) \ No newline at end of file
diff --git a/src/validators/bgp-large-community b/src/validators/bgp-large-community
new file mode 100755
index 000000000..386398308
--- /dev/null
+++ b/src/validators/bgp-large-community
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+
+# Copyright 2019-2022 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/>.
+
+from argparse import ArgumentParser
+from sys import exit
+
+from vyos.template import is_ipv4
+
+COMM_MAX_4_OCTET: int = 4294967295
+
+if __name__ == '__main__':
+ # add an argument with community
+ parser: ArgumentParser = ArgumentParser()
+ parser.add_argument('community', type=str)
+ args = parser.parse_args()
+ community: str = args.community
+ if community.count(':') != 2:
+ print("Invalid community format")
+ exit(1)
+ try:
+ # try to extract community parts from an argument
+ comm_part1: int = int(community.split(':')[0])
+ comm_part2: int = int(community.split(':')[1])
+ comm_part3: int = int(community.split(':')[2])
+
+ # check compatibilities of left and right parts
+ if 0 <= comm_part1 <= COMM_MAX_4_OCTET \
+ and 0 <= comm_part2 <= COMM_MAX_4_OCTET \
+ and 0 <= comm_part3 <= COMM_MAX_4_OCTET:
+ exit(0)
+
+ except Exception:
+ # fail if something was wrong
+ print("Invalid community format")
+ exit(1)
+
+ # fail if none of validators catched the value
+ print("Invalid community format")
+ exit(1) \ No newline at end of file
diff --git a/src/validators/bgp-regular-community b/src/validators/bgp-regular-community
new file mode 100755
index 000000000..d43a71eae
--- /dev/null
+++ b/src/validators/bgp-regular-community
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+
+# Copyright 2019-2022 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/>.
+
+from argparse import ArgumentParser
+from sys import exit
+
+from vyos.template import is_ipv4
+
+COMM_MAX_2_OCTET: int = 65535
+
+if __name__ == '__main__':
+ # add an argument with community
+ parser: ArgumentParser = ArgumentParser()
+ parser.add_argument('community', type=str)
+ args = parser.parse_args()
+ community: str = args.community
+ if community.count(':') != 1:
+ print("Invalid community format")
+ exit(1)
+ try:
+ # try to extract community parts from an argument
+ comm_left: int = int(community.split(':')[0])
+ comm_right: int = int(community.split(':')[1])
+
+ # check compatibilities of left and right parts
+ if 0 <= comm_left <= COMM_MAX_2_OCTET \
+ and 0 <= comm_right <= COMM_MAX_2_OCTET:
+ exit(0)
+ except Exception:
+ # fail if something was wrong
+ print("Invalid community format")
+ exit(1)
+
+ # fail if none of validators catched the value
+ print("Invalid community format")
+ exit(1) \ No newline at end of file
diff --git a/src/validators/range b/src/validators/range
deleted file mode 100755
index d4c25f3c4..000000000
--- a/src/validators/range
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import re
-import sys
-import argparse
-
-class MalformedRange(Exception):
- pass
-
-def validate_range(value, min=None, max=None):
- try:
- lower, upper = re.match(r'^(\d+)-(\d+)$', value).groups()
-
- lower, upper = int(lower), int(upper)
-
- if int(lower) > int(upper):
- raise MalformedRange("the lower bound exceeds the upper bound".format(value))
-
- if min is not None:
- if lower < min:
- raise MalformedRange("the lower bound must not be less than {}".format(min))
-
- if max is not None:
- if upper > max:
- raise MalformedRange("the upper bound must not be greater than {}".format(max))
-
- except (AttributeError, ValueError):
- raise MalformedRange("range syntax error")
-
-parser = argparse.ArgumentParser(description='Range validator.')
-parser.add_argument('--min', type=int, action='store')
-parser.add_argument('--max', type=int, action='store')
-parser.add_argument('value', action='store')
-
-if __name__ == '__main__':
- args = parser.parse_args()
-
- try:
- validate_range(args.value, min=args.min, max=args.max)
- except MalformedRange as e:
- print("Incorrect range '{}': {}".format(args.value, e))
- sys.exit(1)