summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md11
-rw-r--r--.github/workflows/build.yml20
-rw-r--r--.github/workflows/mergifyio_backport.yml22
-rw-r--r--Makefile1
-rw-r--r--data/config-mode-dependencies/vyos-1x.json (renamed from data/config-mode-dependencies.json)19
-rw-r--r--data/op-mode-standardized.json4
-rw-r--r--data/templates/accel-ppp/ipoe.config.j24
-rw-r--r--data/templates/aws/override_aws_gwlbtun.conf.j236
-rw-r--r--data/templates/conntrack/nftables-ct.j2152
-rw-r--r--data/templates/conntrack/nftables-helpers.j270
-rw-r--r--data/templates/conntrack/sysctl.conf.j21
-rw-r--r--data/templates/conntrack/vyos_nf_conntrack.conf.j23
-rw-r--r--data/templates/dhcp-server/10-override.conf.j2 (renamed from src/systemd/isc-dhcp-server.service)8
-rw-r--r--data/templates/dns-dynamic/ddclient.conf.j229
-rw-r--r--data/templates/dns-dynamic/override.conf.j22
-rw-r--r--data/templates/firewall/nftables-bridge.j235
-rw-r--r--data/templates/firewall/nftables-defines.j214
-rw-r--r--data/templates/firewall/nftables-nat.j223
-rw-r--r--data/templates/firewall/nftables-nat66.j217
-rw-r--r--data/templates/firewall/nftables-offload.j29
-rw-r--r--data/templates/firewall/nftables-policy.j24
-rw-r--r--data/templates/firewall/nftables-vrf-zones.j24
-rw-r--r--data/templates/firewall/nftables.j259
-rw-r--r--data/templates/frr/bgpd.frr.j216
-rw-r--r--data/templates/frr/daemons.frr.tmpl130
-rw-r--r--data/templates/frr/igmp.frr.j26
-rw-r--r--data/templates/frr/isisd.frr.j28
-rw-r--r--data/templates/frr/pim6d.frr.j238
-rw-r--r--data/templates/high-availability/10-override.conf.j2 (renamed from src/etc/systemd/system/keepalived.service.d/override.conf)4
-rw-r--r--data/templates/load-balancing/haproxy.cfg.j28
-rw-r--r--data/templates/mdns-repeater/avahi-daemon.conf.j2 (renamed from data/templates/mdns-repeater/avahi-daemon.j2)6
-rw-r--r--data/templates/openvpn/server.conf.j211
-rw-r--r--data/templates/pppoe/peer.j22
-rw-r--r--data/templates/wifi/hostapd.conf.j214
-rw-r--r--data/vyos-firewall-init.conf88
-rw-r--r--debian/changelog4
-rw-r--r--debian/control368
-rwxr-xr-xdebian/rules4
-rw-r--r--debian/vyos-1x-smoketest.install1
-rw-r--r--debian/vyos-1x.postinst25
-rw-r--r--debian/vyos-1x.preinst3
-rw-r--r--interface-definitions/container.xml.in14
-rw-r--r--interface-definitions/dns-dynamic.xml.in27
-rw-r--r--interface-definitions/dns-forwarding.xml.in30
-rw-r--r--interface-definitions/firewall.xml.in49
-rw-r--r--interface-definitions/high-availability.xml.in6
-rw-r--r--interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i14
-rw-r--r--interface-definitions/include/bgp/neighbor-capability.xml.i6
-rw-r--r--interface-definitions/include/bgp/neighbor-path-attribute.xml.i12
-rw-r--r--interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i4
-rw-r--r--interface-definitions/include/dns/dynamic-service-host-name-server.xml.i3
-rw-r--r--interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i28
-rw-r--r--interface-definitions/include/dns/dynamic-service-zone.xml.i14
-rw-r--r--interface-definitions/include/dns/time-to-live.xml.i1
-rw-r--r--interface-definitions/include/firewall/action-forward.xml.i49
-rw-r--r--interface-definitions/include/firewall/action-l2.xml.i37
-rw-r--r--interface-definitions/include/firewall/action.xml.i12
-rw-r--r--interface-definitions/include/firewall/bridge-custom-name.xml.i39
-rw-r--r--interface-definitions/include/firewall/bridge-hook-forward.xml.i34
-rw-r--r--interface-definitions/include/firewall/common-rule-bridge.xml.i57
-rw-r--r--interface-definitions/include/firewall/common-rule-inet.xml.i6
-rw-r--r--interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i1
-rw-r--r--interface-definitions/include/firewall/common-rule.xml.i1
-rw-r--r--interface-definitions/include/firewall/conntrack-helper.xml.i42
-rw-r--r--interface-definitions/include/firewall/default-action-bridge.xml.i34
-rw-r--r--interface-definitions/include/firewall/default-action.xml.i8
-rw-r--r--interface-definitions/include/firewall/firewall-mark.xml.i26
-rw-r--r--interface-definitions/include/firewall/global-options.xml.i32
-rw-r--r--interface-definitions/include/firewall/ipv4-hook-forward.xml.i2
-rw-r--r--interface-definitions/include/firewall/ipv6-hook-forward.xml.i2
-rw-r--r--interface-definitions/include/firewall/match-interface.xml.i4
-rw-r--r--interface-definitions/include/firewall/match-vlan.xml.i41
-rw-r--r--interface-definitions/include/firewall/offload-target.xml.i10
-rw-r--r--interface-definitions/include/firewall/source-destination-group-ipv4.xml.i41
-rw-r--r--interface-definitions/include/firewall/synproxy.xml.i40
-rw-r--r--interface-definitions/include/firewall/tcp-flags.xml.i18
-rw-r--r--interface-definitions/include/firewall/tcp-mss.xml.i25
-rw-r--r--interface-definitions/include/isis/protocol-common-config.xml.i12
-rw-r--r--interface-definitions/include/policy/local-route_rule_ipv4_address.xml.i20
-rw-r--r--interface-definitions/include/policy/local-route_rule_ipv6_address.xml.i20
-rw-r--r--interface-definitions/include/policy/local-route_rule_protocol.xml.i21
-rw-r--r--interface-definitions/include/policy/route-common.xml.i2
-rw-r--r--interface-definitions/include/radius-server-ipv4-ipv6.xml.i20
-rw-r--r--interface-definitions/include/version/conntrack-version.xml.i2
-rw-r--r--interface-definitions/include/version/policy-version.xml.i2
-rw-r--r--interface-definitions/interfaces-ethernet.xml.in6
-rw-r--r--interface-definitions/interfaces-pppoe.xml.in14
-rw-r--r--interface-definitions/interfaces-virtual-ethernet.xml.in1
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in16
-rw-r--r--interface-definitions/load-balancing-haproxy.xml.in6
-rw-r--r--interface-definitions/netns.xml.in2
-rw-r--r--interface-definitions/policy-local-route.xml.in89
-rw-r--r--interface-definitions/protocols-pim6.xml.in132
-rw-r--r--interface-definitions/service-aws-glb.xml.in127
-rw-r--r--interface-definitions/service-mdns-repeater.xml.in25
-rw-r--r--interface-definitions/service-webproxy.xml.in2
-rw-r--r--interface-definitions/system-conntrack.xml.in223
-rw-r--r--interface-definitions/system-login.xml.in20
-rw-r--r--interface-definitions/vrf.xml.in2
-rw-r--r--op-mode-definitions/disks.xml.in20
-rw-r--r--op-mode-definitions/firewall.xml.in84
-rw-r--r--op-mode-definitions/generate_firewall_rule-resequence.xml.in42
-rw-r--r--op-mode-definitions/monitor-command.xml.in28
-rw-r--r--op-mode-definitions/ntp.xml.in49
-rw-r--r--op-mode-definitions/raid.xml.in69
-rw-r--r--op-mode-definitions/restart-frr.xml.in50
-rw-r--r--op-mode-definitions/show-hardware.xml.in2
-rw-r--r--op-mode-definitions/show-ip.xml.in6
-rw-r--r--op-mode-definitions/show-log.xml.in264
-rw-r--r--op-mode-definitions/show-ntp.xml.in21
-rw-r--r--op-mode-definitions/show-system.xml.in49
-rw-r--r--op-mode-definitions/show-techsupport_report.xml.in3
-rw-r--r--op-mode-definitions/vpn-ipsec.xml.in2
-rw-r--r--op-mode-definitions/zone-policy.xml.in24
-rw-r--r--python/vyos/component_version.py25
-rw-r--r--python/vyos/config_mgmt.py57
-rw-r--r--python/vyos/configdep.py61
-rw-r--r--python/vyos/defaults.py1
-rw-r--r--python/vyos/ethtool.py3
-rw-r--r--python/vyos/firewall.py125
-rw-r--r--python/vyos/frr.py9
-rw-r--r--python/vyos/ifconfig/control.py14
-rw-r--r--python/vyos/ifconfig/ethernet.py26
-rw-r--r--python/vyos/ifconfig/interface.py167
-rw-r--r--python/vyos/ifconfig/vxlan.py66
-rw-r--r--python/vyos/raid.py71
-rw-r--r--python/vyos/template.py84
-rw-r--r--python/vyos/utils/config.py34
-rw-r--r--python/vyos/utils/disk.py23
-rw-r--r--python/vyos/utils/network.py173
-rwxr-xr-xsmoketest/bin/vyos-configtest23
-rw-r--r--smoketest/config-tests/basic-vyos62
-rw-r--r--smoketest/config-tests/dialup-router-medium-vpn321
-rw-r--r--smoketest/configs/basic-vyos12
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py8
-rwxr-xr-xsmoketest/scripts/cli/test_component_version.py50
-rwxr-xr-xsmoketest/scripts/cli/test_dependency_graph.py54
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py137
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py76
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pppoe.py9
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py50
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py3
-rwxr-xr-xsmoketest/scripts/cli/test_load_balancing_reverse_proxy.py2
-rwxr-xr-xsmoketest/scripts/cli/test_load_balancing_wan.py7
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py5
-rwxr-xr-xsmoketest/scripts/cli/test_netns.py (renamed from smoketest/scripts/cli/test_interfaces_netns.py)52
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py58
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py16
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py16
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py4
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_pim6.py110
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_dynamic.py192
-rwxr-xr-xsmoketest/scripts/cli/test_service_mdns-repeater.py74
-rwxr-xr-xsmoketest/scripts/cli/test_system_conntrack.py99
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py2
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py120
-rwxr-xr-xsmoketest/scripts/system/test_kernel_options.py6
-rwxr-xr-xsrc/completion/list_ddclient_protocols.sh2
-rwxr-xr-xsrc/conf_mode/conntrack.py203
-rwxr-xr-xsrc/conf_mode/container.py6
-rwxr-xr-xsrc/conf_mode/dhcp_server.py5
-rwxr-xr-xsrc/conf_mode/dns_dynamic.py43
-rwxr-xr-xsrc/conf_mode/firewall.py67
-rwxr-xr-xsrc/conf_mode/flow_accounting_conf.py2
-rwxr-xr-xsrc/conf_mode/high-availability.py34
-rwxr-xr-xsrc/conf_mode/interfaces-dummy.py4
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py13
-rwxr-xr-xsrc/conf_mode/interfaces-pppoe.py5
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py20
-rwxr-xr-xsrc/conf_mode/load-balancing-wan.py5
-rwxr-xr-xsrc/conf_mode/nat.py107
-rwxr-xr-xsrc/conf_mode/nat66.py46
-rwxr-xr-xsrc/conf_mode/policy-local-route.py130
-rwxr-xr-xsrc/conf_mode/protocols_igmp.py2
-rwxr-xr-xsrc/conf_mode/protocols_pim6.py102
-rwxr-xr-xsrc/conf_mode/service_aws_glb.py76
-rwxr-xr-xsrc/conf_mode/service_mdns-repeater.py24
-rwxr-xr-xsrc/conf_mode/snmp.py5
-rwxr-xr-xsrc/conf_mode/system-ip.py28
-rwxr-xr-xsrc/conf_mode/system-ipv6.py25
-rwxr-xr-xsrc/conf_mode/system-login.py27
-rwxr-xr-xsrc/conf_mode/system_frr.py35
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py15
-rw-r--r--src/etc/sysctl.d/30-vyos-router.conf14
-rwxr-xr-xsrc/helpers/config_dependency.py58
-rwxr-xr-xsrc/helpers/read-saved-value.py30
-rwxr-xr-xsrc/helpers/vyos-domain-resolver.py6
-rwxr-xr-xsrc/helpers/vyos-save-config.py24
-rwxr-xr-xsrc/init/vyos-router51
-rwxr-xr-xsrc/migration-scripts/conntrack/3-to-450
-rwxr-xr-xsrc/migration-scripts/policy/5-to-665
-rwxr-xr-xsrc/migration-scripts/system/13-to-142
-rwxr-xr-xsrc/op_mode/dhcp.py4
-rwxr-xr-xsrc/op_mode/firewall.py159
-rwxr-xr-xsrc/op_mode/format_disk.py11
-rwxr-xr-xsrc/op_mode/generate_firewall_rule-resequence.py144
-rwxr-xr-xsrc/op_mode/ipsec.py39
-rwxr-xr-xsrc/op_mode/otp.py124
-rwxr-xr-xsrc/op_mode/raid.py44
-rwxr-xr-xsrc/op_mode/restart_frr.py4
-rwxr-xr-xsrc/op_mode/show_users.py7
-rwxr-xr-xsrc/op_mode/vyos-op-cmd-wrapper.sh6
-rwxr-xr-xsrc/op_mode/zone.py215
-rw-r--r--src/pam-configs/radius17
-rw-r--r--src/pam-configs/radius-mandatory19
-rw-r--r--src/pam-configs/radius-optional19
-rw-r--r--src/pam-configs/tacplus17
-rw-r--r--src/pam-configs/tacplus-mandatory17
-rw-r--r--src/pam-configs/tacplus-optional17
-rw-r--r--src/systemd/aws-gwlbtun.service11
-rw-r--r--src/systemd/vyos-router.service1
-rw-r--r--src/tests/test_dependency_graph.py31
-rwxr-xr-xsrc/validators/ddclient-protocol2
-rw-r--r--src/validators/numeric-exclude8
214 files changed, 6250 insertions, 2033 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 47579e1c6..933894447 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -21,6 +21,9 @@ the box, please use [x]
<!-- All submitted PRs must be linked to a Task on Phabricator. -->
* https://vyos.dev/Txxxx
+## Related PR(s)
+<!-- Link here any PRs in other repositories that are required by this PR -->
+
## Component(s) name
<!-- A rather incomplete list of components: ethernet, wireguard, bgp, mpls, ldp, l2tp, dhcp ... -->
@@ -37,6 +40,14 @@ like this
```
-->
+## Smoketest result
+<!-- Provide the output of the smoketest
+```
+$ /usr/libexec/vyos/tests/smoke/cli/test_xxx_feature.py
+test_01_simple_options (__main__.TestFeature.test_01_simple_options) ... ok
+```
+-->
+
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index d77275d38..000000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: Build
-on:
- push:
- branches:
- - current
- pull_request:
- types: [opened, synchronize, reopened]
-jobs:
- sonarcloud:
- name: SonarCloud
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- with:
- fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- - name: SonarCloud Scan
- uses: SonarSource/sonarcloud-github-action@master
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
- SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/mergifyio_backport.yml b/.github/workflows/mergifyio_backport.yml
new file mode 100644
index 000000000..f1f4312c4
--- /dev/null
+++ b/.github/workflows/mergifyio_backport.yml
@@ -0,0 +1,22 @@
+name: Mergifyio backport
+
+on: [issue_comment]
+
+jobs:
+ mergifyio_backport:
+ if: github.repository == 'vyos/vyos-1x'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: actions-ecosystem/action-regex-match@v2
+ id: regex-match
+ with:
+ text: ${{ github.event.comment.body }}
+ regex: '[Mm]ergifyio backport '
+
+ - uses: actions-ecosystem/action-add-labels@v1
+ if: ${{ steps.regex-match.outputs.match != '' }}
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ labels: backport
diff --git a/Makefile b/Makefile
index b75a78784..97f4de6c7 100644
--- a/Makefile
+++ b/Makefile
@@ -62,7 +62,6 @@ op_mode_definitions: $(op_xml_obj)
rm -f $(OP_TMPL_DIR)/delete/node.def
rm -f $(OP_TMPL_DIR)/generate/node.def
rm -f $(OP_TMPL_DIR)/set/node.def
- rm -f $(OP_TMPL_DIR)/show/tech-support/node.def
# XXX: ping and traceroute must be able to recursivly call itself as the
# options are provided from the script itself
diff --git a/data/config-mode-dependencies.json b/data/config-mode-dependencies/vyos-1x.json
index 91a757c16..6c86642c7 100644
--- a/data/config-mode-dependencies.json
+++ b/data/config-mode-dependencies/vyos-1x.json
@@ -1,6 +1,23 @@
{
- "firewall": {"group_resync": ["nat", "policy-route"]},
+ "conntrack": {"conntrack_sync": ["conntrack_sync"]},
+ "firewall": {
+ "conntrack": ["conntrack"],
+ "conntrack_sync": ["conntrack_sync"],
+ "group_resync": ["conntrack", "nat", "policy-route"]
+ },
"http_api": {"https": ["https"]},
+ "load_balancing_wan": {
+ "conntrack": ["conntrack"],
+ "conntrack_sync": ["conntrack_sync"]
+ },
+ "nat": {
+ "conntrack": ["conntrack"],
+ "conntrack_sync": ["conntrack_sync"]
+ },
+ "nat66": {
+ "conntrack": ["conntrack"],
+ "conntrack_sync": ["conntrack_sync"]
+ },
"pki": {
"ethernet": ["interfaces-ethernet"],
"openvpn": ["interfaces-openvpn"],
diff --git a/data/op-mode-standardized.json b/data/op-mode-standardized.json
index 042c466ab..ed9bb6cad 100644
--- a/data/op-mode-standardized.json
+++ b/data/op-mode-standardized.json
@@ -16,6 +16,7 @@
"neighbor.py",
"nhrp.py",
"openconnect.py",
+"otp.py",
"openvpn.py",
"reset_vpn.py",
"reverseproxy.py",
@@ -25,6 +26,5 @@
"storage.py",
"uptime.py",
"version.py",
-"vrf.py",
-"zone.py"
+"vrf.py"
]
diff --git a/data/templates/accel-ppp/ipoe.config.j2 b/data/templates/accel-ppp/ipoe.config.j2
index add3dc7e4..f59428509 100644
--- a/data/templates/accel-ppp/ipoe.config.j2
+++ b/data/templates/accel-ppp/ipoe.config.j2
@@ -36,7 +36,9 @@ verbose=1
{% set shared = 'shared=0,' %}
{% endif %}
{% set range = 'range=' ~ iface_config.client_subnet ~ ',' if iface_config.client_subnet is vyos_defined else '' %}
-{{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,{{ range }}start=dhcpv4,ipv6=1
+{% set relay = ',' ~ 'relay=' ~ iface_config.external_dhcp.dhcp_relay if iface_config.external_dhcp.dhcp_relay is vyos_defined else '' %}
+{% set giaddr = ',' ~ 'giaddr=' ~ iface_config.external_dhcp.giaddr if iface_config.external_dhcp.giaddr is vyos_defined else '' %}
+{{ tmp }},{{ shared }}mode={{ iface_config.mode | upper }},ifcfg=1,{{ range }}start=dhcpv4,ipv6=1{{ relay }}{{ giaddr }}
{% if iface_config.vlan is vyos_defined %}
vlan-mon={{ iface }},{{ iface_config.vlan | join(',') }}
{% endif %}
diff --git a/data/templates/aws/override_aws_gwlbtun.conf.j2 b/data/templates/aws/override_aws_gwlbtun.conf.j2
new file mode 100644
index 000000000..4c566d852
--- /dev/null
+++ b/data/templates/aws/override_aws_gwlbtun.conf.j2
@@ -0,0 +1,36 @@
+{% set args = [] %}
+{% if script.on_create is vyos_defined %}
+{% set _ = args.append("-c " + script.on_create) %}
+{% endif %}
+{% if script.on_destroy is vyos_defined %}
+{% set _ = args.append("-r " + script.on_destroy) %}
+{% endif %}
+
+{% if status.port is vyos_defined %}
+{% set _ = args.append("-p " + status.port) %}
+{% endif %}
+
+{% if threads.tunnel is vyos_defined %}
+{% set _ = args.append("--tunthreads " + threads.tunnel) %}
+{% endif %}
+{% if threads.tunnel_affinity is vyos_defined %}
+{% set _ = args.append("--tunaffinity " + threads.tunnel_affinity) %}
+{% endif %}
+
+{% if threads.udp is vyos_defined %}
+{% set _ = args.append("--udpthreads " + threads.udp) %}
+{% endif %}
+{% if threads.udp_affinity is vyos_defined %}
+{% set _ = args.append("--udpaffinity " + threads.udp_affinity) %}
+{% endif %}
+
+[Unit]
+StartLimitIntervalSec=0
+After=vyos-router.service
+
+[Service]
+EnvironmentFile=
+ExecStart=/usr/bin/gwlbtun {{ args | join(' ') }}
+CapabilityBoundingSet=CAP_NET_ADMIN
+Restart=always
+RestartSec=10
diff --git a/data/templates/conntrack/nftables-ct.j2 b/data/templates/conntrack/nftables-ct.j2
index 16a03fc6e..1e0fc8065 100644
--- a/data/templates/conntrack/nftables-ct.j2
+++ b/data/templates/conntrack/nftables-ct.j2
@@ -1,43 +1,91 @@
#!/usr/sbin/nft -f
-{% set nft_ct_ignore_name = 'VYOS_CT_IGNORE' %}
-{% set nft_ct_timeout_name = 'VYOS_CT_TIMEOUT' %}
-
-# we first flush all chains and render the content from scratch - this makes
-# any delta check obsolete
-flush chain raw {{ nft_ct_ignore_name }}
-flush chain raw {{ nft_ct_timeout_name }}
-
-table raw {
- chain {{ nft_ct_ignore_name }} {
-{% if ignore.rule is vyos_defined %}
-{% for rule, rule_config in ignore.rule.items() %}
+{% import 'conntrack/nftables-helpers.j2' as helper_tmpl %}
+{% import 'firewall/nftables-defines.j2' as group_tmpl %}
+
+{% if first_install is not vyos_defined %}
+delete table ip vyos_conntrack
+{% endif %}
+table ip vyos_conntrack {
+ chain VYOS_CT_IGNORE {
+{% if ignore.ipv4.rule is vyos_defined %}
+{% for rule, rule_config in ignore.ipv4.rule.items() %}
+ # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }}
+ {{ rule_config | conntrack_ignore_rule(rule, ipv6=False) }}
+{% endfor %}
+{% endif %}
+ return
+ }
+ chain VYOS_CT_TIMEOUT {
+{% if timeout.custom.rule is vyos_defined %}
+{% for rule, rule_config in timeout.custom.rule.items() %}
+ # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }}
+{% endfor %}
+{% endif %}
+ return
+ }
+
+ chain PREROUTING {
+ type filter hook prerouting priority -300; policy accept;
+{% if ipv4_firewall_action == 'accept' or ipv4_nat_action == 'accept' %}
+ counter jump VYOS_CT_HELPER
+{% endif %}
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump FW_CONNTRACK
+ counter jump NAT_CONNTRACK
+ counter jump WLB_CONNTRACK
+ notrack
+ }
+
+ chain OUTPUT {
+ type filter hook output priority -300; policy accept;
+{% if ipv4_firewall_action == 'accept' or ipv4_nat_action == 'accept' %}
+ counter jump VYOS_CT_HELPER
+{% endif %}
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump FW_CONNTRACK
+ counter jump NAT_CONNTRACK
+{% if wlb_local_action %}
+ counter jump WLB_CONNTRACK
+{% endif %}
+ notrack
+ }
+
+{{ helper_tmpl.conntrack_helpers(module_map, modules, ipv4=True) }}
+
+ chain FW_CONNTRACK {
+ {{ ipv4_firewall_action }}
+ }
+
+ chain NAT_CONNTRACK {
+ {{ ipv4_nat_action }}
+ }
+
+ chain WLB_CONNTRACK {
+ {{ wlb_action }}
+ }
+
+{% if firewall.group is vyos_defined %}
+{{ group_tmpl.groups(firewall.group, False, True) }}
+{% endif %}
+}
+
+{% if first_install is not vyos_defined %}
+delete table ip6 vyos_conntrack
+{% endif %}
+table ip6 vyos_conntrack {
+ chain VYOS_CT_IGNORE {
+{% if ignore.ipv6.rule is vyos_defined %}
+{% for rule, rule_config in ignore.ipv6.rule.items() %}
# rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }}
-{% set nft_command = '' %}
-{% if rule_config.inbound_interface is vyos_defined %}
-{% set nft_command = nft_command ~ ' iifname ' ~ rule_config.inbound_interface %}
-{% endif %}
-{% if rule_config.protocol is vyos_defined %}
-{% set nft_command = nft_command ~ ' ip protocol ' ~ rule_config.protocol %}
-{% endif %}
-{% if rule_config.destination.address is vyos_defined %}
-{% set nft_command = nft_command ~ ' ip daddr ' ~ rule_config.destination.address %}
-{% endif %}
-{% if rule_config.destination.port is vyos_defined %}
-{% set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' dport { ' ~ rule_config.destination.port ~ ' }' %}
-{% endif %}
-{% if rule_config.source.address is vyos_defined %}
-{% set nft_command = nft_command ~ ' ip saddr ' ~ rule_config.source.address %}
-{% endif %}
-{% if rule_config.source.port is vyos_defined %}
-{% set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' sport { ' ~ rule_config.source.port ~ ' }' %}
-{% endif %}
- {{ nft_command }} counter notrack comment ignore-{{ rule }}
+ {{ rule_config | conntrack_ignore_rule(rule, ipv6=True) }}
{% endfor %}
{% endif %}
return
}
- chain {{ nft_ct_timeout_name }} {
+ chain VYOS_CT_TIMEOUT {
{% if timeout.custom.rule is vyos_defined %}
{% for rule, rule_config in timeout.custom.rule.items() %}
# rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }}
@@ -45,4 +93,42 @@ table raw {
{% endif %}
return
}
+
+ chain PREROUTING {
+ type filter hook prerouting priority -300; policy accept;
+{% if ipv6_firewall_action == 'accept' or ipv6_nat_action == 'accept' %}
+ counter jump VYOS_CT_HELPER
+{% endif %}
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump FW_CONNTRACK
+ counter jump NAT_CONNTRACK
+ notrack
+ }
+
+ chain OUTPUT {
+ type filter hook output priority -300; policy accept;
+{% if ipv6_firewall_action == 'accept' or ipv6_nat_action == 'accept' %}
+ counter jump VYOS_CT_HELPER
+{% endif %}
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump FW_CONNTRACK
+ counter jump NAT_CONNTRACK
+ notrack
+ }
+
+{{ helper_tmpl.conntrack_helpers(module_map, modules, ipv4=False) }}
+
+ chain FW_CONNTRACK {
+ {{ ipv6_firewall_action }}
+ }
+
+ chain NAT_CONNTRACK {
+ {{ ipv6_nat_action }}
+ }
+
+{% if firewall.group is vyos_defined %}
+{{ group_tmpl.groups(firewall.group, True, True) }}
+{% endif %}
}
diff --git a/data/templates/conntrack/nftables-helpers.j2 b/data/templates/conntrack/nftables-helpers.j2
new file mode 100644
index 000000000..433931162
--- /dev/null
+++ b/data/templates/conntrack/nftables-helpers.j2
@@ -0,0 +1,70 @@
+{% macro conntrack_helpers(module_map, modules, ipv4=True) %}
+{% if modules.ftp is vyos_defined %}
+ ct helper ftp_tcp {
+ type "ftp" protocol tcp;
+ }
+{% endif %}
+
+{% if modules.h323 is vyos_defined %}
+ ct helper ras_udp {
+ type "RAS" protocol udp;
+ }
+
+ ct helper q931_tcp {
+ type "Q.931" protocol tcp;
+ }
+{% endif %}
+
+{% if modules.pptp is vyos_defined and ipv4 %}
+ ct helper pptp_tcp {
+ type "pptp" protocol tcp;
+ }
+{% endif %}
+
+{% if modules.nfs is vyos_defined %}
+ ct helper rpc_tcp {
+ type "rpc" protocol tcp;
+ }
+
+ ct helper rpc_udp {
+ type "rpc" protocol udp;
+ }
+{% endif %}
+
+{% if modules.sip is vyos_defined %}
+ ct helper sip_tcp {
+ type "sip" protocol tcp;
+ }
+
+ ct helper sip_udp {
+ type "sip" protocol udp;
+ }
+{% endif %}
+
+{% if modules.tftp is vyos_defined %}
+ ct helper tftp_udp {
+ type "tftp" protocol udp;
+ }
+{% endif %}
+
+{% if modules.sqlnet is vyos_defined %}
+ ct helper tns_tcp {
+ type "tns" protocol tcp;
+ }
+{% endif %}
+
+ chain VYOS_CT_HELPER {
+{% for module, module_conf in module_map.items() %}
+{% if modules[module] is vyos_defined %}
+{% if 'nftables' in module_conf %}
+{% if module_conf.ipv4 is not vyos_defined or module_conf.ipv4 == ipv4 %}
+{% for rule in module_conf.nftables %}
+ {{ rule }}
+{% endfor %}
+{% endif %}
+{% endif %}
+{% endif %}
+{% endfor %}
+ return
+ }
+{% endmacro %}
diff --git a/data/templates/conntrack/sysctl.conf.j2 b/data/templates/conntrack/sysctl.conf.j2
index 075402c04..3d6fc43f2 100644
--- a/data/templates/conntrack/sysctl.conf.j2
+++ b/data/templates/conntrack/sysctl.conf.j2
@@ -24,3 +24,4 @@ net.netfilter.nf_conntrack_tcp_timeout_time_wait = {{ timeout.tcp.time_wait }}
net.netfilter.nf_conntrack_udp_timeout = {{ timeout.udp.other }}
net.netfilter.nf_conntrack_udp_timeout_stream = {{ timeout.udp.stream }}
+net.netfilter.nf_conntrack_acct = {{ '1' if flow_accounting is vyos_defined else '0' }}
diff --git a/data/templates/conntrack/vyos_nf_conntrack.conf.j2 b/data/templates/conntrack/vyos_nf_conntrack.conf.j2
index 111459485..197155d96 100644
--- a/data/templates/conntrack/vyos_nf_conntrack.conf.j2
+++ b/data/templates/conntrack/vyos_nf_conntrack.conf.j2
@@ -1,3 +1,2 @@
# Autogenerated by conntrack.py
-options nf_conntrack hashsize={{ hash_size }} nf_conntrack_helper=1
-
+options nf_conntrack hashsize={{ hash_size }}
diff --git a/src/systemd/isc-dhcp-server.service b/data/templates/dhcp-server/10-override.conf.j2
index a7d86e69c..1504b6808 100644
--- a/src/systemd/isc-dhcp-server.service
+++ b/data/templates/dhcp-server/10-override.conf.j2
@@ -1,22 +1,28 @@
+### Autogenerated by dhcp_server.py ###
+{% set lease_file = '/config/dhcpd.leases' %}
[Unit]
Description=ISC DHCP IPv4 server
Documentation=man:dhcpd(8)
RequiresMountsFor=/run
+ConditionPathExists=
ConditionPathExists=/run/dhcp-server/dhcpd.conf
+After=
After=vyos-router.service
[Service]
Type=forking
+WorkingDirectory=
WorkingDirectory=/run/dhcp-server
RuntimeDirectory=dhcp-server
RuntimeDirectoryPreserve=yes
-Environment=PID_FILE=/run/dhcp-server/dhcpd.pid CONFIG_FILE=/run/dhcp-server/dhcpd.conf LEASE_FILE=/config/dhcpd.leases
+Environment=PID_FILE=/run/dhcp-server/dhcpd.pid CONFIG_FILE=/run/dhcp-server/dhcpd.conf LEASE_FILE={{ lease_file }}
PIDFile=/run/dhcp-server/dhcpd.pid
ExecStartPre=/bin/sh -ec '\
touch ${LEASE_FILE}; \
chown dhcpd:vyattacfg ${LEASE_FILE}* ; \
chmod 664 ${LEASE_FILE}* ; \
/usr/sbin/dhcpd -4 -t -T -q -user dhcpd -group vyattacfg -pf ${PID_FILE} -cf ${CONFIG_FILE} -lf ${LEASE_FILE} '
+ExecStart=
ExecStart=/usr/sbin/dhcpd -4 -q -user dhcpd -group vyattacfg -pf ${PID_FILE} -cf ${CONFIG_FILE} -lf ${LEASE_FILE}
Restart=always
diff --git a/data/templates/dns-dynamic/ddclient.conf.j2 b/data/templates/dns-dynamic/ddclient.conf.j2
index 3446a9d1b..6e77abdb5 100644
--- a/data/templates/dns-dynamic/ddclient.conf.j2
+++ b/data/templates/dns-dynamic/ddclient.conf.j2
@@ -14,10 +14,8 @@ if{{ ipv }}={{ address }}, \
{% endif %}
{% endfor %}
{# Other service options #}
-{% for k,v in kwargs.items() %}
-{% if v is vyos_defined %}
-{{ k }}={{ v }}{{ ',' if not loop.last }} \
-{% endif %}
+{% for k,v in kwargs.items() if v is vyos_defined %}
+{{ k | replace('_', '-') }}={{ v }}{{ ',' if not loop.last }} \
{% endfor %}
{# Actual hostname for the service #}
{{ host }}
@@ -28,46 +26,45 @@ syslog=yes
ssl=yes
pid={{ config_file | replace('.conf', '.pid') }}
cache={{ config_file | replace('.conf', '.cache') }}
-{# Explicitly override global options for reliability #}
-web=googledomains {# ddclient default ('dyndns') doesn't support ssl and results in process lockup #}
-use=no {# ddclient default ('ip') results in confusing warning message in log #}
+{# ddclient default (web=dyndns) doesn't support ssl and results in process lockup #}
+web=googledomains
+{# ddclient default (use=ip) results in confusing warning message in log #}
+use=no
{% if address is vyos_defined %}
{% for address, service_cfg in address.items() %}
{% if service_cfg.rfc2136 is vyos_defined %}
{% for name, config in service_cfg.rfc2136.items() %}
{% if config.description is vyos_defined %}
-# {{ config.description }}
+# {{ config.description }}
{% endif %}
{% for host in config.host_name if config.host_name is vyos_defined %}
+
# RFC2136 dynamic DNS configuration for {{ name }}: [{{ config.zone }}, {{ host }}]
{# Don't append 'new-style' compliant suffix ('usev4', 'usev6', 'ifv4', 'ifv6' etc.)
to the properties since 'nsupdate' doesn't support that yet. #}
{{ render_config(host, address, service_cfg.web_options,
protocol='nsupdate', server=config.server, zone=config.zone,
password=config.key, ttl=config.ttl) }}
-
{% endfor %}
{% endfor %}
{% endif %}
{% if service_cfg.service is vyos_defined %}
{% for name, config in service_cfg.service.items() %}
{% if config.description is vyos_defined %}
-# {{ config.description }}
+# {{ config.description }}
{% endif %}
{% for host in config.host_name if config.host_name is vyos_defined %}
{% set ip_suffixes = ['v4', 'v6'] if config.ip_version == 'both'
- else (['v6'] if config.ip_version == 'ipv6' else ['']) %}
+ else [config.ip_version[2:]] %}
+
# Web service dynamic DNS configuration for {{ name }}: [{{ config.protocol }}, {{ host }}]
-{# For ipv4 only setup or legacy ipv6 setup, don't append 'new-style' compliant suffix
- ('usev4', 'ifv4', 'webv4' etc.) to the properties and instead live through the
- deprecation warnings for better compatibility with most ddclient protocols. #}
{{ render_config(host, address, service_cfg.web_options, ip_suffixes,
protocol=config.protocol, server=config.server, zone=config.zone,
- login=config.username, password=config.password) }}
-
+ login=config.username, password=config.password, ttl=config.ttl,
+ min_interval=config.wait_time, max_interval=config.expiry_time) }}
{% endfor %}
{% endfor %}
{% endif %}
diff --git a/data/templates/dns-dynamic/override.conf.j2 b/data/templates/dns-dynamic/override.conf.j2
index 6ca1b8a45..4a6851cef 100644
--- a/data/templates/dns-dynamic/override.conf.j2
+++ b/data/templates/dns-dynamic/override.conf.j2
@@ -7,4 +7,4 @@ After=vyos-router.service
PIDFile={{ config_file | replace('.conf', '.pid') }}
EnvironmentFile=
ExecStart=
-ExecStart=/usr/bin/ddclient -file {{ config_file }}
+ExecStart={{ vrf_command }}/usr/bin/ddclient -file {{ config_file }}
diff --git a/data/templates/firewall/nftables-bridge.j2 b/data/templates/firewall/nftables-bridge.j2
new file mode 100644
index 000000000..7f94e10d6
--- /dev/null
+++ b/data/templates/firewall/nftables-bridge.j2
@@ -0,0 +1,35 @@
+{% macro bridge(bridge) %}
+{% set ns = namespace(sets=[]) %}
+{% if bridge.forward is vyos_defined %}
+{% for prior, conf in bridge.forward.items() %}
+{% set def_action = conf.default_action %}
+ chain VYOS_FORWARD_{{ prior }} {
+ type filter hook forward priority {{ prior }}; policy {{ def_action }};
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('FWD', prior, rule_id, 'bri') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ }
+{% endfor %}
+{% endif %}
+
+{% if bridge.name is vyos_defined %}
+{% for name_text, conf in bridge.name.items() %}
+ chain NAME_{{ name_text }} {
+{% if conf.rule is vyos_defined %}
+{% for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
+ {{ rule_conf | nft_rule('NAM', name_text, rule_id, 'bri') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ {{ conf | nft_default_rule(name_text) }}
+ }
+{% endfor %}
+{% endif %}
+{% endmacro %}
diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2
index 0a7e79edd..a20c399ae 100644
--- a/data/templates/firewall/nftables-defines.j2
+++ b/data/templates/firewall/nftables-defines.j2
@@ -1,7 +1,7 @@
-{% macro groups(group, is_ipv6) %}
+{% macro groups(group, is_ipv6, is_l3) %}
{% if group is vyos_defined %}
{% set ip_type = 'ipv6_addr' if is_ipv6 else 'ipv4_addr' %}
-{% if group.address_group is vyos_defined and not is_ipv6 %}
+{% if group.address_group is vyos_defined and not is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.address_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set A_{{ group_name }} {
@@ -14,7 +14,7 @@
}
{% endfor %}
{% endif %}
-{% if group.ipv6_address_group is vyos_defined and is_ipv6 %}
+{% if group.ipv6_address_group is vyos_defined and is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.ipv6_address_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set A6_{{ group_name }} {
@@ -27,7 +27,7 @@
}
{% endfor %}
{% endif %}
-{% if group.domain_group is vyos_defined %}
+{% if group.domain_group is vyos_defined and is_l3 %}
{% for name, name_config in group.domain_group.items() %}
set D_{{ name }} {
type {{ ip_type }}
@@ -46,7 +46,7 @@
}
{% endfor %}
{% endif %}
-{% if group.network_group is vyos_defined and not is_ipv6 %}
+{% if group.network_group is vyos_defined and not is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.network_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set N_{{ group_name }} {
@@ -59,7 +59,7 @@
}
{% endfor %}
{% endif %}
-{% if group.ipv6_network_group is vyos_defined and is_ipv6 %}
+{% if group.ipv6_network_group is vyos_defined and is_ipv6 and is_l3 %}
{% for group_name, group_conf in group.ipv6_network_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set N6_{{ group_name }} {
@@ -72,7 +72,7 @@
}
{% endfor %}
{% endif %}
-{% if group.port_group is vyos_defined %}
+{% if group.port_group is vyos_defined and is_l3 %}
{% for group_name, group_conf in group.port_group.items() %}
{% set includes = group_conf.include if group_conf.include is vyos_defined else [] %}
set P_{{ group_name }} {
diff --git a/data/templates/firewall/nftables-nat.j2 b/data/templates/firewall/nftables-nat.j2
index f0be3cf5d..4254f6a0e 100644
--- a/data/templates/firewall/nftables-nat.j2
+++ b/data/templates/firewall/nftables-nat.j2
@@ -2,27 +2,6 @@
{% import 'firewall/nftables-defines.j2' as group_tmpl %}
-{% 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' %}
-{{ base_command }} PREROUTING handle {{ pre_ct_ignore }}
-{{ base_command }} OUTPUT handle {{ out_ct_ignore }}
-{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
-{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
-
-delete chain ip raw NAT_CONNTRACK
-
-{% elif helper_functions is vyos_defined('add') %}
-{# NAT if enabled - add targets to nftables #}
-add chain ip raw NAT_CONNTRACK
-add rule ip raw NAT_CONNTRACK counter accept
-{% set base_command = 'add rule ip raw' %}
-{{ base_command }} PREROUTING position {{ pre_ct_ignore }} counter jump VYOS_CT_HELPER
-{{ base_command }} OUTPUT position {{ out_ct_ignore }} counter jump VYOS_CT_HELPER
-{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
-{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
-{% endif %}
-
{% if first_install is not vyos_defined %}
delete table ip vyos_nat
{% endif %}
@@ -62,6 +41,6 @@ table ip vyos_nat {
return
}
-{{ group_tmpl.groups(firewall_group, False) }}
+{{ group_tmpl.groups(firewall_group, False, True) }}
}
{% endif %}
diff --git a/data/templates/firewall/nftables-nat66.j2 b/data/templates/firewall/nftables-nat66.j2
index 27b3eec88..67eb2c109 100644
--- a/data/templates/firewall/nftables-nat66.j2
+++ b/data/templates/firewall/nftables-nat66.j2
@@ -1,22 +1,5 @@
#!/usr/sbin/nft -f
-{% 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' %}
-{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
-{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
-
-delete chain ip6 raw NAT_CONNTRACK
-
-{% elif helper_functions is vyos_defined('add') %}
-{# NAT if enabled - add targets to nftables #}
-add chain ip6 raw NAT_CONNTRACK
-add rule ip6 raw NAT_CONNTRACK counter accept
-{% set base_command = 'add rule ip6 raw' %}
-{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
-{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
-{% endif %}
-
{% if first_install is not vyos_defined %}
delete table ip6 vyos_nat
{% endif %}
diff --git a/data/templates/firewall/nftables-offload.j2 b/data/templates/firewall/nftables-offload.j2
new file mode 100644
index 000000000..a893e05b2
--- /dev/null
+++ b/data/templates/firewall/nftables-offload.j2
@@ -0,0 +1,9 @@
+{% macro flowtable(name, config) %}
+ flowtable VYOS_FLOWTABLE_{{ name }} {
+ hook ingress priority 0; devices = { {{ config.interface | join(', ') }} };
+{% if config.offload is vyos_defined('hardware') %}
+ flags offload;
+{% endif %}
+ counter
+ }
+{% endmacro %}
diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2
index 699349e2b..d77e3f6e9 100644
--- a/data/templates/firewall/nftables-policy.j2
+++ b/data/templates/firewall/nftables-policy.j2
@@ -32,7 +32,7 @@ table ip vyos_mangle {
{% endfor %}
{% endif %}
-{{ group_tmpl.groups(firewall_group, False) }}
+{{ group_tmpl.groups(firewall_group, False, True) }}
}
table ip6 vyos_mangle {
@@ -61,5 +61,5 @@ table ip6 vyos_mangle {
{% endfor %}
{% endif %}
-{{ group_tmpl.groups(firewall_group, True) }}
+{{ group_tmpl.groups(firewall_group, True, True) }}
}
diff --git a/data/templates/firewall/nftables-vrf-zones.j2 b/data/templates/firewall/nftables-vrf-zones.j2
index eecf47b78..3bce7312d 100644
--- a/data/templates/firewall/nftables-vrf-zones.j2
+++ b/data/templates/firewall/nftables-vrf-zones.j2
@@ -7,11 +7,11 @@ table inet vrf_zones {
# Chain for inbound traffic
chain vrf_zones_ct_in {
type filter hook prerouting priority raw; policy accept;
- counter ct zone set iifname map @ct_iface_map
+ counter ct original zone set iifname map @ct_iface_map
}
# Chain for locally-generated traffic
chain vrf_zones_ct_out {
type filter hook output priority raw; policy accept;
- counter ct zone set oifname map @ct_iface_map
+ counter ct original zone set oifname map @ct_iface_map
}
}
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 0fbddfaa9..75800ee3d 100644
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -1,33 +1,31 @@
#!/usr/sbin/nft -f
{% import 'firewall/nftables-defines.j2' as group_tmpl %}
+{% import 'firewall/nftables-bridge.j2' as bridge_tmpl %}
+{% import 'firewall/nftables-offload.j2' as offload_tmpl %}
-flush chain raw FW_CONNTRACK
-flush chain ip6 raw FW_CONNTRACK
+flush chain raw vyos_global_rpfilter
+flush chain ip6 raw vyos_global_rpfilter
table raw {
- chain FW_CONNTRACK {
- {{ ipv4_conntrack_action }}
+ chain vyos_global_rpfilter {
+{% if global_options.source_validation is vyos_defined('loose') %}
+ fib saddr oif 0 counter drop
+{% elif global_options.source_validation is vyos_defined('strict') %}
+ fib saddr . iif oif 0 counter drop
+{% endif %}
+ return
}
}
table ip6 raw {
- chain FW_CONNTRACK {
- {{ ipv6_conntrack_action }}
- }
-}
-
-{% if first_install is not vyos_defined %}
-delete table inet vyos_global_rpfilter
-{% endif %}
-table inet vyos_global_rpfilter {
- chain PREROUTING {
- type filter hook prerouting priority -300; policy accept;
-{% if global_options.source_validation is vyos_defined('loose') %}
+ chain vyos_global_rpfilter {
+{% if global_options.ipv6_source_validation is vyos_defined('loose') %}
fib saddr oif 0 counter drop
-{% elif global_options.source_validation is vyos_defined('strict') %}
+{% elif global_options.ipv6_source_validation is vyos_defined('strict') %}
fib saddr . iif oif 0 counter drop
{% endif %}
+ return
}
}
@@ -36,6 +34,12 @@ delete table ip vyos_filter
{% endif %}
table ip vyos_filter {
{% if ipv4 is vyos_defined %}
+{% if flowtable is vyos_defined %}
+{% for name, flowtable_conf in flowtable.items() %}
+{{ offload_tmpl.flowtable(name, flowtable_conf) }}
+{% endfor %}
+{% endif %}
+
{% set ns = namespace(sets=[]) %}
{% if ipv4.forward is vyos_defined %}
{% for prior, conf in ipv4.forward.items() %}
@@ -147,7 +151,7 @@ table ip vyos_filter {
{% endfor %}
{% endif %}
{% endif %}
-{{ group_tmpl.groups(group, False) }}
+{{ group_tmpl.groups(group, False, True) }}
}
{% if first_install is not vyos_defined %}
@@ -155,6 +159,12 @@ delete table ip6 vyos_filter
{% endif %}
table ip6 vyos_filter {
{% if ipv6 is vyos_defined %}
+{% if flowtable is vyos_defined %}
+{% for name, flowtable_conf in flowtable.items() %}
+{{ offload_tmpl.flowtable(name, flowtable_conf) }}
+{% endfor %}
+{% endif %}
+
{% set ns = namespace(sets=[]) %}
{% if ipv6.forward is vyos_defined %}
{% for prior, conf in ipv6.forward.items() %}
@@ -250,5 +260,14 @@ table ip6 vyos_filter {
{% endfor %}
{% endif %}
{% endif %}
-{{ group_tmpl.groups(group, True) }}
-} \ No newline at end of file
+{{ group_tmpl.groups(group, True, True) }}
+}
+
+## Bridge Firewall
+{% if first_install is not vyos_defined %}
+delete table bridge vyos_filter
+{% endif %}
+table bridge vyos_filter {
+{{ bridge_tmpl.bridge(bridge) }}
+{{ group_tmpl.groups(group, False, False) }}
+}
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index 7fa974254..d724dbd79 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -29,13 +29,14 @@
neighbor {{ neighbor }} bfd profile {{ config.bfd.profile }}
{% endif %}
{% endif %}
-{% if config.capability is vyos_defined %}
-{% if config.capability.dynamic is vyos_defined %}
+{% if config.capability.dynamic is vyos_defined %}
neighbor {{ neighbor }} capability dynamic
-{% endif %}
-{% if config.capability.extended_nexthop is vyos_defined %}
+{% endif %}
+{% if config.capability.extended_nexthop is vyos_defined %}
neighbor {{ neighbor }} capability extended-nexthop
-{% endif %}
+{% endif %}
+{% if config.capability.software_version is vyos_defined %}
+ neighbor {{ neighbor }} capability software-version
{% endif %}
{% if config.description is vyos_defined %}
neighbor {{ neighbor }} description {{ config.description }}
@@ -77,6 +78,9 @@
{% if config.path_attribute.discard is vyos_defined %}
neighbor {{ neighbor }} path-attribute discard {{ config.path_attribute.discard }}
{% endif %}
+{% if config.path_attribute.treat_as_withdraw is vyos_defined %}
+ neighbor {{ neighbor }} path-attribute treat-as-withdraw {{ config.path_attribute.treat_as_withdraw }}
+{% endif %}
{% if config.port is vyos_defined %}
neighbor {{ neighbor }} port {{ config.port }}
{% endif %}
@@ -170,7 +174,7 @@
{% endif %}
{% endif %}
{% if afi_config.remove_private_as is vyos_defined %}
- neighbor {{ neighbor }} remove-private-AS
+ neighbor {{ neighbor }} remove-private-AS {{ 'all' if afi_config.remove_private_as.all is vyos_defined }}
{% endif %}
{% if afi_config.route_reflector_client is vyos_defined %}
neighbor {{ neighbor }} route-reflector-client
diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl
index 3aad8e8dd..a65f0868a 100644
--- a/data/templates/frr/daemons.frr.tmpl
+++ b/data/templates/frr/daemons.frr.tmpl
@@ -1,4 +1,26 @@
-zebra=yes
+#
+# The watchfrr, zebra, mgmtd and staticd daemons are always started.
+#
+# Note: The following FRR-services must be kept disabled because they are replaced by other packages in VyOS:
+#
+# pimd Replaced by package igmpproxy.
+# nhrpd Replaced by package opennhrp.
+# pbrd Replaced by PBR in nftables.
+# vrrpd Replaced by package keepalived.
+#
+# And these must be disabled aswell since they are currently missing a VyOS CLI:
+#
+# eigrp
+# sharpd
+# fabricd
+# pathd
+#
+# The zebra, mgmtd and staticd daemons are always started and can not be disabled
+#
+#zebra=yes
+#mgmtd=yes
+#staticd=yes
+
bgpd=yes
ospfd=yes
ospf6d=yes
@@ -6,49 +28,87 @@ ripd=yes
ripngd=yes
isisd=yes
pimd=no
+pim6d=yes
ldpd=yes
nhrpd=no
-eigrpd=yes
+eigrpd=no
babeld=yes
sharpd=no
pbrd=no
bfdd=yes
-staticd=yes
+fabricd=no
+vrrpd=no
+pathd=no
-vtysh_enable=yes
-zebra_options=" -s 90000000 --daemon -A 127.0.0.1
-{%- if irdp is defined %} -M irdp{% endif -%}
-{%- if snmp is defined and snmp.zebra is defined %} -M snmp{% endif -%}
-"
-bgpd_options=" --daemon -A 127.0.0.1
-{%- if bmp is defined %} -M bmp{% endif -%}
-{%- if snmp is defined and snmp.bgpd is defined %} -M snmp{% endif -%}
-"
-ospfd_options=" --daemon -A 127.0.0.1
-{%- if snmp is defined and snmp.ospfd is defined %} -M snmp{% endif -%}
-"
-ospf6d_options=" --daemon -A ::1
-{%- if snmp is defined and snmp.ospf6d is defined %} -M snmp{% endif -%}
-"
-ripd_options=" --daemon -A 127.0.0.1
-{%- if snmp is defined and snmp.ripd is defined %} -M snmp{% endif -%}
-"
+#
+# Define defaults for all services even those who shall be kept disabled.
+#
+
+zebra_options=" --daemon -A 127.0.0.1 -s 90000000{{ ' -M snmp' if snmp.zebra is vyos_defined }}{{ ' -M irdp' if irdp is vyos_defined }}"
+mgmtd_options=" --daemon -A 127.0.0.1"
+staticd_options="--daemon -A 127.0.0.1"
+bgpd_options=" --daemon -A 127.0.0.1 -M rpki{{ ' -M snmp' if snmp.bgpd is vyos_defined }}{{ ' -M bmp' if bmp is vyos_defined }}"
+ospfd_options=" --daemon -A 127.0.0.1{{ ' -M snmp' if snmp.ospfd is vyos_defined }}"
+ospf6d_options=" --daemon -A ::1{{ ' -M snmp' if snmp.ospf6d is vyos_defined }}"
+ripd_options=" --daemon -A 127.0.0.1{{ ' -M snmp' if snmp.ripd is vyos_defined }}"
ripngd_options=" --daemon -A ::1"
-isisd_options=" --daemon -A 127.0.0.1
-{%- if snmp is defined and snmp.isisd is defined %} -M snmp{% endif -%}
-"
-pimd_options=" --daemon -A 127.0.0.1"
-ldpd_options=" --daemon -A 127.0.0.1
-{%- if snmp is defined and snmp.ldpd is defined %} -M snmp{% endif -%}
-"
-mgmtd_options=" --daemon -A 127.0.0.1"
+isisd_options=" --daemon -A 127.0.0.1{{ ' -M snmp' if snmp.isisd is vyos_defined }}"
+pimd_options=" --daemon -A 127.0.0.1"
+pim6d_options=" --daemon -A ::1"
+ldpd_options=" --daemon -A 127.0.0.1{{ ' -M snmp' if snmp.ldpd is vyos_defined }}"
nhrpd_options=" --daemon -A 127.0.0.1"
-eigrpd_options=" --daemon -A 127.0.0.1"
-babeld_options=" --daemon -A 127.0.0.1"
-sharpd_options=" --daemon -A 127.0.0.1"
-pbrd_options=" --daemon -A 127.0.0.1"
-staticd_options=" --daemon -A 127.0.0.1"
-bfdd_options=" --daemon -A 127.0.0.1"
+eigrpd_options=" --daemon -A 127.0.0.1"
+babeld_options=" --daemon -A 127.0.0.1"
+sharpd_options=" --daemon -A 127.0.0.1"
+pbrd_options=" --daemon -A 127.0.0.1"
+bfdd_options=" --daemon -A 127.0.0.1"
+fabricd_options="--daemon -A 127.0.0.1"
+vrrpd_options=" --daemon -A 127.0.0.1"
+pathd_options=" --daemon -A 127.0.0.1"
+
+#frr_global_options=""
+
+#zebra_wrap=""
+#mgmtd_wrap=""
+#staticd_wrap=""
+#bgpd_wrap=""
+#ospfd_wrap=""
+#ospf6d_wrap=""
+#ripd_wrap=""
+#ripngd_wrap=""
+#isisd_wrap=""
+#pimd_wrap=""
+#pim6d_wrap=""
+#ldpd_wrap=""
+#nhrpd_wrap=""
+#eigrpd_wrap=""
+#babeld_wrap=""
+#sharpd_wrap=""
+#pbrd_wrap=""
+#bfdd_wrap=""
+#fabricd_wrap=""
+#vrrpd_wrap=""
+#pathd_wrap=""
+
+#all_wrap=""
+
+#
+# Other options.
+#
+# For more information see:
+# https://github.com/FRRouting/frr/blob/stable/9.0/tools/etc/frr/daemons
+# https://docs.frrouting.org/en/stable-9.0/setup.html
+#
+vtysh_enable=yes
watchfrr_enable=no
valgrind_enable=no
+
+#watchfrr_options=""
+
+frr_profile="traditional"
+
+#MAX_FDS=1024
+
+#FRR_NO_ROOT="yes"
+
diff --git a/data/templates/frr/igmp.frr.j2 b/data/templates/frr/igmp.frr.j2
index ce1f8fdda..b75884484 100644
--- a/data/templates/frr/igmp.frr.j2
+++ b/data/templates/frr/igmp.frr.j2
@@ -27,9 +27,9 @@ interface {{ interface }}
{% if interface_config.query_max_resp_time %}
ip igmp query-max-response-time {{ interface_config.query_max_resp_time }}
{% endif %}
-{% for group in interface_config.gr_join %}
-{% if ifaces[iface].gr_join[group] %}
-{% for source in ifaces[iface].gr_join[group] %}
+{% for group, sources in interface_config.gr_join.items() %}
+{% if sources is vyos_defined %}
+{% for source in sources %}
ip igmp join {{ group }} {{ source }}
{% endfor %}
{% else %}
diff --git a/data/templates/frr/isisd.frr.j2 b/data/templates/frr/isisd.frr.j2
index e1041f01e..1e1cc3c27 100644
--- a/data/templates/frr/isisd.frr.j2
+++ b/data/templates/frr/isisd.frr.j2
@@ -58,6 +58,12 @@ exit
!
router isis VyOS {{ 'vrf ' + vrf if vrf is vyos_defined }}
net {{ net }}
+{% if advertise_high_metrics is vyos_defined %}
+advertise-high-metrics
+{% endif %}
+{% if advertise_passive_only is vyos_defined %}
+advertise-passive-only
+{% endif %}
{% if dynamic_hostname is vyos_defined %}
hostname dynamic
{% endif %}
@@ -233,4 +239,4 @@ fast-reroute remote-lfa prefix-list {{ prefix_list }}
is-type {{ level }}
{% endif %}
exit
-! \ No newline at end of file
+!
diff --git a/data/templates/frr/pim6d.frr.j2 b/data/templates/frr/pim6d.frr.j2
new file mode 100644
index 000000000..8e430541d
--- /dev/null
+++ b/data/templates/frr/pim6d.frr.j2
@@ -0,0 +1,38 @@
+!
+{% if interface is vyos_defined %}
+{% for iface, iface_config in interface.items() %}
+interface {{ iface }}
+{% if iface_config.mld is vyos_defined and iface_config.mld.disable is not vyos_defined %}
+ ipv6 mld
+{% if iface_config.mld.version is vyos_defined %}
+ ipv6 mld version {{ iface_config.mld.version }}
+{% endif %}
+{% if iface_config.mld.interval is vyos_defined %}
+ ipv6 mld query-interval {{ iface_config.mld.interval }}
+{% endif %}
+{% if iface_config.mld.max_response_time is vyos_defined %}
+ ipv6 mld query-max-response-time {{ iface_config.mld.max_response_time // 100 }}
+{% endif %}
+{% if iface_config.mld.last_member_query_count is vyos_defined %}
+ ipv6 mld last-member-query-count {{ iface_config.mld.last_member_query_count }}
+{% endif %}
+{% if iface_config.mld.last_member_query_interval is vyos_defined %}
+ ipv6 mld last-member-query-interval {{ iface_config.mld.last_member_query_interval // 100 }}
+{% endif %}
+{% if iface_config.mld.join is vyos_defined %}
+{% for group, group_config in iface_config.mld.join.items() %}
+{% if group_config.source is vyos_defined %}
+{% for source in group_config.source %}
+ ipv6 mld join {{ group }} {{ source }}
+{% endfor %}
+{% else %}
+ ipv6 mld join {{ group }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
+exit
+!
+{% endfor %}
+!
+{% endif %}
diff --git a/src/etc/systemd/system/keepalived.service.d/override.conf b/data/templates/high-availability/10-override.conf.j2
index d91a824b9..c153f09b4 100644
--- a/src/etc/systemd/system/keepalived.service.d/override.conf
+++ b/data/templates/high-availability/10-override.conf.j2
@@ -1,3 +1,5 @@
+### Autogenerated by ${vyos_conf_scripts_dir}/high-availability.py ###
+{% set snmp = '--snmp' if vrrp.snmp is vyos_defined else '' %}
[Unit]
After=vyos-router.service
# Only start if there is our configuration file - remove Debian default
@@ -10,5 +12,5 @@ KillMode=process
Type=simple
# Read configuration variable file if it is present
ExecStart=
-ExecStart=/usr/sbin/keepalived --use-file /run/keepalived/keepalived.conf --pid /run/keepalived/keepalived.pid --dont-fork --snmp
+ExecStart=/usr/sbin/keepalived --use-file /run/keepalived/keepalived.conf --pid /run/keepalived/keepalived.pid --dont-fork {{ snmp }}
PIDFile=/run/keepalived/keepalived.pid
diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2
index f8e1587f8..a75ee9904 100644
--- a/data/templates/load-balancing/haproxy.cfg.j2
+++ b/data/templates/load-balancing/haproxy.cfg.j2
@@ -146,17 +146,17 @@ backend {{ back }}
{% if back_config.server is vyos_defined %}
{% set ssl_back = 'ssl ca-file /run/haproxy/' ~ back_config.ssl.ca_certificate ~ '.pem' if back_config.ssl.ca_certificate is vyos_defined else '' %}
{% for server, server_config in back_config.server.items() %}
- server {{ server }} {{ server_config.address }}:{{ server_config.port }}{{ ' check' if server_config.check is vyos_defined }}{{ ' send-proxy' if server_config.send_proxy is vyos_defined }}{{ ' send-proxy-v2' if server_config.send_proxy_v2 is vyos_defined }} {{ ssl_back }}
+ server {{ server }} {{ server_config.address }}:{{ server_config.port }}{{ ' check' if server_config.check is vyos_defined }}{{ ' backup' if server_config.backup is vyos_defined }}{{ ' send-proxy' if server_config.send_proxy is vyos_defined }}{{ ' send-proxy-v2' if server_config.send_proxy_v2 is vyos_defined }} {{ ssl_back }}
{% endfor %}
{% endif %}
{% if back_config.timeout.check is vyos_defined %}
- timeout check {{ back_config.timeout.check }}
+ timeout check {{ back_config.timeout.check }}s
{% endif %}
{% if back_config.timeout.connect is vyos_defined %}
- timeout connect {{ back_config.timeout.connect }}
+ timeout connect {{ back_config.timeout.connect }}s
{% endif %}
{% if back_config.timeout.server is vyos_defined %}
- timeout server {{ back_config.timeout.server }}
+ timeout server {{ back_config.timeout.server }}s
{% endif %}
{% endfor %}
diff --git a/data/templates/mdns-repeater/avahi-daemon.j2 b/data/templates/mdns-repeater/avahi-daemon.conf.j2
index e0dfd897e..d562c048f 100644
--- a/data/templates/mdns-repeater/avahi-daemon.j2
+++ b/data/templates/mdns-repeater/avahi-daemon.conf.j2
@@ -1,7 +1,7 @@
### Autogenerated by service_mdns-repeater.py ###
[server]
-use-ipv4=yes
-use-ipv6=yes
+use-ipv4={{ 'yes' if ip_version in ['ipv4', 'both'] else 'no' }}
+use-ipv6={{ 'yes' if ip_version in ['ipv6', 'both'] else 'no' }}
allow-interfaces={{ interface | join(', ') }}
{% if browse_domain is vyos_defined and browse_domain | length %}
browse-domains={{ browse_domain | join(', ') }}
@@ -17,6 +17,8 @@ disable-user-service-publishing=yes
publish-addresses=no
publish-hinfo=no
publish-workstation=no
+publish-aaaa-on-ipv4=no
+publish-a-on-ipv6=no
[reflector]
enable-reflector=yes
diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2
index f76fbbe79..2eb9416fe 100644
--- a/data/templates/openvpn/server.conf.j2
+++ b/data/templates/openvpn/server.conf.j2
@@ -74,7 +74,7 @@ topology {{ server.topology }}
{% endif %}
{% for subnet in server.subnet %}
{% if subnet | is_ipv4 %}
-server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool
+server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} {{ 'nopool' if server.client_ip_pool is vyos_defined and server.client_ip_pool.disable is not vyos_defined else '' }}
{# First ip address is used as gateway. It's allows to use metrics #}
{% if server.push_route is vyos_defined %}
{% for route, route_config in server.push_route.items() %}
@@ -85,15 +85,6 @@ push "route-ipv6 {{ route }}"
{% endif %}
{% endfor %}
{% endif %}
-{# OpenVPN assigns the first IP address to its local interface so the pool used #}
-{# in net30 topology - where each client receives a /30 must start from the second subnet #}
-{% if server.topology is vyos_defined('net30') %}
-ifconfig-pool {{ subnet | inc_ip('4') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tap' else '' }}
-{% else %}
-{# OpenVPN assigns the first IP address to its local interface so the pool must #}
-{# start from the second address and end on the last address #}
-ifconfig-pool {{ subnet | first_host_address | inc_ip('1') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tun' else '' }}
-{% endif %}
{% elif subnet | is_ipv6 %}
server-ipv6 {{ subnet }}
{% endif %}
diff --git a/data/templates/pppoe/peer.j2 b/data/templates/pppoe/peer.j2
index f30cefe63..2a99fcb2a 100644
--- a/data/templates/pppoe/peer.j2
+++ b/data/templates/pppoe/peer.j2
@@ -50,7 +50,7 @@ ifname {{ ifname }}
ipparam {{ ifname }}
debug
mtu {{ mtu }}
-mru {{ mtu }}
+mru {{ mru }}
{% if authentication is vyos_defined %}
{{ 'user "' + authentication.username + '"' if authentication.username is vyos_defined }}
diff --git a/data/templates/wifi/hostapd.conf.j2 b/data/templates/wifi/hostapd.conf.j2
index 613038597..c3f32da72 100644
--- a/data/templates/wifi/hostapd.conf.j2
+++ b/data/templates/wifi/hostapd.conf.j2
@@ -340,6 +340,11 @@ vht_oper_chwidth={{ capabilities.vht.channel_set_width }}
{% endif %}
{% set output = namespace(value='') %}
+{% if capabilities.vht.channel_set_width is vyos_defined('2') %}
+{% set output.value = output.value ~ '[VHT160]' %}
+{% elif capabilities.vht.channel_set_width is vyos_defined('3') %}
+{% set output.value = output.value ~ '[VHT160-80PLUS80]' %}
+{% endif %}
{% if capabilities.vht.stbc.tx is vyos_defined %}
{% set output.value = output.value ~ '[TX-STBC-2BY1]' %}
{% endif %}
@@ -363,30 +368,21 @@ vht_oper_chwidth={{ capabilities.vht.channel_set_width }}
{% endif %}
{% if capabilities.vht.max_mpdu_exp is vyos_defined %}
{% set output.value = output.value ~ '[MAX-A-MPDU-LEN-EXP-' ~ capabilities.vht.max_mpdu_exp ~ ']' %}
-{% if capabilities.vht.max_mpdu_exp is vyos_defined('2') %}
-{% set output.value = output.value ~ '[VHT160]' %}
-{% endif %}
-{% if capabilities.vht.max_mpdu_exp is vyos_defined('3') %}
-{% set output.value = output.value ~ '[VHT160-80PLUS80]' %}
-{% endif %}
{% endif %}
{% if capabilities.vht.link_adaptation is vyos_defined('unsolicited') %}
{% set output.value = output.value ~ '[VHT-LINK-ADAPT2]' %}
{% elif capabilities.vht.link_adaptation is vyos_defined('both') %}
{% set output.value = output.value ~ '[VHT-LINK-ADAPT3]' %}
{% endif %}
-
{% for short_gi in capabilities.vht.short_gi if capabilities.vht.short_gi is vyos_defined %}
{% set output.value = output.value ~ '[SHORT-GI-' ~ short_gi | upper ~ ']' %}
{% endfor %}
-
{% for beamform in capabilities.vht.beamform if capabilities.vht.beamform is vyos_defined %}
{% set output.value = output.value ~ '[SU-BEAMFORMER]' if beamform is vyos_defined('single-user-beamformer') else '' %}
{% set output.value = output.value ~ '[SU-BEAMFORMEE]' if beamform is vyos_defined('single-user-beamformee') else '' %}
{% set output.value = output.value ~ '[MU-BEAMFORMER]' if beamform is vyos_defined('multi-user-beamformer') else '' %}
{% set output.value = output.value ~ '[MU-BEAMFORMEE]' if beamform is vyos_defined('multi-user-beamformee') else '' %}
{% endfor %}
-
{% if capabilities.vht.antenna_count is vyos_defined and capabilities.vht.antenna_count | int > 1 %}
{% if capabilities.vht.beamform is vyos_defined %}
{% if capabilities.vht.beamform == 'single-user-beamformer' %}
diff --git a/data/vyos-firewall-init.conf b/data/vyos-firewall-init.conf
index 41e7627f5..cd7d5011f 100644
--- a/data/vyos-firewall-init.conf
+++ b/data/vyos-firewall-init.conf
@@ -9,6 +9,7 @@ table ip nat {
}
table inet mangle {
+ # Used by system flow-accounting
chain FORWARD {
type filter hook forward priority -150; policy accept;
}
@@ -19,61 +20,18 @@ table raw {
type filter hook forward priority -300; policy accept;
}
- chain PREROUTING {
- type filter hook prerouting priority -300; 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 -300; 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 {
+ chain vyos_global_rpfilter {
return
}
- chain VYOS_CT_OUTPUT_HOOK {
- return
+ chain vyos_rpfilter {
+ type filter hook prerouting priority -300; policy accept;
+ counter jump vyos_global_rpfilter
}
- chain FW_CONNTRACK {
- return
+ # Used by system flow-accounting
+ chain VYOS_PREROUTING_HOOK {
+ type filter hook prerouting priority -300; policy accept;
}
}
@@ -82,33 +40,17 @@ table ip6 raw {
type filter hook forward priority -300; policy accept;
}
- chain vyos_rpfilter {
- type filter hook prerouting 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 {
+ chain vyos_global_rpfilter {
return
}
- chain VYOS_CT_OUTPUT_HOOK {
- return
+ chain vyos_rpfilter {
+ type filter hook prerouting priority -300; policy accept;
+ counter jump vyos_global_rpfilter
}
- chain FW_CONNTRACK {
- return
+ # Used by system flow-accounting
+ chain VYOS_PREROUTING_HOOK {
+ type filter hook prerouting priority -300; policy accept;
}
}
diff --git a/debian/changelog b/debian/changelog
index c9d925253..d64c66818 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-vyos-1x (1.4dev0) unstable; urgency=medium
+vyos-1x (1.5dev0) unstable; urgency=medium
* Dummy changelog entry for vyos-1x repository
This is a internal VyOS package and the VyOS package process does not use
@@ -7,4 +7,4 @@ vyos-1x (1.4dev0) unstable; urgency=medium
The correct verion number of this package is auto-generated by GIT
on build-time
- -- VyOS maintainers and contributors <maintainers@vyos.io> Mon, 11 Jan 2021 19:02:53 +0100
+ -- VyOS maintainers and contributors <maintainers@vyos.io> Sun, 10 Sep 2023 15:42:53 +0200
diff --git a/debian/control b/debian/control
index ee45a5fe3..32de13f1b 100644
--- a/debian/control
+++ b/debian/control
@@ -11,15 +11,17 @@ Build-Depends:
libvyosconfig0 (>= 0.0.7),
libzmq3-dev,
python3 (>= 3.10),
- python3-coverage,
+# For generating command definitions
python3-lxml,
+ python3-xmltodict,
+# For running tests
+ python3-coverage,
python3-netifaces,
python3-nose,
python3-jinja2,
python3-psutil,
python3-setuptools,
python3-sphinx,
- python3-xmltodict,
quilt,
whois
Standards-Version: 3.9.6
@@ -31,106 +33,20 @@ Pre-Depends:
libpam-tacplus [amd64],
libpam-radius-auth [amd64]
Depends:
+## Fundamentals
${python3:Depends} (>= 3.10),
- aardvark-dns,
- accel-ppp,
- auditd,
- avahi-daemon,
- beep,
- bmon,
- bsdmainutils,
- charon-systemd,
- conntrack,
- conntrackd,
- conserver-client,
- conserver-server,
- console-data,
- cron,
- curl,
- dbus,
- ddclient (>= 3.9.1),
- dropbear,
- easy-rsa,
- etherwake,
- ethtool,
- fdisk,
- fastnetmon [amd64],
- file,
- frr (>= 7.5),
- frr-pythontools,
- frr-rpki-rtrlib,
- frr-snmp,
- fuse-overlayfs,
- libpam-google-authenticator,
- grc,
- haproxy,
- hostapd,
- hsflowd,
- hvinfo,
- igmpproxy,
- ipaddrcheck,
- iperf,
- iperf3,
- iproute2 (>= 6.0.0),
- iptables,
- iputils-arping,
- isc-dhcp-client,
- isc-dhcp-relay,
- isc-dhcp-server,
- iw,
- keepalived (>=2.0.5),
- lcdproc,
- lcdproc-extra-drivers,
- libatomic1,
- libauparse0,
- libcharon-extra-plugins (>=5.9),
- libcharon-extauth-plugins (>=5.9),
- libndp-tools,
- libnetfilter-conntrack3,
- libnfnetlink0,
- libqmi-utils,
- libstrongswan-extra-plugins (>=5.9),
- libstrongswan-standard-plugins (>=5.9),
- libvppinfra [amd64],
libvyosconfig0,
- linux-cpupower,
- lldpd,
- lm-sensors,
- lsscsi,
- minisign,
- modemmanager,
- mtr-tiny,
- ndisc6,
- ndppd,
- netavark,
- netplug,
- nfct,
- nftables (>= 0.9.3),
- nginx-light,
- chrony,
- nvme-cli,
- ocserv,
- opennhrp,
- openssh-server,
- openssl,
- openvpn,
- openvpn-auth-ldap,
- openvpn-auth-radius,
- openvpn-otp,
- owamp-client,
- owamp-server,
- pciutils,
- pdns-recursor,
- pmacct (>= 1.6.0),
- podman,
- pppoe,
- procps,
+ vyatta-bash,
+ vyatta-cfg,
+ vyos-http-api-tools,
+ vyos-utils,
+## End of Fundamentals
+## Python libraries used in multiple modules and scripts
python3,
python3-certbot-nginx,
python3-cryptography,
python3-hurry.filesize,
python3-inotify,
- python3-isc-dhcp-leases,
python3-jinja2,
python3-jmespath,
python3-netaddr,
@@ -143,57 +59,257 @@ Depends:
python3-pyudev,
python3-six,
python3-tabulate,
- python3-vici (>= 5.7.2),
python3-voluptuous,
- python3-vpp-api [amd64],
python3-xmltodict,
python3-zmq,
+## End of Python libraries
+## Basic System services and utilities
+ sudo,
+ systemd,
+ bsdmainutils,
+ openssl,
+ curl,
+ dbus,
+ file,
+ iproute2 (>= 6.0.0),
+ linux-cpupower,
+# ipaddrcheck is widely used in IP value validators
+ ipaddrcheck,
+ ethtool,
+ fdisk,
+ lm-sensors,
+ procps,
+ netplug,
+ sed,
+ ssl-cert,
+ tuned,
+ beep,
+ wide-dhcpv6-client,
+# Generic colorizer
+ grc,
+## End of System services and utilities
+## For the installer
+# Image signature verification tool
+ minisign,
+# Live filesystem tools
+ squashfs-tools,
+ fuse-overlayfs,
+## End installer
+ auditd,
+ iputils-arping,
+ isc-dhcp-client,
+# For "vpn pptp", "vpn l2tp", "vpn sstp", "service ipoe-server"
+ accel-ppp,
+# End "vpn pptp", "vpn l2tp", "vpn sstp", "service ipoe-server"
+ avahi-daemon,
+ conntrack,
+ conntrackd,
+## Conf mode features
+# For "interfaces wireless"
+ hostapd,
+ hsflowd,
+ iw,
+ wireless-regdb,
+ wpasupplicant (>= 0.6.7),
+# End "interfaces wireless"
+# For "interfaces wwan"
+ modemmanager,
+ usb-modeswitch,
+ libqmi-utils,
+# End "interfaces wwan"
+# For "interfaces openvpn"
+ openvpn,
+ openvpn-auth-ldap,
+ openvpn-auth-radius,
+ openvpn-otp,
+ libpam-google-authenticator,
+# End "interfaces openvpn"
+# For "interfaces wireguard"
+ wireguard-tools,
qrencode,
+# End "interfaces wireguard"
+# For "interfaces pppoe"
+ pppoe,
+# End "interfaces pppoe"
+# For "interfaces sstpc"
+ sstp-client,
+# End "interfaces sstpc"
+# For "protocols *"
+ frr (>= 7.5),
+ frr-pythontools,
+ frr-rpki-rtrlib,
+ frr-snmp,
+# End "protocols *"
+# For "protocols nhrp" (part of DMVPN)
+ opennhrp,
+# End "protocols nhrp"
+# For "protocols igmp-proxy"
+ igmpproxy,
+# End "protocols igmp-proxy"
+# For "service console-server"
+ conserver-client,
+ conserver-server,
+ console-data,
+ dropbear,
+# End "service console-server"
+# For "set service aws glb"
+ aws-gwlbtun,
+# For "service dns dynamic"
+ ddclient (>= 3.9.1),
+# End "service dns dynamic"
+# # For "service ids"
+ fastnetmon [amd64],
+# End "service ids"
+# For "service router-advert"
radvd,
+# End "service route-advert"
+# For "high-availability reverse-proxy"
+ haproxy,
+# End "high-availability reverse-proxy"
+# For "service dhcp-relay"
+ isc-dhcp-relay,
+# For "service dhcp-server"
+ isc-dhcp-server,
+ python3-isc-dhcp-leases,
+# End "service dhcp-server"
+# For "service lldp"
+ lldpd,
+# End "service lldp"
+# For "service https"
+ nginx-light,
+# End "service https"
+# For "service ssh"
+ openssh-server,
+ sshguard,
+# End "service ssh"
+# For "service salt-minion"
salt-minion,
- sed,
- smartmontools,
+# End "service salt-minion"
+# For "service snmp"
snmp,
snmpd,
- squashfs-tools,
+# End "service snmp"
+# For "service upnp"
+ miniupnpd-nftables,
+# End "service upnp"
+# For "service webproxy"
squid,
squidclient,
squidguard,
- sshguard,
- ssl-cert,
- sstp-client,
- strongswan (>= 5.9),
- strongswan-swanctl (>= 5.9),
- stunnel4,
- sudo,
- systemd,
+# End "service webproxy"
+# For "service monitoring telegraf"
telegraf (>= 1.20),
- tcpdump,
- tcptraceroute,
- telnet,
+# End "service monitoring telegraf"
+# For "service monitoring zabbix-agent"
+ zabbix-agent2,
+# End "service monitoring zabbix-agent"
+# For "service tftp-server"
tftpd-hpa,
- traceroute,
- tuned,
+# End "service tftp-server"
+# For "service dns forwarding"
+ pdns-recursor,
+# End "service dns forwarding"
+# For "service sla owamp"
+ owamp-client,
+ owamp-server,
+# End "service sla owamp"
+# For "service sla twamp"
twamp-client,
twamp-server,
+# End "service sla twamp"
+# For "service broadcast-relay"
udp-broadcast-relay,
- uidmap,
- usb-modeswitch,
+# End "service broadcast-relay"
+# For "high-availability vrrp"
+ keepalived (>=2.0.5),
+# End "high-availability-vrrp"
+# For "system task-scheduler"
+ cron,
+# End "system task-scheduler"
+# For "system lcd"
+ lcdproc,
+ lcdproc-extra-drivers,
+# End "system lcd"
+# For firewall
+ libndp-tools,
+ libnetfilter-conntrack3,
+ libnfnetlink0,
+ nfct,
+ nftables (>= 0.9.3),
+# For "vpn ipsec"
+ strongswan (>= 5.9),
+ strongswan-swanctl (>= 5.9),
+ charon-systemd,
+ libcharon-extra-plugins (>=5.9),
+ libcharon-extauth-plugins (>=5.9),
+ libstrongswan-extra-plugins (>=5.9),
+ libstrongswan-standard-plugins (>=5.9),
+ python3-vici (>= 5.7.2),
+# End "vpn ipsec"
+# For nat66
+ ndppd,
+# End nat66
+# For "system ntp"
+ chrony,
+# End "system ntp"
+# For "vpn openconnect"
+ ocserv,
+# End "vpn openconnect"
+# For "set system flow-accounting"
+ pmacct (>= 1.6.0),
+# End "set system flow-accounting"
+# For container
+ podman,
+ netavark,
+ aardvark-dns,
+# iptables is only used for containers now, not the the firewall CLI
+ iptables,
+# End container
+## End Configuration mode
+## Operational mode
+# Used for hypervisor model in "run show version"
+ hvinfo,
+# For "run traceroute"
+ traceroute,
+# For "run monitor traffic"
+ tcpdump,
+# End "run monitor traffic"
+# For "run show hardware storage smart"
+ smartmontools,
+# For "run show hardware scsi"
+ lsscsi,
+# For "run show hardware pci"
+ pciutils,
+# For "show hardware usb"
usbutils,
+# For "run show hardware storage nvme"
+ nvme-cli,
+# For "run monitor bandwidth-test"
+ iperf,
+ iperf3,
+# End "run monitor bandwidth-test"
+# For "run wake-on-lan"
+ etherwake,
+# For "run force ipv6-nd"
+ ndisc6,
+# For "run monitor bandwidth"
+ bmon,
+# End Operational mode
+## VPP
vpp [amd64],
vpp-plugin-core [amd64],
vpp-plugin-dpdk [amd64],
- vyatta-bash,
- vyatta-cfg,
- vyos-http-api-tools,
- vyos-utils,
- wide-dhcpv6-client,
- wireguard-tools,
- wireless-regdb,
- wpasupplicant (>= 0.6.7),
- zabbix-agent2,
- ndppd,
- miniupnpd-nftables
+ python3-vpp-api [amd64],
+ libvppinfra [amd64],
+## End VPP
+## Optional utilities
+ easy-rsa,
+ tcptraceroute,
+ mtr-tiny,
+ telnet,
+ stunnel4,
+ uidmap
+## End optional utilities
Description: VyOS configuration scripts and data
VyOS configuration scripts, interface definitions, and everything
diff --git a/debian/rules b/debian/rules
index e6bbeeafb..9a6ab2996 100755
--- a/debian/rules
+++ b/debian/rules
@@ -117,6 +117,10 @@ override_dh_auto_install:
mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/config/
cp -r smoketest/configs/* $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/config
+ # Install smoke test config tests
+ mkdir -p $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/config-tests/
+ cp -r smoketest/config-tests/* $(DIR)/$(VYOS_LIBEXEC_DIR)/tests/config-tests
+
# Install system programs
mkdir -p $(DIR)/$(VYOS_BIN_DIR)
cp -r smoketest/bin/* $(DIR)/$(VYOS_BIN_DIR)
diff --git a/debian/vyos-1x-smoketest.install b/debian/vyos-1x-smoketest.install
index 406fef4be..739cb189b 100644
--- a/debian/vyos-1x-smoketest.install
+++ b/debian/vyos-1x-smoketest.install
@@ -3,3 +3,4 @@ usr/bin/vyos-configtest
usr/bin/vyos-configtest-pki
usr/libexec/vyos/tests/smoke
usr/libexec/vyos/tests/config
+usr/libexec/vyos/tests/config-tests
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index b43416152..860319edf 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -29,14 +29,9 @@ do
sed -i "/^# Standard Un\*x authentication\./i${PAM_CONFIG}" $file
done
-# We do not make use of a TACACS UNIX group - drop it
-if grep -q '^tacacs' /etc/group; then
- delgroup tacacs
-fi
-
-# Both RADIUS and TACACS users belong to aaa group - this must be added first
-if ! grep -q '^aaa' /etc/group; then
- addgroup --firstgid 1000 --quiet aaa
+# We need to have a group for RADIUS service users to use it inside PAM rules
+if ! grep -q '^radius' /etc/group; then
+ addgroup --firstgid 1000 --quiet radius
fi
# Remove TACACS user added by base package - we use our own UID range and group
@@ -53,6 +48,11 @@ if grep -q '^tacacs' /etc/passwd; then
fi
fi
+# Remove TACACS+ PAM default profile
+if [[ -e /usr/share/pam-configs/tacplus ]]; then
+ rm /usr/share/pam-configs/tacplus
+fi
+
# Add TACACS system users required for TACACS based system authentication
if ! grep -q '^tacacs' /etc/passwd; then
# Add the tacacs group and all 16 possible tacacs privilege-level users to
@@ -64,14 +64,13 @@ if ! grep -q '^tacacs' /etc/passwd; then
level=0
vyos_group=vyattaop
while [ $level -lt 16 ]; do
- adduser --quiet --system --firstuid 900 --disabled-login --ingroup users \
+ adduser --quiet --system --firstuid 900 --disabled-login --ingroup tacacs \
--no-create-home --gecos "TACACS+ mapped user at privilege level ${level}" \
--shell /bin/vbash tacacs${level}
adduser --quiet tacacs${level} frrvty
adduser --quiet tacacs${level} adm
adduser --quiet tacacs${level} dip
adduser --quiet tacacs${level} users
- adduser --quiet tacacs${level} aaa
if [ $level -lt 15 ]; then
adduser --quiet tacacs${level} vyattaop
adduser --quiet tacacs${level} operator
@@ -87,7 +86,7 @@ fi
# Add RADIUS operator user for RADIUS authenticated users to map to
if ! grep -q '^radius_user' /etc/passwd; then
- adduser --quiet --firstuid 1000 --disabled-login --ingroup users \
+ adduser --quiet --firstuid 1000 --disabled-login --ingroup radius \
--no-create-home --gecos "RADIUS mapped user at privilege level operator" \
--shell /sbin/radius_shell radius_user
adduser --quiet radius_user frrvty
@@ -96,12 +95,11 @@ if ! grep -q '^radius_user' /etc/passwd; then
adduser --quiet radius_user adm
adduser --quiet radius_user dip
adduser --quiet radius_user users
- adduser --quiet radius_user aaa
fi
# Add RADIUS admin user for RADIUS authenticated users to map to
if ! grep -q '^radius_priv_user' /etc/passwd; then
- adduser --quiet --firstuid 1000 --disabled-login --ingroup users \
+ adduser --quiet --firstuid 1000 --disabled-login --ingroup radius \
--no-create-home --gecos "RADIUS mapped user at privilege level admin" \
--shell /sbin/radius_shell radius_priv_user
adduser --quiet radius_priv_user frrvty
@@ -112,7 +110,6 @@ if ! grep -q '^radius_priv_user' /etc/passwd; then
adduser --quiet radius_priv_user disk
adduser --quiet radius_priv_user users
adduser --quiet radius_priv_user frr
- adduser --quiet radius_priv_user aaa
fi
# add hostsd group for vyos-hostsd
diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst
index e355ffa84..9bd6331a8 100644
--- a/debian/vyos-1x.preinst
+++ b/debian/vyos-1x.preinst
@@ -2,11 +2,10 @@ dpkg-divert --package vyos-1x --add --no-rename /etc/securetty
dpkg-divert --package vyos-1x --add --no-rename /etc/security/capability.conf
dpkg-divert --package vyos-1x --add --no-rename /lib/systemd/system/lcdproc.service
dpkg-divert --package vyos-1x --add --no-rename /etc/logrotate.d/conntrackd
-dpkg-divert --package vyos-1x --add --no-rename /usr/share/pam-configs/radius
-dpkg-divert --package vyos-1x --add --no-rename /usr/share/pam-configs/tacplus
dpkg-divert --package vyos-1x --add --no-rename /etc/rsyslog.conf
dpkg-divert --package vyos-1x --add --no-rename /etc/skel/.bashrc
dpkg-divert --package vyos-1x --add --no-rename /etc/skel/.profile
dpkg-divert --package vyos-1x --add --no-rename /etc/sysctl.d/80-vpp.conf
dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplugd.conf
dpkg-divert --package vyos-1x --add --no-rename /etc/netplug/netplug
+dpkg-divert --package vyos-1x --add --no-rename /etc/rsyslog.d/45-frr.conf
diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in
index 8259e7bdf..b35ba8d1c 100644
--- a/interface-definitions/container.xml.in
+++ b/interface-definitions/container.xml.in
@@ -25,7 +25,7 @@
<properties>
<help>Container capabilities/permissions</help>
<completionHelp>
- <list>net-admin net-bind-service net-raw setpcap sys-admin sys-time</list>
+ <list>net-admin net-bind-service net-raw setpcap sys-admin sys-module sys-time</list>
</completionHelp>
<valueHelp>
<format>net-admin</format>
@@ -48,11 +48,15 @@
<description>Administation operations (quotactl, mount, sethostname, setdomainame)</description>
</valueHelp>
<valueHelp>
+ <format>sys-module</format>
+ <description>Load, unload and delete kernel modules</description>
+ </valueHelp>
+ <valueHelp>
<format>sys-time</format>
<description>Permission to set system clock</description>
</valueHelp>
<constraint>
- <regex>(net-admin|net-bind-service|net-raw|setpcap|sys-admin|sys-time)</regex>
+ <regex>(net-admin|net-bind-service|net-raw|setpcap|sys-admin|sys-module|sys-time)</regex>
</constraint>
<multi/>
</properties>
@@ -110,7 +114,7 @@
<constraint>
<regex>[ !#-%&amp;(-~]+</regex>
</constraint>
- <constraintErrorMessage>Entrypoint must be ascii characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
+ <constraintErrorMessage>Entrypoint must be ASCII characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="host-name">
@@ -133,7 +137,7 @@
<constraint>
<regex>[ !#-%&amp;(-~]+</regex>
</constraint>
- <constraintErrorMessage>Command must be ascii characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
+ <constraintErrorMessage>Command must be ASCII characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="arguments">
@@ -142,7 +146,7 @@
<constraint>
<regex>[ !#-%&amp;(-~]+</regex>
</constraint>
- <constraintErrorMessage>The command's arguments must be ascii characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
+ <constraintErrorMessage>The command's arguments must be ASCII characters, use &amp;quot; and &amp;apos for double and single quotes respectively</constraintErrorMessage>
</properties>
</leafNode>
<tagNode name="label">
diff --git a/interface-definitions/dns-dynamic.xml.in b/interface-definitions/dns-dynamic.xml.in
index a0720f3aa..723223f1c 100644
--- a/interface-definitions/dns-dynamic.xml.in
+++ b/interface-definitions/dns-dynamic.xml.in
@@ -61,6 +61,7 @@
<children>
#include <include/generic-description.xml.i>
#include <include/dns/dynamic-service-host-name-server.xml.i>
+ #include <include/dns/dynamic-service-wait-expiry-time.xml.i>
<leafNode name="key">
<properties>
<help>File containing the TSIG secret key shared with remote DNS server</help>
@@ -74,18 +75,7 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
- <leafNode name="zone">
- <properties>
- <help>Forwarding zone to be updated</help>
- <valueHelp>
- <format>txt</format>
- <description>RFC2136 Zone to be updated</description>
- </valueHelp>
- <constraint>
- <validator name="fqdn"/>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/dns/dynamic-service-zone.xml.i>
</children>
</tagNode>
<tagNode name="service">
@@ -99,8 +89,10 @@
<children>
#include <include/generic-description.xml.i>
#include <include/dns/dynamic-service-host-name-server.xml.i>
+ #include <include/dns/dynamic-service-wait-expiry-time.xml.i>
#include <include/generic-username.xml.i>
#include <include/generic-password.xml.i>
+ #include <include/dns/time-to-live.xml.i>
<leafNode name="protocol">
<properties>
<help>ddclient protocol used for Dynamic DNS service</help>
@@ -112,15 +104,7 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="zone">
- <properties>
- <help>DNS zone to update (not used by all protocols)</help>
- <valueHelp>
- <format>txt</format>
- <description>Name of DNS zone</description>
- </valueHelp>
- </properties>
- </leafNode>
+ #include <include/dns/dynamic-service-zone.xml.i>
<leafNode name="ip-version">
<properties>
<help>IP address version to use</help>
@@ -164,6 +148,7 @@
</properties>
<defaultValue>300</defaultValue>
</leafNode>
+ #include <include/interface/vrf.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/dns-forwarding.xml.in b/interface-definitions/dns-forwarding.xml.in
index 86dc47a47..c4295317a 100644
--- a/interface-definitions/dns-forwarding.xml.in
+++ b/interface-definitions/dns-forwarding.xml.in
@@ -158,6 +158,9 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -195,6 +198,9 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -227,6 +233,9 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -274,6 +283,9 @@
</children>
</tagNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -302,6 +314,9 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -334,6 +349,9 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -364,6 +382,9 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -393,6 +414,9 @@
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -477,6 +501,9 @@
</children>
</tagNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
@@ -585,6 +612,9 @@
</children>
</tagNode>
#include <include/dns/time-to-live.xml.i>
+ <leafNode name="ttl">
+ <defaultValue>300</defaultValue>
+ </leafNode>
#include <include/generic-disable-node.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 127f4b7e7..81e6b89ea 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -7,6 +7,46 @@
</properties>
<children>
#include <include/firewall/global-options.xml.i>
+ <tagNode name="flowtable">
+ <properties>
+ <help>Flowtable</help>
+ <constraint>
+ <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ </constraint>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ <leafNode name="interface">
+ <properties>
+ <help>Interfaces to use this flowtable</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="offload">
+ <properties>
+ <help>Offloading method</help>
+ <completionHelp>
+ <list>hardware software</list>
+ </completionHelp>
+ <valueHelp>
+ <format>hardware</format>
+ <description>Hardware offload</description>
+ </valueHelp>
+ <valueHelp>
+ <format>software</format>
+ <description>Software offload</description>
+ </valueHelp>
+ <constraint>
+ <regex>(hardware|software)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>software</defaultValue>
+ </leafNode>
+ </children>
+ </tagNode>
<node name="group">
<properties>
<help>Firewall group</help>
@@ -284,6 +324,15 @@
</tagNode>
</children>
</node>
+ <node name="bridge">
+ <properties>
+ <help>Bridge firewall</help>
+ </properties>
+ <children>
+ #include <include/firewall/bridge-hook-forward.xml.i>
+ #include <include/firewall/bridge-custom-name.xml.i>
+ </children>
+ </node>
<node name="ipv4">
<properties>
<help>IPv4 firewall</help>
diff --git a/interface-definitions/high-availability.xml.in b/interface-definitions/high-availability.xml.in
index 4f55916fa..aa23888a4 100644
--- a/interface-definitions/high-availability.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -12,6 +12,12 @@
<help>Virtual Router Redundancy Protocol settings</help>
</properties>
<children>
+ <leafNode name="snmp">
+ <properties>
+ <valueless/>
+ <help>Enable SNMP</help>
+ </properties>
+ </leafNode>
<node name="global-parameters">
<properties>
<help>VRRP global parameters</help>
diff --git a/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i
index 75221a348..9ec513da9 100644
--- a/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i
+++ b/interface-definitions/include/bgp/neighbor-afi-ipv4-ipv6-common.xml.i
@@ -1,4 +1,5 @@
<!-- include start from bgp/neighbor-afi-ipv4-ipv6-common.xml.i -->
+
<leafNode name="addpath-tx-all">
<properties>
<help>Use addpath to advertise all paths to a neighbor</help>
@@ -156,12 +157,19 @@
</properties>
</leafNode>
#include <include/bgp/afi-nexthop-self.xml.i>
-<leafNode name="remove-private-as">
+<node name="remove-private-as">
<properties>
<help>Remove private AS numbers from AS path in outbound route updates</help>
- <valueless/>
</properties>
-</leafNode>
+ <children>
+ <leafNode name="all">
+ <properties>
+ <help>Remove private AS numbers to all AS numbers in outbound route updates</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+</node>
#include <include/bgp/afi-route-map.xml.i>
#include <include/bgp/afi-route-reflector-client.xml.i>
#include <include/bgp/afi-route-server-client.xml.i>
diff --git a/interface-definitions/include/bgp/neighbor-capability.xml.i b/interface-definitions/include/bgp/neighbor-capability.xml.i
index 2bbfadf1f..c5ed3c840 100644
--- a/interface-definitions/include/bgp/neighbor-capability.xml.i
+++ b/interface-definitions/include/bgp/neighbor-capability.xml.i
@@ -16,6 +16,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="software-version">
+ <properties>
+ <help>Advertise Software Version capability to the peer</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/bgp/neighbor-path-attribute.xml.i b/interface-definitions/include/bgp/neighbor-path-attribute.xml.i
index f4f2fcfa9..30568d8c6 100644
--- a/interface-definitions/include/bgp/neighbor-path-attribute.xml.i
+++ b/interface-definitions/include/bgp/neighbor-path-attribute.xml.i
@@ -16,6 +16,18 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="treat-as-withdraw">
+ <properties>
+ <help>Treat-as-withdraw any incoming BGP UPDATE messages that contain the specified attribute</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Attribute number</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i b/interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i
deleted file mode 100644
index 6a39041a3..000000000
--- a/interface-definitions/include/constraint/interface-name-with-wildcard-and-inverted.xml.i
+++ /dev/null
@@ -1,4 +0,0 @@
-<!-- include start from constraint/interface-name-with-wildcard-and-inverted.xml.i -->
-<regex>(\!?)(bond|br|dum|en|ersp|eth|gnv|ifb|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|veth|vti|vtun|vxlan|wg|wlan|wwan)([0-9]?)(\*?)(.+)?|(\!?)lo</regex>
-<validator name="file-path --lookup-path /sys/class/net --directory"/>
-<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i b/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
index ee1af2a36..9dd14f97c 100644
--- a/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
+++ b/interface-definitions/include/dns/dynamic-service-host-name-server.xml.i
@@ -4,8 +4,9 @@
<help>Hostname to register with Dynamic DNS service</help>
<constraint>
#include <include/constraint/host-name.xml.i>
+ <regex>(\@|\*)[-.A-Za-z0-9]*</regex>
</constraint>
- <constraintErrorMessage>Host-name must be alphanumeric and can contain hyphens</constraintErrorMessage>
+ <constraintErrorMessage>Host-name must be alphanumeric, can contain hyphens and can be prefixed with '@' or '*'</constraintErrorMessage>
<multi/>
</properties>
</leafNode>
diff --git a/interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i b/interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i
new file mode 100644
index 000000000..866690cbe
--- /dev/null
+++ b/interface-definitions/include/dns/dynamic-service-wait-expiry-time.xml.i
@@ -0,0 +1,28 @@
+<!-- include start from dns/dynamic-service-wait-expiry-time.xml.i -->
+<leafNode name="wait-time">
+ <properties>
+ <help>Time in seconds to wait between update attempts</help>
+ <valueHelp>
+ <format>u32:60-86400</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 60-86400"/>
+ </constraint>
+ <constraintErrorMessage>Wait time must be between 60 and 86400 seconds</constraintErrorMessage>
+ </properties>
+</leafNode>
+<leafNode name="expiry-time">
+ <properties>
+ <help>Time in seconds for the hostname to be marked expired in cache</help>
+ <valueHelp>
+ <format>u32:300-2160000</format>
+ <description>Time in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 300-2160000"/>
+ </constraint>
+ <constraintErrorMessage>Expiry time must be between 300 and 2160000 seconds</constraintErrorMessage>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/dns/dynamic-service-zone.xml.i b/interface-definitions/include/dns/dynamic-service-zone.xml.i
new file mode 100644
index 000000000..0cc00468f
--- /dev/null
+++ b/interface-definitions/include/dns/dynamic-service-zone.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from dns/dynamic-service-zone.xml.i -->
+<leafNode name="zone">
+ <properties>
+ <help>DNS zone to be updated</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>Name of DNS zone</description>
+ </valueHelp>
+ <constraint>
+ <validator name="fqdn"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/dns/time-to-live.xml.i b/interface-definitions/include/dns/time-to-live.xml.i
index 5c1a1472d..000eea108 100644
--- a/interface-definitions/include/dns/time-to-live.xml.i
+++ b/interface-definitions/include/dns/time-to-live.xml.i
@@ -10,6 +10,5 @@
<validator name="numeric" argument="--range 0-2147483647"/>
</constraint>
</properties>
- <defaultValue>300</defaultValue>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/firewall/action-forward.xml.i b/interface-definitions/include/firewall/action-forward.xml.i
new file mode 100644
index 000000000..4e59f3c6f
--- /dev/null
+++ b/interface-definitions/include/firewall/action-forward.xml.i
@@ -0,0 +1,49 @@
+<!-- include start from firewall/action-forward.xml.i -->
+<leafNode name="action">
+ <properties>
+ <help>Rule action</help>
+ <completionHelp>
+ <list>accept continue jump reject return drop queue offload synproxy</list>
+ </completionHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept matching entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</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>
+ <valueHelp>
+ <format>queue</format>
+ <description>Enqueue packet to userspace</description>
+ </valueHelp>
+ <valueHelp>
+ <format>offload</format>
+ <description>Offload packet via flowtable</description>
+ </valueHelp>
+ <valueHelp>
+ <format>synproxy</format>
+ <description>Synproxy connections</description>
+ </valueHelp>
+ <constraint>
+ <regex>(accept|continue|jump|reject|return|drop|queue|offload|synproxy)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/action-l2.xml.i b/interface-definitions/include/firewall/action-l2.xml.i
new file mode 100644
index 000000000..84af576c8
--- /dev/null
+++ b/interface-definitions/include/firewall/action-l2.xml.i
@@ -0,0 +1,37 @@
+<!-- include start from firewall/action.xml.i -->
+<leafNode name="action">
+ <properties>
+ <help>Rule action</help>
+ <completionHelp>
+ <list>accept continue jump return drop queue</list>
+ </completionHelp>
+ <valueHelp>
+ <format>accept</format>
+ <description>Accept matching entries</description>
+ </valueHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
+ <valueHelp>
+ <format>jump</format>
+ <description>Jump to another chain</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>
+ <valueHelp>
+ <format>queue</format>
+ <description>Enqueue packet to userspace</description>
+ </valueHelp>
+ <constraint>
+ <regex>(accept|continue|jump|return|drop|queue)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i
index 7c6e33839..954e4f23e 100644
--- a/interface-definitions/include/firewall/action.xml.i
+++ b/interface-definitions/include/firewall/action.xml.i
@@ -3,13 +3,17 @@
<properties>
<help>Rule action</help>
<completionHelp>
- <list>accept jump reject return drop queue</list>
+ <list>accept continue jump reject return drop queue synproxy</list>
</completionHelp>
<valueHelp>
<format>accept</format>
<description>Accept matching entries</description>
</valueHelp>
<valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
+ <valueHelp>
<format>jump</format>
<description>Jump to another chain</description>
</valueHelp>
@@ -29,8 +33,12 @@
<format>queue</format>
<description>Enqueue packet to userspace</description>
</valueHelp>
+ <valueHelp>
+ <format>synproxy</format>
+ <description>Synproxy connections</description>
+ </valueHelp>
<constraint>
- <regex>(accept|jump|reject|return|drop|queue)</regex>
+ <regex>(accept|continue|jump|reject|return|drop|queue|synproxy)</regex>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/bridge-custom-name.xml.i b/interface-definitions/include/firewall/bridge-custom-name.xml.i
new file mode 100644
index 000000000..a85fd5a19
--- /dev/null
+++ b/interface-definitions/include/firewall/bridge-custom-name.xml.i
@@ -0,0 +1,39 @@
+<!-- include start from firewall/bridge-custom-name.xml.i -->
+<tagNode name="name">
+ <properties>
+ <help>Bridge custom firewall</help>
+ <constraint>
+ <regex>[a-zA-Z0-9][\w\-\.]*</regex>
+ </constraint>
+ </properties>
+ <children>
+ #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 bridge name</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <tagNode name="rule">
+ <properties>
+ <help>Bridge Firewall forward filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-bridge.xml.i>
+ </children>
+ </tagNode>
+ </children>
+</tagNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/bridge-hook-forward.xml.i b/interface-definitions/include/firewall/bridge-hook-forward.xml.i
new file mode 100644
index 000000000..23d757070
--- /dev/null
+++ b/interface-definitions/include/firewall/bridge-hook-forward.xml.i
@@ -0,0 +1,34 @@
+<!-- include start from firewall/bridge-hook-forward.xml.i -->
+<node name="forward">
+ <properties>
+ <help>Bridge forward firewall</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Bridge firewall forward filter</help>
+ </properties>
+ <children>
+ #include <include/firewall/default-action-base-chains.xml.i>
+ #include <include/generic-description.xml.i>
+ <tagNode name="rule">
+ <properties>
+ <help>Bridge Firewall forward filter rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number for this firewall rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/firewall/common-rule-bridge.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/common-rule-bridge.xml.i b/interface-definitions/include/firewall/common-rule-bridge.xml.i
new file mode 100644
index 000000000..381e04b1e
--- /dev/null
+++ b/interface-definitions/include/firewall/common-rule-bridge.xml.i
@@ -0,0 +1,57 @@
+<!-- include start from firewall/common-rule-bridge.xml.i -->
+#include <include/firewall/action-l2.xml.i>
+#include <include/firewall/nft-queue.xml.i>
+<node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/mac-address.xml.i>
+ </children>
+</node>
+<leafNode name="disable">
+ <properties>
+ <help>Option to disable firewall rule</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<leafNode name="jump-target">
+ <properties>
+ <help>Set jump target. Action jump must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall bridge name</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<leafNode name="log">
+ <properties>
+ <help>Option to log packets matching rule</help>
+ <completionHelp>
+ <list>enable disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>enable</format>
+ <description>Enable log</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>Disable log</description>
+ </valueHelp>
+ <constraint>
+ <regex>(enable|disable)</regex>
+ </constraint>
+ </properties>
+</leafNode>
+#include <include/firewall/rule-log-options.xml.i>
+<node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/mac-address.xml.i>
+ </children>
+</node>
+#include <include/firewall/inbound-interface.xml.i>
+#include <include/firewall/outbound-interface.xml.i>
+#include <include/firewall/match-vlan.xml.i>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i
index 7a2eb86d4..a55a1a551 100644
--- a/interface-definitions/include/firewall/common-rule-inet.xml.i
+++ b/interface-definitions/include/firewall/common-rule-inet.xml.i
@@ -3,7 +3,9 @@
#include <include/generic-description.xml.i>
#include <include/firewall/dscp.xml.i>
#include <include/firewall/packet-options.xml.i>
+#include <include/firewall/firewall-mark.xml.i>
#include <include/firewall/connection-mark.xml.i>
+#include <include/firewall/conntrack-helper.xml.i>
#include <include/firewall/nft-queue.xml.i>
<leafNode name="disable">
<properties>
@@ -219,6 +221,7 @@
</leafNode>
</children>
</node>
+#include <include/firewall/synproxy.xml.i>
<node name="state">
<properties>
<help>Session state</help>
@@ -303,6 +306,7 @@
</children>
</node>
#include <include/firewall/tcp-flags.xml.i>
+#include <include/firewall/tcp-mss.xml.i>
<node name="time">
<properties>
<help>Time to match rule</help>
@@ -371,4 +375,4 @@
</leafNode>
</children>
</node>
-<!-- include end --> \ No newline at end of file
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i
index a1071a09a..e040c9b13 100644
--- a/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i
+++ b/interface-definitions/include/firewall/common-rule-ipv4-raw.xml.i
@@ -260,6 +260,7 @@
</children>
</node>
#include <include/firewall/tcp-flags.xml.i>
+#include <include/firewall/tcp-mss.xml.i>
<node name="time">
<properties>
<help>Time to match rule</help>
diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i
index 7417a3c58..c62bf2c5f 100644
--- a/interface-definitions/include/firewall/common-rule.xml.i
+++ b/interface-definitions/include/firewall/common-rule.xml.i
@@ -315,6 +315,7 @@
</children>
</node>
#include <include/firewall/tcp-flags.xml.i>
+#include <include/firewall/tcp-mss.xml.i>
<node name="time">
<properties>
<help>Time to match rule</help>
diff --git a/interface-definitions/include/firewall/conntrack-helper.xml.i b/interface-definitions/include/firewall/conntrack-helper.xml.i
new file mode 100644
index 000000000..ee17f2c61
--- /dev/null
+++ b/interface-definitions/include/firewall/conntrack-helper.xml.i
@@ -0,0 +1,42 @@
+<!-- include start from firewall/conntrack-helper.xml.i -->
+<leafNode name="conntrack-helper">
+ <properties>
+ <help>Match related traffic from conntrack helpers</help>
+ <completionHelp>
+ <list>ftp h323 pptp nfs sip tftp sqlnet</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ftp</format>
+ <description>Related traffic from FTP helper</description>
+ </valueHelp>
+ <valueHelp>
+ <format>h323</format>
+ <description>Related traffic from H.323 helper</description>
+ </valueHelp>
+ <valueHelp>
+ <format>pptp</format>
+ <description>Related traffic from PPTP helper</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nfs</format>
+ <description>Related traffic from NFS helper</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sip</format>
+ <description>Related traffic from SIP helper</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tftp</format>
+ <description>Related traffic from TFTP helper</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sqlnet</format>
+ <description>Related traffic from SQLNet helper</description>
+ </valueHelp>
+ <constraint>
+ <regex>(ftp|h323|pptp|nfs|sip|tftp|sqlnet)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/default-action-bridge.xml.i b/interface-definitions/include/firewall/default-action-bridge.xml.i
new file mode 100644
index 000000000..858c7aeeb
--- /dev/null
+++ b/interface-definitions/include/firewall/default-action-bridge.xml.i
@@ -0,0 +1,34 @@
+<!-- include start from firewall/default-action.xml.i -->
+<leafNode name="default-action">
+ <properties>
+ <help>Default-action for rule-set</help>
+ <completionHelp>
+ <list>drop jump return accept continue</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>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>
+ <valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
+ <constraint>
+ <regex>(drop|jump|return|accept|continue)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>drop</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/default-action.xml.i b/interface-definitions/include/firewall/default-action.xml.i
index 80efaf335..53a161495 100644
--- a/interface-definitions/include/firewall/default-action.xml.i
+++ b/interface-definitions/include/firewall/default-action.xml.i
@@ -3,7 +3,7 @@
<properties>
<help>Default-action for rule-set</help>
<completionHelp>
- <list>drop jump reject return accept</list>
+ <list>drop jump reject return accept continue</list>
</completionHelp>
<valueHelp>
<format>drop</format>
@@ -25,8 +25,12 @@
<format>accept</format>
<description>Accept if no prior rules are hit</description>
</valueHelp>
+ <valueHelp>
+ <format>continue</format>
+ <description>Continue parsing next rule</description>
+ </valueHelp>
<constraint>
- <regex>(drop|jump|reject|return|accept)</regex>
+ <regex>(drop|jump|reject|return|accept|continue)</regex>
</constraint>
</properties>
<defaultValue>drop</defaultValue>
diff --git a/interface-definitions/include/firewall/firewall-mark.xml.i b/interface-definitions/include/firewall/firewall-mark.xml.i
new file mode 100644
index 000000000..36a939ba3
--- /dev/null
+++ b/interface-definitions/include/firewall/firewall-mark.xml.i
@@ -0,0 +1,26 @@
+<!-- include start from firewall/firewall-mark.xml.i -->
+<leafNode name="mark">
+ <properties>
+ <help>Firewall mark</help>
+ <valueHelp>
+ <format>u32:0-2147483647</format>
+ <description>Firewall mark to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>!u32:0-2147483647</format>
+ <description>Inverted Firewall mark to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;start-end&gt;</format>
+ <description>Firewall mark range to match</description>
+ </valueHelp>
+ <valueHelp>
+ <format>!&lt;start-end&gt;</format>
+ <description>Firewall mark inverted range to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric-exclude" argument="--allow-range --range 0-2147483647"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
index a63874cb0..e655cd6ac 100644
--- a/interface-definitions/include/firewall/global-options.xml.i
+++ b/interface-definitions/include/firewall/global-options.xml.i
@@ -145,21 +145,21 @@
</leafNode>
<leafNode name="source-validation">
<properties>
- <help>Policy for source validation by reversed path, as specified in RFC3704</help>
+ <help>Policy for IPv4 source validation by reversed path, as specified in RFC3704</help>
<completionHelp>
<list>strict loose disable</list>
</completionHelp>
<valueHelp>
<format>strict</format>
- <description>Enable Strict Reverse Path Forwarding as defined in RFC3704</description>
+ <description>Enable IPv4 Strict Reverse Path Forwarding as defined in RFC3704</description>
</valueHelp>
<valueHelp>
<format>loose</format>
- <description>Enable Loose Reverse Path Forwarding as defined in RFC3704</description>
+ <description>Enable IPv4 Loose Reverse Path Forwarding as defined in RFC3704</description>
</valueHelp>
<valueHelp>
<format>disable</format>
- <description>No source validation</description>
+ <description>No IPv4 source validation</description>
</valueHelp>
<constraint>
<regex>(strict|loose|disable)</regex>
@@ -227,6 +227,30 @@
</properties>
<defaultValue>disable</defaultValue>
</leafNode>
+ <leafNode name="ipv6-source-validation">
+ <properties>
+ <help>Policy for IPv6 source validation by reversed path, as specified in RFC3704</help>
+ <completionHelp>
+ <list>strict loose disable</list>
+ </completionHelp>
+ <valueHelp>
+ <format>strict</format>
+ <description>Enable IPv6 Strict Reverse Path Forwarding as defined in RFC3704</description>
+ </valueHelp>
+ <valueHelp>
+ <format>loose</format>
+ <description>Enable IPv6 Loose Reverse Path Forwarding as defined in RFC3704</description>
+ </valueHelp>
+ <valueHelp>
+ <format>disable</format>
+ <description>No IPv6 source validation</description>
+ </valueHelp>
+ <constraint>
+ <regex>(strict|loose|disable)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>disable</defaultValue>
+ </leafNode>
<leafNode name="ipv6-src-route">
<properties>
<help>Policy for handling IPv6 packets with routing extension header</help>
diff --git a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
index 08ee96419..70c0adb77 100644
--- a/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
+++ b/interface-definitions/include/firewall/ipv4-hook-forward.xml.i
@@ -24,8 +24,10 @@
<constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
+ #include <include/firewall/action-forward.xml.i>
#include <include/firewall/common-rule-ipv4.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/offload-target.xml.i>
#include <include/firewall/outbound-interface.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
index 20ab8dbe8..d83827161 100644
--- a/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
+++ b/interface-definitions/include/firewall/ipv6-hook-forward.xml.i
@@ -24,8 +24,10 @@
<constraintErrorMessage>Firewall rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
+ #include <include/firewall/action-forward.xml.i>
#include <include/firewall/common-rule-ipv6.xml.i>
#include <include/firewall/inbound-interface.xml.i>
+ #include <include/firewall/offload-target.xml.i>
#include <include/firewall/outbound-interface.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/include/firewall/match-interface.xml.i b/interface-definitions/include/firewall/match-interface.xml.i
index 7810f88ab..1cd9f8c4a 100644
--- a/interface-definitions/include/firewall/match-interface.xml.i
+++ b/interface-definitions/include/firewall/match-interface.xml.i
@@ -4,6 +4,7 @@
<help>Match interface</help>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
+ <path>vrf name</path>
</completionHelp>
<valueHelp>
<format>txt</format>
@@ -18,7 +19,8 @@
<description>Inverted interface name to match</description>
</valueHelp>
<constraint>
- #include <include/constraint/interface-name-with-wildcard-and-inverted.xml.i>
+ <regex>(\!?)(bond|br|dum|en|ersp|eth|gnv|ifb|lan|l2tp|l2tpeth|macsec|peth|ppp|pppoe|pptp|sstp|tun|veth|vti|vtun|vxlan|wg|wlan|wwan)([0-9]?)(\*?)(.+)?|(\!?)lo</regex>
+ <validator name="vrf-name"/>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/include/firewall/match-vlan.xml.i b/interface-definitions/include/firewall/match-vlan.xml.i
new file mode 100644
index 000000000..44ad02c99
--- /dev/null
+++ b/interface-definitions/include/firewall/match-vlan.xml.i
@@ -0,0 +1,41 @@
+<!-- include start from firewall/match-vlan.xml.i -->
+<node name="vlan">
+ <properties>
+ <help>VLAN parameters</help>
+ </properties>
+ <children>
+ <leafNode name="id">
+ <properties>
+ <help>Vlan id</help>
+ <valueHelp>
+ <format>u32:0-4096</format>
+ <description>Vlan id</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;start-end&gt;</format>
+ <description>Vlan id range to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-4095"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="priority">
+ <properties>
+ <help>Vlan priority(pcp)</help>
+ <valueHelp>
+ <format>u32:0-7</format>
+ <description>Vlan priority</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;start-end&gt;</format>
+ <description>Vlan priority range to match</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-7"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end --> \ No newline at end of file
diff --git a/interface-definitions/include/firewall/offload-target.xml.i b/interface-definitions/include/firewall/offload-target.xml.i
new file mode 100644
index 000000000..940ed8091
--- /dev/null
+++ b/interface-definitions/include/firewall/offload-target.xml.i
@@ -0,0 +1,10 @@
+<!-- include start from firewall/offload-target.xml.i -->
+<leafNode name="offload-target">
+ <properties>
+ <help>Set flowtable offload target. Action offload must be defined to use this setting</help>
+ <completionHelp>
+ <path>firewall flowtable</path>
+ </completionHelp>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/source-destination-group-ipv4.xml.i b/interface-definitions/include/firewall/source-destination-group-ipv4.xml.i
new file mode 100644
index 000000000..8c34fb933
--- /dev/null
+++ b/interface-definitions/include/firewall/source-destination-group-ipv4.xml.i
@@ -0,0 +1,41 @@
+<!-- include start from firewall/source-destination-group-ipv4.xml.i -->
+<node name="group">
+ <properties>
+ <help>Group</help>
+ </properties>
+ <children>
+ <leafNode name="address-group">
+ <properties>
+ <help>Group of addresses</help>
+ <completionHelp>
+ <path>firewall group address-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="domain-group">
+ <properties>
+ <help>Group of domains</help>
+ <completionHelp>
+ <path>firewall group domain-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="network-group">
+ <properties>
+ <help>Group of networks</help>
+ <completionHelp>
+ <path>firewall group network-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="port-group">
+ <properties>
+ <help>Group of ports</help>
+ <completionHelp>
+ <path>firewall group port-group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/synproxy.xml.i b/interface-definitions/include/firewall/synproxy.xml.i
new file mode 100644
index 000000000..a65126ea9
--- /dev/null
+++ b/interface-definitions/include/firewall/synproxy.xml.i
@@ -0,0 +1,40 @@
+<!-- include start from firewall/synproxy.xml.i -->
+<node name="synproxy">
+ <properties>
+ <help>Synproxy options</help>
+ </properties>
+ <children>
+ <node name="tcp">
+ <properties>
+ <help>TCP synproxy options</help>
+ </properties>
+ <children>
+ <leafNode name="mss">
+ <properties>
+ <help>TCP Maximum segment size</help>
+ <valueHelp>
+ <format>u32:501-65535</format>
+ <description>Maximum segment size for synproxy connections</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 501-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="window-scale">
+ <properties>
+ <help>TCP window scale for synproxy connections</help>
+ <valueHelp>
+ <format>u32:1-14</format>
+ <description>TCP window scale</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-14"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/tcp-flags.xml.i b/interface-definitions/include/firewall/tcp-flags.xml.i
index e2ce7b9fd..36546c2e4 100644
--- a/interface-definitions/include/firewall/tcp-flags.xml.i
+++ b/interface-definitions/include/firewall/tcp-flags.xml.i
@@ -1,7 +1,7 @@
<!-- include start from firewall/tcp-flags.xml.i -->
<node name="tcp">
<properties>
- <help>TCP flags to match</help>
+ <help>TCP options to match</help>
</properties>
<children>
<node name="flags">
@@ -114,22 +114,6 @@
</node>
</children>
</node>
- <leafNode name="mss">
- <properties>
- <help>Maximum segment size (MSS)</help>
- <valueHelp>
- <format>u32:1-16384</format>
- <description>Maximum segment size</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;min&gt;-&lt;max&gt;</format>
- <description>TCP MSS range (use '-' as delimiter)</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--allow-range --range 1-16384"/>
- </constraint>
- </properties>
- </leafNode>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/firewall/tcp-mss.xml.i b/interface-definitions/include/firewall/tcp-mss.xml.i
new file mode 100644
index 000000000..dc49b4272
--- /dev/null
+++ b/interface-definitions/include/firewall/tcp-mss.xml.i
@@ -0,0 +1,25 @@
+<!-- include start from firewall/tcp-mss.xml.i -->
+<node name="tcp">
+ <properties>
+ <help>TCP options to match</help>
+ </properties>
+ <children>
+ <leafNode name="mss">
+ <properties>
+ <help>Maximum segment size (MSS)</help>
+ <valueHelp>
+ <format>u32:1-16384</format>
+ <description>Maximum segment size</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;min&gt;-&lt;max&gt;</format>
+ <description>TCP MSS range (use '-' as delimiter)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 1-16384"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/isis/protocol-common-config.xml.i b/interface-definitions/include/isis/protocol-common-config.xml.i
index 87b9d552f..404f03cb5 100644
--- a/interface-definitions/include/isis/protocol-common-config.xml.i
+++ b/interface-definitions/include/isis/protocol-common-config.xml.i
@@ -1,4 +1,16 @@
<!-- include start from isis/protocol-common-config.xml.i -->
+<leafNode name="advertise-high-metrics">
+ <properties>
+ <help>Advertise high metric value on all interfaces</help>
+ <valueless/>
+ </properties>
+</leafNode>
+<leafNode name="advertise-passive-only">
+ <properties>
+ <help>Advertise prefixes of passive interfaces only</help>
+ <valueless/>
+ </properties>
+</leafNode>
<node name="area-password">
<properties>
<help>Configure the authentication password for an area</help>
diff --git a/interface-definitions/include/policy/local-route_rule_ipv4_address.xml.i b/interface-definitions/include/policy/local-route_rule_ipv4_address.xml.i
new file mode 100644
index 000000000..ffe73ee32
--- /dev/null
+++ b/interface-definitions/include/policy/local-route_rule_ipv4_address.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from policy/local-route_rule_ipv4_address.xml.i -->
+<leafNode name="address">
+ <properties>
+ <help>IPv4 address or prefix</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Address to match against</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>Prefix to match against</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ip-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/policy/local-route_rule_ipv6_address.xml.i b/interface-definitions/include/policy/local-route_rule_ipv6_address.xml.i
new file mode 100644
index 000000000..d8fb6c074
--- /dev/null
+++ b/interface-definitions/include/policy/local-route_rule_ipv6_address.xml.i
@@ -0,0 +1,20 @@
+<!-- include start from policy/local-route_rule_ipv6_address.xml.i -->
+<leafNode name="address">
+ <properties>
+ <help>IPv6 address or prefix</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Address to match against</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>Prefix to match against</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ <validator name="ipv6-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/policy/local-route_rule_protocol.xml.i b/interface-definitions/include/policy/local-route_rule_protocol.xml.i
new file mode 100644
index 000000000..57582eb37
--- /dev/null
+++ b/interface-definitions/include/policy/local-route_rule_protocol.xml.i
@@ -0,0 +1,21 @@
+<!-- include start from policy/local-route_rule_protocol.xml.i -->
+<leafNode name="protocol">
+ <properties>
+ <help>Protocol to match (protocol name or number)</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_protocols.sh</script>
+ </completionHelp>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>IP protocol number</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;protocol&gt;</format>
+ <description>IP protocol name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-protocol"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/policy/route-common.xml.i b/interface-definitions/include/policy/route-common.xml.i
index 216ec9bea..8eab04d4a 100644
--- a/interface-definitions/include/policy/route-common.xml.i
+++ b/interface-definitions/include/policy/route-common.xml.i
@@ -1,6 +1,7 @@
<!-- include start from policy/route-common.xml.i -->
#include <include/policy/route-rule-action.xml.i>
#include <include/generic-description.xml.i>
+#include <include/firewall/firewall-mark.xml.i>
<leafNode name="disable">
<properties>
<help>Option to disable firewall rule</help>
@@ -314,6 +315,7 @@
</children>
</node>
#include <include/firewall/tcp-flags.xml.i>
+#include <include/firewall/tcp-mss.xml.i>
<node name="time">
<properties>
<help>Time to match rule</help>
diff --git a/interface-definitions/include/radius-server-ipv4-ipv6.xml.i b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i
index efd418bb2..a0cdcd7c3 100644
--- a/interface-definitions/include/radius-server-ipv4-ipv6.xml.i
+++ b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i
@@ -46,6 +46,26 @@
<multi/>
</properties>
</leafNode>
+ <leafNode name="security-mode">
+ <properties>
+ <help>Security mode for RADIUS authentication</help>
+ <completionHelp>
+ <list>mandatory optional</list>
+ </completionHelp>
+ <valueHelp>
+ <format>mandatory</format>
+ <description>Deny access immediately if RADIUS answers with Access-Reject</description>
+ </valueHelp>
+ <valueHelp>
+ <format>optional</format>
+ <description>Pass to the next authentication method if RADIUS answers with Access-Reject</description>
+ </valueHelp>
+ <constraint>
+ <regex>(mandatory|optional)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>optional</defaultValue>
+ </leafNode>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/include/version/conntrack-version.xml.i b/interface-definitions/include/version/conntrack-version.xml.i
index 696f76362..c0f632c70 100644
--- a/interface-definitions/include/version/conntrack-version.xml.i
+++ b/interface-definitions/include/version/conntrack-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/conntrack-version.xml.i -->
-<syntaxVersion component='conntrack' version='3'></syntaxVersion>
+<syntaxVersion component='conntrack' version='4'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/include/version/policy-version.xml.i b/interface-definitions/include/version/policy-version.xml.i
index f1494eaa3..2c96e0f15 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='5'></syntaxVersion>
+<syntaxVersion component='policy' version='6'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 3669336fd..5aaa7095c 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -80,6 +80,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="hw-tc-offload">
+ <properties>
+ <help>Enable Hardware Flow Offload</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="lro">
<properties>
<help>Enable Large Receive Offload</help>
diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in
index b78f92c85..30fcb8573 100644
--- a/interface-definitions/interfaces-pppoe.xml.in
+++ b/interface-definitions/interfaces-pppoe.xml.in
@@ -109,6 +109,20 @@
<leafNode name="mtu">
<defaultValue>1492</defaultValue>
</leafNode>
+ <leafNode name="mru">
+ <properties>
+ <help>Maximum Receive Unit (MRU)</help>
+ <valueHelp>
+ <format>u32:128-16384</format>
+ <description>Maximum Receive Unit in byte</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 128-16384"/>
+ </constraint>
+ <constraintErrorMessage>MRU must be between 128 and 16384</constraintErrorMessage>
+ </properties>
+ <defaultValue>1492</defaultValue>
+ </leafNode>
#include <include/interface/no-peer-dns.xml.i>
<leafNode name="remote-address">
<properties>
diff --git a/interface-definitions/interfaces-virtual-ethernet.xml.in b/interface-definitions/interfaces-virtual-ethernet.xml.in
index 1daa764d4..5f205f354 100644
--- a/interface-definitions/interfaces-virtual-ethernet.xml.in
+++ b/interface-definitions/interfaces-virtual-ethernet.xml.in
@@ -21,6 +21,7 @@
#include <include/interface/dhcp-options.xml.i>
#include <include/interface/dhcpv6-options.xml.i>
#include <include/interface/disable.xml.i>
+ #include <include/interface/netns.xml.i>
#include <include/interface/vif-s.xml.i>
#include <include/interface/vif.xml.i>
#include <include/interface/vrf.xml.i>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index fb60c93d0..b246d9a09 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -101,6 +101,22 @@
#include <include/interface/redirect.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/vni.xml.i>
+ <tagNode name="vlan-to-vni">
+ <properties>
+ <help>Configuring VLAN-to-VNI mappings for EVPN-VXLAN</help>
+ <valueHelp>
+ <format>u32:0-4094</format>
+ <description>Virtual Local Area Network (VLAN) ID</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4094"/>
+ </constraint>
+ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage>
+ </properties>
+ <children>
+ #include <include/vni.xml.i>
+ </children>
+ </tagNode>
</children>
</tagNode>
</children>
diff --git a/interface-definitions/load-balancing-haproxy.xml.in b/interface-definitions/load-balancing-haproxy.xml.in
index f955a2fb7..564c335ec 100644
--- a/interface-definitions/load-balancing-haproxy.xml.in
+++ b/interface-definitions/load-balancing-haproxy.xml.in
@@ -124,6 +124,12 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="backup">
+ <properties>
+ <help>Use backup server if other servers are not available</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="check">
<properties>
<help>Active health check backend server</help>
diff --git a/interface-definitions/netns.xml.in b/interface-definitions/netns.xml.in
index 5d958968f..d5026bfae 100644
--- a/interface-definitions/netns.xml.in
+++ b/interface-definitions/netns.xml.in
@@ -3,7 +3,7 @@
<node name="netns" owner="${vyos_conf_scripts_dir}/netns.py">
<properties>
<help>Network namespace</help>
- <priority>291</priority>
+ <priority>10</priority>
</properties>
<children>
<tagNode name="name">
diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in
index 8619e839e..6827bd64e 100644
--- a/interface-definitions/policy-local-route.xml.in
+++ b/interface-definitions/policy-local-route.xml.in
@@ -53,42 +53,23 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="source">
+ #include <include/policy/local-route_rule_protocol.xml.i>
+ <node name="source">
<properties>
- <help>Source address or prefix</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Address to match against</description>
- </valueHelp>
- <valueHelp>
- <format>ipv4net</format>
- <description>Prefix to match against</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ip-prefix"/>
- </constraint>
- <multi/>
+ <help>Source parameters</help>
</properties>
- </leafNode>
- <leafNode name="destination">
+ <children>
+ #include <include/policy/local-route_rule_ipv4_address.xml.i>
+ </children>
+ </node>
+ <node name="destination">
<properties>
- <help>Destination address or prefix</help>
- <valueHelp>
- <format>ipv4</format>
- <description>Address to match against</description>
- </valueHelp>
- <valueHelp>
- <format>ipv4net</format>
- <description>Prefix to match against</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- <validator name="ip-prefix"/>
- </constraint>
- <multi/>
+ <help>Destination parameters</help>
</properties>
- </leafNode>
+ <children>
+ #include <include/policy/local-route_rule_ipv4_address.xml.i>
+ </children>
+ </node>
#include <include/interface/inbound-interface.xml.i>
</children>
</tagNode>
@@ -144,42 +125,22 @@
</constraint>
</properties>
</leafNode>
- <leafNode name="source">
+ <node name="source">
<properties>
- <help>Source address or prefix</help>
- <valueHelp>
- <format>ipv6</format>
- <description>Address to match against</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6net</format>
- <description>Prefix to match against</description>
- </valueHelp>
- <constraint>
- <validator name="ipv6-address"/>
- <validator name="ipv6-prefix"/>
- </constraint>
- <multi/>
+ <help>Source parameters</help>
</properties>
- </leafNode>
- <leafNode name="destination">
+ <children>
+ #include <include/policy/local-route_rule_ipv6_address.xml.i>
+ </children>
+ </node>
+ <node name="destination">
<properties>
- <help>Destination address or prefix</help>
- <valueHelp>
- <format>ipv6</format>
- <description>Address to match against</description>
- </valueHelp>
- <valueHelp>
- <format>ipv6net</format>
- <description>Prefix to match against</description>
- </valueHelp>
- <constraint>
- <validator name="ipv6-address"/>
- <validator name="ipv6-prefix"/>
- </constraint>
- <multi/>
+ <help>Destination parameters</help>
</properties>
- </leafNode>
+ <children>
+ #include <include/policy/local-route_rule_ipv6_address.xml.i>
+ </children>
+ </node>
#include <include/interface/inbound-interface.xml.i>
</children>
</tagNode>
diff --git a/interface-definitions/protocols-pim6.xml.in b/interface-definitions/protocols-pim6.xml.in
new file mode 100644
index 000000000..58ef5a1e3
--- /dev/null
+++ b/interface-definitions/protocols-pim6.xml.in
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<!-- Protocol Independent Multicast for IPv6 (PIMv6) configuration -->
+<interfaceDefinition>
+ <node name="protocols">
+ <children>
+ <node name="pim6" owner="${vyos_conf_scripts_dir}/protocols_pim6.py">
+ <properties>
+ <help>Protocol Independent Multicast for IPv6 (PIMv6)</help>
+ <priority>400</priority>
+ </properties>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>PIMv6 interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="mld">
+ <properties>
+ <help>Multicast Listener Discovery (MLD)</help>
+ </properties>
+ <children>
+ #include <include/generic-disable-node.xml.i>
+ <tagNode name="join">
+ <properties>
+ <help>MLD join multicast group</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Multicast group address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="source">
+ <properties>
+ <help>Source address</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Source address</description>
+ </valueHelp>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_local_ips.sh --ipv6</script>
+ </completionHelp>
+ <constraint>
+ <validator name="ipv6-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="version">
+ <properties>
+ <help>MLD version</help>
+ <completionHelp>
+ <list>1 2</list>
+ </completionHelp>
+ <valueHelp>
+ <format>1</format>
+ <description>MLD version 1</description>
+ </valueHelp>
+ <valueHelp>
+ <format>2</format>
+ <description>MLD version 2</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-2"/>
+ </constraint>
+ </properties>
+ <defaultValue>2</defaultValue>
+ </leafNode>
+ <leafNode name="interval">
+ <properties>
+ <help>Query interval</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Query interval in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="max-response-time">
+ <properties>
+ <help>Max query response time</help>
+ <valueHelp>
+ <format>u32:100-6553500</format>
+ <description>Query response value in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 100-6553500"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="last-member-query-count">
+ <properties>
+ <help>Last member query count</help>
+ <valueHelp>
+ <format>u32:1-255</format>
+ <description>Count</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="last-member-query-interval">
+ <properties>
+ <help>Last member query interval</help>
+ <valueHelp>
+ <format>u32:100-6553500</format>
+ <description>Last member query interval in milliseconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 100-6553500"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/service-aws-glb.xml.in b/interface-definitions/service-aws-glb.xml.in
new file mode 100644
index 000000000..c749fd04e
--- /dev/null
+++ b/interface-definitions/service-aws-glb.xml.in
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="aws">
+ <properties>
+ <help>Amazon Web Service</help>
+ <priority>1280</priority>
+ </properties>
+ <children>
+ <node name="glb" owner="${vyos_conf_scripts_dir}/service_aws_glb.py">
+ <properties>
+ <help>Gateway load-balancer tunnel handler</help>
+ </properties>
+ <children>
+ <node name="script">
+ <properties>
+ <help>Script executed on create or destroy tunnel</help>
+ </properties>
+ <children>
+ <leafNode name="on-create">
+ <properties>
+ <help>Script to run when interface is created</help>
+ <constraint>
+ <validator name="script"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="on-destroy">
+ <properties>
+ <help>Script to run when interface is destroyed</help>
+ <constraint>
+ <validator name="script"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="status">
+ <properties>
+ <help>Status</help>
+ </properties>
+ <children>
+ <leafNode name="format">
+ <properties>
+ <help>Statistic format</help>
+ <completionHelp>
+ <list>simple full</list>
+ </completionHelp>
+ <valueHelp>
+ <format>simple</format>
+ <description>Simple format</description>
+ </valueHelp>
+ <valueHelp>
+ <format>full</format>
+ <description>Full format</description>
+ </valueHelp>
+ <constraint>
+ <regex>(simple|full)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ </children>
+ </node>
+ <node name="threads">
+ <properties>
+ <help>Threads settings</help>
+ </properties>
+ <children>
+ <leafNode name="tunnel">
+ <properties>
+ <help>Number of threads for each tunnel processor</help>
+ <valueHelp>
+ <format>u32:1-256</format>
+ <description>Number of threads</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-256"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="tunnel-affinity">
+ <properties>
+ <help>List of cores worker threads</help>
+ <valueHelp>
+ <format>&lt;idN&gt;-&lt;idM&gt;</format>
+ <description>CPU core id range (use '-' as delimiter)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="udp">
+ <properties>
+ <help>Number of threads for UDP receiver</help>
+ <valueHelp>
+ <format>u32:1-256</format>
+ <description>Number of threads</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-256"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="udp-affinity">
+ <properties>
+ <help>List of cores worker threads</help>
+ <valueHelp>
+ <format>&lt;idN&gt;-&lt;idM&gt;</format>
+ <description>CPU core id range (use '-' as delimiter)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--allow-range --range 0-255"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/service-mdns-repeater.xml.in b/interface-definitions/service-mdns-repeater.xml.in
index 653dbbbe4..67870946c 100644
--- a/interface-definitions/service-mdns-repeater.xml.in
+++ b/interface-definitions/service-mdns-repeater.xml.in
@@ -15,6 +15,31 @@
<children>
#include <include/generic-disable-node.xml.i>
#include <include/generic-interface-multi.xml.i>
+ <leafNode name="ip-version">
+ <properties>
+ <help>IP address version to use</help>
+ <valueHelp>
+ <format>_ipv4</format>
+ <description>Use only IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>_ipv6</format>
+ <description>Use only IPv6 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>both</format>
+ <description>Use both IPv4 and IPv6 address</description>
+ </valueHelp>
+ <completionHelp>
+ <list>ipv4 ipv6 both</list>
+ </completionHelp>
+ <constraint>
+ <regex>(ipv[46]|both)</regex>
+ </constraint>
+ <constraintErrorMessage>IP Version must be literal 'ipv4', 'ipv6' or 'both'</constraintErrorMessage>
+ </properties>
+ <defaultValue>both</defaultValue>
+ </leafNode>
<leafNode name="browse-domain">
<properties>
<help>mDNS browsing domains in addition to the default one</help>
diff --git a/interface-definitions/service-webproxy.xml.in b/interface-definitions/service-webproxy.xml.in
index b24997816..637d57891 100644
--- a/interface-definitions/service-webproxy.xml.in
+++ b/interface-definitions/service-webproxy.xml.in
@@ -353,7 +353,7 @@
<description>Object size in KB</description>
</valueHelp>
<constraint>
- <validator name="numeric" argument="--range 1-100000"/>
+ <validator name="numeric" argument="--range 1-1000000"/>
</constraint>
</properties>
</leafNode>
diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in
index 8dad048b8..4452f1a74 100644
--- a/interface-definitions/system-conntrack.xml.in
+++ b/interface-definitions/system-conntrack.xml.in
@@ -9,6 +9,12 @@
<priority>218</priority>
</properties>
<children>
+ <leafNode name="flow-accounting">
+ <properties>
+ <help>Enable connection tracking flow accounting</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="expect-table-size">
<properties>
<help>Size of connection tracking expect table</help>
@@ -40,82 +46,179 @@
<help>Customized rules to ignore selective connection tracking</help>
</properties>
<children>
- <tagNode name="rule">
+ <node name="ipv4">
<properties>
- <help>Rule number</help>
- <valueHelp>
- <format>u32:1-999999</format>
- <description>Number of conntrack ignore rule</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-999999"/>
- </constraint>
- <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage>
+ <help>IPv4 rules</help>
</properties>
<children>
- #include <include/generic-description.xml.i>
- <node name="destination">
+ <tagNode name="rule">
<properties>
- <help>Destination parameters</help>
+ <help>Rule number</help>
+ <valueHelp>
+ <format>u32:1-999999</format>
+ <description>Number of conntrack ignore rule</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-999999"/>
+ </constraint>
+ <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
- #include <include/nat-address.xml.i>
- #include <include/nat-port.xml.i>
+ #include <include/generic-description.xml.i>
+ <node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/source-destination-group-ipv4.xml.i>
+ #include <include/nat-address.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ <leafNode name="inbound-interface">
+ <properties>
+ <help>Interface to ignore connections tracking on</help>
+ <completionHelp>
+ <list>any</list>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ #include <include/ip-protocol.xml.i>
+ <leafNode name="protocol">
+ <properties>
+ <help>Protocol to match (protocol name, number, or "all")</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_protocols.sh</script>
+ <list>all tcp_udp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>all</format>
+ <description>All IP protocols</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tcp_udp</format>
+ <description>Both TCP and UDP</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>IP protocol number</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;protocol&gt;</format>
+ <description>IP protocol name</description>
+ </valueHelp>
+ <valueHelp>
+ <format>!&lt;protocol&gt;</format>
+ <description>IP protocol name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-protocol"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/source-destination-group-ipv4.xml.i>
+ #include <include/nat-address.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ #include <include/firewall/tcp-flags.xml.i>
</children>
- </node>
- <leafNode name="inbound-interface">
- <properties>
- <help>Interface to ignore connections tracking on</help>
- <completionHelp>
- <list>any</list>
- <script>${vyos_completion_dir}/list_interfaces</script>
- </completionHelp>
- </properties>
- </leafNode>
- #include <include/ip-protocol.xml.i>
- <leafNode name="protocol">
+ </tagNode>
+ </children>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>IPv6 rules</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
<properties>
- <help>Protocol to match (protocol name, number, or "all")</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_protocols.sh</script>
- <list>all tcp_udp</list>
- </completionHelp>
- <valueHelp>
- <format>all</format>
- <description>All IP protocols</description>
- </valueHelp>
- <valueHelp>
- <format>tcp_udp</format>
- <description>Both TCP and UDP</description>
- </valueHelp>
- <valueHelp>
- <format>u32:0-255</format>
- <description>IP protocol number</description>
- </valueHelp>
- <valueHelp>
- <format>&lt;protocol&gt;</format>
- <description>IP protocol name</description>
- </valueHelp>
+ <help>Rule number</help>
<valueHelp>
- <format>!&lt;protocol&gt;</format>
- <description>IP protocol name</description>
+ <format>u32:1-999999</format>
+ <description>Number of conntrack ignore rule</description>
</valueHelp>
<constraint>
- <validator name="ip-protocol"/>
+ <validator name="numeric" argument="--range 1-999999"/>
</constraint>
- </properties>
- </leafNode>
- <node name="source">
- <properties>
- <help>Source parameters</help>
+ <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
- #include <include/nat-address.xml.i>
- #include <include/nat-port.xml.i>
+ #include <include/generic-description.xml.i>
+ <node name="destination">
+ <properties>
+ <help>Destination parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address-ipv6.xml.i>
+ #include <include/firewall/source-destination-group-ipv6.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ <leafNode name="inbound-interface">
+ <properties>
+ <help>Interface to ignore connections tracking on</help>
+ <completionHelp>
+ <list>any</list>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ #include <include/ip-protocol.xml.i>
+ <leafNode name="protocol">
+ <properties>
+ <help>Protocol to match (protocol name, number, or "all")</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_protocols.sh</script>
+ <list>all tcp_udp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>all</format>
+ <description>All IP protocols</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tcp_udp</format>
+ <description>Both TCP and UDP</description>
+ </valueHelp>
+ <valueHelp>
+ <format>u32:0-255</format>
+ <description>IP protocol number</description>
+ </valueHelp>
+ <valueHelp>
+ <format>&lt;protocol&gt;</format>
+ <description>IP protocol name</description>
+ </valueHelp>
+ <valueHelp>
+ <format>!&lt;protocol&gt;</format>
+ <description>IP protocol name</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-protocol"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="source">
+ <properties>
+ <help>Source parameters</help>
+ </properties>
+ <children>
+ #include <include/firewall/address-ipv6.xml.i>
+ #include <include/firewall/source-destination-group-ipv6.xml.i>
+ #include <include/nat-port.xml.i>
+ </children>
+ </node>
+ #include <include/firewall/tcp-flags.xml.i>
</children>
- </node>
+ </tagNode>
</children>
- </tagNode>
+ </node>
+
</children>
</node>
<node name="log">
diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in
index 71db8b1d6..30fea91b0 100644
--- a/interface-definitions/system-login.xml.in
+++ b/interface-definitions/system-login.xml.in
@@ -259,6 +259,26 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="security-mode">
+ <properties>
+ <help>Security mode for TACACS+ authentication</help>
+ <completionHelp>
+ <list>mandatory optional</list>
+ </completionHelp>
+ <valueHelp>
+ <format>mandatory</format>
+ <description>Deny access immediately if TACACS+ answers with REJECT</description>
+ </valueHelp>
+ <valueHelp>
+ <format>optional</format>
+ <description>Pass to the next authentication method if TACACS+ answers with REJECT</description>
+ </valueHelp>
+ <constraint>
+ <regex>(mandatory|optional)</regex>
+ </constraint>
+ </properties>
+ <defaultValue>optional</defaultValue>
+ </leafNode>
#include <include/radius-timeout.xml.i>
#include <include/interface/vrf.xml.i>
</children>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 3783785ce..e5ec539d3 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -4,7 +4,7 @@
<properties>
<help>Virtual Routing and Forwarding</help>
<!-- must be before any interface, check /opt/vyatta/sbin/priority.pl -->
- <priority>299</priority>
+ <priority>11</priority>
</properties>
<children>
<leafNode name="bind-to-all">
diff --git a/op-mode-definitions/disks.xml.in b/op-mode-definitions/disks.xml.in
index 117ac5065..8a1e2c86f 100644
--- a/op-mode-definitions/disks.xml.in
+++ b/op-mode-definitions/disks.xml.in
@@ -5,6 +5,26 @@
<help>Format a device</help>
</properties>
<children>
+ <node name="by-id">
+ <properties>
+ <help>Find disk by ending of id string</help>
+ </properties>
+ <children>
+ <tagNode name="disk">
+ <properties>
+ <help>Format a disk drive</help>
+ </properties>
+ <children>
+ <tagNode name="like">
+ <properties>
+ <help>Format this disk the same as another disk</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/format_disk.py --by-id --target $4 --proto $6</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
<tagNode name="disk">
<properties>
<help>Format a disk drive</help>
diff --git a/op-mode-definitions/firewall.xml.in b/op-mode-definitions/firewall.xml.in
index 0f296c272..4a7ffbb66 100644
--- a/op-mode-definitions/firewall.xml.in
+++ b/op-mode-definitions/firewall.xml.in
@@ -132,6 +132,58 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group</command>
</leafNode>
+ <node name="bridge">
+ <properties>
+ <help>Show bridge firewall</help>
+ </properties>
+ <children>
+ <node name="forward">
+ <properties>
+ <help>Show bridge forward firewall ruleset</help>
+ </properties>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show bridge forward filter firewall ruleset</help>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of bridge forward filter firewall rules</help>
+ <completionHelp>
+ <path>firewall bridge forward filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </node>
+ </children>
+ </node>
+ <tagNode name="name">
+ <properties>
+ <help>Show bridge custom firewall chains</help>
+ <completionHelp>
+ <path>firewall bridge name</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show summary of bridge custom firewall ruleset</help>
+ <completionHelp>
+ <path>firewall bridge name ${COMP_WORDS[6]} rule</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
+ </tagNode>
+ </children>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
+ </node>
<node name="ipv6">
<properties>
<help>Show IPv6 firewall</help>
@@ -154,10 +206,10 @@
<path>firewall ipv6 forward filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -178,10 +230,10 @@
<path>firewall ipv6 input filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -202,10 +254,10 @@
<path>firewall ipv6 output filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -224,10 +276,10 @@
<path>firewall ipv6 ipv6-name ${COMP_WORDS[6]} rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --ipv6</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</tagNode>
</children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
@@ -254,10 +306,10 @@
<path>firewall ipv4 forward filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -278,10 +330,10 @@
<path>firewall ipv4 input filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -302,10 +354,10 @@
<path>firewall ipv4 output filter rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</node>
</children>
</node>
@@ -324,10 +376,10 @@
<path>firewall ipv4 name ${COMP_WORDS[6]} rule</path>
</completionHelp>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5 --rule $7</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5 --rule $7</command>
</tagNode>
</children>
- <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --hook $4 --priority $5</command>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show --family $3 --hook $4 --priority $5</command>
</tagNode>
</children>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_family --family $3</command>
diff --git a/op-mode-definitions/generate_firewall_rule-resequence.xml.in b/op-mode-definitions/generate_firewall_rule-resequence.xml.in
new file mode 100644
index 000000000..66078deb9
--- /dev/null
+++ b/op-mode-definitions/generate_firewall_rule-resequence.xml.in
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="generate">
+ <children>
+ <node name="firewall">
+ <properties>
+ <help>Firewall</help>
+ </properties>
+ <children>
+ <node name="rule-resequence">
+ <properties>
+ <help>Resequence the firewall rules</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/generate_firewall_rule-resequence.py</command>
+ <children>
+ <tagNode name="start">
+ <properties>
+ <help>Set the first sequence number</help>
+ <completionHelp>
+ <list>1-1000</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/generate_firewall_rule-resequence.py --start $5</command>
+ <children>
+ <tagNode name="step">
+ <properties>
+ <help>Step between rules</help>
+ <completionHelp>
+ <list>1-1000</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/generate_firewall_rule-resequence.py --start $5 --step $7</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/monitor-command.xml.in b/op-mode-definitions/monitor-command.xml.in
new file mode 100644
index 000000000..31c68f029
--- /dev/null
+++ b/op-mode-definitions/monitor-command.xml.in
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="monitor">
+ <children>
+ <tagNode name="command">
+ <properties>
+ <help>Monitor operational mode command (refreshes every 2 seconds)</help>
+ </properties>
+ <command>watch --no-title ${vyos_op_scripts_dir}/vyos-op-cmd-wrapper.sh ${@:3}</command>
+ </tagNode>
+ <node name="command">
+ <children>
+ <node name="diff">
+ <properties>
+ <help>Show differences during each run</help>
+ </properties>
+ </node>
+ <tagNode name="diff">
+ <properties>
+ <help>Monitor operational mode command (refreshes every 2 seconds)</help>
+ </properties>
+ <command>watch --no-title --differences ${vyos_op_scripts_dir}/vyos-op-cmd-wrapper.sh ${@:4}</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/ntp.xml.in b/op-mode-definitions/ntp.xml.in
new file mode 100644
index 000000000..b8d0c43ec
--- /dev/null
+++ b/op-mode-definitions/ntp.xml.in
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="show">
+ <children>
+ <node name="ntp">
+ <properties>
+ <help>Show peer status of NTP daemon</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_ntp.sh --sourcestats</command>
+ <children>
+ <node name="system">
+ <properties>
+ <help>Show parameters about the system clock performance</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/show_ntp.sh --tracking</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="force">
+ <children>
+ <node name="ntp">
+ <properties>
+ <help>NTP (Network Time Protocol) operations</help>
+ </properties>
+ <children>
+ <node name="synchronization">
+ <properties>
+ <help>Force NTP time synchronization</help>
+ </properties>
+ <children>
+ <tagNode name="vrf">
+ <properties>
+ <help>Force NTP time synchronization in given VRF</help>
+ <completionHelp>
+ <path>vrf name</path>
+ </completionHelp>
+ </properties>
+ <command>sudo ip vrf exec $5 chronyc makestep</command>
+ </tagNode>
+ </children>
+ <command>sudo chronyc makestep</command>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/raid.xml.in b/op-mode-definitions/raid.xml.in
new file mode 100644
index 000000000..85fbf4566
--- /dev/null
+++ b/op-mode-definitions/raid.xml.in
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="add">
+ <children>
+ <tagNode name="raid">
+ <properties>
+ <help>Add a RAID set element</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_raidset.sh</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="by-id">
+ <properties>
+ <help>Add a member by disk id to a RAID set</help>
+ </properties>
+ <children>
+ <tagNode name="member">
+ <properties>
+ <help>Add a member to a RAID set</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/raid.py add --raid-set-name $3 --by-id --member $6</command>
+ </tagNode>
+ </children>
+ </node>
+ <tagNode name="member">
+ <properties>
+ <help>Add a member to a RAID set</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/raid.py add --raid-set-name $3 --member $5</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <node name="delete">
+ <children>
+ <tagNode name="raid">
+ <properties>
+ <help>Delete a RAID set element</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_raidset.sh</script>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="by-id">
+ <properties>
+ <help>Add a member by disk id to a RAID set</help>
+ </properties>
+ <children>
+ <tagNode name="member">
+ <properties>
+ <help>Delete a member from a RAID set</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/raid.py delete --raid-set-name $3 --by-id --member $6</command>
+ </tagNode>
+ </children>
+ </node>
+ <tagNode name="member">
+ <properties>
+ <help>Delete a member from a RAID set</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/raid.py delete --raid-set-name $3 --member $5</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/op-mode-definitions/restart-frr.xml.in b/op-mode-definitions/restart-frr.xml.in
index 4572858b5..2c9d4b1cc 100644
--- a/op-mode-definitions/restart-frr.xml.in
+++ b/op-mode-definitions/restart-frr.xml.in
@@ -8,29 +8,23 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart</command>
</leafNode>
- <leafNode name="bfd">
- <properties>
- <help>Restart Bidirectional Forwarding Detection (BFD) daemon</help>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd</command>
- </leafNode>
- <leafNode name="bgp">
+ <leafNode name="zebra">
<properties>
- <help>Restart Border Gateway Protocol (BGP) routing daemon</help>
+ <help>Restart Routing Information Base (RIB) IP manager daemon</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd</command>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra</command>
</leafNode>
- <leafNode name="isis">
+ <leafNode name="static">
<properties>
- <help>Restart Intermediate System to Intermediate System (IS-IS) routing daemon</help>
+ <help>Restart static routing daemon</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon isisd</command>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd</command>
</leafNode>
- <leafNode name="ldp">
+ <leafNode name="bgp">
<properties>
- <help>Restart the Label Distribution Protocol (LDP) daemon</help>
+ <help>Restart Border Gateway Protocol (BGP) routing daemon</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ldpd</command>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bgpd</command>
</leafNode>
<leafNode name="ospf">
<properties>
@@ -52,21 +46,27 @@
</leafNode>
<leafNode name="ripng">
<properties>
- <help>Restart Routing Information Protocol NG (RIPng) routing daemon</help>
+ <help>Restart IPv6 Routing Information Protocol (RIPng) routing daemon</help>
</properties>
<command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ripngd</command>
</leafNode>
- <leafNode name="static">
+ <leafNode name="isis">
<properties>
- <help>Restart static routing daemon</help>
+ <help>Restart Intermediate System to Intermediate System (IS-IS) routing daemon</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon staticd</command>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon isisd</command>
</leafNode>
- <leafNode name="zebra">
+ <leafNode name="pim6">
<properties>
- <help>Restart Routing Information Base (RIB) manager daemon</help>
+ <help>Restart IPv6 Protocol Independent Multicast (PIM) daemon</help>
</properties>
- <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon zebra</command>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon pim6d</command>
+ </leafNode>
+ <leafNode name="ldp">
+ <properties>
+ <help>Restart Label Distribution Protocol (LDP) daemon used by MPLS</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon ldpd</command>
</leafNode>
<leafNode name="babel">
<properties>
@@ -74,6 +74,12 @@
</properties>
<command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon babeld</command>
</leafNode>
+ <leafNode name="bfd">
+ <properties>
+ <help>Restart Bidirectional Forwarding Detection (BFD) daemon</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/restart_frr.py --action restart --daemon bfdd</command>
+ </leafNode>
</children>
</node>
</interfaceDefinition>
diff --git a/op-mode-definitions/show-hardware.xml.in b/op-mode-definitions/show-hardware.xml.in
index ebd806ba5..21079765a 100644
--- a/op-mode-definitions/show-hardware.xml.in
+++ b/op-mode-definitions/show-hardware.xml.in
@@ -31,7 +31,7 @@
<properties>
<help>Show system DMI details</help>
</properties>
- <command>${vyatta_bindir}/vyatta-show-dmi</command>
+ <command>sudo dmidecode</command>
</node>
<node name="mem">
<properties>
diff --git a/op-mode-definitions/show-ip.xml.in b/op-mode-definitions/show-ip.xml.in
index d5dbb7850..3caf1f1ea 100644
--- a/op-mode-definitions/show-ip.xml.in
+++ b/op-mode-definitions/show-ip.xml.in
@@ -33,6 +33,12 @@
</tagNode>
</children>
</node>
+ <leafNode name="nht">
+ <properties>
+ <help>Show IPv4 nexthop tracking table</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/vtysh_wrapper.sh $@</command>
+ </leafNode>
</children>
</node>
</children>
diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in
index 747622db6..a2a210543 100644
--- a/op-mode-definitions/show-log.xml.in
+++ b/op-mode-definitions/show-log.xml.in
@@ -133,47 +133,267 @@
<properties>
<help>Show log for Firewall</help>
</properties>
+ <command>journalctl --no-hostname --boot -k | egrep "(ipv[46]|bri)-(FWD|INP|OUT|NAM)"</command>
<children>
- <tagNode name="ipv6-name">
+ <node name="bridge">
<properties>
- <help>Show log for a specified firewall (IPv6)</help>
- <completionHelp>
- <path>firewall ipv6-name</path>
- </completionHelp>
+ <help>Show firewall bridge log</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr ) | egrep "\[$5-([0-9]+|default)-[ADR]\]"</command>
+ <command>journalctl --no-hostname --boot -k | egrep "bri-(FWD|INP|OUT|NAM)"</command>
<children>
- <tagNode name="rule">
+ <node name="forward">
+ <properties>
+ <help>Show Bridge forward firewall log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-FWD</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show Bridge firewall forward filter</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep bri-FWD-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall bridge forward filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[bri-FWD-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <tagNode name="name">
<properties>
- <help>Show log for a rule in the specified firewall</help>
+ <help>Show custom Bridge firewall log</help>
<completionHelp>
- <path>firewall ipv6-name ${COMP_WORDS[4]} rule</path>
+ <path>firewall bridge name</path>
</completionHelp>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | grep -e "\[$5-$7-[ADR]\]"</command>
+ <command>journalctl --no-hostname --boot -k | grep bri-NAM-$6</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall bridge name ${COMP_WORDS[5]} rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[bri-NAM-$6-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
</tagNode>
</children>
- </tagNode>
- <tagNode name="name">
+ </node>
+ <node name="ipv4">
<properties>
- <help>Show log for a specified firewall (IPv4)</help>
- <completionHelp>
- <path>firewall name</path>
- </completionHelp>
+ <help>Show firewall IPv4 log</help>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr ) | egrep "\[$5-([0-9]+|default)-[ADR]\]"</command>
+ <command>journalctl --no-hostname --boot -k | egrep "ipv4-(FWD|INP|OUT|NAM)"</command>
<children>
- <tagNode name="rule">
+ <node name="forward">
<properties>
- <help>Show log for a rule in the specified firewall</help>
+ <help>Show firewall IPv4 forward log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-FWD</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show firewall IPv4 forward filter log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-FWD-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv4 forward filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-FWD-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="input">
+ <properties>
+ <help>Show firewall IPv4 input log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-INP</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show firewall IPv4 input filter log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-INP-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv4 input filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-INP-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <tagNode name="name">
+ <properties>
+ <help>Show custom IPv4 firewall log</help>
<completionHelp>
- <path>firewall name ${COMP_WORDS[4]} rule</path>
+ <path>firewall ipv4 name</path>
</completionHelp>
</properties>
- <command>cat $(printf "%s\n" /var/log/messages* | sort -nr) | egrep "\[$5-$7-[ADR]\]"</command>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-NAM-$6</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv4 name ${COMP_WORDS[5]} rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-NAM-$6-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
</tagNode>
+ <node name="output">
+ <properties>
+ <help>Show firewall IPv4 output log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-OUT</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show firewall IPv4 output filter log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv4-OUT-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv4 output filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv4-OUT-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
</children>
- </tagNode>
+ </node>
+ <node name="ipv6">
+ <properties>
+ <help>Show firewall IPv6 log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "ipv6-(FWD|INP|OUT|NAM)"</command>
+ <children>
+ <node name="forward">
+ <properties>
+ <help>Show firewall IPv6 forward log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-FWD</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show firewall IPv6 forward filter log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-FWD-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv6 forward filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-FWD-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <node name="input">
+ <properties>
+ <help>Show firewall IPv6 input log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-INP</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show firewall IPv6 input filter log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-INP-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv6 input filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-INP-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ <tagNode name="name">
+ <properties>
+ <help>Show custom IPv6 firewall log</help>
+ <completionHelp>
+ <path>firewall ipv6 name</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-NAM-$6</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv6 name ${COMP_WORDS[5]} rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-NAM-$6-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ <node name="output">
+ <properties>
+ <help>Show firewall IPv6 output log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-OUT</command>
+ <children>
+ <node name="filter">
+ <properties>
+ <help>Show firewall IPv6 output filter log</help>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | grep ipv6-OUT-filter</command>
+ <children>
+ <tagNode name="rule">
+ <properties>
+ <help>Show log for a rule in the specified firewall</help>
+ <completionHelp>
+ <path>firewall ipv6 output filter rule</path>
+ </completionHelp>
+ </properties>
+ <command>journalctl --no-hostname --boot -k | egrep "\[ipv6-OUT-filter-$8-[ADRJC]\]"</command>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
</children>
</node>
<leafNode name="flow-accounting">
diff --git a/op-mode-definitions/show-ntp.xml.in b/op-mode-definitions/show-ntp.xml.in
deleted file mode 100644
index 0907722af..000000000
--- a/op-mode-definitions/show-ntp.xml.in
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="show">
- <children>
- <node name="ntp">
- <properties>
- <help>Show peer status of NTP daemon</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_ntp.sh --sourcestats</command>
- <children>
- <node name="system">
- <properties>
- <help>Show parameters about the system clock performance</help>
- </properties>
- <command>${vyos_op_scripts_dir}/show_ntp.sh --tracking</command>
- </node>
- </children>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in
index 85bfdcdba..116c7460f 100644
--- a/op-mode-definitions/show-system.xml.in
+++ b/op-mode-definitions/show-system.xml.in
@@ -102,6 +102,55 @@
<help>Show user accounts</help>
</properties>
<children>
+ <node name="authentication">
+ <properties>
+ <help>Show user account authentication information</help>
+ </properties>
+ <children>
+ <tagNode name="user">
+ <properties>
+ <help>Show configured user</help>
+ <completionHelp>
+ <path>system login user</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <node name="otp">
+ <properties>
+ <help>Show OTP key information</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/otp.py show_login --username="$6" --info="full"</command>
+ <children>
+ <leafNode name="full">
+ <properties>
+ <help>Show full settings, including QR code and commands for VyOS</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/otp.py show_login --username="$6" --info="full"</command>
+ </leafNode>
+ <leafNode name="key-b32">
+ <properties>
+ <help>Show OTP authentication secret in Base32 (used in mobile apps)</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/otp.py show_login --username="$6" --info="key-b32"</command>
+ </leafNode>
+ <leafNode name="qrcode">
+ <properties>
+ <help>Show OTP authentication QR code</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/otp.py show_login --username="$6" --info="qrcode"</command>
+ </leafNode>
+ <leafNode name="uri">
+ <properties>
+ <help>Show OTP authentication otpauth URI</help>
+ </properties>
+ <command>${vyos_op_scripts_dir}/otp.py show_login --username="$6" --info="uri"</command>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
<node name="users">
<properties>
<help>Show user account information</help>
diff --git a/op-mode-definitions/show-techsupport_report.xml.in b/op-mode-definitions/show-techsupport_report.xml.in
index aa51eacd9..ef051e940 100644
--- a/op-mode-definitions/show-techsupport_report.xml.in
+++ b/op-mode-definitions/show-techsupport_report.xml.in
@@ -3,6 +3,9 @@
<node name="show">
<children>
<node name="tech-support">
+ <properties>
+ <help>Show tech-support report</help>
+ </properties>
<children>
<node name="report">
<properties>
diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in
index c7ba780a3..b551af2be 100644
--- a/op-mode-definitions/vpn-ipsec.xml.in
+++ b/op-mode-definitions/vpn-ipsec.xml.in
@@ -177,7 +177,7 @@
<properties>
<help>Show all the pre-shared key secrets</help>
</properties>
- <command>sudo cat /etc/ipsec.secrets | sed 's/#.*//'</command>
+ <command>${vyos_op_scripts_dir}/ipsec.py show_psk</command>
</node>
<node name="status">
<properties>
diff --git a/op-mode-definitions/zone-policy.xml.in b/op-mode-definitions/zone-policy.xml.in
deleted file mode 100644
index 9d65ddd3d..000000000
--- a/op-mode-definitions/zone-policy.xml.in
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-<interfaceDefinition>
- <node name="show">
- <children>
- <node name="zone-policy">
- <properties>
- <help>Show zone policy information</help>
- </properties>
- <children>
- <tagNode name="zone">
- <properties>
- <help>Show summary of zone policy for a specific zone</help>
- <completionHelp>
- <path>firewall zone</path>
- </completionHelp>
- </properties>
- <command>sudo ${vyos_op_scripts_dir}/zone.py show --zone $4</command>
- </tagNode>
- </children>
- <command>sudo ${vyos_op_scripts_dir}/zone.py show</command>
- </node>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/python/vyos/component_version.py b/python/vyos/component_version.py
index 84e0ae51a..9662ebfcf 100644
--- a/python/vyos/component_version.py
+++ b/python/vyos/component_version.py
@@ -90,31 +90,6 @@ def from_system():
"""
return component_version()
-def legacy_from_system():
- """
- Get system component version dict from legacy location.
- This is for a transitional sanity check; the directory will eventually
- be removed.
- """
- system_versions = {}
- legacy_dir = directories['current']
-
- # To be removed:
- if not os.path.isdir(legacy_dir):
- return system_versions
-
- try:
- version_info = os.listdir(legacy_dir)
- except OSError as err:
- sys.exit(repr(err))
-
- for info in version_info:
- if re.match(r'[\w,-]+@\d+', info):
- pair = info.split('@')
- system_versions[pair[0]] = int(pair[1])
-
- return system_versions
-
def format_string(ver: dict) -> str:
"""
Version dict to string.
diff --git a/python/vyos/config_mgmt.py b/python/vyos/config_mgmt.py
index 0fc72e660..654a8d698 100644
--- a/python/vyos/config_mgmt.py
+++ b/python/vyos/config_mgmt.py
@@ -25,16 +25,19 @@ from datetime import datetime
from textwrap import dedent
from pathlib import Path
from tabulate import tabulate
+from shutil import copy, chown
from vyos.config import Config
from vyos.configtree import ConfigTree, ConfigTreeError, show_diff
from vyos.defaults import directories
from vyos.version import get_full_version_data
from vyos.utils.io import ask_yes_no
+from vyos.utils.boot import boot_configuration_complete
from vyos.utils.process import is_systemd_service_active
from vyos.utils.process import rc_cmd
SAVE_CONFIG = '/usr/libexec/vyos/vyos-save-config.py'
+config_json = '/run/vyatta/config/config.json'
# created by vyatta-cfg-postinst
commit_post_hook_dir = '/etc/commit/post-hooks.d'
@@ -62,8 +65,11 @@ formatter = logging.Formatter('%(funcName)s: %(levelname)s:%(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
-def save_config(target):
- cmd = f'{SAVE_CONFIG} {target}'
+def save_config(target, json_out=None):
+ if json_out is None:
+ cmd = f'{SAVE_CONFIG} {target}'
+ else:
+ cmd = f'{SAVE_CONFIG} {target} --write-json-file {json_out}'
rc, out = rc_cmd(cmd)
if rc != 0:
logger.critical(f'save config failed: {out}')
@@ -200,9 +206,9 @@ Proceed ?'''
raise ConfigMgmtError(out)
entry = self._read_tmp_log_entry()
- self._add_log_entry(**entry)
if self._archive_active_config():
+ self._add_log_entry(**entry)
self._update_archive()
msg = 'Reboot timer stopped'
@@ -223,8 +229,6 @@ Proceed ?'''
def rollback(self, rev: int, no_prompt: bool=False) -> Tuple[str,int]:
"""Reboot to config revision 'rev'.
"""
- from shutil import copy
-
msg = ''
if not self._check_revision_number(rev):
@@ -326,6 +330,12 @@ Proceed ?'''
"""
mask = os.umask(0o002)
os.makedirs(archive_dir, exist_ok=True)
+ json_dir = os.path.dirname(config_json)
+ try:
+ os.makedirs(json_dir, exist_ok=True)
+ chown(json_dir, group='vyattacfg')
+ except OSError as e:
+ logger.warning(f'cannot create {json_dir}: {e}')
self._add_logrotate_conf()
@@ -334,10 +344,10 @@ Proceed ?'''
user = self._get_user()
via = 'init'
comment = ''
- self._add_log_entry(user, via, comment)
# add empty init config before boot-config load for revision
# and diff consistency
if self._archive_active_config():
+ self._add_log_entry(user, via, comment)
self._update_archive()
os.umask(mask)
@@ -352,9 +362,8 @@ Proceed ?'''
self._new_log_entry(tmp_file=tmp_log_entry)
return
- self._add_log_entry()
-
if self._archive_active_config():
+ self._add_log_entry()
self._update_archive()
def commit_archive(self):
@@ -475,22 +484,37 @@ Proceed ?'''
conf_file.chmod(0o644)
def _archive_active_config(self) -> bool:
+ save_to_tmp = (boot_configuration_complete() or not
+ os.path.isfile(archive_config_file))
mask = os.umask(0o113)
ext = os.getpid()
- tmp_save = f'/tmp/config.boot.{ext}'
- save_config(tmp_save)
+ cmp_saved = f'/tmp/config.boot.{ext}'
+ if save_to_tmp:
+ save_config(cmp_saved, json_out=config_json)
+ else:
+ copy(config_file, cmp_saved)
+
+ # on boot, we need to manually create the config.json file; after
+ # boot, it is written by save_config, above
+ if not os.path.exists(config_json):
+ ct = self._get_saved_config_tree()
+ try:
+ with open(config_json, 'w') as f:
+ f.write(ct.to_json())
+ chown(config_json, group='vyattacfg')
+ except OSError as e:
+ logger.warning(f'cannot create {config_json}: {e}')
try:
- if cmp(tmp_save, archive_config_file, shallow=False):
- # this will be the case on boot, as well as certain
- # re-initialiation instances after delete/set
- os.unlink(tmp_save)
+ if cmp(cmp_saved, archive_config_file, shallow=False):
+ os.unlink(cmp_saved)
+ os.umask(mask)
return False
except FileNotFoundError:
pass
- rc, out = rc_cmd(f'sudo mv {tmp_save} {archive_config_file}')
+ rc, out = rc_cmd(f'sudo mv {cmp_saved} {archive_config_file}')
os.umask(mask)
if rc != 0:
@@ -522,9 +546,8 @@ Proceed ?'''
return len(l)
def _check_revision_number(self, rev: int) -> bool:
- # exclude init revision:
maxrev = self._get_number_of_revisions()
- if not 0 <= rev < maxrev - 1:
+ if not 0 <= rev < maxrev:
return False
return True
diff --git a/python/vyos/configdep.py b/python/vyos/configdep.py
index 7a8559839..05d9a3fa3 100644
--- a/python/vyos/configdep.py
+++ b/python/vyos/configdep.py
@@ -1,4 +1,4 @@
-# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023 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,8 +17,10 @@ import os
import json
import typing
from inspect import stack
+from graphlib import TopologicalSorter, CycleError
from vyos.utils.system import load_as_module
+from vyos.configdict import dict_merge
from vyos.defaults import directories
from vyos.configsource import VyOSError
from vyos import ConfigError
@@ -28,6 +30,9 @@ from vyos import ConfigError
if typing.TYPE_CHECKING:
from vyos.config import Config
+dependency_dir = os.path.join(directories['data'],
+ 'config-mode-dependencies')
+
dependent_func: dict[str, list[typing.Callable]] = {}
def canon_name(name: str) -> str:
@@ -40,12 +45,20 @@ def canon_name_of_path(path: str) -> str:
def caller_name() -> str:
return stack()[-1].filename
-def read_dependency_dict() -> dict:
- path = os.path.join(directories['data'],
- 'config-mode-dependencies.json')
- with open(path) as f:
- d = json.load(f)
- return d
+def read_dependency_dict(dependency_dir: str = dependency_dir) -> dict:
+ res = {}
+ for dep_file in os.listdir(dependency_dir):
+ if not dep_file.endswith('.json'):
+ continue
+ path = os.path.join(dependency_dir, dep_file)
+ with open(path) as f:
+ d = json.load(f)
+ if dep_file == 'vyos-1x.json':
+ res = dict_merge(res, d)
+ else:
+ res = dict_merge(d, res)
+
+ return res
def get_dependency_dict(config: 'Config') -> dict:
if hasattr(config, 'cached_dependency_dict'):
@@ -93,3 +106,37 @@ def call_dependents():
while l:
f = l.pop(0)
f()
+
+def graph_from_dependency_dict(d: dict) -> dict:
+ g = {}
+ for k in list(d):
+ g[k] = set()
+ # add the dependencies for every sub-case; should there be cases
+ # that are mutally exclusive in the future, the graphs will be
+ # distinguished
+ for el in list(d[k]):
+ g[k] |= set(d[k][el])
+
+ return g
+
+def is_acyclic(d: dict) -> bool:
+ g = graph_from_dependency_dict(d)
+ ts = TopologicalSorter(g)
+ try:
+ # get node iterator
+ order = ts.static_order()
+ # try iteration
+ _ = [*order]
+ except CycleError:
+ return False
+
+ return True
+
+def check_dependency_graph(dependency_dir: str = dependency_dir,
+ supplement: str = None) -> bool:
+ d = read_dependency_dict(dependency_dir=dependency_dir)
+ if supplement is not None:
+ with open(supplement) as f:
+ d = dict_merge(json.load(f), d)
+
+ return is_acyclic(d)
diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py
index a5314790d..a229533bd 100644
--- a/python/vyos/defaults.py
+++ b/python/vyos/defaults.py
@@ -24,7 +24,6 @@ directories = {
'op_mode' : f'{base_dir}/op_mode',
'services' : f'{base_dir}/services',
'config' : '/opt/vyatta/etc/config',
- 'current' : '/opt/vyatta/etc/config-migrate/current',
'migrate' : '/opt/vyatta/etc/config-migrate/migrate',
'log' : '/var/log/vyatta',
'templates' : '/usr/share/vyos/templates/',
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index ca3bcfc3d..f19632719 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -172,6 +172,9 @@ class Ethtool:
def get_generic_segmentation_offload(self):
return self._get_generic('generic-segmentation-offload')
+ def get_hw_tc_offload(self):
+ return self._get_generic('hw-tc-offload')
+
def get_large_receive_offload(self):
return self._get_generic('large-receive-offload')
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 53ff8259e..c07ed1adf 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -87,7 +87,14 @@ def nft_action(vyos_action):
def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
output = []
- def_suffix = '6' if ip_name == 'ip6' else ''
+ #def_suffix = '6' if ip_name == 'ip6' else ''
+
+ if ip_name == 'ip6':
+ def_suffix = '6'
+ family = 'ipv6'
+ else:
+ def_suffix = ''
+ family = 'bri' if ip_name == 'bri' else 'ipv4'
if 'state' in rule_conf and rule_conf['state']:
states = ",".join([s for s, v in rule_conf['state'].items() if v == 'enable'])
@@ -95,6 +102,20 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
if states:
output.append(f'ct state {{{states}}}')
+ if 'conntrack_helper' in rule_conf:
+ helper_map = {'h323': ['RAS', 'Q.931'], 'nfs': ['rpc'], 'sqlnet': ['tns']}
+ helper_out = []
+
+ for helper in rule_conf['conntrack_helper']:
+ if helper in helper_map:
+ helper_out.extend(helper_map[helper])
+ else:
+ helper_out.append(helper)
+
+ if helper_out:
+ helper_str = ','.join(f'"{s}"' for s in helper_out)
+ output.append(f'ct helper {{{helper_str}}}')
+
if 'connection_status' in rule_conf and rule_conf['connection_status']:
status = rule_conf['connection_status']
if status['nat'] == 'destination':
@@ -242,27 +263,8 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
output.append(f'{proto} {prefix}port {operator} @P_{group_name}')
- if 'log' in rule_conf and rule_conf['log'] == 'enable':
- action = rule_conf['action'] if 'action' in rule_conf else 'accept'
- output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}]"')
-
- if 'log_options' in rule_conf:
-
- if 'level' in rule_conf['log_options']:
- log_level = rule_conf['log_options']['level']
- output.append(f'log level {log_level}')
-
- if 'group' in rule_conf['log_options']:
- log_group = rule_conf['log_options']['group']
- output.append(f'log group {log_group}')
-
- if 'queue_threshold' in rule_conf['log_options']:
- queue_threshold = rule_conf['log_options']['queue_threshold']
- output.append(f'queue-threshold {queue_threshold}')
-
- if 'snapshot_length' in rule_conf['log_options']:
- log_snaplen = rule_conf['log_options']['snapshot_length']
- output.append(f'snaplen {log_snaplen}')
+ if dict_search_args(rule_conf, 'action') == 'synproxy':
+ output.append('ct state invalid,untracked')
if 'hop_limit' in rule_conf:
operators = {'eq': '==', 'gt': '>', 'lt': '<'}
@@ -379,6 +381,43 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
conn_mark_str = ','.join(rule_conf['connection_mark'])
output.append(f'ct mark {{{conn_mark_str}}}')
+ if 'mark' in rule_conf:
+ mark = rule_conf['mark']
+ operator = ''
+ if mark[0] == '!':
+ operator = '!='
+ mark = mark[1:]
+ output.append(f'meta mark {operator} {{{mark}}}')
+
+ if 'vlan' in rule_conf:
+ if 'id' in rule_conf['vlan']:
+ output.append(f'vlan id {rule_conf["vlan"]["id"]}')
+ if 'priority' in rule_conf['vlan']:
+ output.append(f'vlan pcp {rule_conf["vlan"]["priority"]}')
+
+ if 'log' in rule_conf and rule_conf['log'] == 'enable':
+ action = rule_conf['action'] if 'action' in rule_conf else 'accept'
+ #output.append(f'log prefix "[{fw_name[:19]}-{rule_id}-{action[:1].upper()}]"')
+ output.append(f'log prefix "[{family}-{hook}-{fw_name}-{rule_id}-{action[:1].upper()}]"')
+ ##{family}-{hook}-{fw_name}-{rule_id}
+ if 'log_options' in rule_conf:
+
+ if 'level' in rule_conf['log_options']:
+ log_level = rule_conf['log_options']['level']
+ output.append(f'log level {log_level}')
+
+ if 'group' in rule_conf['log_options']:
+ log_group = rule_conf['log_options']['group']
+ output.append(f'log group {log_group}')
+
+ if 'queue_threshold' in rule_conf['log_options']:
+ queue_threshold = rule_conf['log_options']['queue_threshold']
+ output.append(f'queue-threshold {queue_threshold}')
+
+ if 'snapshot_length' in rule_conf['log_options']:
+ log_snaplen = rule_conf['log_options']['snapshot_length']
+ output.append(f'snaplen {log_snaplen}')
+
output.append('counter')
if 'set' in rule_conf:
@@ -387,24 +426,38 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name):
if 'action' in rule_conf:
# Change action=return to action=action
# #output.append(nft_action(rule_conf['action']))
- output.append(f'{rule_conf["action"]}')
- if 'jump' in rule_conf['action']:
- target = rule_conf['jump_target']
- output.append(f'NAME{def_suffix}_{target}')
-
- if 'queue' in rule_conf['action']:
- if 'queue' in rule_conf:
- target = rule_conf['queue']
- output.append(f'num {target}')
-
- if 'queue_options' in rule_conf:
- queue_opts = ','.join(rule_conf['queue_options'])
- output.append(f'{queue_opts}')
+ if rule_conf['action'] == 'offload':
+ offload_target = rule_conf['offload_target']
+ output.append(f'flow add @VYOS_FLOWTABLE_{offload_target}')
+ else:
+ output.append(f'{rule_conf["action"]}')
+
+ if 'jump' in rule_conf['action']:
+ target = rule_conf['jump_target']
+ output.append(f'NAME{def_suffix}_{target}')
+
+ if 'queue' in rule_conf['action']:
+ if 'queue' in rule_conf:
+ target = rule_conf['queue']
+ output.append(f'num {target}')
+
+ if 'queue_options' in rule_conf:
+ queue_opts = ','.join(rule_conf['queue_options'])
+ output.append(f'{queue_opts}')
+
+ # Synproxy
+ if 'synproxy' in rule_conf:
+ synproxy_mss = dict_search_args(rule_conf, 'synproxy', 'tcp', 'mss')
+ if synproxy_mss:
+ output.append(f'mss {synproxy_mss}')
+ synproxy_ws = dict_search_args(rule_conf, 'synproxy', 'tcp', 'window_scale')
+ if synproxy_ws:
+ output.append(f'wscale {synproxy_ws} timestamp sack-perm')
else:
output.append('return')
- output.append(f'comment "{hook}-{fw_name}-{rule_id}"')
+ output.append(f'comment "{family}-{hook}-{fw_name}-{rule_id}"')
return " ".join(output)
def parse_tcp_flags(flags):
diff --git a/python/vyos/frr.py b/python/vyos/frr.py
index 2e3c8a271..ad5c207f5 100644
--- a/python/vyos/frr.py
+++ b/python/vyos/frr.py
@@ -86,9 +86,12 @@ ch2 = logging.StreamHandler(stream=sys.stdout)
LOG.addHandler(ch)
LOG.addHandler(ch2)
-_frr_daemons = ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd',
- 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd',
- 'bfdd', 'eigrpd', 'babeld']
+# Full list of FRR 9.0/stable daemons for reference
+#_frr_daemons = ['zebra', 'staticd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd',
+# 'isisd', 'pim6d', 'ldpd', 'eigrpd', 'babeld', 'sharpd', 'bfdd',
+# 'fabricd', 'pathd']
+_frr_daemons = ['zebra', 'staticd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd',
+ 'isisd', 'pim6d', 'ldpd', 'babeld', 'bfdd']
path_vtysh = '/usr/bin/vtysh'
path_frr_reload = '/usr/lib/frr/frr-reload.py'
diff --git a/python/vyos/ifconfig/control.py b/python/vyos/ifconfig/control.py
index c8366cb58..7402da55a 100644
--- a/python/vyos/ifconfig/control.py
+++ b/python/vyos/ifconfig/control.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -49,6 +49,18 @@ class Control(Section):
return popen(command, self.debug)
def _cmd(self, command):
+ import re
+ if 'netns' in self.config:
+ # This command must be executed from default netns 'ip link set dev X netns X'
+ # exclude set netns cmd from netns to avoid:
+ # failed to run command: ip netns exec ns01 ip link set dev veth20 netns ns01
+ pattern = r'ip link set dev (\S+) netns (\S+)'
+ matches = re.search(pattern, command)
+ if matches and matches.group(2) == self.config['netns']:
+ # Command already includes netns and matches desired namespace:
+ command = command
+ else:
+ command = f'ip netns exec {self.config["netns"]} {command}'
return cmd(command, self.debug)
def _get_command(self, config, name):
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 24ce3a803..285542057 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -57,6 +57,10 @@ class EthernetIf(Interface):
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'gso', v),
},
+ 'hw-tc-offload': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'possible': lambda i, v: EthernetIf.feature(i, 'hw-tc-offload', v),
+ },
'lro': {
'validate': lambda v: assert_list(v, ['on', 'off']),
'possible': lambda i, v: EthernetIf.feature(i, 'lro', v),
@@ -222,6 +226,25 @@ class EthernetIf(Interface):
print('Adapter does not support changing generic-segmentation-offload settings!')
return False
+ def set_hw_tc_offload(self, state):
+ """
+ Enable hardware TC flow offload. State can be either True or False.
+ Example:
+ >>> from vyos.ifconfig import EthernetIf
+ >>> i = EthernetIf('eth0')
+ >>> i.set_hw_tc_offload(True)
+ """
+ if not isinstance(state, bool):
+ raise ValueError('Value out of range')
+
+ enabled, fixed = self.ethtool.get_hw_tc_offload()
+ if enabled != state:
+ if not fixed:
+ return self.set_interface('hw-tc-offload', 'on' if state else 'off')
+ else:
+ print('Adapter does not support changing hw-tc-offload settings!')
+ return False
+
def set_lro(self, state):
"""
Enable Large Receive offload. State can be either True or False.
@@ -358,6 +381,9 @@ class EthernetIf(Interface):
# GSO (generic segmentation offload)
self.set_gso(dict_search('offload.gso', config) != None)
+ # GSO (generic segmentation offload)
+ self.set_hw_tc_offload(dict_search('offload.hw-tc-offload', config) != None)
+
# LRO (large receive offload)
self.set_lro(dict_search('offload.lro', config) != None)
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 41ce352ad..050095364 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -38,7 +38,9 @@ from vyos.utils.dict import dict_search
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import get_interface_namespace
+from vyos.utils.network import is_netns_interface
from vyos.utils.process import is_systemd_service_active
+from vyos.utils.process import run
from vyos.template import is_ipv4
from vyos.template import is_ipv6
from vyos.utils.network import is_intf_addr_assigned
@@ -138,9 +140,6 @@ class Interface(Control):
'validate': assert_mtu,
'shellcmd': 'ip link set dev {ifname} mtu {value}',
},
- 'netns': {
- 'shellcmd': 'ip link set dev {ifname} netns {value}',
- },
'vrf': {
'convert': lambda v: f'master {v}' if v else 'nomaster',
'shellcmd': 'ip link set dev {ifname} {value}',
@@ -175,10 +174,6 @@ class Interface(Control):
'validate': assert_boolean,
'location': '/proc/sys/net/ipv4/conf/{ifname}/bc_forwarding',
},
- 'rp_filter': {
- 'validate': lambda flt: assert_range(flt,0,3),
- 'location': '/proc/sys/net/ipv4/conf/{ifname}/rp_filter',
- },
'ipv6_accept_ra': {
'validate': lambda ara: assert_range(ara,0,3),
'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_ra',
@@ -252,9 +247,6 @@ class Interface(Control):
'ipv4_directed_broadcast': {
'location': '/proc/sys/net/ipv4/conf/{ifname}/bc_forwarding',
},
- 'rp_filter': {
- 'location': '/proc/sys/net/ipv4/conf/{ifname}/rp_filter',
- },
'ipv6_accept_ra': {
'location': '/proc/sys/net/ipv6/conf/{ifname}/accept_ra',
},
@@ -286,8 +278,11 @@ class Interface(Control):
}
@classmethod
- def exists(cls, ifname):
- return os.path.exists(f'/sys/class/net/{ifname}')
+ def exists(cls, ifname: str, netns: str=None) -> bool:
+ cmd = f'ip link show dev {ifname}'
+ if netns:
+ cmd = f'ip netns exec {netns} {cmd}'
+ return run(cmd) == 0
@classmethod
def get_config(cls):
@@ -355,7 +350,13 @@ class Interface(Control):
self.vrrp = VRRP(ifname)
def _create(self):
+ # Do not create interface that already exist or exists in netns
+ netns = self.config.get('netns', None)
+ if self.exists(f'{self.ifname}', netns=netns):
+ return
+
cmd = 'ip link add dev {ifname} type {type}'.format(**self.config)
+ if 'netns' in self.config: cmd = f'ip netns exec {netns} {cmd}'
self._cmd(cmd)
def remove(self):
@@ -390,6 +391,9 @@ class Interface(Control):
# after interface removal no other commands should be allowed
# to be called and instead should raise an Exception:
cmd = 'ip link del dev {ifname}'.format(**self.config)
+ # for delete we can't get data from self.config{'netns'}
+ netns = get_interface_namespace(self.ifname)
+ if netns: cmd = f'ip netns exec {netns} {cmd}'
return self._cmd(cmd)
def _set_vrf_ct_zone(self, vrf):
@@ -397,6 +401,10 @@ class Interface(Control):
Add/Remove rules in nftables to associate traffic in VRF to an
individual conntack zone
"""
+ # Don't allow for netns yet
+ if 'netns' in self.config:
+ return None
+
if vrf:
# Get routing table ID for VRF
vrf_table_id = get_interface_config(vrf).get('linkinfo', {}).get(
@@ -540,36 +548,30 @@ class Interface(Control):
if prev_state == 'up':
self.set_admin_state('up')
- def del_netns(self, netns):
- """
- Remove interface from given NETNS.
- """
-
- # If NETNS does not exist then there is nothing to delete
+ def del_netns(self, netns: str) -> bool:
+ """ Remove interface from given network namespace """
+ # If network namespace does not exist then there is nothing to delete
if not os.path.exists(f'/run/netns/{netns}'):
- return None
-
- # As a PoC we only allow 'dummy' interfaces
- if 'dum' not in self.ifname:
- return None
+ return False
- # Check if interface realy exists in namespace
- if get_interface_namespace(self.ifname) != None:
- self._cmd(f'ip netns exec {get_interface_namespace(self.ifname)} ip link del dev {self.ifname}')
- return
+ # Check if interface exists in network namespace
+ if is_netns_interface(self.ifname, netns):
+ self._cmd(f'ip netns exec {netns} ip link del dev {self.ifname}')
+ return True
+ return False
- def set_netns(self, netns):
+ def set_netns(self, netns: str) -> bool:
"""
- Add interface from given NETNS.
+ Add interface from given network namespace
Example:
>>> from vyos.ifconfig import Interface
>>> Interface('dum0').set_netns('foo')
"""
+ self._cmd(f'ip link set dev {self.ifname} netns {netns}')
+ return True
- self.set_interface('netns', netns)
-
- def set_vrf(self, vrf):
+ def set_vrf(self, vrf: str) -> bool:
"""
Add/Remove interface from given VRF instance.
@@ -581,10 +583,11 @@ class Interface(Control):
tmp = self.get_interface('vrf')
if tmp == vrf:
- return None
+ return False
self.set_interface('vrf', vrf)
self._set_vrf_ct_zone(vrf)
+ return True
def set_arp_cache_tmo(self, tmo):
"""
@@ -621,6 +624,10 @@ class Interface(Control):
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_tcp_ipv4_mss(1340)
"""
+ # Don't allow for netns yet
+ if 'netns' in self.config:
+ return None
+
self._cleanup_mss_rules('raw', self.ifname)
nft_prefix = 'nft add rule raw VYOS_TCP_MSS'
base_cmd = f'oifname "{self.ifname}" tcp flags & (syn|rst) == syn'
@@ -641,6 +648,10 @@ class Interface(Control):
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_tcp_mss(1320)
"""
+ # Don't allow for netns yet
+ if 'netns' in self.config:
+ return None
+
self._cleanup_mss_rules('ip6 raw', self.ifname)
nft_prefix = 'nft add rule ip6 raw VYOS_TCP_MSS'
base_cmd = f'oifname "{self.ifname}" tcp flags & (syn|rst) == syn'
@@ -745,40 +756,36 @@ class Interface(Control):
return None
return self.set_interface('ipv4_directed_broadcast', forwarding)
- def set_ipv4_source_validation(self, value):
- """
- Help prevent attacks used by Spoofing IP Addresses. Reverse path
- filtering is a Kernel feature that, when enabled, is designed to ensure
- packets that are not routable to be dropped. The easiest example of this
- would be and IP Address of the range 10.0.0.0/8, a private IP Address,
- being received on the Internet facing interface of the router.
+ def _cleanup_ipv4_source_validation_rules(self, ifname):
+ results = self._cmd(f'nft -a list chain ip raw vyos_rpfilter').split("\n")
+ for line in results:
+ if f'iifname "{ifname}"' in line:
+ handle_search = re.search('handle (\d+)', line)
+ if handle_search:
+ self._cmd(f'nft delete rule ip raw vyos_rpfilter handle {handle_search[1]}')
- As per RFC3074.
+ def set_ipv4_source_validation(self, mode):
"""
- if value == 'strict':
- value = 1
- elif value == 'loose':
- value = 2
- else:
- value = 0
-
- all_rp_filter = int(read_file('/proc/sys/net/ipv4/conf/all/rp_filter'))
- if all_rp_filter > value:
- global_setting = 'disable'
- if all_rp_filter == 1: global_setting = 'strict'
- elif all_rp_filter == 2: global_setting = 'loose'
-
- from vyos.base import Warning
- Warning(f'Global source-validation is set to "{global_setting}", this '\
- f'overrides per interface setting on "{self.ifname}"!')
+ Set IPv4 reverse path validation
- tmp = self.get_interface('rp_filter')
- if int(tmp) == value:
+ Example:
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_ipv4_source_validation('strict')
+ """
+ # Don't allow for netns yet
+ if 'netns' in self.config:
return None
- return self.set_interface('rp_filter', value)
+
+ self._cleanup_ipv4_source_validation_rules(self.ifname)
+ nft_prefix = f'nft insert rule ip raw vyos_rpfilter iifname "{self.ifname}"'
+ if mode in ['strict', 'loose']:
+ self._cmd(f"{nft_prefix} counter return")
+ if mode == 'strict':
+ self._cmd(f"{nft_prefix} fib saddr . iif oif 0 counter drop")
+ elif mode == 'loose':
+ self._cmd(f"{nft_prefix} fib saddr oif 0 counter drop")
def _cleanup_ipv6_source_validation_rules(self, ifname):
- commands = []
results = self._cmd(f'nft -a list chain ip6 raw vyos_rpfilter').split("\n")
for line in results:
if f'iifname "{ifname}"' in line:
@@ -794,8 +801,14 @@ class Interface(Control):
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_ipv6_source_validation('strict')
"""
+ # Don't allow for netns yet
+ if 'netns' in self.config:
+ return None
+
self._cleanup_ipv6_source_validation_rules(self.ifname)
- nft_prefix = f'nft add rule ip6 raw vyos_rpfilter iifname "{self.ifname}"'
+ nft_prefix = f'nft insert rule ip6 raw vyos_rpfilter iifname "{self.ifname}"'
+ if mode in ['strict', 'loose']:
+ self._cmd(f"{nft_prefix} counter return")
if mode == 'strict':
self._cmd(f"{nft_prefix} fib saddr . iif oif 0 counter drop")
elif mode == 'loose':
@@ -1143,13 +1156,17 @@ class Interface(Control):
if addr in self._addr:
return False
+ # get interface network namespace if specified
+ netns = self.config.get('netns', None)
+
# add to interface
if addr == 'dhcp':
self.set_dhcp(True)
elif addr == 'dhcpv6':
self.set_dhcpv6(True)
- elif not is_intf_addr_assigned(self.ifname, addr):
- tmp = f'ip addr add {addr} dev {self.ifname}'
+ elif not is_intf_addr_assigned(self.ifname, addr, netns=netns):
+ netns_cmd = f'ip netns exec {netns}' if netns else ''
+ tmp = f'{netns_cmd} ip addr add {addr} dev {self.ifname}'
# Add broadcast address for IPv4
if is_ipv4(addr): tmp += ' brd +'
@@ -1189,13 +1206,17 @@ class Interface(Control):
if not addr:
raise ValueError()
+ # get interface network namespace if specified
+ netns = self.config.get('netns', None)
+
# remove from interface
if addr == 'dhcp':
self.set_dhcp(False)
elif addr == 'dhcpv6':
self.set_dhcpv6(False)
- elif is_intf_addr_assigned(self.ifname, addr):
- self._cmd(f'ip addr del "{addr}" dev "{self.ifname}"')
+ elif is_intf_addr_assigned(self.ifname, addr, netns=netns):
+ netns_cmd = f'ip netns exec {netns}' if netns else ''
+ self._cmd(f'{netns_cmd} ip addr del {addr} dev {self.ifname}')
else:
return False
@@ -1215,8 +1236,11 @@ class Interface(Control):
self.set_dhcp(False)
self.set_dhcpv6(False)
+ netns = get_interface_namespace(self.ifname)
+ netns_cmd = f'ip netns exec {netns}' if netns else ''
+ cmd = f'{netns_cmd} ip addr flush dev {self.ifname}'
# flush all addresses
- self._cmd(f'ip addr flush dev "{self.ifname}"')
+ self._cmd(cmd)
def add_to_bridge(self, bridge_dict):
"""
@@ -1371,6 +1395,11 @@ class Interface(Control):
# - https://man7.org/linux/man-pages/man8/tc-mirred.8.html
# Depening if we are the source or the target interface of the port
# mirror we need to setup some variables.
+
+ # Don't allow for netns yet
+ if 'netns' in self.config:
+ return None
+
source_if = self.config['ifname']
mirror_config = None
@@ -1471,8 +1500,8 @@ class Interface(Control):
# Since the interface is pushed onto a separate logical stack
# Configure NETNS
if dict_search('netns', config) != None:
- self.set_netns(config.get('netns', ''))
- return
+ if not is_netns_interface(self.ifname, self.config['netns']):
+ self.set_netns(config.get('netns', ''))
else:
self.del_netns(config.get('netns', ''))
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index 6a9911588..1fe5db7cd 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2023 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
@@ -13,9 +13,15 @@
# 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 json import loads
+
from vyos import ConfigError
+from vyos.configdict import list_diff
from vyos.ifconfig import Interface
+from vyos.utils.assertion import assert_list
from vyos.utils.dict import dict_search
+from vyos.utils.network import get_interface_config
+from vyos.utils.network import get_vxlan_vlan_tunnels
@Interface.register
class VXLANIf(Interface):
@@ -49,6 +55,13 @@ class VXLANIf(Interface):
}
}
+ _command_set = {**Interface._command_set, **{
+ 'vlan_tunnel': {
+ 'validate': lambda v: assert_list(v, ['on', 'off']),
+ 'shellcmd': 'bridge link set dev {ifname} vlan_tunnel {value}',
+ },
+ }}
+
def _create(self):
# This table represents a mapping from VyOS internal config dict to
# arguments used by iproute2. For more information please refer to:
@@ -99,3 +112,54 @@ class VXLANIf(Interface):
cmd = f'bridge fdb append to 00:00:00:00:00:00 dst {remote} ' \
'port {port} dev {ifname}'
self._cmd(cmd.format(**self.config))
+
+ def set_vlan_vni_mapping(self, state):
+ """
+ Controls whether vlan to tunnel mapping is enabled on the port.
+ By default this flag is off.
+ """
+ if not isinstance(state, bool):
+ raise ValueError('Value out of range')
+
+ cur_vlan_ids = []
+ if 'vlan_to_vni_removed' in self.config:
+ cur_vlan_ids = self.config['vlan_to_vni_removed']
+ for vlan in cur_vlan_ids:
+ self._cmd(f'bridge vlan del dev {self.ifname} vid {vlan}')
+
+ # Determine current OS Kernel vlan_tunnel setting - only adjust when needed
+ tmp = get_interface_config(self.ifname)
+ cur_state = 'on' if dict_search(f'linkinfo.info_slave_data.vlan_tunnel', tmp) == True else 'off'
+ new_state = 'on' if state else 'off'
+ if cur_state != new_state:
+ self.set_interface('vlan_tunnel', new_state)
+
+ # Determine current OS Kernel configured VLANs
+ os_configured_vlan_ids = get_vxlan_vlan_tunnels(self.ifname)
+
+ if 'vlan_to_vni' in self.config:
+ add_vlan = list_diff(list(self.config['vlan_to_vni'].keys()), os_configured_vlan_ids)
+
+ for vlan, vlan_config in self.config['vlan_to_vni'].items():
+ # VLAN mapping already exists - skip
+ if vlan not in add_vlan:
+ continue
+
+ vni = vlan_config['vni']
+ # The following commands must be run one after another,
+ # they can not be combined with linux 6.1 and iproute2 6.1
+ self._cmd(f'bridge vlan add dev {self.ifname} vid {vlan}')
+ self._cmd(f'bridge vlan add dev {self.ifname} vid {vlan} tunnel_info id {vni}')
+
+ def update(self, config):
+ """ General helper function which works on a dictionary retrived by
+ get_config_dict(). It's main intention is to consolidate the scattered
+ interface setup code and provide a single point of entry when workin
+ on any interface. """
+
+ # call base class last
+ super().update(config)
+
+ # Enable/Disable VLAN tunnel mapping
+ # This is only possible after the interface was assigned to the bridge
+ self.set_vlan_vni_mapping(dict_search('vlan_to_vni', config) != None)
diff --git a/python/vyos/raid.py b/python/vyos/raid.py
new file mode 100644
index 000000000..7fb794817
--- /dev/null
+++ b/python/vyos/raid.py
@@ -0,0 +1,71 @@
+# Copyright 2023 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 vyos.utils.disk import device_from_id
+from vyos.utils.process import cmd
+
+def raid_sets():
+ """
+ Returns a list of RAID sets
+ """
+ with open('/proc/mdstat') as f:
+ return [line.split()[0].rstrip(':') for line in f if line.startswith('md')]
+
+def raid_set_members(raid_set_name: str):
+ """
+ Returns a list of members of a RAID set
+ """
+ with open('/proc/mdstat') as f:
+ for line in f:
+ if line.startswith(raid_set_name):
+ return [l.split('[')[0] for l in line.split()[4:]]
+ return []
+
+def partitions():
+ """
+ Returns a list of partitions
+ """
+ with open('/proc/partitions') as f:
+ p = [l.strip().split()[-1] for l in list(f) if l.strip()]
+ p.remove('name')
+ return p
+
+def add_raid_member(raid_set_name: str, member: str, by_id: bool = False):
+ """
+ Add a member to an existing RAID set
+ """
+ if by_id:
+ member = device_from_id(member)
+ if raid_set_name not in raid_sets():
+ raise ValueError(f"RAID set {raid_set_name} does not exist")
+ if member not in partitions():
+ raise ValueError(f"Partition {member} does not exist")
+ if member in raid_set_members(raid_set_name):
+ raise ValueError(f"Partition {member} is already a member of RAID set {raid_set_name}")
+ cmd(f'mdadm --add /dev/{raid_set_name} /dev/{member}')
+ disk = cmd(f'lsblk -ndo PKNAME /dev/{member}')
+ cmd(f'grub-install /dev/{disk}')
+
+def delete_raid_member(raid_set_name: str, member: str, by_id: bool = False):
+ """
+ Delete a member from an existing RAID set
+ """
+ if by_id:
+ member = device_from_id(member)
+ if raid_set_name not in raid_sets():
+ raise ValueError(f"RAID set {raid_set_name} does not exist")
+ if member not in raid_set_members(raid_set_name):
+ raise ValueError(f"Partition {member} is not a member of RAID set {raid_set_name}")
+ cmd(f'mdadm --remove /dev/{raid_set_name} /dev/{member}')
diff --git a/python/vyos/template.py b/python/vyos/template.py
index e167488c6..3be486cc4 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -663,6 +663,90 @@ 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('conntrack_ignore_rule')
+def conntrack_ignore_rule(rule_conf, rule_id, ipv6=False):
+ ip_prefix = 'ip6' if ipv6 else 'ip'
+ def_suffix = '6' if ipv6 else ''
+ output = []
+
+ if 'inbound_interface' in rule_conf:
+ ifname = rule_conf['inbound_interface']
+ if ifname != 'any':
+ output.append(f'iifname {ifname}')
+
+ if 'protocol' in rule_conf:
+ proto = rule_conf['protocol']
+ output.append(f'meta l4proto {proto}')
+
+ tcp_flags = dict_search_args(rule_conf, 'tcp', 'flags')
+ if tcp_flags:
+ from vyos.firewall import parse_tcp_flags
+ output.append(parse_tcp_flags(tcp_flags))
+
+ for side in ['source', 'destination']:
+ if side in rule_conf:
+ side_conf = rule_conf[side]
+ prefix = side[0]
+
+ if 'address' in side_conf:
+ address = side_conf['address']
+ operator = ''
+ if address[0] == '!':
+ operator = '!='
+ address = address[1:]
+ output.append(f'{ip_prefix} {prefix}addr {operator} {address}')
+
+ if 'port' in side_conf:
+ port = side_conf['port']
+ operator = ''
+ if port[0] == '!':
+ operator = '!='
+ port = port[1:]
+ output.append(f'th {prefix}port {operator} {port}')
+
+ if 'group' in side_conf:
+ group = side_conf['group']
+
+ if 'address_group' in group:
+ group_name = group['address_group']
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+ output.append(f'{ip_prefix} {prefix}addr {operator} @A{def_suffix}_{group_name}')
+ # Generate firewall group domain-group
+ elif 'domain_group' in group:
+ group_name = group['domain_group']
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+ output.append(f'{ip_prefix} {prefix}addr {operator} @D_{group_name}')
+ elif 'network_group' in group:
+ group_name = group['network_group']
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+ output.append(f'{ip_prefix} {prefix}addr {operator} @N{def_suffix}_{group_name}')
+ if 'port_group' in group:
+ group_name = group['port_group']
+
+ if proto == 'tcp_udp':
+ proto = 'th'
+
+ operator = ''
+ if group_name[0] == '!':
+ operator = '!='
+ group_name = group_name[1:]
+
+ output.append(f'{proto} {prefix}port {operator} @P_{group_name}')
+
+ output.append('counter notrack')
+ output.append(f'comment "ignore-{rule_id}"')
+
+ return " ".join(output)
+
@register_filter('range_to_regex')
def range_to_regex(num_range):
"""Convert range of numbers or list of ranges
diff --git a/python/vyos/utils/config.py b/python/vyos/utils/config.py
new file mode 100644
index 000000000..bd363ce46
--- /dev/null
+++ b/python/vyos/utils/config.py
@@ -0,0 +1,34 @@
+# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+from vyos.defaults import directories
+
+config_file = os.path.join(directories['config'], 'config.boot')
+
+def read_saved_value(path: list):
+ if not isinstance(path, list) or not path:
+ return ''
+ from vyos.configtree import ConfigTree
+ try:
+ with open(config_file) as f:
+ config_string = f.read()
+ ct = ConfigTree(config_string)
+ except Exception:
+ return ''
+ if not ct.exists(path):
+ return ''
+ res = ct.return_values(path)
+ return res[0] if len(res) == 1 else res
diff --git a/python/vyos/utils/disk.py b/python/vyos/utils/disk.py
new file mode 100644
index 000000000..ee540b107
--- /dev/null
+++ b/python/vyos/utils/disk.py
@@ -0,0 +1,23 @@
+# Copyright 2023 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 pathlib import Path
+
+def device_from_id(id):
+ """ Return the device name from (partial) disk id """
+ path = Path('/dev/disk/by-id')
+ for device in path.iterdir():
+ if device.name.endswith(id):
+ return device.readlink().stem
diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py
index 2f181d8d9..9354bd495 100644
--- a/python/vyos/utils/network.py
+++ b/python/vyos/utils/network.py
@@ -40,13 +40,19 @@ def interface_exists(interface) -> bool:
import os
return os.path.exists(f'/sys/class/net/{interface}')
-def interface_exists_in_netns(interface_name, netns):
+def is_netns_interface(interface, netns):
from vyos.utils.process import rc_cmd
- rc, out = rc_cmd(f'ip netns exec {netns} ip link show dev {interface_name}')
+ rc, out = rc_cmd(f'sudo ip netns exec {netns} ip link show dev {interface}')
if rc == 0:
return True
return False
+def get_netns_all() -> list:
+ from json import loads
+ from vyos.utils.process import cmd
+ tmp = loads(cmd('ip --json netns ls'))
+ return [ netns['name'] for netns in tmp ]
+
def get_vrf_members(vrf: str) -> list:
"""
Get list of interface VRF members
@@ -78,8 +84,7 @@ def get_interface_config(interface):
""" Returns the used encapsulation protocol for given interface.
If interface does not exist, None is returned.
"""
- import os
- if not os.path.exists(f'/sys/class/net/{interface}'):
+ if not interface_exists(interface):
return None
from json import loads
from vyos.utils.process import cmd
@@ -90,33 +95,63 @@ def get_interface_address(interface):
""" Returns the used encapsulation protocol for given interface.
If interface does not exist, None is returned.
"""
- import os
- if not os.path.exists(f'/sys/class/net/{interface}'):
+ if not interface_exists(interface):
return None
from json import loads
from vyos.utils.process import cmd
tmp = loads(cmd(f'ip --detail --json addr show dev {interface}'))[0]
return tmp
-def get_interface_namespace(iface):
+def get_interface_namespace(interface: str):
"""
Returns wich netns the interface belongs to
"""
from json import loads
from vyos.utils.process import cmd
- # Check if netns exist
- tmp = loads(cmd(f'ip --json netns ls'))
- if len(tmp) == 0:
- return None
- for ns in tmp:
+ # Bail out early if netns does not exist
+ tmp = cmd(f'ip --json netns ls')
+ if not tmp: return None
+
+ for ns in loads(tmp):
netns = f'{ns["name"]}'
# Search interface in each netns
data = loads(cmd(f'ip netns exec {netns} ip --json link show'))
for tmp in data:
- if iface == tmp["ifname"]:
+ if interface == tmp["ifname"]:
return netns
+def is_ipv6_tentative(iface: str, ipv6_address: str) -> bool:
+ """Check if IPv6 address is in tentative state.
+
+ This function checks if an IPv6 address on a specific network interface is
+ in the tentative state. IPv6 tentative addresses are not fully configured
+ and are undergoing Duplicate Address Detection (DAD) to ensure they are
+ unique on the network.
+
+ Args:
+ iface (str): The name of the network interface.
+ ipv6_address (str): The IPv6 address to check.
+
+ Returns:
+ bool: True if the IPv6 address is tentative, False otherwise.
+ """
+ import json
+ from vyos.utils.process import rc_cmd
+
+ rc, out = rc_cmd(f'ip -6 --json address show dev {iface}')
+ if rc:
+ return False
+
+ data = json.loads(out)
+ for addr_info in data[0]['addr_info']:
+ if (
+ addr_info.get('local') == ipv6_address and
+ addr_info.get('tentative', False)
+ ):
+ return True
+ return False
+
def is_wwan_connected(interface):
""" Determine if a given WWAN interface, e.g. wwan0 is connected to the
carrier network or not """
@@ -141,8 +176,7 @@ def is_wwan_connected(interface):
def get_bridge_fdb(interface):
""" Returns the forwarding database entries for a given interface """
- import os
- if not os.path.exists(f'/sys/class/net/{interface}'):
+ if not interface_exists(interface):
return None
from json import loads
from vyos.utils.process import cmd
@@ -274,57 +308,33 @@ def is_addr_assigned(ip_address, vrf=None) -> bool:
return False
-def is_intf_addr_assigned(intf, address) -> bool:
+def is_intf_addr_assigned(ifname: str, addr: str, netns: str=None) -> bool:
"""
Verify if the given IPv4/IPv6 address is assigned to specific interface.
It can check both a single IP address (e.g. 192.0.2.1 or a assigned CIDR
address 192.0.2.1/24.
"""
- from vyos.template import is_ipv4
-
- from netifaces import ifaddresses
- from netifaces import AF_INET
- from netifaces import AF_INET6
-
- # check if the requested address type is configured at all
- # {
- # 17: [{'addr': '08:00:27:d9:5b:04', 'broadcast': 'ff:ff:ff:ff:ff:ff'}],
- # 2: [{'addr': '10.0.2.15', 'netmask': '255.255.255.0', 'broadcast': '10.0.2.255'}],
- # 10: [{'addr': 'fe80::a00:27ff:fed9:5b04%eth0', 'netmask': 'ffff:ffff:ffff:ffff::'}]
- # }
- try:
- addresses = ifaddresses(intf)
- except ValueError as e:
- print(e)
- return False
-
- # determine IP version (AF_INET or AF_INET6) depending on passed address
- addr_type = AF_INET if is_ipv4(address) else AF_INET6
-
- # Check every IP address on this interface for a match
- netmask = None
- if '/' in address:
- address, netmask = address.split('/')
- for ip in addresses.get(addr_type, []):
- # ip can have the interface name in the 'addr' field, we need to remove it
- # {'addr': 'fe80::a00:27ff:fec5:f821%eth2', 'netmask': 'ffff:ffff:ffff:ffff::'}
- ip_addr = ip['addr'].split('%')[0]
-
- if not _are_same_ip(address, ip_addr):
- continue
-
- # we do not have a netmask to compare against, they are the same
- if not netmask:
- return True
+ import json
+ import jmespath
- prefixlen = ''
- if is_ipv4(ip_addr):
- prefixlen = sum([bin(int(_)).count('1') for _ in ip['netmask'].split('.')])
- else:
- prefixlen = sum([bin(int(_,16)).count('1') for _ in ip['netmask'].split('/')[0].split(':') if _])
+ from vyos.utils.process import rc_cmd
+ from ipaddress import ip_interface
- if str(prefixlen) == netmask:
- return True
+ netns_cmd = f'ip netns exec {netns}' if netns else ''
+ rc, out = rc_cmd(f'{netns_cmd} ip --json address show dev {ifname}')
+ if rc == 0:
+ json_out = json.loads(out)
+ addresses = jmespath.search("[].addr_info[].{family: family, address: local, prefixlen: prefixlen}", json_out)
+ for address_info in addresses:
+ family = address_info['family']
+ address = address_info['address']
+ prefixlen = address_info['prefixlen']
+ # Remove the interface name if present in the given address
+ if '%' in addr:
+ addr = addr.split('%')[0]
+ interface = ip_interface(f"{address}/{prefixlen}")
+ if ip_interface(addr) == interface or address == addr:
+ return True
return False
@@ -398,7 +408,7 @@ def is_subnet_connected(subnet, primary=False):
return False
-def is_afi_configured(interface, afi):
+def is_afi_configured(interface: str, afi):
""" Check if given address family is configured, or in other words - an IP
address is assigned to the interface. """
from netifaces import ifaddresses
@@ -415,3 +425,46 @@ def is_afi_configured(interface, afi):
return False
return afi in addresses
+
+def get_vxlan_vlan_tunnels(interface: str) -> list:
+ """ Return a list of strings with VLAN IDs configured in the Kernel """
+ from json import loads
+ from vyos.utils.process import cmd
+
+ if not interface.startswith('vxlan'):
+ raise ValueError('Only applicable for VXLAN interfaces!')
+
+ # Determine current OS Kernel configured VLANs
+ #
+ # $ bridge -j -p vlan tunnelshow dev vxlan0
+ # [ {
+ # "ifname": "vxlan0",
+ # "tunnels": [ {
+ # "vlan": 10,
+ # "vlanEnd": 11,
+ # "tunid": 10010,
+ # "tunidEnd": 10011
+ # },{
+ # "vlan": 20,
+ # "tunid": 10020
+ # } ]
+ # } ]
+ #
+ os_configured_vlan_ids = []
+ tmp = loads(cmd(f'bridge --json vlan tunnelshow dev {interface}'))
+ if tmp:
+ for tunnel in tmp[0].get('tunnels', {}):
+ vlanStart = tunnel['vlan']
+ if 'vlanEnd' in tunnel:
+ vlanEnd = tunnel['vlanEnd']
+ # Build a real list for user VLAN IDs
+ vlan_list = list(range(vlanStart, vlanEnd +1))
+ # Convert list of integers to list or strings
+ os_configured_vlan_ids.extend(map(str, vlan_list))
+ # Proceed with next tunnel - this one is complete
+ continue
+
+ # Add single tunel id - not part of a range
+ os_configured_vlan_ids.append(str(vlanStart))
+
+ return os_configured_vlan_ids
diff --git a/smoketest/bin/vyos-configtest b/smoketest/bin/vyos-configtest
index 3e42b0380..c1b602737 100755
--- a/smoketest/bin/vyos-configtest
+++ b/smoketest/bin/vyos-configtest
@@ -24,6 +24,7 @@ from vyos.configsession import ConfigSession, ConfigSessionError
from vyos import ConfigError
config_dir = '/usr/libexec/vyos/tests/config'
+config_test_dir = '/usr/libexec/vyos/tests/config-tests'
save_config = '/tmp/vyos-configtest-save'
class DynamicClassBase(unittest.TestCase):
@@ -42,7 +43,7 @@ class DynamicClassBase(unittest.TestCase):
except OSError:
pass
-def make_test_function(filename):
+def make_test_function(filename, test_path=None):
def test_config_load(self):
config_path = os.path.join(config_dir, filename)
self.session.migrate_and_load_config(config_path)
@@ -51,6 +52,16 @@ def make_test_function(filename):
except (ConfigError, ConfigSessionError):
self.session.discard()
self.fail()
+
+ if test_path:
+ config_commands = self.session.show(['configuration', 'commands'])
+
+ with open(test_path, 'r') as f:
+ for line in f.readlines():
+ if not line or line.startswith("#"):
+ continue
+
+ self.assertIn(line, config_commands)
return test_config_load
def class_name_from_func_name(s):
@@ -69,10 +80,18 @@ if __name__ == '__main__':
config_list.sort()
for config in config_list:
- test_func = make_test_function(config)
+ test_path = os.path.join(config_test_dir, config)
+
+ if not os.path.exists(test_path):
+ test_path = None
+ else:
+ log.info(f'Loaded migration result test for config "{config}"')
+
+ test_func = make_test_function(config, test_path)
func_name = config.replace('-', '_')
klassname = f'TestConfig{class_name_from_func_name(func_name)}'
+
globals()[klassname] = type(klassname,
(DynamicClassBase,),
{f'test_{func_name}': test_func})
diff --git a/smoketest/config-tests/basic-vyos b/smoketest/config-tests/basic-vyos
new file mode 100644
index 000000000..ef8bf374a
--- /dev/null
+++ b/smoketest/config-tests/basic-vyos
@@ -0,0 +1,62 @@
+set interfaces ethernet eth0 address '192.168.0.1/24'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 speed 'auto'
+set interfaces ethernet eth2 duplex 'auto'
+set interfaces ethernet eth2 speed 'auto'
+set interfaces ethernet eth2 vif 100 address '100.100.0.1/24'
+set interfaces ethernet eth2 vif-s 200 address '100.64.200.254/24'
+set interfaces ethernet eth2 vif-s 200 vif-c 201 address '100.64.201.254/24'
+set interfaces ethernet eth2 vif-s 200 vif-c 202 address '100.64.202.254/24'
+set interfaces loopback lo
+set protocols static arp interface eth0 address 192.168.0.20 mac '00:50:00:00:00:20'
+set protocols static arp interface eth0 address 192.168.0.30 mac '00:50:00:00:00:30'
+set protocols static arp interface eth0 address 192.168.0.40 mac '00:50:00:00:00:40'
+set protocols static arp interface eth2.100 address 100.100.0.2 mac '00:50:00:00:02:02'
+set protocols static arp interface eth2.100 address 100.100.0.3 mac '00:50:00:00:02:03'
+set protocols static arp interface eth2.100 address 100.100.0.4 mac '00:50:00:00:02:04'
+set protocols static arp interface eth2.200 address 100.64.200.1 mac '00:50:00:00:00:01'
+set protocols static arp interface eth2.200 address 100.64.200.2 mac '00:50:00:00:00:02'
+set protocols static arp interface eth2.200.201 address 100.64.201.10 mac '00:50:00:00:00:10'
+set protocols static arp interface eth2.200.201 address 100.64.201.20 mac '00:50:00:00:00:20'
+set protocols static arp interface eth2.200.202 address 100.64.202.30 mac '00:50:00:00:00:30'
+set protocols static arp interface eth2.200.202 address 100.64.202.40 mac '00:50:00:00:00:40'
+set protocols static route 0.0.0.0/0 next-hop 100.64.0.1
+set service dhcp-server shared-network-name LAN authoritative
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 default-router '192.168.0.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 domain-name 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 domain-search 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 name-server '192.168.0.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic start '192.168.0.20'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic stop '192.168.0.240'
+set service dns forwarding allow-from '192.168.0.0/16'
+set service dns forwarding cache-size '10000'
+set service dns forwarding dnssec 'off'
+set service dns forwarding listen-address '192.168.0.1'
+set service ssh ciphers 'aes128-ctr'
+set service ssh ciphers 'aes192-ctr'
+set service ssh ciphers 'aes256-ctr'
+set service ssh ciphers 'chacha20-poly1305@openssh.com'
+set service ssh ciphers 'rijndael-cbc@lysator.liu.se'
+set service ssh key-exchange 'curve25519-sha256@libssh.org'
+set service ssh key-exchange 'diffie-hellman-group1-sha1'
+set service ssh key-exchange 'diffie-hellman-group-exchange-sha1'
+set service ssh key-exchange 'diffie-hellman-group-exchange-sha256'
+set service ssh listen-address '192.168.0.1'
+set service ssh port '22'
+set system config-management commit-revisions '100'
+set system console device ttyS0 speed '115200'
+set system host-name 'vyos'
+set system name-server '192.168.0.1'
+set system syslog console facility all level 'emerg'
+set system syslog console facility mail level 'info'
+set system syslog global facility all level 'info'
+set system syslog global facility auth level 'info'
+set system syslog global facility local7 level 'debug'
+set system syslog global preserve-fqdn
+set system syslog host syslog.vyos.net facility auth level 'warning'
+set system syslog host syslog.vyos.net facility local7 level 'notice'
+set system syslog host syslog.vyos.net format octet-counted
+set system syslog host syslog.vyos.net port '8000'
+set system time-zone 'Europe/Berlin'
diff --git a/smoketest/config-tests/dialup-router-medium-vpn b/smoketest/config-tests/dialup-router-medium-vpn
new file mode 100644
index 000000000..37baee0fd
--- /dev/null
+++ b/smoketest/config-tests/dialup-router-medium-vpn
@@ -0,0 +1,321 @@
+set firewall global-options all-ping 'enable'
+set firewall global-options broadcast-ping 'disable'
+set firewall global-options ip-src-route 'disable'
+set firewall global-options ipv6-receive-redirects 'disable'
+set firewall global-options ipv6-src-route 'disable'
+set firewall global-options log-martians 'enable'
+set firewall global-options receive-redirects 'disable'
+set firewall global-options send-redirects 'enable'
+set firewall global-options source-validation 'disable'
+set firewall global-options syn-cookies 'disable'
+set firewall global-options twa-hazards-protection 'enable'
+set firewall ipv4 name test_tcp_flags rule 1 action 'drop'
+set firewall ipv4 name test_tcp_flags rule 1 protocol 'tcp'
+set firewall ipv4 name test_tcp_flags rule 1 tcp flags ack
+set firewall ipv4 name test_tcp_flags rule 1 tcp flags not fin
+set firewall ipv4 name test_tcp_flags rule 1 tcp flags not rst
+set firewall ipv4 name test_tcp_flags rule 1 tcp flags syn
+set high-availability vrrp group LAN address 192.168.0.1/24
+set high-availability vrrp group LAN hello-source-address '192.168.0.250'
+set high-availability vrrp group LAN interface 'eth1'
+set high-availability vrrp group LAN peer-address '192.168.0.251'
+set high-availability vrrp group LAN priority '200'
+set high-availability vrrp group LAN vrid '1'
+set high-availability vrrp sync-group failover-group member 'LAN'
+set interfaces ethernet eth0 duplex 'auto'
+set interfaces ethernet eth0 mtu '9000'
+set interfaces ethernet eth0 offload gro
+set interfaces ethernet eth0 offload gso
+set interfaces ethernet eth0 offload sg
+set interfaces ethernet eth0 offload tso
+set interfaces ethernet eth0 speed 'auto'
+set interfaces ethernet eth1 address '192.168.0.250/24'
+set interfaces ethernet eth1 duplex 'auto'
+set interfaces ethernet eth1 ip source-validation 'strict'
+set interfaces ethernet eth1 mtu '9000'
+set interfaces ethernet eth1 offload gro
+set interfaces ethernet eth1 offload gso
+set interfaces ethernet eth1 offload sg
+set interfaces ethernet eth1 offload tso
+set interfaces ethernet eth1 speed 'auto'
+set interfaces loopback lo
+set interfaces openvpn vtun0 encryption cipher 'aes256'
+set interfaces openvpn vtun0 hash 'sha512'
+set interfaces openvpn vtun0 ip adjust-mss '1380'
+set interfaces openvpn vtun0 ip source-validation 'strict'
+set interfaces openvpn vtun0 keep-alive failure-count '3'
+set interfaces openvpn vtun0 keep-alive interval '30'
+set interfaces openvpn vtun0 mode 'client'
+set interfaces openvpn vtun0 openvpn-option 'comp-lzo adaptive'
+set interfaces openvpn vtun0 openvpn-option 'fast-io'
+set interfaces openvpn vtun0 openvpn-option 'persist-key'
+set interfaces openvpn vtun0 openvpn-option 'reneg-sec 86400'
+set interfaces openvpn vtun0 persistent-tunnel
+set interfaces openvpn vtun0 remote-host '192.0.2.10'
+set interfaces openvpn vtun0 tls auth-key 'openvpn_vtun0_auth'
+set interfaces openvpn vtun0 tls ca-certificate 'openvpn_vtun0_1'
+set interfaces openvpn vtun0 tls ca-certificate 'openvpn_vtun0_2'
+set interfaces openvpn vtun0 tls certificate 'openvpn_vtun0'
+set interfaces openvpn vtun1 authentication password 'vyos1'
+set interfaces openvpn vtun1 authentication username 'vyos1'
+set interfaces openvpn vtun1 encryption cipher 'aes256'
+set interfaces openvpn vtun1 hash 'sha1'
+set interfaces openvpn vtun1 ip adjust-mss '1380'
+set interfaces openvpn vtun1 keep-alive failure-count '3'
+set interfaces openvpn vtun1 keep-alive interval '30'
+set interfaces openvpn vtun1 mode 'client'
+set interfaces openvpn vtun1 openvpn-option 'comp-lzo adaptive'
+set interfaces openvpn vtun1 openvpn-option 'tun-mtu 1500'
+set interfaces openvpn vtun1 openvpn-option 'tun-mtu-extra 32'
+set interfaces openvpn vtun1 openvpn-option 'mssfix 1300'
+set interfaces openvpn vtun1 openvpn-option 'persist-key'
+set interfaces openvpn vtun1 openvpn-option 'mute 10'
+set interfaces openvpn vtun1 openvpn-option 'route-nopull'
+set interfaces openvpn vtun1 openvpn-option 'fast-io'
+set interfaces openvpn vtun1 openvpn-option 'reneg-sec 86400'
+set interfaces openvpn vtun1 persistent-tunnel
+set interfaces openvpn vtun1 protocol 'udp'
+set interfaces openvpn vtun1 remote-host '01.foo.com'
+set interfaces openvpn vtun1 remote-port '1194'
+set interfaces openvpn vtun1 tls auth-key 'openvpn_vtun1_auth'
+set interfaces openvpn vtun1 tls ca-certificate 'openvpn_vtun1_1'
+set interfaces openvpn vtun1 tls ca-certificate 'openvpn_vtun1_2'
+set interfaces openvpn vtun2 authentication password 'vyos2'
+set interfaces openvpn vtun2 authentication username 'vyos2'
+set interfaces openvpn vtun2 disable
+set interfaces openvpn vtun2 encryption cipher 'aes256'
+set interfaces openvpn vtun2 hash 'sha512'
+set interfaces openvpn vtun2 ip adjust-mss '1380'
+set interfaces openvpn vtun2 keep-alive failure-count '3'
+set interfaces openvpn vtun2 keep-alive interval '30'
+set interfaces openvpn vtun2 mode 'client'
+set interfaces openvpn vtun2 openvpn-option 'tun-mtu 1500'
+set interfaces openvpn vtun2 openvpn-option 'tun-mtu-extra 32'
+set interfaces openvpn vtun2 openvpn-option 'mssfix 1300'
+set interfaces openvpn vtun2 openvpn-option 'persist-key'
+set interfaces openvpn vtun2 openvpn-option 'mute 10'
+set interfaces openvpn vtun2 openvpn-option 'route-nopull'
+set interfaces openvpn vtun2 openvpn-option 'fast-io'
+set interfaces openvpn vtun2 openvpn-option 'remote-random'
+set interfaces openvpn vtun2 openvpn-option 'reneg-sec 86400'
+set interfaces openvpn vtun2 persistent-tunnel
+set interfaces openvpn vtun2 protocol 'udp'
+set interfaces openvpn vtun2 remote-host '01.myvpn.com'
+set interfaces openvpn vtun2 remote-host '02.myvpn.com'
+set interfaces openvpn vtun2 remote-host '03.myvpn.com'
+set interfaces openvpn vtun2 remote-port '1194'
+set interfaces openvpn vtun2 tls auth-key 'openvpn_vtun2_auth'
+set interfaces openvpn vtun2 tls ca-certificate 'openvpn_vtun2_1'
+set interfaces pppoe pppoe0 authentication password 'password'
+set interfaces pppoe pppoe0 authentication username 'vyos'
+set interfaces pppoe pppoe0 mtu '1500'
+set interfaces pppoe pppoe0 source-interface 'eth0'
+set interfaces wireguard wg0 address '192.168.10.1/24'
+set interfaces wireguard wg0 ip adjust-mss '1380'
+set interfaces wireguard wg0 peer blue allowed-ips '192.168.10.3/32'
+set interfaces wireguard wg0 peer blue persistent-keepalive '20'
+set interfaces wireguard wg0 peer blue preshared-key 'ztFDOY9UyaDvn8N3X97SFMDwIfv7EEfuUIPP2yab6UI='
+set interfaces wireguard wg0 peer blue public-key 'G4pZishpMRrLmd96Kr6V7LIuNGdcUb81gWaYZ+FWkG0='
+set interfaces wireguard wg0 peer green allowed-ips '192.168.10.21/32'
+set interfaces wireguard wg0 peer green persistent-keepalive '25'
+set interfaces wireguard wg0 peer green preshared-key 'LQ9qmlTh9G4nZu4UgElxRUwg7JB/qoV799aADJOijnY='
+set interfaces wireguard wg0 peer green public-key '5iQUD3VoCDBTPXAPHOwUJ0p7xzKGHEY/wQmgvBVmaFI='
+set interfaces wireguard wg0 peer pink allowed-ips '192.168.10.14/32'
+set interfaces wireguard wg0 peer pink allowed-ips '192.168.10.16/32'
+set interfaces wireguard wg0 peer pink persistent-keepalive '25'
+set interfaces wireguard wg0 peer pink preshared-key 'Qi9Odyx0/5itLPN5C5bEy3uMX+tmdl15QbakxpKlWqQ='
+set interfaces wireguard wg0 peer pink public-key 'i4qNPmxyy9EETL4tIoZOLKJF4p7IlVmpAE15gglnAk4='
+set interfaces wireguard wg0 peer red allowed-ips '192.168.10.4/32'
+set interfaces wireguard wg0 peer red persistent-keepalive '20'
+set interfaces wireguard wg0 peer red preshared-key 'CumyXX7osvUT9AwnS+m2TEfCaL0Ptc2LfuZ78Sujuk8='
+set interfaces wireguard wg0 peer red public-key 'ALGWvMJCKpHF2tVH3hEIHqUe9iFfAmZATUUok/WQzks='
+set interfaces wireguard wg0 port '7777'
+set interfaces wireguard wg1 address '10.89.90.2/30'
+set interfaces wireguard wg1 ip adjust-mss '1380'
+set interfaces wireguard wg1 peer sam address '192.0.2.45'
+set interfaces wireguard wg1 peer sam allowed-ips '10.1.1.0/24'
+set interfaces wireguard wg1 peer sam allowed-ips '10.89.90.1/32'
+set interfaces wireguard wg1 peer sam persistent-keepalive '20'
+set interfaces wireguard wg1 peer sam port '1200'
+set interfaces wireguard wg1 peer sam preshared-key 'XpFtzx2Z+nR8pBv9/sSf7I94OkZkVYTz0AeU5Q/QQUE='
+set interfaces wireguard wg1 peer sam public-key 'v5zfKGvH6W/lfDXJ0en96lvKo1gfFxMUWxe02+Fj5BU='
+set interfaces wireguard wg1 port '7778'
+set nat destination rule 50 destination port '49371'
+set nat destination rule 50 inbound-interface 'pppoe0'
+set nat destination rule 50 protocol 'tcp_udp'
+set nat destination rule 50 translation address '192.168.0.5'
+set nat destination rule 51 destination port '58050-58051'
+set nat destination rule 51 inbound-interface 'pppoe0'
+set nat destination rule 51 protocol 'tcp'
+set nat destination rule 51 translation address '192.168.0.5'
+set nat destination rule 52 destination port '22067-22070'
+set nat destination rule 52 inbound-interface 'pppoe0'
+set nat destination rule 52 protocol 'tcp'
+set nat destination rule 52 translation address '192.168.0.5'
+set nat destination rule 53 destination port '34342'
+set nat destination rule 53 inbound-interface 'pppoe0'
+set nat destination rule 53 protocol 'tcp_udp'
+set nat destination rule 53 translation address '192.168.0.121'
+set nat destination rule 54 destination port '45459'
+set nat destination rule 54 inbound-interface 'pppoe0'
+set nat destination rule 54 protocol 'tcp_udp'
+set nat destination rule 54 translation address '192.168.0.120'
+set nat destination rule 55 destination port '22'
+set nat destination rule 55 inbound-interface 'pppoe0'
+set nat destination rule 55 protocol 'tcp'
+set nat destination rule 55 translation address '192.168.0.5'
+set nat destination rule 56 destination port '8920'
+set nat destination rule 56 inbound-interface 'pppoe0'
+set nat destination rule 56 protocol 'tcp'
+set nat destination rule 56 translation address '192.168.0.5'
+set nat destination rule 60 destination port '80,443'
+set nat destination rule 60 inbound-interface 'pppoe0'
+set nat destination rule 60 protocol 'tcp'
+set nat destination rule 60 translation address '192.168.0.5'
+set nat destination rule 70 destination port '5001'
+set nat destination rule 70 inbound-interface 'pppoe0'
+set nat destination rule 70 protocol 'tcp'
+set nat destination rule 70 translation address '192.168.0.5'
+set nat destination rule 80 destination port '25'
+set nat destination rule 80 inbound-interface 'pppoe0'
+set nat destination rule 80 protocol 'tcp'
+set nat destination rule 80 translation address '192.168.0.5'
+set nat destination rule 90 destination port '8123'
+set nat destination rule 90 inbound-interface 'pppoe0'
+set nat destination rule 90 protocol 'tcp'
+set nat destination rule 90 translation address '192.168.0.7'
+set nat destination rule 91 destination port '1880'
+set nat destination rule 91 inbound-interface 'pppoe0'
+set nat destination rule 91 protocol 'tcp'
+set nat destination rule 91 translation address '192.168.0.7'
+set nat destination rule 500 destination address '!192.168.0.0/24'
+set nat destination rule 500 destination port '53'
+set nat destination rule 500 inbound-interface 'eth1'
+set nat destination rule 500 protocol 'tcp_udp'
+set nat destination rule 500 source address '!192.168.0.1-192.168.0.5'
+set nat destination rule 500 translation address '192.168.0.1'
+set nat source rule 1000 outbound-interface 'pppoe0'
+set nat source rule 1000 translation address 'masquerade'
+set nat source rule 2000 outbound-interface 'vtun0'
+set nat source rule 2000 source address '192.168.0.0/16'
+set nat source rule 2000 translation address 'masquerade'
+set nat source rule 3000 outbound-interface 'vtun1'
+set nat source rule 3000 translation address 'masquerade'
+set policy prefix-list user1-routes rule 1 action 'permit'
+set policy prefix-list user1-routes rule 1 prefix '192.168.0.0/24'
+set policy prefix-list user2-routes rule 1 action 'permit'
+set policy prefix-list user2-routes rule 1 prefix '10.1.1.0/24'
+set policy route LAN-POLICY-BASED-ROUTING interface 'eth1'
+set policy route LAN-POLICY-BASED-ROUTING rule 10 destination
+set policy route LAN-POLICY-BASED-ROUTING rule 10 disable
+set policy route LAN-POLICY-BASED-ROUTING rule 10 set table '10'
+set policy route LAN-POLICY-BASED-ROUTING rule 10 source address '192.168.0.119/32'
+set policy route LAN-POLICY-BASED-ROUTING rule 20 destination
+set policy route LAN-POLICY-BASED-ROUTING rule 20 set table '100'
+set policy route LAN-POLICY-BASED-ROUTING rule 20 source address '192.168.0.240'
+set policy route-map rm-static-to-bgp rule 10 action 'permit'
+set policy route-map rm-static-to-bgp rule 10 match ip address prefix-list 'user1-routes'
+set policy route-map rm-static-to-bgp rule 100 action 'deny'
+set policy route6 LAN6-POLICY-BASED-ROUTING interface 'eth1'
+set policy route6 LAN6-POLICY-BASED-ROUTING rule 10 destination
+set policy route6 LAN6-POLICY-BASED-ROUTING rule 10 disable
+set policy route6 LAN6-POLICY-BASED-ROUTING rule 10 set table '10'
+set policy route6 LAN6-POLICY-BASED-ROUTING rule 10 source address '2002::1'
+set policy route6 LAN6-POLICY-BASED-ROUTING rule 20 destination
+set policy route6 LAN6-POLICY-BASED-ROUTING rule 20 set table '100'
+set policy route6 LAN6-POLICY-BASED-ROUTING rule 20 source address '2008::f'
+set protocols bgp address-family ipv4-unicast redistribute connected route-map 'rm-static-to-bgp'
+set protocols bgp neighbor 10.89.90.1 address-family ipv4-unicast nexthop-self
+set protocols bgp neighbor 10.89.90.1 address-family ipv4-unicast prefix-list export 'user1-routes'
+set protocols bgp neighbor 10.89.90.1 address-family ipv4-unicast prefix-list import 'user2-routes'
+set protocols bgp neighbor 10.89.90.1 address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp neighbor 10.89.90.1 password 'ericandre2020'
+set protocols bgp neighbor 10.89.90.1 remote-as '64589'
+set protocols bgp parameters log-neighbor-changes
+set protocols bgp parameters router-id '10.89.90.2'
+set protocols bgp system-as '64590'
+set protocols static route 100.64.160.23/32 interface pppoe0
+set protocols static route 100.64.165.25/32 interface pppoe0
+set protocols static route 100.64.165.26/32 interface pppoe0
+set protocols static route 100.64.198.0/24 interface vtun0
+set protocols static table 10 route 0.0.0.0/0 interface vtun1
+set protocols static table 100 route 0.0.0.0/0 next-hop 192.168.10.5
+set service conntrack-sync accept-protocol 'tcp'
+set service conntrack-sync accept-protocol 'udp'
+set service conntrack-sync accept-protocol 'icmp'
+set service conntrack-sync disable-external-cache
+set service conntrack-sync event-listen-queue-size '8'
+set service conntrack-sync expect-sync 'all'
+set service conntrack-sync failover-mechanism vrrp sync-group 'failover-group'
+set service conntrack-sync interface eth1 peer '192.168.0.251'
+set service conntrack-sync sync-queue-size '8'
+set service dhcp-server failover name 'DHCP02'
+set service dhcp-server failover remote '192.168.0.251'
+set service dhcp-server failover source-address '192.168.0.250'
+set service dhcp-server failover status 'primary'
+set service dhcp-server shared-network-name LAN authoritative
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 default-router '192.168.0.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 domain-name 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 domain-search 'vyos.net'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 enable-failover
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 lease '86400'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 name-server '192.168.0.1'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic start '192.168.0.200'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic stop '192.168.0.240'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Audio ip-address '192.168.0.107'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Audio mac-address '00:50:01:dc:91:14'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping IPTV ip-address '192.168.0.104'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping IPTV mac-address '00:50:01:31:b5:f6'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping McPrintus ip-address '192.168.0.60'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping McPrintus mac-address '00:50:01:58:ac:95'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping McPrintus static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Mobile01 ip-address '192.168.0.109'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Mobile01 mac-address '00:50:01:bc:ac:51'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Mobile01 static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera1 ip-address '192.168.0.11'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera1 mac-address '00:50:01:70:b9:4d'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera1 static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera2 ip-address '192.168.0.12'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera2 mac-address '00:50:01:70:b7:4f'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera2 static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping pearTV ip-address '192.168.0.101'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping pearTV mac-address '00:50:01:ba:62:79'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping sand ip-address '192.168.0.110'
+set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping sand mac-address '00:50:01:af:c5:d2'
+set service dns forwarding allow-from '192.168.0.0/16'
+set service dns forwarding cache-size '8192'
+set service dns forwarding dnssec 'off'
+set service dns forwarding listen-address '192.168.0.1'
+set service dns forwarding name-server 100.64.0.1
+set service dns forwarding name-server 100.64.0.2
+set service ntp allow-client address '192.168.0.0/16'
+set service ntp server nz.pool.ntp.org prefer
+set service snmp community AwesomeCommunity authorization 'ro'
+set service snmp community AwesomeCommunity client '127.0.0.1'
+set service snmp community AwesomeCommunity network '192.168.0.0/24'
+set service ssh access-control allow user 'vyos'
+set service ssh client-keepalive-interval '60'
+set service ssh listen-address '192.168.0.1'
+set service ssh listen-address '192.168.10.1'
+set service ssh listen-address '192.168.0.250'
+set system config-management commit-revisions '100'
+set system console device ttyS0 speed '115200'
+set system host-name 'vyos'
+set system ip arp table-size '1024'
+set system name-server '192.168.0.1'
+set system name-server 'pppoe0'
+set system option ctrl-alt-delete 'ignore'
+set system option reboot-on-panic
+set system option startup-beep
+set system static-host-mapping host-name host60.vyos.net inet '192.168.0.60'
+set system static-host-mapping host-name host104.vyos.net inet '192.168.0.104'
+set system static-host-mapping host-name host107.vyos.net inet '192.168.0.107'
+set system static-host-mapping host-name host109.vyos.net inet '192.168.0.109'
+set system sysctl parameter net.core.default_qdisc value 'fq'
+set system sysctl parameter net.ipv4.tcp_congestion_control value 'bbr'
+set system syslog global facility all level 'info'
+set system syslog host 192.168.0.252 facility all level 'debug'
+set system syslog host 192.168.0.252 protocol 'udp'
+set system task-scheduler task Update-Blacklists executable path '/config/scripts/vyos-foo-update.script'
+set system task-scheduler task Update-Blacklists interval '3h'
+set system time-zone 'Pacific/Auckland'
diff --git a/smoketest/configs/basic-vyos b/smoketest/configs/basic-vyos
index 033c1a518..78dba3ee2 100644
--- a/smoketest/configs/basic-vyos
+++ b/smoketest/configs/basic-vyos
@@ -116,6 +116,18 @@ system {
speed 115200
}
}
+ conntrack {
+ ignore {
+ rule 1 {
+ destination {
+ address 192.0.2.2
+ }
+ source {
+ address 192.0.2.1
+ }
+ }
+ }
+ }
host-name vyos
login {
user vyos {
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 820024dc9..51ccbc9e6 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -834,8 +834,12 @@ class BasicInterfaceTest:
self.assertEqual('1', tmp)
if cli_defined(self._base_path + ['ip'], 'source-validation'):
- tmp = read_file(f'{proc_base}/rp_filter')
- self.assertEqual('2', tmp)
+ base_options = f'iifname "{interface}"'
+ out = cmd('sudo nft list chain ip raw vyos_rpfilter')
+ for line in out.splitlines():
+ if line.startswith(base_options):
+ self.assertIn('fib saddr oif 0', line)
+ self.assertIn('drop', line)
def test_interface_ipv6_options(self):
if not self._test_ipv6:
diff --git a/smoketest/scripts/cli/test_component_version.py b/smoketest/scripts/cli/test_component_version.py
deleted file mode 100755
index 7b1b12c53..000000000
--- a/smoketest/scripts/cli/test_component_version.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/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 unittest
-
-import vyos.component_version as component_version
-
-# After T3474, component versions should be updated in the files in
-# vyos-1x/interface-definitions/include/version/
-# This test verifies that the legacy version in curver_DATA does not exceed
-# that in the xml cache.
-class TestComponentVersion(unittest.TestCase):
- def setUp(self):
- self.legacy_d = component_version.legacy_from_system()
- self.xml_d = component_version.from_system()
- self.set_legacy_d = set(self.legacy_d)
- self.set_xml_d = set(self.xml_d)
-
- def test_component_version(self):
- bool_issubset = (self.set_legacy_d.issubset(self.set_xml_d))
- if not bool_issubset:
- missing = self.set_legacy_d.difference(self.set_xml_d)
- print(f'\n\ncomponents in legacy but not in XML: {missing}')
- print('new components must be listed in xml-component-version.xml.in')
- self.assertTrue(bool_issubset)
-
- bad_component_version = False
- for k, v in self.legacy_d.items():
- bool_inequality = (v <= self.xml_d[k])
- if not bool_inequality:
- print(f'\n\n{k} has not been updated in XML component versions:')
- print(f'legacy version {v}; XML version {self.xml_d[k]}')
- bad_component_version = True
- self.assertFalse(bad_component_version)
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_dependency_graph.py b/smoketest/scripts/cli/test_dependency_graph.py
deleted file mode 100755
index 45a40acc4..000000000
--- a/smoketest/scripts/cli/test_dependency_graph.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/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 json
-import unittest
-from graphlib import TopologicalSorter, CycleError
-
-DEP_FILE = '/usr/share/vyos/config-mode-dependencies.json'
-
-def graph_from_dict(d):
- g = {}
- for k in list(d):
- g[k] = set()
- # add the dependencies for every sub-case; should there be cases
- # that are mutally exclusive in the future, the graphs will be
- # distinguished
- for el in list(d[k]):
- g[k] |= set(d[k][el])
- return g
-
-class TestDependencyGraph(unittest.TestCase):
- def setUp(self):
- with open(DEP_FILE) as f:
- dd = json.load(f)
- self.dependency_graph = graph_from_dict(dd)
-
- def test_cycles(self):
- ts = TopologicalSorter(self.dependency_graph)
- out = None
- try:
- # get node iterator
- order = ts.static_order()
- # try iteration
- _ = [*order]
- except CycleError as e:
- out = e.args
-
- self.assertIsNone(out)
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index ee6ccb710..7b4ba11d0 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -274,8 +274,8 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['meta l4proto gre', f'oifname != "{interface}"', 'drop'],
['meta l4proto icmp', f'ct mark {mark_hex}', 'return'],
['chain NAME_smoketest'],
- ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'accept'],
- ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'],
+ ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[ipv4-NAM-smoketest-1-A]" log level debug', 'ip ttl 15', 'accept'],
+ ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[ipv4-NAM-smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'],
['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop']
]
@@ -308,10 +308,12 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'default-action', 'drop'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'source', 'address', '198.51.100.1'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'mark', '1010'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'jump'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'jump-target', name])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '2', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '2', 'mark', '!98765'])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '2', 'action', 'queue'])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '2', 'queue', '3'])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '3', 'protocol', 'udp'])
@@ -325,19 +327,44 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
nftables_search = [
['chain VYOS_FORWARD_filter'],
['type filter hook forward priority filter; policy drop;'],
- ['ip saddr 198.51.100.1', f'jump NAME_{name}'],
+ ['ip saddr 198.51.100.1', 'meta mark 0x000003f2', f'jump NAME_{name}'],
['chain VYOS_INPUT_filter'],
['type filter hook input priority filter; policy accept;'],
- [f'meta l4proto tcp','queue to 3'],
- [f'meta l4proto udp','queue flags bypass,fanout to 0-15'],
+ ['meta mark != 0x000181cd', 'meta l4proto tcp','queue to 3'],
+ ['meta l4proto udp','queue flags bypass,fanout to 0-15'],
[f'chain NAME_{name}'],
- ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'accept'],
+ ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[ipv4-NAM-{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'accept'],
['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'accept'],
[f'log prefix "[{name}-default-D]"', 'drop']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
+ def test_ipv4_synproxy(self):
+ tcp_mss = '1460'
+ tcp_wscale = '7'
+ dport = '22'
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'destination', 'port', dport])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'synproxy', 'tcp', 'mss', tcp_mss])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'synproxy', 'tcp', 'window-scale', tcp_wscale])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'synproxy'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'tcp dport {dport} ct state invalid,untracked', f'synproxy mss {tcp_mss} wscale {tcp_wscale} timestamp sack-perm']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+
+
def test_ipv4_mask(self):
name = 'smoketest-mask'
interface = 'eth0'
@@ -412,7 +439,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['type filter hook output priority filter; policy drop;'],
['meta l4proto gre', f'oifname "{interface}"', 'return'],
[f'chain NAME6_{name}'],
- ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'accept'],
+ ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[ipv6-NAM-v6-smoketest-1-A]" log level crit', 'accept'],
[f'"{name} default-action drop"', f'log prefix "[{name}-default-D]"', 'drop']
]
@@ -441,6 +468,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'default-action', 'accept'])
self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'source', 'address', '2001:db8::/64'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'mark', '!6655-7766'])
self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'action', 'jump'])
self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '1', 'jump-target', name])
@@ -452,7 +480,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['ip6 length 1-1999', 'ip6 length != 60000-65535', 'ip6 dscp 0x04-0x0e', 'ip6 dscp != 0x1f-0x23', 'accept'],
['chain VYOS_IPV6_INPUT_filter'],
['type filter hook input priority filter; policy accept;'],
- ['ip6 saddr 2001:db8::/64', f'jump NAME6_{name}'],
+ ['ip6 saddr 2001:db8::/64', 'meta mark != 0x000019ff-0x00001e56', f'jump NAME6_{name}'],
[f'chain NAME6_{name}'],
['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'accept'],
[f'log prefix "[{name}-default-D]"', 'drop']
@@ -503,12 +531,15 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'state', 'invalid', 'enable'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'state', 'new', 'enable'])
-
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '3', 'connection-status', 'nat', 'destination'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'state', 'new', 'enable'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'state', 'established', 'enable'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'connection-status', 'nat', 'source'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '5', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '5', 'state', 'related', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '5', 'conntrack-helper', 'ftp'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '5', 'conntrack-helper', 'pptp'])
self.cli_commit()
@@ -517,35 +548,75 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['ct state invalid', 'reject'],
['ct state new', 'ct status dnat', 'accept'],
['ct state { established, new }', 'ct status snat', 'accept'],
+ ['ct state related', 'ct helper { "ftp", "pptp" }', 'accept'],
['drop', f'comment "{name} default-action drop"']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
# Check conntrack
- self.verify_nftables_chain([['accept']], 'raw', 'FW_CONNTRACK')
- self.verify_nftables_chain([['return']], 'ip6 raw', 'FW_CONNTRACK')
+ self.verify_nftables_chain([['accept']], 'ip vyos_conntrack', 'FW_CONNTRACK')
+ self.verify_nftables_chain([['return']], 'ip6 vyos_conntrack', 'FW_CONNTRACK')
+
+ def test_bridge_basic_rules(self):
+ name = 'smoketest'
+ interface_in = 'eth0'
+ mac_address = '00:53:00:00:00:01'
+ vlan_id = '12'
+ vlan_prior = '3'
+
+ self.cli_set(['firewall', 'bridge', 'name', name, 'default-action', 'accept'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'source', 'mac-address', mac_address])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'inbound-interface', 'interface-name', interface_in])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log', 'enable'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log-options', 'level', 'crit'])
+
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '1', 'vlan', 'id', vlan_id])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'action', 'jump'])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'jump-target', name])
+ self.cli_set(['firewall', 'bridge', 'forward', 'filter', 'rule', '2', 'vlan', 'priority', vlan_prior])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['chain VYOS_FORWARD_filter'],
+ ['type filter hook forward priority filter; policy drop;'],
+ [f'vlan id {vlan_id}', 'accept'],
+ [f'vlan pcp {vlan_prior}', f'jump NAME_{name}'],
+ [f'chain NAME_{name}'],
+ [f'ether saddr {mac_address}', f'iifname "{interface_in}"', f'log prefix "[bri-NAM-{name}-1-A]" log level crit', 'accept']
+ ]
+
+ self.verify_nftables(nftables_search, 'bridge vyos_filter')
def test_source_validation(self):
# Strict
self.cli_set(['firewall', 'global-options', 'source-validation', 'strict'])
+ self.cli_set(['firewall', 'global-options', 'ipv6-source-validation', 'strict'])
self.cli_commit()
nftables_strict_search = [
['fib saddr . iif oif 0', 'drop']
]
- self.verify_nftables(nftables_strict_search, 'inet vyos_global_rpfilter')
+ self.verify_nftables_chain(nftables_strict_search, 'ip raw', 'vyos_global_rpfilter')
+ self.verify_nftables_chain(nftables_strict_search, 'ip6 raw', 'vyos_global_rpfilter')
# Loose
self.cli_set(['firewall', 'global-options', 'source-validation', 'loose'])
+ self.cli_set(['firewall', 'global-options', 'ipv6-source-validation', 'loose'])
self.cli_commit()
nftables_loose_search = [
['fib saddr oif 0', 'drop']
]
- self.verify_nftables(nftables_loose_search, 'inet vyos_global_rpfilter')
+ self.verify_nftables_chain(nftables_loose_search, 'ip raw', 'vyos_global_rpfilter')
+ self.verify_nftables_chain(nftables_loose_search, 'ip6 raw', 'vyos_global_rpfilter')
def test_sysfs(self):
for name, conf in sysfs_config.items():
@@ -564,5 +635,43 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with open(path, 'r') as f:
self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
+ def test_flow_offload(self):
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'hardware'])
+
+ # QEMU virtual NIC does not support hw-tc-offload
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'software'])
+
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'offload'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'offload-target', 'smoketest'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'established', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'related', 'enable'])
+
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'action', 'offload'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'offload-target', 'smoketest'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'state', 'established', 'enable'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'state', 'related', 'enable'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['flowtable VYOS_FLOWTABLE_smoketest'],
+ ['hook ingress priority filter'],
+ ['devices = { eth0 }'],
+ ['ct state { established, related }', 'meta l4proto { tcp, udp }', 'flow add @VYOS_FLOWTABLE_smoketest'],
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+ self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+
+ # Check conntrack
+ self.verify_nftables_chain([['accept']], 'ip vyos_conntrack', 'FW_CONNTRACK')
+ self.verify_nftables_chain([['accept']], 'ip6 vyos_conntrack', 'FW_CONNTRACK')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_openvpn.py b/smoketest/scripts/cli/test_interfaces_openvpn.py
index d1ece84d6..4a7e2418c 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -421,7 +421,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
# IP pool configuration
netmask = IPv4Network(subnet).netmask
network = IPv4Network(subnet).network_address
- self.assertIn(f'server {network} {netmask} nopool', config)
+ self.assertIn(f'server {network} {netmask}', config)
# Verify client
client_config = read_file(client_config_file)
@@ -442,80 +442,6 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
interface = f'vtun{ii}'
self.assertNotIn(interface, interfaces())
- def test_openvpn_server_net30_topology(self):
- # Create OpenVPN server interfaces (net30) using different client
- # subnets. Validate configuration afterwards.
- auth_hash = 'sha256'
- num_range = range(20, 25)
- port = ''
- for ii in num_range:
- interface = f'vtun{ii}'
- subnet = f'192.0.{ii}.0/24'
- path = base_path + [interface]
- port = str(2000 + ii)
-
- self.cli_set(path + ['device-type', 'tun'])
- self.cli_set(path + ['encryption', 'cipher', 'aes192'])
- self.cli_set(path + ['hash', auth_hash])
- self.cli_set(path + ['mode', 'server'])
- self.cli_set(path + ['local-port', port])
- self.cli_set(path + ['server', 'subnet', subnet])
- self.cli_set(path + ['server', 'topology', 'net30'])
- self.cli_set(path + ['replace-default-route'])
- self.cli_set(path + ['keep-alive', 'failure-count', '10'])
- self.cli_set(path + ['keep-alive', 'interval', '5'])
- self.cli_set(path + ['tls', 'ca-certificate', 'ovpn_test'])
- self.cli_set(path + ['tls', 'certificate', 'ovpn_test'])
- self.cli_set(path + ['tls', 'dh-params', 'ovpn_test'])
- self.cli_set(path + ['vrf', vrf_name])
-
- self.cli_commit()
-
- for ii in num_range:
- interface = f'vtun{ii}'
- subnet = f'192.0.{ii}.0/24'
- start_addr = inc_ip(subnet, '4')
- stop_addr = dec_ip(last_host_address(subnet), '1')
- port = str(2000 + ii)
-
- config_file = f'/run/openvpn/{interface}.conf'
- config = read_file(config_file)
-
- self.assertIn(f'dev {interface}', config)
- self.assertIn(f'dev-type tun', config)
- self.assertIn(f'persist-key', config)
- self.assertIn(f'proto udp', config) # default protocol
- self.assertIn(f'auth {auth_hash}', config)
- self.assertIn(f'cipher AES-192-CBC', config)
- self.assertIn(f'topology net30', config)
- self.assertIn(f'lport {port}', config)
- self.assertIn(f'push "redirect-gateway def1"', config)
- self.assertIn(f'keepalive 5 50', config)
-
- # TLS options
- self.assertIn(f'ca /run/openvpn/{interface}_ca.pem', config)
- self.assertIn(f'cert /run/openvpn/{interface}_cert.pem', config)
- self.assertIn(f'key /run/openvpn/{interface}_cert.key', config)
- self.assertIn(f'dh /run/openvpn/{interface}_dh.pem', config)
-
- # IP pool configuration
- netmask = IPv4Network(subnet).netmask
- network = IPv4Network(subnet).network_address
- self.assertIn(f'server {network} {netmask} nopool', config)
- self.assertIn(f'ifconfig-pool {start_addr} {stop_addr}', config)
-
- self.assertTrue(process_named_running(PROCESS_NAME))
- self.assertEqual(get_vrf(interface), vrf_name)
- self.assertIn(interface, interfaces())
-
- # check that no interface remained after deleting them
- self.cli_delete(base_path)
- self.cli_commit()
-
- for ii in num_range:
- interface = f'vtun{ii}'
- self.assertNotIn(interface, interfaces())
-
def test_openvpn_site2site_verify(self):
# Create one OpenVPN site2site interface and check required
# verify() stages
diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py
index 0ce5e2fe0..7b702759f 100755
--- a/smoketest/scripts/cli/test_interfaces_pppoe.py
+++ b/smoketest/scripts/cli/test_interfaces_pppoe.py
@@ -59,10 +59,12 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
user = f'VyOS-user-{interface}'
passwd = f'VyOS-passwd-{interface}'
mtu = '1400'
+ mru = '1300'
self.cli_set(base_path + [interface, 'authentication', 'username', user])
self.cli_set(base_path + [interface, 'authentication', 'password', passwd])
self.cli_set(base_path + [interface, 'mtu', mtu])
+ self.cli_set(base_path + [interface, 'mru', '9000'])
self.cli_set(base_path + [interface, 'no-peer-dns'])
# check validate() - a source-interface is required
@@ -70,6 +72,11 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
+ # check validate() - MRU needs to be less or equal then MTU
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + [interface, 'mru', mru])
+
# commit changes
self.cli_commit()
@@ -80,6 +87,8 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value(interface, 'mtu')[1]
self.assertEqual(tmp, mtu)
+ tmp = get_config_value(interface, 'mru')[1]
+ self.assertEqual(tmp, mru)
tmp = get_config_value(interface, 'user')[1].replace('"', '')
self.assertEqual(tmp, user)
tmp = get_config_value(interface, 'password')[1].replace('"', '')
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index f6b203de4..e9c9e68fd 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -20,6 +20,8 @@ from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.utils.network import get_bridge_fdb
from vyos.utils.network import get_interface_config
+from vyos.utils.network import interface_exists
+from vyos.utils.network import get_vxlan_vlan_tunnels
from vyos.template import is_ipv6
from base_interfaces_test import BasicInterfaceTest
@@ -133,5 +135,53 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
self.assertTrue(options['linkinfo']['info_data']['external'])
self.assertEqual('vxlan', options['linkinfo']['info_kind'])
+ def test_vxlan_vlan_vni_mapping(self):
+ bridge = 'br0'
+ interface = 'vxlan0'
+ source_interface = 'eth0'
+
+ vlan_to_vni = {
+ '10': '10010',
+ '11': '10011',
+ '12': '10012',
+ '13': '10013',
+ '20': '10020',
+ '30': '10030',
+ '31': '10031',
+ }
+
+ self.cli_set(self._base_path + [interface, 'external'])
+ self.cli_set(self._base_path + [interface, 'source-interface', source_interface])
+
+ for vlan, vni in vlan_to_vni.items():
+ self.cli_set(self._base_path + [interface, 'vlan-to-vni', vlan, 'vni', vni])
+
+ # This must fail as this VXLAN interface is not associated with any bridge
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(['interfaces', 'bridge', bridge, 'member', 'interface', interface])
+
+ # It is not allowed to use duplicate VNIs
+ self.cli_set(self._base_path + [interface, 'vlan-to-vni', '11', 'vni', vlan_to_vni['10']])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ # restore VLAN - VNI mappings
+ for vlan, vni in vlan_to_vni.items():
+ self.cli_set(self._base_path + [interface, 'vlan-to-vni', vlan, 'vni', vni])
+
+ # commit configuration
+ self.cli_commit()
+
+ self.assertTrue(interface_exists(bridge))
+ self.assertTrue(interface_exists(interface))
+
+ tmp = get_interface_config(interface)
+ self.assertEqual(tmp['master'], bridge)
+
+ tmp = get_vxlan_vlan_tunnels('vxlan0')
+ self.assertEqual(tmp, list(vlan_to_vni))
+
+ self.cli_delete(['interfaces', 'bridge', bridge])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index f8686edd8..95246a7b9 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -97,6 +97,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
vht_opt = {
# VyOS CLI option hostapd - ht_capab setting
+ 'channel-set-width 3' : '[VHT160-80PLUS80]',
'stbc tx' : '[TX-STBC-2BY1]',
'stbc rx 12' : '[RX-STBC-12]',
'ldpc' : '[RXLDPC]',
@@ -104,7 +105,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
'vht-cf' : '[HTC-VHT]',
'antenna-pattern-fixed' : '[RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]',
'max-mpdu 11454' : '[MAX-MPDU-11454]',
- 'max-mpdu-exp 2' : '[MAX-A-MPDU-LEN-EXP-2][VHT160]',
+ 'max-mpdu-exp 2' : '[MAX-A-MPDU-LEN-EXP-2]',
'link-adaptation both' : '[VHT-LINK-ADAPT3]',
'short-gi 80' : '[SHORT-GI-80]',
'short-gi 160' : '[SHORT-GI-160]',
diff --git a/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py b/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
index a33fd5c18..274b97f22 100755
--- a/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
+++ b/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
@@ -74,6 +74,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.cli_set(back_base + [bk_second_name, 'mode', mode])
self.cli_set(back_base + [bk_second_name, 'server', bk_second_name, 'address', bk_server_second])
self.cli_set(back_base + [bk_second_name, 'server', bk_second_name, 'port', bk_server_port])
+ self.cli_set(back_base + [bk_second_name, 'server', bk_second_name, 'backup'])
self.cli_set(base_path + ['global-parameters', 'max-connections', max_connections])
@@ -106,6 +107,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'backend {bk_second_name}', config)
self.assertIn(f'mode {mode}', config)
self.assertIn(f'server {bk_second_name} {bk_server_second}:{bk_server_port}', config)
+ self.assertIn(f'server {bk_second_name} {bk_server_second}:{bk_server_port} backup', config)
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_load_balancing_wan.py b/smoketest/scripts/cli/test_load_balancing_wan.py
index 9b2cb0fac..47ca19b27 100755
--- a/smoketest/scripts/cli/test_load_balancing_wan.py
+++ b/smoketest/scripts/cli/test_load_balancing_wan.py
@@ -124,11 +124,12 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
self.assertEqual(tmp, original)
# Delete veth interfaces and netns
- for iface in [iface1, iface2, iface3, container_iface1, container_iface2, container_iface3]:
+ for iface in [iface1, iface2, iface3]:
call(f'sudo ip link del dev {iface}')
delete_netns(ns1)
delete_netns(ns2)
+ delete_netns(ns3)
def test_check_chains(self):
@@ -246,11 +247,13 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
self.assertEqual(tmp, nat_vyos_pre_snat_hook)
# Delete veth interfaces and netns
- for iface in [iface1, iface2, iface3, container_iface1, container_iface2, container_iface3]:
+ for iface in [iface1, iface2, iface3]:
call(f'sudo ip link del dev {iface}')
delete_netns(ns1)
delete_netns(ns2)
+ delete_netns(ns3)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 31dfcef87..703e5ab28 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -155,11 +155,6 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
rule = '5'
self.cli_set(src_path + ['rule', rule, 'source', 'address', '192.0.2.0/24'])
- # check validate() - outbound-interface must be defined
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'eth0'])
-
# check validate() - translation address not specified
with self.assertRaises(ConfigSessionError):
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_interfaces_netns.py b/smoketest/scripts/cli/test_netns.py
index b8bebb221..fd04dd520 100755
--- a/smoketest/scripts/cli/test_interfaces_netns.py
+++ b/smoketest/scripts/cli/test_netns.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -16,7 +16,6 @@
import unittest
-from netifaces import interfaces
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSession
@@ -24,56 +23,61 @@ from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
from vyos.utils.process import cmd
+from vyos.utils.network import is_netns_interface
+from vyos.utils.network import get_netns_all
base_path = ['netns']
-namespaces = ['mgmt', 'front', 'back', 'ams-ix']
+interfaces = ['dum10', 'dum12', 'dum50']
-class NETNSTest(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self._interfaces = ['dum10', 'dum12', 'dum50']
+class NetNSTest(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ self.cli_delete(base_path)
+ # commit changes
+ self.cli_commit()
+
+ # There should be no network namespace remaining
+ tmp = cmd('ip netns ls')
+ self.assertFalse(tmp)
+
+ super(NetNSTest, self).tearDown()
- def test_create_netns(self):
+ def test_netns_create(self):
+ namespaces = ['mgmt', 'front', 'back']
for netns in namespaces:
- base = base_path + ['name', netns]
- self.cli_set(base)
+ self.cli_set(base_path + ['name', netns])
# commit changes
self.cli_commit()
- netns_list = cmd('ip netns ls')
-
# Verify NETNS configuration
for netns in namespaces:
- self.assertTrue(netns in netns_list)
-
+ self.assertIn(netns, get_netns_all())
- def test_netns_assign_interface(self):
+ def test_netns_interface(self):
netns = 'foo'
- self.cli_set(['netns', 'name', netns])
+ self.cli_set(base_path + ['name', netns])
# Set
- for iface in self._interfaces:
+ for iface in interfaces:
self.cli_set(['interfaces', 'dummy', iface, 'netns', netns])
# commit changes
self.cli_commit()
- netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show')
-
- for iface in self._interfaces:
- self.assertTrue(iface in netns_iface_list)
+ for interface in interfaces:
+ self.assertTrue(is_netns_interface(interface, netns))
# Delete
- for iface in self._interfaces:
- self.cli_delete(['interfaces', 'dummy', iface, 'netns', netns])
+ for interface in interfaces:
+ self.cli_delete(['interfaces', 'dummy', interface])
# commit changes
self.cli_commit()
netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show')
- for iface in self._interfaces:
- self.assertNotIn(iface, netns_iface_list)
+ for interface in interfaces:
+ self.assertFalse(is_netns_interface(interface, netns))
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index 354f791bd..4ac422d5f 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -1467,7 +1467,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '23'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
@@ -1508,7 +1508,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '154'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_commit()
@@ -1519,6 +1519,28 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.assertEqual(sort_ip(tmp), sort_ip(original))
+ # Test set table for destination and protocol
+ def test_protocol_destination_table_id(self):
+ path = base_path + ['local-route']
+
+ dst = '203.0.113.12'
+ rule = '85'
+ table = '104'
+ proto = 'tcp'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
+ self.cli_set(path + ['rule', rule, 'protocol', proto])
+
+ self.cli_commit()
+
+ original = """
+ 85: from all to 203.0.113.12 ipproto tcp lookup 104
+ """
+ tmp = cmd('ip rule show prio 85')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
# Test set table for sources with fwmark
def test_fwmark_sources_table_id(self):
path = base_path + ['local-route']
@@ -1529,7 +1551,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '150'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
@@ -1554,7 +1576,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_set(path + ['rule', rule, 'set', 'table', table])
self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
for src in sources:
- self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
@@ -1580,8 +1602,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
for src in sources:
for dst in destinations:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
- self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
@@ -1605,7 +1627,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '23'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
@@ -1646,7 +1668,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '154'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_commit()
@@ -1667,7 +1689,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '150'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
@@ -1690,7 +1712,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '150'
for src in sources:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
self.cli_commit()
@@ -1717,8 +1739,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
for src in sources:
for dst in destinations:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
- self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
@@ -1748,15 +1770,15 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
for src in sources:
for dst in destinations:
self.cli_set(path + ['rule', rule, 'set', 'table', table])
- self.cli_set(path + ['rule', rule, 'source', src])
- self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
for src in sources_v6:
for dst in destinations_v6:
self.cli_set(path_v6 + ['rule', rule, 'set', 'table', table])
- self.cli_set(path_v6 + ['rule', rule, 'source', src])
- self.cli_set(path_v6 + ['rule', rule, 'destination', dst])
+ self.cli_set(path_v6 + ['rule', rule, 'source', 'address', src])
+ self.cli_set(path_v6 + ['rule', rule, 'destination', 'address', dst])
self.cli_set(path_v6 + ['rule', rule, 'fwmark', fwmk])
self.cli_commit()
@@ -1799,7 +1821,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
table = '151'
self.cli_set(path + ['rule', rule, 'set', 'table', table])
for src in sources:
- self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
self.cli_commit()
@@ -1812,7 +1834,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.assertEqual(sort_ip(tmp), sort_ip(original_first))
# Create second commit with added destination
- self.cli_set(path + ['rule', rule, 'destination', destination])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', destination])
self.cli_commit()
original_second = """
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index d9b64544a..72192fb98 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -191,15 +191,18 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
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', '1', 'mark', '2020'])
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', 'mark', '2-3000'])
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', 'mark', '!456'])
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'])
@@ -210,6 +213,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
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', 'mark', '!456-500'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'udp'])
@@ -247,11 +251,11 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
# IPv4
nftables_search = [
['iifname { "' + interface + '", "' + interface_wc + '" }', 'jump VYOS_PBR_UD_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 pkttype other', 'meta mark set ' + mark_hex],
- ['ip dscp { 0x29, 0x39-0x3b }', 'meta mark set ' + mark_hex]
+ ['meta l4proto udp', 'meta mark 0x000007e4', 'drop'],
+ ['tcp flags syn / syn,ack', 'meta mark 0x00000002-0x00000bb8', 'meta mark set ' + mark_hex],
+ ['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark != 0x000001c8', 'meta mark set ' + mark_hex],
+ ['log prefix "[ipv4-route-smoketest-4-A]"', 'icmp type echo-request', 'ip length { 128, 1024-2048 }', 'meta pkttype other', 'meta mark set ' + mark_hex],
+ ['ip dscp { 0x29, 0x39-0x3b }', 'meta mark != 0x000001c8-0x000001f4', 'meta mark set ' + mark_hex]
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
@@ -262,7 +266,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
['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 pkttype multicast', 'meta mark set ' + mark_hex],
+ ['log prefix "[ipv6-route6-smoketest6-4-A]"', 'icmpv6 type echo-request', 'ip6 length != { 128, 1024-2048 }', 'meta pkttype multicast', 'meta mark set ' + mark_hex],
['ip6 dscp != { 0x0e-0x13, 0x3d }', 'meta mark set ' + mark_hex]
]
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 77952d8d9..967958cab 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -44,6 +44,7 @@ neighbor_config = {
'bfd' : '',
'cap_dynamic' : '',
'cap_ext_next' : '',
+ 'cap_ext_sver' : '',
'remote_as' : '100',
'adv_interv' : '400',
'passive' : '',
@@ -71,6 +72,7 @@ neighbor_config = {
'pfx_list_out' : prefix_list_out,
'no_send_comm_std' : '',
'local_role' : 'rs-client',
+ 'p_attr_taw' : '200',
},
'192.0.2.3' : {
'advertise_map' : route_map_in,
@@ -87,6 +89,7 @@ neighbor_config = {
'exist_map' : route_map_out,
'cap_dynamic' : '',
'cap_ext_next' : '',
+ 'cap_ext_sver' : '',
'remote_as' : '123',
'adv_interv' : '400',
'passive' : '',
@@ -137,6 +140,7 @@ peer_group_config = {
'remote_as' : '111',
'graceful_rst_no' : '',
'port' : '667',
+ 'p_attr_taw' : '126',
},
'foo-bar' : {
'advertise_map' : route_map_in,
@@ -217,6 +221,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {peer} capability dynamic', frrconfig)
if 'cap_ext_next' in peer_config:
self.assertIn(f' neighbor {peer} capability extended-nexthop', frrconfig)
+ if 'cap_ext_sver' in peer_config:
+ self.assertIn(f' neighbor {peer} capability software-version', frrconfig)
if 'description' in peer_config:
self.assertIn(f' neighbor {peer} description {peer_config["description"]}', frrconfig)
if 'no_cap_nego' in peer_config:
@@ -264,6 +270,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig)
if 'p_attr_discard' in peer_config:
self.assertIn(f' neighbor {peer} path-attribute discard {peer_config["p_attr_discard"]}', frrconfig)
+ if 'p_attr_taw' in peer_config:
+ self.assertIn(f' neighbor {peer} path-attribute treat-as-withdraw {peer_config["p_attr_taw"]}', frrconfig)
if 'addpath_per_as' in peer_config:
self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig)
if 'advertise_map' in peer_config:
@@ -390,6 +398,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['neighbor', peer, 'capability', 'dynamic'])
if 'cap_ext_next' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'extended-nexthop'])
+ if 'cap_ext_sver' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'capability', 'software-version'])
if 'description' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'description', peer_config["description"]])
if 'no_cap_nego' in peer_config:
@@ -424,6 +434,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['neighbor', peer, 'update-source', peer_config["update_src"]])
if 'p_attr_discard' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'path-attribute', 'discard', peer_config["p_attr_discard"]])
+ if 'p_attr_taw' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'path-attribute', 'treat-as-withdraw', peer_config["p_attr_taw"]])
if 'route_map_in' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'import', peer_config["route_map_in"]])
if 'route_map_out' in peer_config:
@@ -490,6 +502,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'dynamic'])
if 'cap_ext_next' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'extended-nexthop'])
+ if 'cap_ext_sver' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'software-version'])
if 'description' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'description', config["description"]])
if 'no_cap_nego' in config:
@@ -544,6 +558,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer-group', peer_group, 'disable-connected-check'])
if 'p_attr_discard' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'path-attribute', 'discard', config["p_attr_discard"]])
+ if 'p_attr_taw' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'path-attribute', 'treat-as-withdraw', config["p_attr_taw"]])
# Conditional advertisement
if 'advertise_map' in config:
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index af171c8b2..8b423dbea 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.py
@@ -100,6 +100,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_set(vrf_base + ['table', table])
self.cli_set(vrf_base + ['protocols', 'isis', 'net', net])
self.cli_set(vrf_base + ['protocols', 'isis', 'interface', vrf_iface])
+ self.cli_set(vrf_base + ['protocols', 'isis', 'advertise-high-metrics'])
+ self.cli_set(vrf_base + ['protocols', 'isis', 'advertise-passive-only'])
self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
# Also set a default VRF IS-IS config
@@ -115,6 +117,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}', daemon='isisd')
self.assertIn(f'router isis {domain} vrf {vrf}', tmp)
self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' advertise-high-metrics', tmp)
+ self.assertIn(f' advertise-passive-only', tmp)
self.cli_delete(['vrf', 'name', vrf])
self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
diff --git a/smoketest/scripts/cli/test_protocols_pim6.py b/smoketest/scripts/cli/test_protocols_pim6.py
new file mode 100755
index 000000000..1be12836d
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_pim6.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.utils.process import process_named_running
+
+PROCESS_NAME = 'pim6d'
+base_path = ['protocols', 'pim6']
+
+
+class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ def test_pim6_01_mld_simple(self):
+ # commit changes
+ interfaces = Section.interfaces('ethernet')
+
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface])
+
+ self.cli_commit()
+
+ # Verify FRR pim6d configuration
+ for interface in interfaces:
+ config = self.getFRRconfig(
+ f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertIn(f'interface {interface}', config)
+ self.assertIn(f' ipv6 mld', config)
+ self.assertNotIn(f' ipv6 mld version 1', config)
+
+ # Change to MLD version 1
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface',
+ interface, 'mld', 'version', '1'])
+
+ self.cli_commit()
+
+ # Verify FRR pim6d configuration
+ for interface in interfaces:
+ config = self.getFRRconfig(
+ f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertIn(f'interface {interface}', config)
+ self.assertIn(f' ipv6 mld', config)
+ self.assertIn(f' ipv6 mld version 1', config)
+
+ def test_pim6_02_mld_join(self):
+ # commit changes
+ interfaces = Section.interfaces('ethernet')
+
+ # Use an invalid multiple group address
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface',
+ interface, 'mld', 'join', 'fd00::1234'])
+
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['interface'])
+
+ # Use a valid multiple group address
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface',
+ interface, 'mld', 'join', 'ff18::1234'])
+
+ self.cli_commit()
+
+ # Verify FRR pim6d configuration
+ for interface in interfaces:
+ config = self.getFRRconfig(
+ f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertIn(f'interface {interface}', config)
+ self.assertIn(f' ipv6 mld join ff18::1234', config)
+
+ # Join a source-specific multicast group
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface,
+ 'mld', 'join', 'ff38::5678', 'source', '2001:db8::5678'])
+
+ self.cli_commit()
+
+ # Verify FRR pim6d configuration
+ for interface in interfaces:
+ config = self.getFRRconfig(
+ f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertIn(f'interface {interface}', config)
+ self.assertIn(f' ipv6 mld join ff38::5678 2001:db8::5678', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index ee8a07b37..acabc0070 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.py
@@ -17,6 +17,8 @@
import os
import unittest
import tempfile
+import random
+import string
from base_vyostest_shim import VyOSUnitTestSHIM
@@ -24,16 +26,25 @@ from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
from vyos.utils.process import process_running
+DDCLIENT_SYSTEMD_UNIT = '/run/systemd/system/ddclient.service.d/override.conf'
DDCLIENT_CONF = '/run/ddclient/ddclient.conf'
DDCLIENT_PID = '/run/ddclient/ddclient.pid'
+DDCLIENT_PNAME = 'ddclient'
base_path = ['service', 'dns', 'dynamic']
+server = 'ddns.vyos.io'
hostname = 'test.ddns.vyos.io'
zone = 'vyos.io'
+username = 'vyos_user'
password = 'paSS_@4ord'
+ttl = '300'
interface = 'eth0'
class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
+ def setUp(self):
+ # Always start with a clean CLI instance
+ self.cli_delete(base_path)
+
def tearDown(self):
# Check for running process
self.assertTrue(process_running(DDCLIENT_PID))
@@ -47,30 +58,38 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# IPv4 standard DDNS service configuration
def test_01_dyndns_service_standard(self):
- ddns = ['address', interface, 'service']
+ svc_path = ['address', interface, 'service']
services = {'cloudflare': {'protocol': 'cloudflare'},
- 'freedns': {'protocol': 'freedns', 'username': 'vyos_user'},
- 'zoneedit': {'protocol': 'zoneedit1', 'username': 'vyos_user'}}
+ 'freedns': {'protocol': 'freedns', 'username': username},
+ 'zoneedit': {'protocol': 'zoneedit1', 'username': username}}
for svc, details in services.items():
- # Always start with a clean CLI instance
- self.cli_delete(base_path)
-
- self.cli_set(base_path + ddns + [svc, 'host-name', hostname])
- self.cli_set(base_path + ddns + [svc, 'password', password])
- self.cli_set(base_path + ddns + [svc, 'zone', zone])
+ self.cli_set(base_path + svc_path + [svc, 'host-name', hostname])
+ self.cli_set(base_path + svc_path + [svc, 'password', password])
+ self.cli_set(base_path + svc_path + [svc, 'zone', zone])
+ self.cli_set(base_path + svc_path + [svc, 'ttl', ttl])
for opt, value in details.items():
- self.cli_set(base_path + ddns + [svc, opt, value])
+ self.cli_set(base_path + svc_path + [svc, opt, value])
- # commit changes
+ # 'zone' option is supported and required by 'cloudfare', but not 'freedns' and 'zoneedit'
+ self.cli_set(base_path + svc_path + [svc, 'zone', zone])
if details['protocol'] == 'cloudflare':
pass
else:
- # zone option does not work on all protocols, an exception is
- # raised for all others
+ # exception is raised for unsupported ones
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_delete(base_path + ddns + [svc, 'zone', zone])
+ self.cli_delete(base_path + svc_path + [svc, 'zone'])
+
+ # 'ttl' option is supported by 'cloudfare', but not 'freedns' and 'zoneedit'
+ self.cli_set(base_path + svc_path + [svc, 'ttl', ttl])
+ if details['protocol'] == 'cloudflare':
+ pass
+ else:
+ # exception is raised for unsupported ones
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + svc_path + [svc, 'ttl'])
# commit changes
self.cli_commit()
@@ -79,8 +98,8 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
# default value 300 seconds
self.assertIn(f'daemon=300', ddclient_conf)
- self.assertIn(f'use=if', ddclient_conf)
- self.assertIn(f'if={interface}', ddclient_conf)
+ self.assertIn(f'usev4=ifv4', ddclient_conf)
+ self.assertIn(f'ifv4={interface}', ddclient_conf)
self.assertIn(f'password={password}', ddclient_conf)
for opt in details.keys():
@@ -94,20 +113,27 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# IPv6 only DDNS service configuration
def test_02_dyndns_service_ipv6(self):
timeout = '60'
- ddns = ['address', interface, 'service', 'dynv6']
+ svc_path = ['address', interface, 'service', 'dynv6']
proto = 'dyndns2'
- user = 'none'
- password = 'paSS_4ord'
- srv = 'ddns.vyos.io'
ip_version = 'ipv6'
+ wait_time = '600'
+ expiry_time_good = '3600'
+ expiry_time_bad = '360'
self.cli_set(base_path + ['timeout', timeout])
- self.cli_set(base_path + ddns + ['ip-version', ip_version])
- self.cli_set(base_path + ddns + ['protocol', proto])
- self.cli_set(base_path + ddns + ['server', srv])
- self.cli_set(base_path + ddns + ['username', user])
- self.cli_set(base_path + ddns + ['password', password])
- self.cli_set(base_path + ddns + ['host-name', hostname])
+ self.cli_set(base_path + svc_path + ['ip-version', ip_version])
+ self.cli_set(base_path + svc_path + ['protocol', proto])
+ self.cli_set(base_path + svc_path + ['server', server])
+ self.cli_set(base_path + svc_path + ['username', username])
+ self.cli_set(base_path + svc_path + ['password', password])
+ self.cli_set(base_path + svc_path + ['host-name', hostname])
+ self.cli_set(base_path + svc_path + ['wait-time', wait_time])
+
+ # expiry-time must be greater than wait-time, exception is raised otherwise
+ self.cli_set(base_path + svc_path + ['expiry-time', expiry_time_bad])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + svc_path + ['expiry-time', expiry_time_good])
# commit changes
self.cli_commit()
@@ -118,37 +144,47 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'usev6=ifv6', ddclient_conf)
self.assertIn(f'ifv6={interface}', ddclient_conf)
self.assertIn(f'protocol={proto}', ddclient_conf)
- self.assertIn(f'server={srv}', ddclient_conf)
- self.assertIn(f'login={user}', ddclient_conf)
+ self.assertIn(f'server={server}', ddclient_conf)
+ self.assertIn(f'login={username}', ddclient_conf)
self.assertIn(f'password={password}', ddclient_conf)
+ self.assertIn(f'min-interval={wait_time}', ddclient_conf)
+ self.assertIn(f'max-interval={expiry_time_good}', ddclient_conf)
# IPv4+IPv6 dual DDNS service configuration
def test_03_dyndns_service_dual_stack(self):
- ddns = ['address', interface, 'service']
- services = {'cloudflare': {'protocol': 'cloudflare', 'zone': 'vyos.io'},
- 'freedns': {'protocol': 'freedns', 'username': 'vyos_user'}}
- password = 'vyos_pass'
+ svc_path = ['address', interface, 'service']
+ services = {'cloudflare': {'protocol': 'cloudflare', 'zone': zone},
+ 'freedns': {'protocol': 'freedns', 'username': username},
+ 'google': {'protocol': 'googledomains', 'username': username}}
ip_version = 'both'
- for svc, details in services.items():
- # Always start with a clean CLI instance
- self.cli_delete(base_path)
-
- self.cli_set(base_path + ddns + [svc, 'host-name', hostname])
- self.cli_set(base_path + ddns + [svc, 'password', password])
- self.cli_set(base_path + ddns + [svc, 'ip-version', ip_version])
+ for name, details in services.items():
+ self.cli_set(base_path + svc_path + [name, 'host-name', hostname])
+ self.cli_set(base_path + svc_path + [name, 'password', password])
for opt, value in details.items():
- self.cli_set(base_path + ddns + [svc, opt, value])
+ self.cli_set(base_path + svc_path + [name, opt, value])
+
+ # Dual stack is supported by 'cloudfare' and 'freedns' but not 'googledomains'
+ # exception is raised for unsupported ones
+ self.cli_set(base_path + svc_path + [name, 'ip-version', ip_version])
+ if details['protocol'] not in ['cloudflare', 'freedns']:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + svc_path + [name, 'ip-version'])
# commit changes
self.cli_commit()
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
- self.assertIn(f'usev4=ifv4', ddclient_conf)
- self.assertIn(f'usev6=ifv6', ddclient_conf)
- self.assertIn(f'ifv4={interface}', ddclient_conf)
- self.assertIn(f'ifv6={interface}', ddclient_conf)
+ if details['protocol'] not in ['cloudflare', 'freedns']:
+ self.assertIn(f'usev4=ifv4', ddclient_conf)
+ self.assertIn(f'ifv4={interface}', ddclient_conf)
+ else:
+ self.assertIn(f'usev4=ifv4', ddclient_conf)
+ self.assertIn(f'usev6=ifv6', ddclient_conf)
+ self.assertIn(f'ifv4={interface}', ddclient_conf)
+ self.assertIn(f'ifv6={interface}', ddclient_conf)
self.assertIn(f'password={password}', ddclient_conf)
for opt in details.keys():
@@ -161,19 +197,16 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
def test_04_dyndns_rfc2136(self):
# Check if DDNS service can be configured and runs
- ddns = ['address', interface, 'rfc2136', 'vyos']
- srv = 'ns1.vyos.io'
- zone = 'vyos.io'
- ttl = '300'
+ svc_path = ['address', interface, 'rfc2136', 'vyos']
with tempfile.NamedTemporaryFile(prefix='/config/auth/') as key_file:
key_file.write(b'S3cretKey')
- self.cli_set(base_path + ddns + ['server', srv])
- self.cli_set(base_path + ddns + ['zone', zone])
- self.cli_set(base_path + ddns + ['key', key_file.name])
- self.cli_set(base_path + ddns + ['ttl', ttl])
- self.cli_set(base_path + ddns + ['host-name', hostname])
+ self.cli_set(base_path + svc_path + ['server', server])
+ self.cli_set(base_path + svc_path + ['zone', zone])
+ self.cli_set(base_path + svc_path + ['key', key_file.name])
+ self.cli_set(base_path + svc_path + ['ttl', ttl])
+ self.cli_set(base_path + svc_path + ['host-name', hostname])
# commit changes
self.cli_commit()
@@ -183,10 +216,61 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'use=if', ddclient_conf)
self.assertIn(f'if={interface}', ddclient_conf)
self.assertIn(f'protocol=nsupdate', ddclient_conf)
- self.assertIn(f'server={srv}', ddclient_conf)
+ self.assertIn(f'server={server}', ddclient_conf)
self.assertIn(f'zone={zone}', ddclient_conf)
self.assertIn(f'password={key_file.name}', ddclient_conf)
self.assertIn(f'ttl={ttl}', ddclient_conf)
+ def test_05_dyndns_hostname(self):
+ # Check if DDNS service can be configured and runs
+ svc_path = ['address', interface, 'service', 'namecheap']
+ proto = 'namecheap'
+ hostnames = ['@', 'www', hostname, f'@.{hostname}']
+
+ for name in hostnames:
+ self.cli_set(base_path + svc_path + ['protocol', proto])
+ self.cli_set(base_path + svc_path + ['server', server])
+ self.cli_set(base_path + svc_path + ['username', username])
+ self.cli_set(base_path + svc_path + ['password', password])
+ self.cli_set(base_path + svc_path + ['host-name', name])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check the generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ self.assertIn(f'protocol={proto}', ddclient_conf)
+ self.assertIn(f'server={server}', ddclient_conf)
+ self.assertIn(f'login={username}', ddclient_conf)
+ self.assertIn(f'password={password}', ddclient_conf)
+ self.assertIn(f'{name}', ddclient_conf)
+
+ def test_06_dyndns_vrf(self):
+ vrf_name = f'vyos-test-{"".join(random.choices(string.ascii_letters + string.digits, k=5))}'
+ svc_path = ['address', interface, 'service', 'cloudflare']
+
+ self.cli_set(['vrf', 'name', vrf_name, 'table', '12345'])
+ self.cli_set(base_path + ['vrf', vrf_name])
+
+ self.cli_set(base_path + svc_path + ['protocol', 'cloudflare'])
+ self.cli_set(base_path + svc_path + ['host-name', hostname])
+ self.cli_set(base_path + svc_path + ['zone', zone])
+ self.cli_set(base_path + svc_path + ['password', password])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check for process in VRF
+ systemd_override = cmd(f'cat {DDCLIENT_SYSTEMD_UNIT}')
+ self.assertIn(f'ExecStart=ip vrf exec {vrf_name} /usr/bin/ddclient -file {DDCLIENT_CONF}',
+ systemd_override)
+
+ # Check for process in VRF
+ proc = cmd(f'ip vrf pids {vrf_name}')
+ self.assertIn(DDCLIENT_PNAME, proc)
+
+ # Cleanup VRF
+ self.cli_delete(['vrf', 'name', vrf_name])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_mdns-repeater.py b/smoketest/scripts/cli/test_service_mdns-repeater.py
index 9a9839025..f2fb3b509 100755
--- a/smoketest/scripts/cli/test_service_mdns-repeater.py
+++ b/smoketest/scripts/cli/test_service_mdns-repeater.py
@@ -19,6 +19,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from configparser import ConfigParser
+from vyos.configsession import ConfigSessionError
from vyos.utils.process import process_named_running
base_path = ['service', 'mdns', 'repeater']
@@ -27,6 +28,20 @@ config_file = '/run/avahi-daemon/avahi-daemon.conf'
class TestServiceMDNSrepeater(VyOSUnitTestSHIM.TestCase):
+ def setUp(self):
+ # Start with a clean CLI instance
+ self.cli_delete(base_path)
+
+ # Service required a configured IP address on the interface
+ self.cli_set(intf_base + ['dum10', 'address', '192.0.2.1/30'])
+ self.cli_set(intf_base + ['dum10', 'ipv6', 'address', 'no-default-link-local'])
+ self.cli_set(intf_base + ['dum20', 'address', '192.0.2.5/30'])
+ self.cli_set(intf_base + ['dum20', 'address', '2001:db8:0:2::5/64'])
+ self.cli_set(intf_base + ['dum30', 'address', '192.0.2.9/30'])
+ self.cli_set(intf_base + ['dum30', 'address', '2001:db8:0:2::9/64'])
+ self.cli_set(intf_base + ['dum40', 'address', '2001:db8:0:2::11/64'])
+ self.cli_commit()
+
def tearDown(self):
# Check for running process
self.assertTrue(process_named_running('avahi-daemon'))
@@ -34,24 +49,23 @@ class TestServiceMDNSrepeater(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_delete(intf_base + ['dum10'])
self.cli_delete(intf_base + ['dum20'])
+ self.cli_delete(intf_base + ['dum30'])
+ self.cli_delete(intf_base + ['dum40'])
self.cli_commit()
# Check that there is no longer a running process
self.assertFalse(process_named_running('avahi-daemon'))
- def test_service(self):
+ def test_service_dual_stack(self):
# mDNS browsing domains in addition to the default one (local)
domains = ['dom1.home.arpa', 'dom2.home.arpa']
# mDNS services to be repeated
services = ['_ipp._tcp', '_smb._tcp', '_ssh._tcp']
- # Service required a configured IP address on the interface
- self.cli_set(intf_base + ['dum10', 'address', '192.0.2.1/30'])
- self.cli_set(intf_base + ['dum20', 'address', '192.0.2.5/30'])
-
- self.cli_set(base_path + ['interface', 'dum10'])
+ self.cli_set(base_path + ['ip-version', 'both'])
self.cli_set(base_path + ['interface', 'dum20'])
+ self.cli_set(base_path + ['interface', 'dum30'])
for domain in domains:
self.cli_set(base_path + ['browse-domain', domain])
@@ -65,10 +79,56 @@ class TestServiceMDNSrepeater(VyOSUnitTestSHIM.TestCase):
conf = ConfigParser(delimiters='=')
conf.read(config_file)
- self.assertEqual(conf['server']['allow-interfaces'], 'dum10, dum20')
+ self.assertEqual(conf['server']['use-ipv4'], 'yes')
+ self.assertEqual(conf['server']['use-ipv6'], 'yes')
+ self.assertEqual(conf['server']['allow-interfaces'], 'dum20, dum30')
self.assertEqual(conf['server']['browse-domains'], ', '.join(domains))
self.assertEqual(conf['reflector']['enable-reflector'], 'yes')
self.assertEqual(conf['reflector']['reflect-filters'], ', '.join(services))
+ def test_service_ipv4(self):
+ # partcipating interfaces should have IPv4 addresses
+ self.cli_set(base_path + ['ip-version', 'ipv4'])
+ self.cli_set(base_path + ['interface', 'dum10'])
+ self.cli_set(base_path + ['interface', 'dum40'])
+
+ # exception is raised if partcipating interfaces do not have IPv4 address
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['interface', 'dum40'])
+ self.cli_set(base_path + ['interface', 'dum20'])
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(delimiters='=')
+ conf.read(config_file)
+
+ self.assertEqual(conf['server']['use-ipv4'], 'yes')
+ self.assertEqual(conf['server']['use-ipv6'], 'no')
+ self.assertEqual(conf['server']['allow-interfaces'], 'dum10, dum20')
+ self.assertEqual(conf['reflector']['enable-reflector'], 'yes')
+
+ def test_service_ipv6(self):
+ # partcipating interfaces should have IPv6 addresses
+ self.cli_set(base_path + ['ip-version', 'ipv6'])
+ self.cli_set(base_path + ['interface', 'dum10'])
+ self.cli_set(base_path + ['interface', 'dum30'])
+
+ # exception is raised if partcipating interfaces do not have IPv4 address
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['interface', 'dum10'])
+ self.cli_set(base_path + ['interface', 'dum40'])
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(delimiters='=')
+ conf.read(config_file)
+
+ self.assertEqual(conf['server']['use-ipv4'], 'no')
+ self.assertEqual(conf['server']['use-ipv6'], 'yes')
+ self.assertEqual(conf['server']['allow-interfaces'], 'dum30, dum40')
+ self.assertEqual(conf['reflector']['enable-reflector'], 'yes')
+
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 2a89aa98b..7657ab724 100755
--- a/smoketest/scripts/cli/test_system_conntrack.py
+++ b/smoketest/scripts/cli/test_system_conntrack.py
@@ -35,6 +35,17 @@ class TestSystemConntrack(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_conntrack_options(self):
conntrack_config = {
'net.netfilter.nf_conntrack_expect_max' : {
@@ -151,27 +162,34 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
def test_conntrack_module_enable(self):
# conntrack helper modules are disabled by default
modules = {
- 'ftp' : {
- 'driver' : ['nf_nat_ftp', 'nf_conntrack_ftp'],
+ 'ftp': {
+ 'driver': ['nf_nat_ftp', 'nf_conntrack_ftp'],
+ 'nftables': ['ct helper set "ftp_tcp"']
},
- 'h323' : {
- 'driver' : ['nf_nat_h323', 'nf_conntrack_h323'],
+ 'h323': {
+ 'driver': ['nf_nat_h323', 'nf_conntrack_h323'],
+ 'nftables': ['ct helper set "ras_udp"',
+ 'ct helper set "q931_tcp"']
},
- 'nfs' : {
- 'nftables' : ['ct helper set "rpc_tcp"',
- 'ct helper set "rpc_udp"']
+ 'nfs': {
+ 'nftables': ['ct helper set "rpc_tcp"',
+ 'ct helper set "rpc_udp"']
},
- 'pptp' : {
- 'driver' : ['nf_nat_pptp', 'nf_conntrack_pptp'],
+ 'pptp': {
+ 'driver': ['nf_nat_pptp', 'nf_conntrack_pptp'],
+ 'nftables': ['ct helper set "pptp_tcp"']
},
- 'sip' : {
- 'driver' : ['nf_nat_sip', 'nf_conntrack_sip'],
+ 'sip': {
+ 'driver': ['nf_nat_sip', 'nf_conntrack_sip'],
+ 'nftables': ['ct helper set "sip_tcp"',
+ 'ct helper set "sip_udp"']
},
- 'sqlnet' : {
- 'nftables' : ['ct helper set "tns_tcp"']
+ 'sqlnet': {
+ 'nftables': ['ct helper set "tns_tcp"']
},
- 'tftp' : {
- 'driver' : ['nf_nat_tftp', 'nf_conntrack_tftp'],
+ 'tftp': {
+ 'driver': ['nf_nat_tftp', 'nf_conntrack_tftp'],
+ 'nftables': ['ct helper set "tftp_udp"']
},
}
@@ -189,7 +207,7 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
self.assertTrue(os.path.isdir(f'/sys/module/{driver}'))
if 'nftables' in module_options:
for rule in module_options['nftables']:
- self.assertTrue(find_nftables_rule('raw', 'VYOS_CT_HELPER', [rule]) != None)
+ self.assertTrue(find_nftables_rule('ip vyos_conntrack', 'VYOS_CT_HELPER', [rule]) != None)
# unload modules
for module in modules:
@@ -205,7 +223,7 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
self.assertFalse(os.path.isdir(f'/sys/module/{driver}'))
if 'nftables' in module_options:
for rule in module_options['nftables']:
- self.assertTrue(find_nftables_rule('raw', 'VYOS_CT_HELPER', [rule]) == None)
+ self.assertTrue(find_nftables_rule('ip vyos_conntrack', 'VYOS_CT_HELPER', [rule]) == None)
def test_conntrack_hash_size(self):
hash_size = '65536'
@@ -232,5 +250,52 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
tmp = read_file('/etc/modprobe.d/vyatta_nf_conntrack.conf')
self.assertIn(hash_size_default, tmp)
+ def test_conntrack_ignore(self):
+ address_group = 'conntracktest'
+ address_group_member = '192.168.0.1'
+ ipv6_address_group = 'conntracktest6'
+ ipv6_address_group_member = 'dead:beef::1'
+
+ self.cli_set(['firewall', 'group', 'address-group', address_group, 'address', address_group_member])
+ self.cli_set(['firewall', 'group', 'ipv6-address-group', ipv6_address_group, 'address', ipv6_address_group_member])
+
+ self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'source', 'address', '192.0.2.1'])
+ self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'destination', 'address', '192.0.2.2'])
+ self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'destination', 'port', '22'])
+ self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'protocol', 'tcp'])
+ self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '1', 'tcp', 'flags', 'syn'])
+
+ self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '2', 'source', 'address', '192.0.2.1'])
+ self.cli_set(base_path + ['ignore', 'ipv4', 'rule', '2', 'destination', 'group', 'address-group', address_group])
+
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'source', 'address', 'fe80::1'])
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'destination', 'address', 'fe80::2'])
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'destination', 'port', '22'])
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '11', 'protocol', 'tcp'])
+
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '12', 'source', 'address', 'fe80::1'])
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '12', 'destination', 'group', 'address-group', ipv6_address_group])
+
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '13', 'source', 'address', 'fe80::1'])
+ self.cli_set(base_path + ['ignore', 'ipv6', 'rule', '13', 'destination', 'address', '!fe80::3'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['ip saddr 192.0.2.1', 'ip daddr 192.0.2.2', 'tcp dport 22', 'tcp flags & syn == syn', 'notrack'],
+ ['ip saddr 192.0.2.1', 'ip daddr @A_conntracktest', 'notrack']
+ ]
+
+ nftables6_search = [
+ ['ip6 saddr fe80::1', 'ip6 daddr fe80::2', 'tcp dport 22', 'notrack'],
+ ['ip6 saddr fe80::1', 'ip6 daddr @A6_conntracktest6', 'notrack'],
+ ['ip6 saddr fe80::1', 'ip6 daddr != fe80::3', 'notrack']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_conntrack')
+ self.verify_nftables(nftables6_search, 'ip6 vyos_conntrack')
+
+ self.cli_delete(['firewall'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
index d55ea616e..6c761579b 100755
--- a/smoketest/scripts/cli/test_system_flow-accounting.py
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -67,7 +67,7 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# verify configuration
- nftables_output = cmd('sudo nft list chain raw VYOS_CT_PREROUTING_HOOK').splitlines()
+ nftables_output = cmd('sudo nft list chain raw VYOS_PREROUTING_HOOK').splitlines()
for interface in Section.interfaces('ethernet'):
rule_found = False
ifname_search = f'iifname "{interface}"'
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index 01b0406bf..17b1b395c 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -45,77 +45,62 @@ PROCESS_NAME = 'charon-systemd'
regex_uuid4 = '[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}'
ca_pem = """
-MIIDSzCCAjOgAwIBAgIUQHK+ZgTUYZksvXY2/MyW+Jiels4wDQYJKoZIhvcNAQEL
-BQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMjEwNjE0MTk0NTI3WhcNMzEw
-NjEyMTk0NTI3WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcN
-AQEBBQADggEPADCCAQoCggEBAKCAzpatA8yywXhGunWD//6Qg9EMJMb+7didNr10
-DuYPPGyTOXwG4Xicbr0FJ6cNkWg4wj3ZXEqqBzgS1Z9u78yuYPt5LE9eM8Wtawp7
-qIUCMTlSu4uD3/4A3c1xfHDpTOEl1BDvxMtQxQZcMNQVUG5ZMdcWQvqvQG6F7Nak
-+jgkaQ+Gyhwq++KVTEHJsA6+POuD0uaqAJv3tLGrRf4y4zdOn4thuTQ9swIBjKW6
-ci78Dk0F4u24YYV2BHKsPEPIyCQxKSRrMvqVWWljX9HmNsGawyEhLvW34aphj0aD
-JL/n1kWm+DnGyM+Rp6pXQz5y3xAnmKeYziaQNnvHoQi+gY0CAwEAAaOBkDCBjTAd
-BgNVHQ4EFgQUy43jkjE+CORrxeddqofQztZ9UxYwUQYDVR0jBEowSIAUy43jkjE+
-CORrxeddqofQztZ9UxahGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghRAcr5m
-BNRhmSy9djb8zJb4mJ6WzjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq
-hkiG9w0BAQsFAAOCAQEALHdd1JXq6EUF9dSUijPLEiDVwn2TTIBIxvQqFzpWDDHg
-EWLzRJESyNUbIiwuUGwvqcVki0TmQcFR9XwmcDFDotlXz9OQISBlCW+Twuf4/XAL
-11njH8qXSaWF/wPbF35NOPhV5xOOCZ6K7Vilp3tK6LeOWvz2AUtwiVE1prNV3cIA
-B2ham0JASS0HIkfrcjpZNcx4NlSBaFf4MK5A11p13zPqMqzdEqn6n8fbYEADfVzy
-TfdqX1dPVc9zaM8uwyh5VyYBMDV7DoL384ZHJZYLENK/pT4kbl+sM/Cnhvyu0UCe
-RVqJGQtCdChZpDAVkzJRQYw3/FR8Mj+M+8GrgOrJ0w==
+MIICMDCCAdegAwIBAgIUBCzIjYvD7SPbx5oU18IYg7NVxQ0wCgYIKoZIzj0EAwIw
+ZzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzEgMB4GA1UEAwwXSVBTZWMgU21va2V0ZXN0
+IFJvb3QgQ0EwHhcNMjMwOTI0MTIwMzQxWhcNMzMwOTIxMTIwMzQxWjBnMQswCQYD
+VQQGEwJHQjETMBEGA1UECAwKU29tZS1TdGF0ZTESMBAGA1UEBwwJU29tZS1DaXR5
+MQ0wCwYDVQQKDARWeU9TMSAwHgYDVQQDDBdJUFNlYyBTbW9rZXRlc3QgUm9vdCBD
+QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEh8/yU572B3zmFxrGgHk+H7grYt
+EHUJodY3gXNWMHz0gySrbGhsGtECDfP/G+T4Suk7cuVzB1wnLocSafD8TcqjYTBf
+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsG
+AQUFBwMCBggrBgEFBQcDATAdBgNVHQ4EFgQUTYoQJNlk7X87/gRegHnCnPef39Aw
+CgYIKoZIzj0EAwIDRwAwRAIgX1spXjrUc10r3g/Zm4O31LU5O08J2vVqFo94zHE5
+0VgCIG4JK9Zg5O/yn4mYksZux7efiHRUzL2y2TXQ9IqrqM8W
+"""
+
+int_ca_pem = """
+MIICYDCCAgWgAwIBAgIUcFx2BVYErHI+SneyPYHijxXt1cgwCgYIKoZIzj0EAwIw
+ZzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzEgMB4GA1UEAwwXSVBTZWMgU21va2V0ZXN0
+IFJvb3QgQ0EwHhcNMjMwOTI0MTIwNTE5WhcNMzMwOTIwMTIwNTE5WjBvMQswCQYD
+VQQGEwJHQjETMBEGA1UECAwKU29tZS1TdGF0ZTESMBAGA1UEBwwJU29tZS1DaXR5
+MQ0wCwYDVQQKDARWeU9TMSgwJgYDVQQDDB9JUFNlYyBTbW9rZXRlc3QgSW50ZXJt
+ZWRpYXRlIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIHw2G5dq3c715AcA
+tzR++dYu1fLRFmHzRGTZOT7hLrh2Fg4hnKFPLOeUA5Qi50xCvjJ9JnonTyy2RfRH
+axYizKOBhjCBgzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAd
+BgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwHQYDVR0OBBYEFC9KrFYtA+hO
+l7vdMbWxTMAyLB7BMB8GA1UdIwQYMBaAFE2KECTZZO1/O/4EXoB5wpz3n9/QMAoG
+CCqGSM49BAMCA0kAMEYCIQCnqWbElgOL9dGO3iLxasFNq/hM7vM/DzaiHi4BowxW
+0gIhAMohefNj+QgLfPhvyODHIPE9LMyfp7lJEaCC2K8PCSFD
"""
peer_cert = """
-MIIDZjCCAk6gAwIBAgIRAKHpoE0rTcB/YXhnFpeckngwDQYJKoZIhvcNAQELBQAw
-FjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMjEwNjE0MjAwNDQ3WhcNMjQwNTI5
-MjAwNDQ3WjAQMQ4wDAYDVQQDDAVwZWVyMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBALNwjDC1Lj2ojfCi1TESsyD0MLuqUVLTBZaXCXFtQdB/Aw3b3eBc
-J8+FUYQ6xMplmklXcjJEyXSMvqENpLX6xEDNWWvqTf22eEWt36QTfBeyFyDKtXnm
-4Y+ufXAHl3sLtyZN/7q+Xl4ubYvtAHVRLYzkXAtj1tVdaYEZQy8x/F3ZFFUsCfxR
-RqJBKTxcENP8STpIz9X8dS9iif9SBA42C0eHqMWv1tYW1IHO9gQxYFS3cvoPDPlD
-AJ3ihu5x3fO892S7FtZLVN/GsN1TKRKL217eVPyW0+QcnUwbrXWc7fnmm1btXVmh
-9YKPdtX8WnEeOtMCVZGKqdydnI3iAqvPmd0CAwEAAaOBtDCBsTAJBgNVHRMEAjAA
-MB0GA1UdDgQWBBQGsAPY4cHnTNUv7l+l8OYRSqcX8jBRBgNVHSMESjBIgBTLjeOS
-MT4I5GvF512qh9DO1n1TFqEapBgwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0GCFEBy
-vmYE1GGZLL12NvzMlviYnpbOMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsGA1UdDwQE
-AwIFoDAQBgNVHREECTAHggVwZWVyMTANBgkqhkiG9w0BAQsFAAOCAQEAdJr+11eG
-FvChxu/LkwsXe2V+OZzGRq+hmQlaK3kG/AyI5hVA/IVHJkDe281wbBNKBWYxeSMn
-lAKbwuhPluO99oldzY9ZVkSiRmLh3r27wy/y+1plvoNxyTN7644Hvtk/8P/LV67R
-amXvVgkhpvIQSBfgifXzqUs+BV/x7TSeN3isxNOB8FP6imODsw8lF0Ir1Ze34emr
-TMNo5wNR5xp2dUa9OkzjRpgpifh20zM3UeVOixIPoq78IDjT0aZP8Lve2/g4Ccc6
-RHNF31r/2UL8rZfQRUAMijVdAvIINCk0kRBhNcr9MCi3czmmgiXXMGwLWLvSkfnE
-W06wKX1lpPSptg==
+MIICSTCCAfCgAwIBAgIUPxYleUgCo/glVVePze3QmAFgi6MwCgYIKoZIzj0EAwIw
+bzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzEoMCYGA1UEAwwfSVBTZWMgU21va2V0ZXN0
+IEludGVybWVkaWF0ZSBDQTAeFw0yMzA5MjQxMjA2NDJaFw0yODA5MjIxMjA2NDJa
+MGQxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMRIwEAYDVQQHDAlT
+b21lLUNpdHkxDTALBgNVBAoMBFZ5T1MxHTAbBgNVBAMMFElQU2VjIFNtb2tldGVz
+dCBQZWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZJtuTDu84uy++GMwRNLl
+10JAXZxXQSDl+CdTWwjbQZURcdY+ia7BoaoYX/0VKPel3Se64rIUQQLQoY/9MJb9
+UKN1MHMwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI
+KwYBBQUHAwEwHQYDVR0OBBYEFNJCdnkm3cAmf04UwOKL7IqMJ6OXMB8GA1UdIwQY
+MBaAFC9KrFYtA+hOl7vdMbWxTMAyLB7BMAoGCCqGSM49BAMCA0cAMEQCIGVnDRUy
+UJ0U/deDvrBo1+AakZndkNAMN/XNo5a5GzhEAiBCY7E/3b0BIO8FiIbVB3iDcaxg
+g7ET2RgWxvhEoN3ZRw==
"""
peer_key = """
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzcIwwtS49qI3w
-otUxErMg9DC7qlFS0wWWlwlxbUHQfwMN293gXCfPhVGEOsTKZZpJV3IyRMl0jL6h
-DaS1+sRAzVlr6k39tnhFrd+kE3wXshcgyrV55uGPrn1wB5d7C7cmTf+6vl5eLm2L
-7QB1US2M5FwLY9bVXWmBGUMvMfxd2RRVLAn8UUaiQSk8XBDT/Ek6SM/V/HUvYon/
-UgQONgtHh6jFr9bWFtSBzvYEMWBUt3L6Dwz5QwCd4obucd3zvPdkuxbWS1TfxrDd
-UykSi9te3lT8ltPkHJ1MG611nO355ptW7V1ZofWCj3bV/FpxHjrTAlWRiqncnZyN
-4gKrz5ndAgMBAAECggEACvAya4mv3uxWcrPKYSptpvWbvuTb/juE3LAqUDLDz0ze
-x8p+VP3pI1pSJMhcVKYq6IufF3df/G3T9Qda4gj+S6D48X4f8PZdkInP1zWk2+Ds
-TgBtXZf4agTN+rVLw6FsMbaRfzW5lO4pmV0CKSSgrTUCc2NLpkgCdW8vzEG0y5ek
-15uBOyvuydWM4CFgZT/cUvnu4UtPFL1vaTdD4Lw0FfZq4iS8SWsGbbMoTPKkJRlS
-k9oMEOvhA1WIfSgiG0FyaidoNEormB6J1SKVo27P8SOYu2etiFdF9SJUYg9cBzM3
-z3HcAsXeSh2kpc8Fc2yOS6zI5AsC0Len2SQmKQD8YQKBgQDlgg5cZV5AY2Ji6b+T
-nTHjna7dg/kzUOYs0AmK9DHHziZJ2SKucJlB9smynPLjY/MQbKcNWQ1Cad+olDNP
-Ts4lLhs4kbITkmgPQME3it1fGstHy/sGcF0m+YRsSxfwt5bxLXH86+d067C0XMhg
-URMgGv9ZBTe/P1LuhIUTEjYzlQKBgQDIJvl7sSXHRRB0k7NU/uV3Tut3NTqIzXiz
-pq9hMyF+3aIqaA7kdjIIJczv1grVYz+RUdX3Gu1FyHMl8ynoEz5NNWsbe+Ay/moa
-ztijak3UH3M+d6WsxSRehdYl6DaMstHwWfKZvWNJCGyl7ckz9gGjc3DY/qYqZDrx
-p3LlZsY7KQKBgQCj3ur2GgLkIpI7Yf9CHPlkNlCHJhYnB9pxoNFPf/CTY6R/EiTr
-PMaRDO8TM3FR3ynMTmgw5abMBuCFc9v3AqO6dGNHTvBBfUYDrg7H48UQhQckaocA
-H/bDP2HIGQ4s+Ek0R2ieWKpZF3iCL8V60CjBwcUVAN6/FS3X1JNX/KbqyQKBgQDA
-8dlk5PN/MlPXnZ6t2/7G0bxpsVVZFYI65P+CGvE6RFuUt7VLhalbc10pAtR0unVI
-GHTD/iAnOkHOnqeSQiK3+TvkRbluTxVn/GiYt9yJFTxaRqrebzlNKYW0CzOy1JtP
-MNaOYCS6/bUHC7//KDKSJ7HsbScwDGlKFVrMTBPiaQKBgQCjkIJDZ4pC3er7QiC3
-RXWPyxIG5iTjn4fizphaBt6+pkBAlBh0V6inmleAWa5DJSpgU4jQv4mZsAQs6ctq
-usmoy47ke8pTXPHgQ8ZUwsfM4IztqOm+w0X6mSZi6HdJCnMdxCZBBpO225UvonSR
-rgiyCHemtMepq57Pl1Nmj49eEA==
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgVDEZDK7q/T+tiJUV
+WLKS3ZYDfZ4lZv0C1gJpYq0gWP2hRANCAARkm25MO7zi7L74YzBE0uXXQkBdnFdB
+IOX4J1NbCNtBlRFx1j6JrsGhqhhf/RUo96XdJ7rishRBAtChj/0wlv1Q
"""
+swanctl_dir = '/etc/swanctl'
+CERT_PATH = f'{swanctl_dir}/x509/'
+CA_PATH = f'{swanctl_dir}/x509ca/'
+
class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
skip_process_check = False
@@ -400,7 +385,9 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# Enable PKI
peer_name = 'peer1'
ca_name = 'MyVyOS-CA'
+ int_ca_name = 'MyVyOS-IntCA'
self.cli_set(['pki', 'ca', ca_name, 'certificate', ca_pem.replace('\n','')])
+ self.cli_set(['pki', 'ca', int_ca_name, 'certificate', int_ca_pem.replace('\n','')])
self.cli_set(['pki', 'certificate', peer_name, 'certificate', peer_cert.replace('\n','')])
self.cli_set(['pki', 'certificate', peer_name, 'private', 'key', peer_key.replace('\n','')])
@@ -415,7 +402,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
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])
+ self.cli_set(peer_base_path + ['authentication', 'x509', 'ca-certificate', int_ca_name])
self.cli_set(peer_base_path + ['authentication', 'x509', 'certificate', peer_name])
self.cli_set(peer_base_path + ['connection-type', 'initiate'])
self.cli_set(peer_base_path + ['ike-group', ike_group])
@@ -466,6 +453,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
for line in swanctl_secrets_lines:
self.assertIn(line, swanctl_conf)
+ # Check Root CA, Intermediate CA and Peer cert/key pair is present
+ self.assertTrue(os.path.exists(os.path.join(CA_PATH, f'{int_ca_name}_1.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CA_PATH, f'{int_ca_name}_2.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
+
# There is only one VTI test so no need to delete this globally in tearDown()
self.cli_delete(vti_path)
diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py
index a39ae50bc..0e3cbd0ed 100755
--- a/smoketest/scripts/system/test_kernel_options.py
+++ b/smoketest/scripts/system/test_kernel_options.py
@@ -76,7 +76,6 @@ class TestKernelModules(unittest.TestCase):
self.assertTrue(tmp)
def test_qemu_support(self):
- # The bond/lacp interface must be enabled in the OS Kernel
for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO',
'CONFIG_VIRTIO_NET', 'CONFIG_VIRTIO_CONSOLE',
'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI',
@@ -86,11 +85,14 @@ class TestKernelModules(unittest.TestCase):
self.assertTrue(tmp)
def test_vmware_support(self):
- # The bond/lacp interface must be enabled in the OS Kernel
for option in ['CONFIG_VMXNET3']:
tmp = re.findall(f'{option}=(y|m)', config)
self.assertTrue(tmp)
+ def test_container_cgroup_support(self):
+ for option in ['CONFIG_CGROUPS', 'CONFIG_MEMCG', 'CONFIG_CGROUP_PIDS', 'CONFIG_CGROUP_BPF']:
+ tmp = re.findall(f'{option}=(y|m)', config)
+ self.assertTrue(tmp)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/completion/list_ddclient_protocols.sh b/src/completion/list_ddclient_protocols.sh
index 75fb0cf44..3b4eff4d6 100755
--- a/src/completion/list_ddclient_protocols.sh
+++ b/src/completion/list_ddclient_protocols.sh
@@ -14,4 +14,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-echo -n $(ddclient -list-protocols)
+echo -n $(ddclient -list-protocols | grep -vE 'nsupdate|cloudns')
diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py
index 9c43640a9..4cece6921 100755
--- a/src/conf_mode/conntrack.py
+++ b/src/conf_mode/conntrack.py
@@ -20,11 +20,13 @@ import re
from sys import exit
from vyos.config import Config
-from vyos.firewall import find_nftables_rule
-from vyos.firewall import remove_nftables_rule
+from vyos.configdep import set_dependents, call_dependents
from vyos.utils.process import process_named_running
from vyos.utils.dict import dict_search
+from vyos.utils.dict import dict_search_args
+from vyos.utils.dict import dict_search_recursive
from vyos.utils.process import cmd
+from vyos.utils.process import rc_cmd
from vyos.utils.process import run
from vyos.template import render
from vyos import ConfigError
@@ -38,34 +40,44 @@ nftables_ct_file = r'/run/nftables-ct.conf'
# Every ALG (Application Layer Gateway) consists of either a Kernel Object
# also called a Kernel Module/Driver or some rules present in iptables
module_map = {
- 'ftp' : {
- 'ko' : ['nf_nat_ftp', 'nf_conntrack_ftp'],
+ 'ftp': {
+ 'ko': ['nf_nat_ftp', 'nf_conntrack_ftp'],
+ 'nftables': ['ct helper set "ftp_tcp" tcp dport {21} return']
},
- 'h323' : {
- 'ko' : ['nf_nat_h323', 'nf_conntrack_h323'],
+ 'h323': {
+ 'ko': ['nf_nat_h323', 'nf_conntrack_h323'],
+ 'nftables': ['ct helper set "ras_udp" udp dport {1719} return',
+ 'ct helper set "q931_tcp" tcp dport {1720} return']
},
- 'nfs' : {
- 'nftables' : ['ct helper set "rpc_tcp" tcp dport "{111}" return',
- 'ct helper set "rpc_udp" udp dport "{111}" return']
+ 'nfs': {
+ 'nftables': ['ct helper set "rpc_tcp" tcp dport {111} return',
+ 'ct helper set "rpc_udp" udp dport {111} return']
},
- 'pptp' : {
- 'ko' : ['nf_nat_pptp', 'nf_conntrack_pptp'],
+ 'pptp': {
+ 'ko': ['nf_nat_pptp', 'nf_conntrack_pptp'],
+ 'nftables': ['ct helper set "pptp_tcp" tcp dport {1723} return'],
+ 'ipv4': True
},
- 'sip' : {
- 'ko' : ['nf_nat_sip', 'nf_conntrack_sip'],
+ 'sip': {
+ 'ko': ['nf_nat_sip', 'nf_conntrack_sip'],
+ 'nftables': ['ct helper set "sip_tcp" tcp dport {5060,5061} return',
+ 'ct helper set "sip_udp" udp dport {5060,5061} return']
},
- 'sqlnet' : {
- 'nftables' : ['ct helper set "tns_tcp" tcp dport "{1521,1525,1536}" return']
+ 'sqlnet': {
+ 'nftables': ['ct helper set "tns_tcp" tcp dport {1521,1525,1536} return']
},
- 'tftp' : {
- 'ko' : ['nf_nat_tftp', 'nf_conntrack_tftp'],
+ 'tftp': {
+ 'ko': ['nf_nat_tftp', 'nf_conntrack_tftp'],
+ 'nftables': ['ct helper set "tftp_udp" udp dport {69} return']
},
}
-def resync_conntrackd():
- tmp = run('/usr/libexec/vyos/conf_mode/conntrack_sync.py')
- if tmp > 0:
- print('ERROR: error restarting conntrackd!')
+valid_groups = [
+ 'address_group',
+ 'domain_group',
+ 'network_group',
+ 'port_group'
+]
def get_config(config=None):
if config:
@@ -78,71 +90,134 @@ def get_config(config=None):
get_first_key=True,
with_recursive_defaults=True)
+ conntrack['firewall'] = conf.get_config_dict(['firewall'], key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ conntrack['ipv4_nat_action'] = 'accept' if conf.exists(['nat']) else 'return'
+ conntrack['ipv6_nat_action'] = 'accept' if conf.exists(['nat66']) else 'return'
+ conntrack['wlb_action'] = 'accept' if conf.exists(['load-balancing', 'wan']) else 'return'
+ conntrack['wlb_local_action'] = conf.exists(['load-balancing', 'wan', 'enable-local-traffic'])
+
+ conntrack['module_map'] = module_map
+
+ if conf.exists(['service', 'conntrack-sync']):
+ set_dependents('conntrack_sync', conf)
+
return conntrack
def verify(conntrack):
- if dict_search('ignore.rule', conntrack) != None:
- for rule, rule_config in conntrack['ignore']['rule'].items():
- if dict_search('destination.port', rule_config) or \
- dict_search('source.port', rule_config):
- if 'protocol' not in rule_config or rule_config['protocol'] not in ['tcp', 'udp']:
- raise ConfigError(f'Port requires tcp or udp as protocol in rule {rule}')
+ for inet in ['ipv4', 'ipv6']:
+ if dict_search_args(conntrack, 'ignore', inet, 'rule') != None:
+ for rule, rule_config in conntrack['ignore'][inet]['rule'].items():
+ if dict_search('destination.port', rule_config) or \
+ dict_search('destination.group.port_group', rule_config) or \
+ dict_search('source.port', rule_config) or \
+ dict_search('source.group.port_group', rule_config):
+ if 'protocol' not in rule_config or rule_config['protocol'] not in ['tcp', 'udp']:
+ raise ConfigError(f'Port requires tcp or udp as protocol in rule {rule}')
+
+ tcp_flags = dict_search_args(rule_config, 'tcp', 'flags')
+ if tcp_flags:
+ if dict_search_args(rule_config, 'protocol') != 'tcp':
+ raise ConfigError('Protocol must be tcp when specifying tcp flags')
+
+ not_flags = dict_search_args(rule_config, 'tcp', 'flags', 'not')
+ if not_flags:
+ duplicates = [flag for flag in tcp_flags if flag in not_flags]
+ if duplicates:
+ raise ConfigError(f'Cannot match a tcp flag as set and not set')
+
+ for side in ['destination', 'source']:
+ if side in rule_config:
+ side_conf = rule_config[side]
+
+ if 'group' in side_conf:
+ if len({'address_group', 'network_group', 'domain_group'} & set(side_conf['group'])) > 1:
+ raise ConfigError('Only one address-group, network-group or domain-group can be specified')
+
+ for group in valid_groups:
+ if group in side_conf['group']:
+ group_name = side_conf['group'][group]
+ error_group = group.replace("_", "-")
+
+ if group in ['address_group', 'network_group', 'domain_group']:
+ if 'address' in side_conf:
+ raise ConfigError(f'{error_group} and address cannot both be defined')
+
+ if group_name and group_name[0] == '!':
+ group_name = group_name[1:]
+
+ if inet == 'ipv6':
+ group = f'ipv6_{group}'
+
+ group_obj = dict_search_args(conntrack['firewall'], 'group', group, group_name)
+
+ if group_obj is None:
+ raise ConfigError(f'Invalid {error_group} "{group_name}" on ignore rule')
+
+ if not group_obj:
+ Warning(f'{error_group} "{group_name}" has no members!')
return None
def generate(conntrack):
+ if not os.path.exists(nftables_ct_file):
+ conntrack['first_install'] = True
+
+ # Determine if conntrack is needed
+ conntrack['ipv4_firewall_action'] = 'return'
+ conntrack['ipv6_firewall_action'] = 'return'
+
+ for rules, path in dict_search_recursive(conntrack['firewall'], 'rule'):
+ if any(('state' in rule_conf or 'connection_status' in rule_conf or 'offload_target' in rule_conf) for rule_conf in rules.values()):
+ if path[0] == 'ipv4':
+ conntrack['ipv4_firewall_action'] = 'accept'
+ elif path[0] == 'ipv6':
+ conntrack['ipv6_firewall_action'] = 'accept'
+
render(conntrack_config, 'conntrack/vyos_nf_conntrack.conf.j2', conntrack)
render(sysctl_file, 'conntrack/sysctl.conf.j2', conntrack)
render(nftables_ct_file, 'conntrack/nftables-ct.j2', conntrack)
-
- # dry-run newly generated configuration
- tmp = run(f'nft -c -f {nftables_ct_file}')
- if tmp > 0:
- if os.path.exists(nftables_ct_file):
- os.unlink(nftables_ct_file)
- raise ConfigError('Configuration file errors encountered!')
-
return None
-def find_nftables_ct_rule(rule):
- helper_search = re.search('ct helper set "(\w+)"', rule)
- if helper_search:
- rule = helper_search[1]
- return find_nftables_rule('raw', 'VYOS_CT_HELPER', [rule])
-
-def find_remove_rule(rule):
- handle = find_nftables_ct_rule(rule)
- if handle:
- remove_nftables_rule('raw', 'VYOS_CT_HELPER', handle)
-
def apply(conntrack):
# Depending on the enable/disable state of the ALG (Application Layer Gateway)
# modules we need to either insmod or rmmod the helpers.
+
+ add_modules = []
+ rm_modules = []
+
for module, module_config in module_map.items():
- if dict_search(f'modules.{module}', conntrack) is None:
+ if dict_search_args(conntrack, 'modules', module) is None:
if 'ko' in module_config:
- for mod in module_config['ko']:
- # Only remove the module if it's loaded
- if os.path.exists(f'/sys/module/{mod}'):
- cmd(f'rmmod {mod}')
- if 'nftables' in module_config:
- for rule in module_config['nftables']:
- find_remove_rule(rule)
+ unloaded = [mod for mod in module_config['ko'] if os.path.exists(f'/sys/module/{mod}')]
+ rm_modules.extend(unloaded)
else:
if 'ko' in module_config:
- for mod in module_config['ko']:
- cmd(f'modprobe {mod}')
- if 'nftables' in module_config:
- for rule in module_config['nftables']:
- if not find_nftables_ct_rule(rule):
- cmd(f'nft insert rule ip raw VYOS_CT_HELPER {rule}')
+ add_modules.extend(module_config['ko'])
+
+ # Add modules before nftables uses them
+ if add_modules:
+ module_str = ' '.join(add_modules)
+ cmd(f'modprobe -a {module_str}')
# Load new nftables ruleset
- cmd(f'nft -f {nftables_ct_file}')
+ install_result, output = rc_cmd(f'nft -f {nftables_ct_file}')
+ if install_result == 1:
+ raise ConfigError(f'Failed to apply configuration: {output}')
+
+ # Remove modules after nftables stops using them
+ if rm_modules:
+ module_str = ' '.join(rm_modules)
+ cmd(f'rmmod {module_str}')
- if process_named_running('conntrackd'):
- # Reload conntrack-sync daemon to fetch new sysctl values
- resync_conntrackd()
+ try:
+ call_dependents()
+ except ConfigError:
+ # Ignore config errors on dependent due to being called too early. Example:
+ # ConfigError("ConfigError('Interface ethN requires an IP address!')")
+ pass
# We silently ignore all errors
# See: https://bugzilla.redhat.com/show_bug.cgi?id=1264080
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index 46eb10714..daad9186e 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -274,10 +274,10 @@ def generate_run_arguments(name, container_config):
env_opt += f" --env \"{k}={v['value']}\""
# Check/set label options "--label foo=bar"
- env_opt = ''
+ label = ''
if 'label' in container_config:
for k, v in container_config['label'].items():
- env_opt += f" --label \"{k}={v['value']}\""
+ label += f" --label \"{k}={v['value']}\""
hostname = ''
if 'host_name' in container_config:
@@ -314,7 +314,7 @@ def generate_run_arguments(name, container_config):
container_base_cmd = f'--detach --interactive --tty --replace {cap_add} ' \
f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} ' \
- f'--name {name} {hostname} {device} {port} {volume} {env_opt}'
+ f'--name {name} {hostname} {device} {port} {volume} {env_opt} {label}'
entrypoint = ''
if 'entrypoint' in container_config:
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index c4c72aae9..ac7d95632 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -34,6 +34,7 @@ from vyos import airbag
airbag.enable()
config_file = '/run/dhcp-server/dhcpd.conf'
+systemd_override = r'/run/systemd/system/isc-dhcp-server.service.d/10-override.conf'
def dhcp_slice_range(exclude_list, range_dict):
"""
@@ -295,6 +296,7 @@ def generate(dhcp):
# render the "real" configuration
render(config_file, 'dhcp-server/dhcpd.conf.j2', dhcp,
formater=lambda _: _.replace("&quot;", '"'))
+ render(systemd_override, 'dhcp-server/10-override.conf.j2', dhcp)
# Clean up configuration test file
if os.path.exists(tmp_file):
@@ -303,6 +305,7 @@ def generate(dhcp):
return None
def apply(dhcp):
+ call('systemctl daemon-reload')
# bail out early - looks like removal from running config
if not dhcp or 'disable' in dhcp:
call('systemctl stop isc-dhcp-server.service')
diff --git a/src/conf_mode/dns_dynamic.py b/src/conf_mode/dns_dynamic.py
index ab80defe8..874c4b689 100755
--- a/src/conf_mode/dns_dynamic.py
+++ b/src/conf_mode/dns_dynamic.py
@@ -19,6 +19,7 @@ import os
from sys import exit
from vyos.config import Config
+from vyos.configverify import verify_interface_exists
from vyos.template import render
from vyos.utils.process import call
from vyos import ConfigError
@@ -29,25 +30,32 @@ config_file = r'/run/ddclient/ddclient.conf'
systemd_override = r'/run/systemd/system/ddclient.service.d/override.conf'
# Protocols that require zone
-zone_allowed = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn']
+zone_necessary = ['cloudflare', 'godaddy', 'hetzner', 'gandi', 'nfsn']
# Protocols that do not require username
username_unnecessary = ['1984', 'cloudflare', 'cloudns', 'duckdns', 'freemyip', 'hetzner', 'keysystems', 'njalla']
+# Protocols that support TTL
+ttl_supported = ['cloudflare', 'gandi', 'hetzner', 'dnsexit', 'godaddy', 'nfsn']
+
# Protocols that support both IPv4 and IPv6
dualstack_supported = ['cloudflare', 'dyndns2', 'freedns', 'njalla']
+# dyndns2 protocol in ddclient honors dual stack for selective servers
+# because of the way it is implemented in ddclient
+dyndns_dualstack_servers = ['members.dyndns.org', 'dynv6.com']
+
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
- base_level = ['service', 'dns', 'dynamic']
- if not conf.exists(base_level):
+ base = ['service', 'dns', 'dynamic']
+ if not conf.exists(base):
return None
- dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'),
+ dyndns = conf.get_config_dict(base, key_mangling=('-', '_'),
no_tag_node_value_mangle=True,
get_first_key=True,
with_recursive_defaults=True)
@@ -61,6 +69,10 @@ def verify(dyndns):
return None
for address in dyndns['address']:
+ # If dyndns address is an interface, ensure it exists
+ if address != 'web':
+ verify_interface_exists(address)
+
# RFC2136 - configuration validation
if 'rfc2136' in dyndns['address'][address]:
for config in dyndns['address'][address]['rfc2136'].values():
@@ -78,25 +90,30 @@ def verify(dyndns):
if field not in config:
raise ConfigError(f'"{field.replace("_", "-")}" {error_msg}')
- if config['protocol'] in zone_allowed and 'zone' not in config:
- raise ConfigError(f'"zone" {error_msg}')
+ if config['protocol'] in zone_necessary and 'zone' not in config:
+ raise ConfigError(f'"zone" {error_msg}')
- if config['protocol'] not in zone_allowed and 'zone' in config:
- raise ConfigError(f'"{config["protocol"]}" does not support "zone"')
+ if config['protocol'] not in zone_necessary and 'zone' in config:
+ raise ConfigError(f'"{config["protocol"]}" does not support "zone"')
- if config['protocol'] not in username_unnecessary:
- if 'username' not in config:
- raise ConfigError(f'"username" {error_msg}')
+ if config['protocol'] not in username_unnecessary and 'username' not in config:
+ raise ConfigError(f'"username" {error_msg}')
+
+ if config['protocol'] not in ttl_supported and 'ttl' in config:
+ raise ConfigError(f'"{config["protocol"]}" does not support "ttl"')
if config['ip_version'] == 'both':
if config['protocol'] not in dualstack_supported:
raise ConfigError(f'"{config["protocol"]}" does not support '
f'both IPv4 and IPv6 at the same time')
# dyndns2 protocol in ddclient honors dual stack only for dyn.com (dyndns.org)
- if config['protocol'] == 'dyndns2' and 'server' in config and config['server'] != 'members.dyndns.org':
+ if config['protocol'] == 'dyndns2' and 'server' in config and config['server'] not in dyndns_dualstack_servers:
raise ConfigError(f'"{config["protocol"]}" does not support '
f'both IPv4 and IPv6 at the same time for "{config["server"]}"')
+ if {'wait_time', 'expiry_time'} <= config.keys() and int(config['expiry_time']) < int(config['wait_time']):
+ raise ConfigError(f'"expiry-time" must be greater than "wait-time"')
+
return None
def generate(dyndns):
@@ -104,7 +121,7 @@ def generate(dyndns):
if not dyndns or 'address' not in dyndns:
return None
- render(config_file, 'dns-dynamic/ddclient.conf.j2', dyndns)
+ render(config_file, 'dns-dynamic/ddclient.conf.j2', dyndns, permission=0o600)
render(systemd_override, 'dns-dynamic/override.conf.j2', dyndns)
return None
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index c3b1ee015..f6480ab0a 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -26,7 +26,8 @@ from vyos.config import Config
from vyos.configdict import node_changed
from vyos.configdiff import get_config_diff, Diff
from vyos.configdep import set_dependents, call_dependents
-# from vyos.configverify import verify_interface_exists
+from vyos.configverify import verify_interface_exists
+from vyos.ethtool import Ethtool
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
from vyos.template import render
@@ -38,6 +39,7 @@ from vyos.utils.process import process_named_running
from vyos.utils.process import rc_cmd
from vyos import ConfigError
from vyos import airbag
+
airbag.enable()
nat_conf_script = 'nat.py'
@@ -100,7 +102,7 @@ def geoip_updated(conf, firewall):
elif (path[0] == 'ipv6'):
set_name = f'GEOIP_CC6_{path[1]}_{path[2]}_{path[4]}'
out['ipv6_name'].append(set_name)
-
+
updated = True
if 'delete' in node_diff:
@@ -140,6 +142,8 @@ def get_config(config=None):
fqdn_config_parse(firewall)
+ set_dependents('conntrack', conf)
+
return firewall
def verify_rule(firewall, rule_conf, ipv6):
@@ -160,6 +164,25 @@ def verify_rule(firewall, rule_conf, ipv6):
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 rule_conf['action'] == 'offload':
+ if 'offload_target' not in rule_conf:
+ raise ConfigError('Action set to offload, but no offload-target specified')
+
+ offload_target = rule_conf['offload_target']
+
+ if not dict_search_args(firewall, 'flowtable', offload_target):
+ raise ConfigError(f'Invalid offload-target. Flowtable "{offload_target}" does not exist on the system')
+
+ if rule_conf['action'] != 'synproxy' and 'synproxy' in rule_conf:
+ raise ConfigError('"synproxy" option allowed only for action synproxy')
+ if rule_conf['action'] == 'synproxy':
+ if 'state' in rule_conf:
+ raise ConfigError('For action "synproxy" state cannot be defined')
+ if not rule_conf.get('synproxy', {}).get('tcp'):
+ raise ConfigError('synproxy TCP MSS is not defined')
+ if rule_conf.get('protocol', {}) != 'tcp':
+ raise ConfigError('For action "synproxy" the protocol must be set to TCP')
+
if 'queue_options' in rule_conf:
if 'queue' not in rule_conf['action']:
raise ConfigError('queue-options defined, but action queue needed and it is not defined')
@@ -279,7 +302,31 @@ def verify_nested_group(group_name, group, groups, seen):
if 'include' in groups[g]:
verify_nested_group(g, groups[g], groups, seen)
+def verify_hardware_offload(ifname):
+ ethtool = Ethtool(ifname)
+ enabled, fixed = ethtool.get_hw_tc_offload()
+
+ if not enabled and fixed:
+ raise ConfigError(f'Interface "{ifname}" does not support hardware offload')
+
+ if not enabled:
+ raise ConfigError(f'Interface "{ifname}" requires "offload hw-tc-offload"')
+
def verify(firewall):
+ if 'flowtable' in firewall:
+ for flowtable, flowtable_conf in firewall['flowtable'].items():
+ if 'interface' not in flowtable_conf:
+ raise ConfigError(f'Flowtable "{flowtable}" requires at least one interface')
+
+ for ifname in flowtable_conf['interface']:
+ verify_interface_exists(ifname)
+
+ if dict_search_args(flowtable_conf, 'offload') == 'hardware':
+ interfaces = flowtable_conf['interface']
+
+ for ifname in interfaces:
+ verify_hardware_offload(ifname)
+
if 'group' in firewall:
for group_type in nested_group_types:
if group_type in firewall['group']:
@@ -333,17 +380,6 @@ def generate(firewall):
if not os.path.exists(nftables_conf):
firewall['first_install'] = True
- # Determine if conntrack is needed
- firewall['ipv4_conntrack_action'] = 'return'
- firewall['ipv6_conntrack_action'] = 'return'
-
- for rules, path in dict_search_recursive(firewall, 'rule'):
- if any(('state' in rule_conf or 'connection_status' in rule_conf) for rule_conf in rules.values()):
- if path[0] == 'ipv4':
- firewall['ipv4_conntrack_action'] = 'accept'
- elif path[0] == 'ipv6':
- firewall['ipv6_conntrack_action'] = 'accept'
-
render(nftables_conf, 'firewall/nftables.j2', firewall)
return None
@@ -373,8 +409,7 @@ def apply(firewall):
apply_sysfs(firewall)
- if firewall['group_resync']:
- call_dependents()
+ call_dependents()
# T970 Enable a resolver (systemd daemon) that checks
# domain-group/fqdn addresses and update entries for domains by timeout
diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py
index 71acd69fa..81ee39df1 100755
--- a/src/conf_mode/flow_accounting_conf.py
+++ b/src/conf_mode/flow_accounting_conf.py
@@ -37,7 +37,7 @@ uacctd_conf_path = '/run/pmacct/uacctd.conf'
systemd_service = 'uacctd.service'
systemd_override = f'/run/systemd/system/{systemd_service}.d/override.conf'
nftables_nflog_table = 'raw'
-nftables_nflog_chain = 'VYOS_CT_PREROUTING_HOOK'
+nftables_nflog_chain = 'VYOS_PREROUTING_HOOK'
egress_nftables_nflog_table = 'inet mangle'
egress_nftables_nflog_chain = 'FORWARD'
diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py
index 626a3757e..b3b27b14e 100755
--- a/src/conf_mode/high-availability.py
+++ b/src/conf_mode/high-availability.py
@@ -15,6 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import os
+import time
+
from sys import exit
from ipaddress import ip_interface
from ipaddress import IPv4Interface
@@ -22,15 +25,21 @@ from ipaddress import IPv6Interface
from vyos.base import Warning
from vyos.config import Config
+from vyos.configdict import leaf_node_changed
from vyos.ifconfig.vrrp import VRRP
from vyos.template import render
from vyos.template import is_ipv4
from vyos.template import is_ipv6
+from vyos.utils.network import is_ipv6_tentative
from vyos.utils.process import call
from vyos import ConfigError
from vyos import airbag
airbag.enable()
+
+systemd_override = r'/run/systemd/system/keepalived.service.d/10-override.conf'
+
+
def get_config(config=None):
if config:
conf = config
@@ -50,6 +59,9 @@ def get_config(config=None):
if conf.exists(conntrack_path):
ha['conntrack_sync_group'] = conf.return_value(conntrack_path)
+ if leaf_node_changed(conf, base + ['vrrp', 'snmp']):
+ ha.update({'restart_required': {}})
+
return ha
def verify(ha):
@@ -160,18 +172,38 @@ def verify(ha):
def generate(ha):
if not ha or 'disable' in ha:
+ if os.path.isfile(systemd_override):
+ os.unlink(systemd_override)
return None
render(VRRP.location['config'], 'high-availability/keepalived.conf.j2', ha)
+ render(systemd_override, 'high-availability/10-override.conf.j2', ha)
return None
def apply(ha):
service_name = 'keepalived.service'
+ call('systemctl daemon-reload')
if not ha or 'disable' in ha:
call(f'systemctl stop {service_name}')
return None
- call(f'systemctl reload-or-restart {service_name}')
+ # Check if IPv6 address is tentative T5533
+ for group, group_config in ha.get('vrrp', {}).get('group', {}).items():
+ if 'hello_source_address' in group_config:
+ if is_ipv6(group_config['hello_source_address']):
+ ipv6_address = group_config['hello_source_address']
+ interface = group_config['interface']
+ checks = 20
+ interval = 0.1
+ for _ in range(checks):
+ if is_ipv6_tentative(interface, ipv6_address):
+ time.sleep(interval)
+
+ systemd_action = 'reload-or-restart'
+ if 'restart_required' in ha:
+ systemd_action = 'restart'
+
+ call(f'systemctl {systemd_action} {service_name}')
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/interfaces-dummy.py b/src/conf_mode/interfaces-dummy.py
index e771581e1..db768b94d 100755
--- a/src/conf_mode/interfaces-dummy.py
+++ b/src/conf_mode/interfaces-dummy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -55,7 +55,7 @@ def generate(dummy):
return None
def apply(dummy):
- d = DummyIf(dummy['ifname'])
+ d = DummyIf(**dummy)
# Remove dummy interface
if 'deleted' in dummy:
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 1d0feb56f..bdeb44837 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -30,6 +30,7 @@ from netifaces import interfaces
from secrets import SystemRandom
from shutil import rmtree
+from vyos.base import DeprecationWarning
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configdict import is_node_changed
@@ -165,6 +166,11 @@ def verify_pki(openvpn):
if shared_secret_key not in pki['openvpn']['shared_secret']:
raise ConfigError(f'Invalid shared-secret on openvpn interface {interface}')
+ # If PSK settings are correct, warn about its deprecation
+ DeprecationWarning("OpenVPN shared-secret support will be removed in future VyOS versions.\n\
+ Please migrate your site-to-site tunnels to TLS.\n\
+ You can use self-signed certificates with peer fingerprint verification, consult the documentation for details.")
+
if tls:
if (mode in ['server', 'client']) and ('ca_certificate' not in tls):
raise ConfigError(f'Must specify "tls ca-certificate" on openvpn interface {interface},\
@@ -344,9 +350,6 @@ def verify(openvpn):
if v6_subnets > 1:
raise ConfigError('Cannot specify more than 1 IPv6 server subnet')
- if v6_subnets > 0 and v4_subnets == 0:
- raise ConfigError('IPv6 server requires an IPv4 server subnet')
-
for subnet in tmp:
if is_ipv4(subnet):
subnet = IPv4Network(subnet)
@@ -388,6 +391,10 @@ def verify(openvpn):
for v4PoolNet in v4PoolNets:
if IPv4Address(client['ip'][0]) in v4PoolNet:
print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.')
+ # configuring a client_ip_pool will set 'server ... nopool' which is currently incompatible with 'server-ipv6' (probably to be fixed upstream)
+ for subnet in (dict_search('server.subnet', openvpn) or []):
+ if is_ipv6(subnet):
+ raise ConfigError(f'Setting client-ip-pool is incompatible having an IPv6 server subnet.')
for subnet in (dict_search('server.subnet', openvpn) or []):
if is_ipv6(subnet):
diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py
index fca91253c..0a03a172c 100755
--- a/src/conf_mode/interfaces-pppoe.py
+++ b/src/conf_mode/interfaces-pppoe.py
@@ -77,6 +77,11 @@ def verify(pppoe):
if {'connect_on_demand', 'vrf'} <= set(pppoe):
raise ConfigError('On-demand dialing and VRF can not be used at the same time')
+ # both MTU and MRU have default values, thus we do not need to check
+ # if the key exists
+ if int(pppoe['mru']) > int(pppoe['mtu']):
+ raise ConfigError('PPPoE MRU needs to be lower then MTU!')
+
return None
def generate(pppoe):
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index a3b0867e0..05f68112a 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -24,6 +24,7 @@ from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configdict import leaf_node_changed
from vyos.configdict import is_node_changed
+from vyos.configdict import node_changed
from vyos.configverify import verify_address
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_mtu_ipv6
@@ -58,6 +59,9 @@ def get_config(config=None):
vxlan.update({'rebuild_required': {}})
break
+ tmp = node_changed(conf, base + [ifname, 'vlan-to-vni'], recursive=True)
+ if tmp: vxlan.update({'vlan_to_vni_removed': tmp})
+
# We need to verify that no other VXLAN tunnel is configured when external
# mode is in use - Linux Kernel limitation
conf.set_level(base)
@@ -146,6 +150,20 @@ def verify(vxlan):
raise ConfigError(error_msg)
protocol = 'ipv4'
+ if 'vlan_to_vni' in vxlan:
+ if 'is_bridge_member' not in vxlan:
+ raise ConfigError('VLAN to VNI mapping requires that VXLAN interface '\
+ 'is member of a bridge interface!')
+
+ vnis_used = []
+ for vif, vif_config in vxlan['vlan_to_vni'].items():
+ if 'vni' not in vif_config:
+ raise ConfigError(f'Must define VNI for VLAN "{vif}"!')
+ vni = vif_config['vni']
+ if vni in vnis_used:
+ raise ConfigError(f'VNI "{vni}" is already assigned to a different VLAN!')
+ vnis_used.append(vni)
+
verify_mtu_ipv6(vxlan)
verify_address(vxlan)
verify_bond_bridge_member(vxlan)
diff --git a/src/conf_mode/load-balancing-wan.py b/src/conf_mode/load-balancing-wan.py
index ad9c80d72..5da0b906b 100755
--- a/src/conf_mode/load-balancing-wan.py
+++ b/src/conf_mode/load-balancing-wan.py
@@ -21,6 +21,7 @@ from shutil import rmtree
from vyos.base import Warning
from vyos.config import Config
+from vyos.configdep import set_dependents, call_dependents
from vyos.utils.process import cmd
from vyos.template import render
from vyos import ConfigError
@@ -49,6 +50,8 @@ def get_config(config=None):
if lb.from_defaults(['rule', rule, 'limit']):
del lb['rule'][rule]['limit']
+ set_dependents('conntrack', conf)
+
return lb
@@ -132,6 +135,8 @@ def apply(lb):
cmd('sudo sysctl -w net.netfilter.nf_conntrack_acct=1')
cmd(f'systemctl restart {systemd_service}')
+ call_dependents()
+
return None
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 9da7fbe80..52a7a71fd 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -18,13 +18,12 @@ import jmespath
import json
import os
-from distutils.version import LooseVersion
-from platform import release as kernel_version
from sys import exit
from netifaces import interfaces
from vyos.base import Warning
from vyos.config import Config
+from vyos.configdep import set_dependents, call_dependents
from vyos.template import render
from vyos.template import is_ip_network
from vyos.utils.kernel import check_kmod
@@ -38,10 +37,7 @@ from vyos import ConfigError
from vyos import airbag
airbag.enable()
-if LooseVersion(kernel_version()) > LooseVersion('5.1'):
- k_mod = ['nft_nat', 'nft_chain_nat']
-else:
- k_mod = ['nft_nat', 'nft_chain_nat_ipv4']
+k_mod = ['nft_nat', 'nft_chain_nat']
nftables_nat_config = '/run/nftables_nat.conf'
nftables_static_nat_conf = '/run/nftables_static-nat-rules.nft'
@@ -53,18 +49,27 @@ valid_groups = [
'port_group'
]
-def get_handler(json, chain, target):
- """ Get nftable rule handler number of given chain/target combination.
- Handler is required when adding NAT/Conntrack helper targets """
- for x in json:
- if x['chain'] != chain:
- continue
- if x['target'] != target:
- continue
- return x['handle']
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
- return None
+ base = ['nat']
+ nat = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ with_recursive_defaults=True)
+
+ set_dependents('conntrack', conf)
+
+ if not conf.exists(base):
+ nat['deleted'] = ''
+ return nat
+
+ nat['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True,
+ no_tag_node_value_mangle=True)
+ return nat
def verify_rule(config, err_msg, groups_dict):
""" Common verify steps used for both source and destination NAT """
@@ -112,7 +117,7 @@ def verify_rule(config, err_msg, groups_dict):
group_obj = dict_search_args(groups_dict, group, group_name)
if group_obj is None:
- raise ConfigError(f'Invalid {error_group} "{group_name}" on firewall rule')
+ raise ConfigError(f'Invalid {error_group} "{group_name}" on nat rule')
if not group_obj:
Warning(f'{error_group} "{group_name}" has no members!')
@@ -136,70 +141,18 @@ def verify_rule(config, err_msg, groups_dict):
if count != 100:
Warning(f'Sum of weight for nat load balance rule is not 100. You may get unexpected behaviour')
-def get_config(config=None):
- if config:
- conf = config
- else:
- conf = Config()
-
- base = ['nat']
- nat = conf.get_config_dict(base, key_mangling=('-', '_'),
- get_first_key=True,
- with_recursive_defaults=True)
-
- # read in current nftable (once) for further processing
- tmp = cmd('nft -j list table raw')
- nftable_json = json.loads(tmp)
-
- # condense the full JSON table into a list with only relevand informations
- pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}'
- condensed_json = jmespath.search(pattern, nftable_json)
-
- if not conf.exists(base):
- if get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_HELPER'):
- nat['helper_functions'] = 'remove'
-
- # Retrieve current table handler positions
- nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_HELPER')
- nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK')
- nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_HELPER')
- nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK')
- nat['deleted'] = ''
- return nat
-
- nat['firewall_group'] = conf.get_config_dict(['firewall', 'group'], key_mangling=('-', '_'), get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # check if NAT connection tracking helpers need to be set up - this has to
- # be done only once
- if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'):
- nat['helper_functions'] = 'add'
-
- # Retrieve current table handler positions
- nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_IGNORE')
- nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_PREROUTING_HOOK')
- nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_IGNORE')
- nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_OUTPUT_HOOK')
-
- return nat
-
def verify(nat):
if not nat or 'deleted' in nat:
# no need to verify the CLI as NAT is going to be deactivated
return None
- if 'helper_functions' in nat:
- if not (nat['pre_ct_ignore'] or nat['pre_ct_conntrack'] or nat['out_ct_ignore'] or nat['out_ct_conntrack']):
- raise Exception('could not determine nftable ruleset handlers')
-
if dict_search('source.rule', nat):
for rule, config in dict_search('source.rule', nat).items():
err_msg = f'Source NAT configuration error in rule {rule}:'
- if 'outbound_interface' not in config:
- raise ConfigError(f'{err_msg} outbound-interface not specified')
- if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces():
- Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
+ if 'outbound_interface' in config:
+ if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces():
+ Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
if not dict_search('translation.address', config) and not dict_search('translation.port', config):
if 'exclude' not in config and 'backend' not in config['load_balance']:
@@ -218,11 +171,9 @@ def verify(nat):
for rule, config in dict_search('destination.rule', nat).items():
err_msg = f'Destination NAT configuration error in rule {rule}:'
- if 'inbound_interface' not in config:
- raise ConfigError(f'{err_msg}\n' \
- 'inbound-interface 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')
+ if 'inbound_interface' in config:
+ 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 not dict_search('translation.address', config) and not dict_search('translation.port', config) and 'redirect' not in config['translation']:
if 'exclude' not in config and 'backend' not in config['load_balance']:
@@ -270,6 +221,8 @@ def apply(nat):
os.unlink(nftables_nat_config)
os.unlink(nftables_static_nat_conf)
+ call_dependents()
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py
index 4c12618bc..46d796bc8 100755
--- a/src/conf_mode/nat66.py
+++ b/src/conf_mode/nat66.py
@@ -23,6 +23,7 @@ from netifaces import interfaces
from vyos.base import Warning
from vyos.config import Config
+from vyos.configdep import set_dependents, call_dependents
from vyos.template import render
from vyos.utils.process import cmd
from vyos.utils.kernel import check_kmod
@@ -37,18 +38,6 @@ k_mod = ['nft_nat', 'nft_chain_nat']
nftables_nat66_config = '/run/nftables_nat66.nft'
ndppd_config = '/run/ndppd/ndppd.conf'
-def get_handler(json, chain, target):
- """ Get nftable rule handler number of given chain/target combination.
- Handler is required when adding NAT66/Conntrack helper targets """
- for x in json:
- if x['chain'] != chain:
- continue
- if x['target'] != target:
- continue
- return x['handle']
-
- return None
-
def get_config(config=None):
if config:
conf = config
@@ -58,35 +47,10 @@ def get_config(config=None):
base = ['nat66']
nat = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
- # read in current nftable (once) for further processing
- tmp = cmd('nft -j list table ip6 raw')
- nftable_json = json.loads(tmp)
-
- # condense the full JSON table into a list with only relevand informations
- pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}'
- condensed_json = jmespath.search(pattern, nftable_json)
+ set_dependents('conntrack', conf)
if not conf.exists(base):
- nat['helper_functions'] = 'remove'
- nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_HELPER')
- nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK')
- nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_HELPER')
- nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK')
nat['deleted'] = ''
- return nat
-
- # check if NAT66 connection tracking helpers need to be set up - this has to
- # be done only once
- if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'):
- nat['helper_functions'] = 'add'
-
- # Retrieve current table handler positions
- nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_IGNORE')
- nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYOS_CT_PREROUTING_HOOK')
- nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_IGNORE')
- nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYOS_CT_OUTPUT_HOOK')
- else:
- nat['helper_functions'] = 'has'
return nat
@@ -95,10 +59,6 @@ def verify(nat):
# no need to verify the CLI as NAT66 is going to be deactivated
return None
- if 'helper_functions' in nat and nat['helper_functions'] != 'has':
- if not (nat['pre_ct_conntrack'] or nat['out_ct_conntrack']):
- raise Exception('could not determine nftable ruleset handlers')
-
if dict_search('source.rule', nat):
for rule, config in dict_search('source.rule', nat).items():
err_msg = f'Source NAT66 configuration error in rule {rule}:'
@@ -155,6 +115,8 @@ def apply(nat):
else:
cmd('systemctl restart ndppd')
+ call_dependents()
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/policy-local-route.py b/src/conf_mode/policy-local-route.py
index 79526f82a..2e8aabb80 100755
--- a/src/conf_mode/policy-local-route.py
+++ b/src/conf_mode/policy-local-route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -16,6 +16,7 @@
import os
+from itertools import product
from sys import exit
from netifaces import interfaces
@@ -50,19 +51,22 @@ def get_config(config=None):
tmp = node_changed(conf, base_rule, key_mangling=('-', '_'))
if tmp:
for rule in (tmp or []):
- src = leaf_node_changed(conf, base_rule + [rule, 'source'])
+ src = leaf_node_changed(conf, base_rule + [rule, 'source', 'address'])
fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark'])
iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface'])
- dst = leaf_node_changed(conf, base_rule + [rule, 'destination'])
+ dst = leaf_node_changed(conf, base_rule + [rule, 'destination', 'address'])
+ proto = leaf_node_changed(conf, base_rule + [rule, 'protocol'])
rule_def = {}
if src:
- rule_def = dict_merge({'source' : src}, rule_def)
+ rule_def = dict_merge({'source': {'address': src}}, rule_def)
if fwmk:
rule_def = dict_merge({'fwmark' : fwmk}, rule_def)
if iif:
rule_def = dict_merge({'inbound_interface' : iif}, rule_def)
if dst:
- rule_def = dict_merge({'destination' : dst}, rule_def)
+ rule_def = dict_merge({'destination': {'address': dst}}, rule_def)
+ if proto:
+ rule_def = dict_merge({'protocol' : proto}, rule_def)
dict = dict_merge({dict_id : {rule : rule_def}}, dict)
pbr.update(dict)
@@ -74,10 +78,11 @@ def get_config(config=None):
# delete policy local-route rule x destination x.x.x.x
if 'rule' in pbr[route]:
for rule, rule_config in pbr[route]['rule'].items():
- src = leaf_node_changed(conf, base_rule + [rule, 'source'])
+ src = leaf_node_changed(conf, base_rule + [rule, 'source', 'address'])
fwmk = leaf_node_changed(conf, base_rule + [rule, 'fwmark'])
iif = leaf_node_changed(conf, base_rule + [rule, 'inbound-interface'])
- dst = leaf_node_changed(conf, base_rule + [rule, 'destination'])
+ dst = leaf_node_changed(conf, base_rule + [rule, 'destination', 'address'])
+ proto = leaf_node_changed(conf, base_rule + [rule, 'protocol'])
# keep track of changes in configuration
# otherwise we might remove an existing node although nothing else has changed
changed = False
@@ -89,7 +94,8 @@ def get_config(config=None):
# if a new selector is added, we have to remove all previous rules without this selector
# to make sure we remove all previous rules with this source(s), it will be included
if 'source' in rule_config:
- rule_def = dict_merge({'source': rule_config['source']}, rule_def)
+ if 'address' in rule_config['source']:
+ rule_def = dict_merge({'source': {'address': rule_config['source']['address']}}, rule_def)
else:
# if src is not None, it's previous content will be returned
# this can be an empty array if it's just being set, or the previous value
@@ -97,7 +103,8 @@ def get_config(config=None):
changed = True
# set the old value for removal if it's not empty
if len(src) > 0:
- rule_def = dict_merge({'source' : src}, rule_def)
+ rule_def = dict_merge({'source': {'address': src}}, rule_def)
+
if fwmk is None:
if 'fwmark' in rule_config:
rule_def = dict_merge({'fwmark': rule_config['fwmark']}, rule_def)
@@ -105,6 +112,7 @@ def get_config(config=None):
changed = True
if len(fwmk) > 0:
rule_def = dict_merge({'fwmark' : fwmk}, rule_def)
+
if iif is None:
if 'inbound_interface' in rule_config:
rule_def = dict_merge({'inbound_interface': rule_config['inbound_interface']}, rule_def)
@@ -112,13 +120,24 @@ def get_config(config=None):
changed = True
if len(iif) > 0:
rule_def = dict_merge({'inbound_interface' : iif}, rule_def)
+
if dst is None:
if 'destination' in rule_config:
- rule_def = dict_merge({'destination': rule_config['destination']}, rule_def)
+ if 'address' in rule_config['destination']:
+ rule_def = dict_merge({'destination': {'address': rule_config['destination']['address']}}, rule_def)
else:
changed = True
if len(dst) > 0:
- rule_def = dict_merge({'destination' : dst}, rule_def)
+ rule_def = dict_merge({'destination': {'address': dst}}, rule_def)
+
+ if proto is None:
+ if 'protocol' in rule_config:
+ rule_def = dict_merge({'protocol': rule_config['protocol']}, rule_def)
+ else:
+ changed = True
+ if len(proto) > 0:
+ rule_def = dict_merge({'protocol' : proto}, rule_def)
+
if changed:
dict = dict_merge({dict_id : {rule : rule_def}}, dict)
pbr.update(dict)
@@ -137,18 +156,22 @@ def verify(pbr):
pbr_route = pbr[route]
if 'rule' in pbr_route:
for rule in pbr_route['rule']:
- if 'source' not in pbr_route['rule'][rule] \
- and 'destination' not in pbr_route['rule'][rule] \
- and 'fwmark' not in pbr_route['rule'][rule] \
- and 'inbound_interface' not in pbr_route['rule'][rule]:
- raise ConfigError('Source or destination address or fwmark or inbound-interface is required!')
- else:
- if 'set' not in pbr_route['rule'][rule] or 'table' not in pbr_route['rule'][rule]['set']:
- raise ConfigError('Table set is required!')
- if 'inbound_interface' in pbr_route['rule'][rule]:
- interface = pbr_route['rule'][rule]['inbound_interface']
- if interface not in interfaces():
- raise ConfigError(f'Interface "{interface}" does not exist')
+ if (
+ 'source' not in pbr_route['rule'][rule] and
+ 'destination' not in pbr_route['rule'][rule] and
+ 'fwmark' not in pbr_route['rule'][rule] and
+ 'inbound_interface' not in pbr_route['rule'][rule] and
+ 'protocol' not in pbr_route['rule'][rule]
+ ):
+ raise ConfigError('Source or destination address or fwmark or inbound-interface or protocol is required!')
+
+ if 'set' not in pbr_route['rule'][rule] or 'table' not in pbr_route['rule'][rule]['set']:
+ raise ConfigError('Table set is required!')
+
+ if 'inbound_interface' in pbr_route['rule'][rule]:
+ interface = pbr_route['rule'][rule]['inbound_interface']
+ if interface not in interfaces():
+ raise ConfigError(f'Interface "{interface}" does not exist')
return None
@@ -166,20 +189,22 @@ def apply(pbr):
for rule_rm in ['rule_remove', 'rule6_remove']:
if rule_rm in pbr:
v6 = " -6" if rule_rm == 'rule6_remove' else ""
+
for rule, rule_config in pbr[rule_rm].items():
- rule_config['source'] = rule_config['source'] if 'source' in rule_config else ['']
- for src in rule_config['source']:
+ source = rule_config.get('source', {}).get('address', [''])
+ destination = rule_config.get('destination', {}).get('address', [''])
+ fwmark = rule_config.get('fwmark', [''])
+ inbound_interface = rule_config.get('inbound_interface', [''])
+ protocol = rule_config.get('protocol', [''])
+
+ for src, dst, fwmk, iif, proto in product(source, destination, fwmark, inbound_interface, protocol):
f_src = '' if src == '' else f' from {src} '
- rule_config['destination'] = rule_config['destination'] if 'destination' in rule_config else ['']
- for dst in rule_config['destination']:
- f_dst = '' if dst == '' else f' to {dst} '
- rule_config['fwmark'] = rule_config['fwmark'] if 'fwmark' in rule_config else ['']
- for fwmk in rule_config['fwmark']:
- f_fwmk = '' if fwmk == '' else f' fwmark {fwmk} '
- rule_config['inbound_interface'] = rule_config['inbound_interface'] if 'inbound_interface' in rule_config else ['']
- for iif in rule_config['inbound_interface']:
- f_iif = '' if iif == '' else f' iif {iif} '
- call(f'ip{v6} rule del prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif}')
+ f_dst = '' if dst == '' else f' to {dst} '
+ f_fwmk = '' if fwmk == '' else f' fwmark {fwmk} '
+ f_iif = '' if iif == '' else f' iif {iif} '
+ f_proto = '' if proto == '' else f' ipproto {proto} '
+
+ call(f'ip{v6} rule del prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif}')
# Generate new config
for route in ['local_route', 'local_route6']:
@@ -187,27 +212,26 @@ def apply(pbr):
continue
v6 = " -6" if route == 'local_route6' else ""
-
pbr_route = pbr[route]
+
if 'rule' in pbr_route:
for rule, rule_config in pbr_route['rule'].items():
- table = rule_config['set']['table']
-
- rule_config['source'] = rule_config['source'] if 'source' in rule_config else ['all']
- for src in rule_config['source'] or ['all']:
- f_src = '' if src == '' else f' from {src} '
- rule_config['destination'] = rule_config['destination'] if 'destination' in rule_config else ['all']
- for dst in rule_config['destination']:
- f_dst = '' if dst == '' else f' to {dst} '
- f_fwmk = ''
- if 'fwmark' in rule_config:
- fwmk = rule_config['fwmark']
- f_fwmk = f' fwmark {fwmk} '
- f_iif = ''
- if 'inbound_interface' in rule_config:
- iif = rule_config['inbound_interface']
- f_iif = f' iif {iif} '
- call(f'ip{v6} rule add prio {rule} {f_src}{f_dst}{f_fwmk}{f_iif} lookup {table}')
+ table = rule_config['set'].get('table', '')
+ source = rule_config.get('source', {}).get('address', ['all'])
+ destination = rule_config.get('destination', {}).get('address', ['all'])
+ fwmark = rule_config.get('fwmark', '')
+ inbound_interface = rule_config.get('inbound_interface', '')
+ protocol = rule_config.get('protocol', '')
+
+ for src in source:
+ f_src = f' from {src} ' if src else ''
+ for dst in destination:
+ f_dst = f' to {dst} ' if dst else ''
+ f_fwmk = f' fwmark {fwmark} ' if fwmark else ''
+ f_iif = f' iif {inbound_interface} ' if inbound_interface else ''
+ f_proto = f' ipproto {protocol} ' if protocol else ''
+
+ call(f'ip{v6} rule add prio {rule}{f_src}{f_dst}{f_proto}{f_fwmk}{f_iif} lookup {table}')
return None
diff --git a/src/conf_mode/protocols_igmp.py b/src/conf_mode/protocols_igmp.py
index f6097e282..435189025 100755
--- a/src/conf_mode/protocols_igmp.py
+++ b/src/conf_mode/protocols_igmp.py
@@ -102,7 +102,7 @@ def verify(igmp):
# Check, is this multicast group
for intfc in igmp['ifaces']:
for gr_addr in igmp['ifaces'][intfc]['gr_join']:
- if IPv4Address(gr_addr) < IPv4Address('224.0.0.0'):
+ if not IPv4Address(gr_addr).is_multicast:
raise ConfigError(gr_addr + " not a multicast group")
def generate(igmp):
diff --git a/src/conf_mode/protocols_pim6.py b/src/conf_mode/protocols_pim6.py
new file mode 100755
index 000000000..6a1235ba5
--- /dev/null
+++ b/src/conf_mode/protocols_pim6.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from ipaddress import IPv6Address
+from sys import exit
+from typing import Optional
+
+from vyos import ConfigError, airbag, frr
+from vyos.config import Config, ConfigDict
+from vyos.configdict import node_changed
+from vyos.configverify import verify_interface_exists
+from vyos.template import render_to_string
+
+airbag.enable()
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['protocols', 'pim6']
+ pim6 = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True, with_recursive_defaults=True)
+
+ # FRR has VRF support for different routing daemons. As interfaces belong
+ # to VRFs - or the global VRF, we need to check for changed interfaces so
+ # that they will be properly rendered for the FRR config. Also this eases
+ # removal of interfaces from the running configuration.
+ interfaces_removed = node_changed(conf, base + ['interface'])
+ if interfaces_removed:
+ pim6['interface_removed'] = list(interfaces_removed)
+
+ return pim6
+
+
+def verify(pim6):
+ if pim6 is None:
+ return
+
+ for interface, interface_config in pim6.get('interface', {}).items():
+ verify_interface_exists(interface)
+ if 'mld' in interface_config:
+ mld = interface_config['mld']
+ for group in mld.get('join', {}).keys():
+ # Validate multicast group address
+ if not IPv6Address(group).is_multicast:
+ raise ConfigError(f"{group} is not a multicast group")
+
+
+def generate(pim6):
+ if pim6 is None:
+ return
+
+ pim6['new_frr_config'] = render_to_string('frr/pim6d.frr.j2', pim6)
+
+
+def apply(pim6):
+ if pim6 is None:
+ return
+
+ pim6_daemon = 'pim6d'
+
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+
+ frr_cfg.load_configuration(pim6_daemon)
+
+ for key in ['interface', 'interface_removed']:
+ if key not in pim6:
+ continue
+ for interface in pim6[key]:
+ frr_cfg.modify_section(
+ f'^interface {interface}', stop_pattern='^exit', remove_stop_mark=True)
+
+ if 'new_frr_config' in pim6:
+ frr_cfg.add_before(frr.default_add_before, pim6['new_frr_config'])
+ frr_cfg.commit_configuration(pim6_daemon)
+
+
+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/service_aws_glb.py b/src/conf_mode/service_aws_glb.py
new file mode 100755
index 000000000..d1ed5a07b
--- /dev/null
+++ b/src/conf_mode/service_aws_glb.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from sys import exit
+
+from vyos.config import Config
+from vyos.template import render
+from vyos.utils.process import call
+from vyos import ConfigError
+from vyos import airbag
+airbag.enable()
+
+systemd_service = 'aws-gwlbtun.service'
+systemd_override = '/run/systemd/system/aws-gwlbtun.service.d/10-override.conf'
+
+
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
+ base = ['service', 'aws', 'glb']
+ if not conf.exists(base):
+ return None
+
+ glb = conf.get_config_dict(base, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ return glb
+
+
+def verify(glb):
+ # bail out early - looks like removal from running config
+ if not glb:
+ return None
+
+
+def generate(glb):
+ if not glb:
+ return None
+
+ render(systemd_override, 'aws/override_aws_gwlbtun.conf.j2', glb)
+
+
+def apply(glb):
+ call('systemctl daemon-reload')
+ if not glb:
+ call(f'systemctl stop {systemd_service}')
+ else:
+ call(f'systemctl restart {systemd_service}')
+ 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/service_mdns-repeater.py b/src/conf_mode/service_mdns-repeater.py
index a2c90b537..6909731ff 100755
--- a/src/conf_mode/service_mdns-repeater.py
+++ b/src/conf_mode/service_mdns-repeater.py
@@ -18,7 +18,7 @@ import os
from json import loads
from sys import exit
-from netifaces import ifaddresses, interfaces, AF_INET
+from netifaces import ifaddresses, interfaces, AF_INET, AF_INET6
from vyos.config import Config
from vyos.ifconfig.vrrp import VRRP
@@ -36,18 +36,22 @@ def get_config(config=None):
conf = config
else:
conf = Config()
+
base = ['service', 'mdns', 'repeater']
- mdns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ if not conf.exists(base):
+ return None
+
+ mdns = conf.get_config_dict(base, key_mangling=('-', '_'),
+ no_tag_node_value_mangle=True,
+ get_first_key=True,
+ with_recursive_defaults=True)
if mdns:
mdns['vrrp_exists'] = conf.exists('high-availability vrrp')
return mdns
def verify(mdns):
- if not mdns:
- return None
-
- if 'disable' in mdns:
+ if not mdns or 'disable' in mdns:
return None
# We need at least two interfaces to repeat mDNS advertisments
@@ -60,10 +64,14 @@ def verify(mdns):
if interface not in interfaces():
raise ConfigError(f'Interface "{interface}" does not exist!')
- if AF_INET not in ifaddresses(interface):
+ if mdns['ip_version'] in ['ipv4', 'both'] and AF_INET not in ifaddresses(interface):
raise ConfigError('mDNS repeater requires an IPv4 address to be '
f'configured on interface "{interface}"')
+ if mdns['ip_version'] in ['ipv6', 'both'] and AF_INET6 not in ifaddresses(interface):
+ raise ConfigError('mDNS repeater requires an IPv6 address to be '
+ f'configured on interface "{interface}"')
+
return None
# Get VRRP states from interfaces, returns only interfaces where state is MASTER
@@ -92,7 +100,7 @@ def generate(mdns):
if len(mdns['interface']) < 2:
return None
- render(config_file, 'mdns-repeater/avahi-daemon.j2', mdns)
+ render(config_file, 'mdns-repeater/avahi-daemon.conf.j2', mdns)
return None
def apply(mdns):
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 7882f8510..d2ed5414f 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -253,9 +253,8 @@ def apply(snmp):
# Enable AgentX in FRR
# This should be done for each daemon individually because common command
# works only if all the daemons started with SNMP support
- frr_daemons_list = [
- 'bgpd', 'ospf6d', 'ospfd', 'ripd', 'ripngd', 'isisd', 'ldpd', 'zebra'
- ]
+ # Following daemons from FRR 9.0/stable have SNMP module compiled in VyOS
+ frr_daemons_list = ['zebra', 'bgpd', 'ospf6d', 'ospfd', 'ripd', 'isisd', 'ldpd']
for frr_daemon in frr_daemons_list:
call(
f'vtysh -c "configure terminal" -d {frr_daemon} -c "agentx" >/dev/null'
diff --git a/src/conf_mode/system-ip.py b/src/conf_mode/system-ip.py
index 5e4e5ec28..7612e2c0d 100755
--- a/src/conf_mode/system-ip.py
+++ b/src/conf_mode/system-ip.py
@@ -20,10 +20,12 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_map
from vyos.template import render_to_string
-from vyos.utils.process import call
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
+from vyos.utils.process import call
+from vyos.utils.process import is_systemd_service_active
from vyos.utils.system import sysctl_write
+
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -115,16 +117,20 @@ def apply(opt):
value = '48' if (tmp is None) else tmp
sysctl_write('net.ipv4.tcp_mtu_probe_floor', value)
- zebra_daemon = 'zebra'
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
-
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(zebra_daemon)
- frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- if 'frr_zebra_config' in opt:
- frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
- frr_cfg.commit_configuration(zebra_daemon)
+ # During startup of vyos-router that brings up FRR, the service is not yet
+ # running when this script is called first. Skip this part and wait for initial
+ # commit of the configuration to trigger this statement
+ if is_systemd_service_active('frr.service'):
+ zebra_daemon = 'zebra'
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
+
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
+ if 'frr_zebra_config' in opt:
+ frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py
index e40ed38e2..90a1a8087 100755
--- a/src/conf_mode/system-ipv6.py
+++ b/src/conf_mode/system-ipv6.py
@@ -22,8 +22,9 @@ from vyos.configdict import dict_merge
from vyos.configverify import verify_route_map
from vyos.template import render_to_string
from vyos.utils.dict import dict_search
-from vyos.utils.system import sysctl_write
from vyos.utils.file import write_file
+from vyos.utils.process import is_systemd_service_active
+from vyos.utils.system import sysctl_write
from vyos import ConfigError
from vyos import frr
from vyos import airbag
@@ -93,16 +94,20 @@ def apply(opt):
if name == 'accept_dad':
write_file(os.path.join(root, name), value)
- zebra_daemon = 'zebra'
- # Save original configuration prior to starting any commit actions
- frr_cfg = frr.FRRConfig()
+ # During startup of vyos-router that brings up FRR, the service is not yet
+ # running when this script is called first. Skip this part and wait for initial
+ # commit of the configuration to trigger this statement
+ if is_systemd_service_active('frr.service'):
+ zebra_daemon = 'zebra'
+ # Save original configuration prior to starting any commit actions
+ frr_cfg = frr.FRRConfig()
- # The route-map used for the FIB (zebra) is part of the zebra daemon
- frr_cfg.load_configuration(zebra_daemon)
- frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
- if 'frr_zebra_config' in opt:
- frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
- frr_cfg.commit_configuration(zebra_daemon)
+ # The route-map used for the FIB (zebra) is part of the zebra daemon
+ frr_cfg.load_configuration(zebra_daemon)
+ frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
+ if 'frr_zebra_config' in opt:
+ frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
+ frr_cfg.commit_configuration(zebra_daemon)
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 02c97afaa..87a269499 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -104,6 +104,9 @@ def get_config(config=None):
# prune TACACS global defaults if not set by user
if login.from_defaults(['tacacs']):
del login['tacacs']
+ # same for RADIUS
+ if login.from_defaults(['radius']):
+ del login['radius']
# create a list of all users, cli and users
all_users = list(set(local_users + cli_users))
@@ -377,17 +380,23 @@ def apply(login):
except Exception as e:
raise ConfigError(f'Deleting user "{user}" raised exception: {e}')
- # Enable RADIUS in PAM configuration
- pam_cmd = '--remove'
+ # Enable/disable RADIUS in PAM configuration
+ cmd('pam-auth-update --disable radius-mandatory radius-optional')
if 'radius' in login:
- pam_cmd = '--enable'
- cmd(f'pam-auth-update --package {pam_cmd} radius')
-
- # Enable/Disable TACACS in PAM configuration
- pam_cmd = '--remove'
+ if login['radius'].get('security_mode', '') == 'mandatory':
+ pam_profile = 'radius-mandatory'
+ else:
+ pam_profile = 'radius-optional'
+ cmd(f'pam-auth-update --enable {pam_profile}')
+
+ # Enable/disable TACACS+ in PAM configuration
+ cmd('pam-auth-update --disable tacplus-mandatory tacplus-optional')
if 'tacacs' in login:
- pam_cmd = '--enable'
- cmd(f'pam-auth-update --package {pam_cmd} tacplus')
+ if login['tacacs'].get('security_mode', '') == 'mandatory':
+ pam_profile = 'tacplus-mandatory'
+ else:
+ pam_profile = 'tacplus-optional'
+ cmd(f'pam-auth-update --enable {pam_profile}')
return None
diff --git a/src/conf_mode/system_frr.py b/src/conf_mode/system_frr.py
index fb252238a..6727b63c2 100755
--- a/src/conf_mode/system_frr.py
+++ b/src/conf_mode/system_frr.py
@@ -18,21 +18,20 @@ from pathlib import Path
from sys import exit
from vyos import ConfigError
-from vyos import airbag
+from vyos.base import Warning
from vyos.config import Config
from vyos.logger import syslog
from vyos.template import render_to_string
+from vyos.utils.boot import boot_configuration_complete
from vyos.utils.file import read_file
from vyos.utils.file import write_file
-from vyos.utils.process import run
+from vyos.utils.process import call
+
+from vyos import airbag
airbag.enable()
# path to daemons config and config status files
config_file = '/etc/frr/daemons'
-vyos_status_file = '/tmp/vyos-config-status'
-# path to watchfrr for FRR control
-watchfrr = '/usr/lib/frr/watchfrr.sh'
-
def get_config(config=None):
if config:
@@ -45,12 +44,10 @@ def get_config(config=None):
return frr_config
-
def verify(frr_config):
# Nothing to verify here
pass
-
def generate(frr_config):
# read daemons config file
daemons_config_current = read_file(config_file)
@@ -62,25 +59,19 @@ def generate(frr_config):
write_file(config_file, daemons_config_new)
frr_config['config_file_changed'] = True
-
def apply(frr_config):
- # check if this is initial commit during boot or intiated by CLI
- # if the file exists, this must be CLI commit
- commit_type_cli = Path(vyos_status_file).exists()
# display warning to user
- if commit_type_cli and frr_config.get('config_file_changed'):
+ if boot_configuration_complete() and frr_config.get('config_file_changed'):
# Since FRR restart is not safe thing, better to give
# control over this to users
- print('''
- You need to reboot a router (preferred) or restart FRR
- to apply changes in modules settings
- ''')
- # restart FRR automatically. DUring the initial boot this should be
- # safe in most cases
- if not commit_type_cli and frr_config.get('config_file_changed'):
- syslog.warning('Restarting FRR to apply changes in modules')
- run(f'{watchfrr} restart')
+ Warning('You need to reboot the router (preferred) or restart '\
+ 'FRR to apply changes in modules settings')
+ # restart FRR automatically
+ # During initial boot this should be safe in most cases
+ if not boot_configuration_complete() and frr_config.get('config_file_changed'):
+ syslog.warning('Restarting FRR to apply changes in modules')
+ call(f'systemctl restart frr.service')
if __name__ == '__main__':
try:
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index fa271cbdb..9e9385ddb 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -29,7 +29,10 @@ from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_interface_exists
from vyos.defaults import directories
from vyos.ifconfig import Interface
+from vyos.pki import encode_certificate
from vyos.pki import encode_public_key
+from vyos.pki import find_chain
+from vyos.pki import load_certificate
from vyos.pki import load_private_key
from vyos.pki import wrap_certificate
from vyos.pki import wrap_crl
@@ -431,15 +434,23 @@ def generate_pki_files_x509(pki, x509_conf):
ca_cert_name = x509_conf['ca_certificate']
ca_cert_data = dict_search_args(pki, 'ca', ca_cert_name, 'certificate')
ca_cert_crls = dict_search_args(pki, 'ca', ca_cert_name, 'crl') or []
+ ca_index = 1
crl_index = 1
+ ca_cert = load_certificate(ca_cert_data)
+ pki_ca_certs = [load_certificate(ca['certificate']) for ca in pki['ca'].values()]
+
+ ca_cert_chain = find_chain(ca_cert, pki_ca_certs)
+
cert_name = x509_conf['certificate']
cert_data = dict_search_args(pki, 'certificate', cert_name, 'certificate')
key_data = dict_search_args(pki, 'certificate', cert_name, 'private', 'key')
protected = 'passphrase' in x509_conf
- with open(os.path.join(CA_PATH, f'{ca_cert_name}.pem'), 'w') as f:
- f.write(wrap_certificate(ca_cert_data))
+ for ca_cert_obj in ca_cert_chain:
+ with open(os.path.join(CA_PATH, f'{ca_cert_name}_{ca_index}.pem'), 'w') as f:
+ f.write(encode_certificate(ca_cert_obj))
+ ca_index += 1
for crl in ca_cert_crls:
with open(os.path.join(CRL_PATH, f'{ca_cert_name}_{crl_index}.pem'), 'w') as f:
diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf
index f5d84be4b..fcdc1b21d 100644
--- a/src/etc/sysctl.d/30-vyos-router.conf
+++ b/src/etc/sysctl.d/30-vyos-router.conf
@@ -98,15 +98,11 @@ net.ipv6.route.skip_notify_on_dev_down=1
# Default value of 20 seems to interfere with larger OSPF and VRRP setups
net.ipv4.igmp_max_memberships = 512
-# Increase default garbage collection thresholds
-net.ipv4.neigh.default.gc_thresh1 = 1024
-net.ipv4.neigh.default.gc_thresh2 = 4096
-net.ipv4.neigh.default.gc_thresh3 = 8192
-#
-net.ipv6.neigh.default.gc_thresh1 = 1024
-net.ipv6.neigh.default.gc_thresh2 = 4096
-net.ipv6.neigh.default.gc_thresh3 = 8192
-
# Enable global RFS (Receive Flow Steering) configuration. RFS is inactive
# until explicitly configured at the interface level
net.core.rps_sock_flow_entries = 32768
+
+# Congestion control
+net.core.default_qdisc=fq
+net.ipv4.tcp_congestion_control=bbr
+
diff --git a/src/helpers/config_dependency.py b/src/helpers/config_dependency.py
new file mode 100755
index 000000000..50c72956e
--- /dev/null
+++ b/src/helpers/config_dependency.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import os
+import sys
+from argparse import ArgumentParser
+from argparse import ArgumentTypeError
+
+try:
+ from vyos.configdep import check_dependency_graph
+ from vyos.defaults import directories
+except ImportError:
+ # allow running during addon package build
+ _here = os.path.dirname(__file__)
+ sys.path.append(os.path.join(_here, '../../python/vyos'))
+ from configdep import check_dependency_graph
+ from defaults import directories
+
+# addon packages will need to specify the dependency directory
+dependency_dir = os.path.join(directories['data'],
+ 'config-mode-dependencies')
+
+def path_exists(s):
+ if not os.path.exists(s):
+ raise ArgumentTypeError("Must specify a valid vyos-1x dependency directory")
+ return s
+
+def main():
+ parser = ArgumentParser(description='generate and save dict from xml defintions')
+ parser.add_argument('--dependency-dir', type=path_exists,
+ default=dependency_dir,
+ help='location of vyos-1x dependency directory')
+ parser.add_argument('--supplement', type=str,
+ help='supplemental dependency file')
+ args = vars(parser.parse_args())
+
+ if not check_dependency_graph(**args):
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/src/helpers/read-saved-value.py b/src/helpers/read-saved-value.py
new file mode 100755
index 000000000..1463e9ffe
--- /dev/null
+++ b/src/helpers/read-saved-value.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from argparse import ArgumentParser
+from vyos.utils.config import read_saved_value
+
+if __name__ == '__main__':
+ parser = ArgumentParser()
+ parser.add_argument('--path', nargs='*')
+ args = parser.parse_args()
+
+ out = read_saved_value(args.path) if args.path else ''
+ if isinstance(out, list):
+ out = ' '.join(out)
+ print(out)
diff --git a/src/helpers/vyos-domain-resolver.py b/src/helpers/vyos-domain-resolver.py
index 7e2fe2462..eac3d37af 100755
--- a/src/helpers/vyos-domain-resolver.py
+++ b/src/helpers/vyos-domain-resolver.py
@@ -37,12 +37,14 @@ domain_state = {}
ipv4_tables = {
'ip vyos_mangle',
'ip vyos_filter',
- 'ip vyos_nat'
+ 'ip vyos_nat',
+ 'ip raw'
}
ipv6_tables = {
'ip6 vyos_mangle',
- 'ip6 vyos_filter'
+ 'ip6 vyos_filter',
+ 'ip6 raw'
}
def get_config(conf):
diff --git a/src/helpers/vyos-save-config.py b/src/helpers/vyos-save-config.py
index 2812155e8..518bd9864 100755
--- a/src/helpers/vyos-save-config.py
+++ b/src/helpers/vyos-save-config.py
@@ -19,6 +19,7 @@ import os
import re
import sys
from tempfile import NamedTemporaryFile
+from argparse import ArgumentParser
from vyos.config import Config
from vyos.remote import urlc
@@ -28,8 +29,15 @@ from vyos.defaults import directories
DEFAULT_CONFIG_PATH = os.path.join(directories['config'], 'config.boot')
remote_save = None
-if len(sys.argv) > 1:
- save_file = sys.argv[1]
+parser = ArgumentParser(description='Save configuration')
+parser.add_argument('file', type=str, nargs='?', help='Save configuration to file')
+parser.add_argument('--write-json-file', type=str, help='Save JSON of configuration to file')
+args = parser.parse_args()
+file = args.file
+json_file = args.write_json_file
+
+if file is not None:
+ save_file = file
else:
save_file = DEFAULT_CONFIG_PATH
@@ -44,10 +52,20 @@ ct = config.get_config_tree(effective=True)
write_file = save_file if remote_save is None else NamedTemporaryFile(delete=False).name
with open(write_file, 'w') as f:
- f.write(ct.to_string())
+ # config_tree is None before boot configuration is complete;
+ # automated saves should check boot_configuration_complete
+ if ct is not None:
+ f.write(ct.to_string())
f.write("\n")
f.write(system_footer())
+if json_file is not None and ct is not None:
+ try:
+ with open(json_file, 'w') as f:
+ f.write(ct.to_json())
+ except OSError as e:
+ print(f'failed to write JSON file: {e}')
+
if remote_save is not None:
try:
remote_save.upload(write_file)
diff --git a/src/init/vyos-router b/src/init/vyos-router
index 96f163213..35095afe4 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -105,6 +105,9 @@ load_bootfile ()
restore_if_missing_preconfig_script ()
{
if [ ! -x ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script ]; then
+ mkdir -p ${vyatta_sysconfdir}/config/scripts
+ chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts
+ chmod 775 ${vyatta_sysconfdir}/config/scripts
cp ${vyos_rootfs_dir}/opt/vyatta/etc/config/scripts/vyos-preconfig-bootup.script ${vyatta_sysconfdir}/config/scripts/
chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script
chmod 750 ${vyatta_sysconfdir}/config/scripts/vyos-preconfig-bootup.script
@@ -123,6 +126,9 @@ run_preconfig_script ()
restore_if_missing_postconfig_script ()
{
if [ ! -x ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script ]; then
+ mkdir -p ${vyatta_sysconfdir}/config/scripts
+ chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts
+ chmod 775 ${vyatta_sysconfdir}/config/scripts
cp ${vyos_rootfs_dir}/opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script ${vyatta_sysconfdir}/config/scripts/
chgrp ${GROUP} ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script
chmod 750 ${vyatta_sysconfdir}/config/scripts/vyos-postconfig-bootup.script
@@ -228,10 +234,31 @@ cleanup_post_commit_hooks () {
# system defaults.
security_reset ()
{
+
+ # restore NSS cofniguration back to sane system defaults
+ # will be overwritten later when configuration is loaded
+ cat <<EOF >/etc/nsswitch.conf
+passwd: files
+group: files
+shadow: files
+gshadow: files
+
+# Per T2678, commenting out myhostname
+hosts: files dns #myhostname
+networks: files
+
+protocols: db files
+services: db files
+ethers: db files
+rpc: db files
+
+netgroup: nis
+EOF
+
# restore PAM back to virgin state (no radius/tacacs services)
- pam-auth-update --package --remove radius
+ pam-auth-update --disable radius-mandatory radius-optional
rm -f /etc/pam_radius_auth.conf
- pam-auth-update --package --remove tacplus
+ pam-auth-update --disable tacplus-mandatory tacplus-optional
rm -f /etc/tacplus_nss.conf /etc/tacplus_servers
# Certain configuration files are re-generated by the configuration
@@ -335,18 +362,18 @@ start ()
nfct helper add rpc inet tcp
nfct helper add rpc inet udp
nfct helper add tns inet tcp
+ nfct helper add rpc inet6 tcp
+ nfct helper add rpc inet6 udp
+ nfct helper add tns inet6 tcp
nft -f /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules"
- rm -f /etc/hostname
- ${vyos_conf_scripts_dir}/host_name.py || log_failure_msg "could not reset host-name"
- systemctl start frr.service
-
# As VyOS does not execute commands that are not present in the CLI we call
# the script by hand to have a single source for the login banner and MOTD
${vyos_conf_scripts_dir}/system_console.py || log_failure_msg "could not reset serial console"
- ${vyos_conf_scripts_dir}/system-login.py || log_failure_msg "could not reset system login"
${vyos_conf_scripts_dir}/system-login-banner.py || log_failure_msg "could not reset motd and issue files"
${vyos_conf_scripts_dir}/system-option.py || log_failure_msg "could not reset system option files"
+ ${vyos_conf_scripts_dir}/system-ip.py || log_failure_msg "could not reset system IPv4 options"
+ ${vyos_conf_scripts_dir}/system-ipv6.py || log_failure_msg "could not reset system IPv6 options"
${vyos_conf_scripts_dir}/conntrack.py || log_failure_msg "could not reset conntrack subsystem"
${vyos_conf_scripts_dir}/container.py || log_failure_msg "could not reset container subsystem"
@@ -373,6 +400,16 @@ start ()
&& chgrp ${GROUP} ${vyatta_configdir}
log_action_end_msg $?
+ # T5239: early read of system hostname as this value is read-only once during
+ # FRR initialisation
+ tmp=$(${vyos_libexec_dir}/read-saved-value.py --path "system host-name")
+ hostnamectl set-hostname --static "$tmp"
+
+ ${vyos_conf_scripts_dir}/system_frr.py || log_failure_msg "could not reset FRR config"
+ # If for any reason FRR was not started by system_frr.py - start it anyways.
+ # This is a safety net!
+ systemctl start frr.service
+
disabled bootfile || init_bootfile
cleanup_post_commit_hooks
diff --git a/src/migration-scripts/conntrack/3-to-4 b/src/migration-scripts/conntrack/3-to-4
new file mode 100755
index 000000000..e90c383af
--- /dev/null
+++ b/src/migration-scripts/conntrack/3-to-4
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Add support for IPv6 conntrack ignore, move existing nodes to `system conntrack ignore ipv4`
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['system', 'conntrack']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+if config.exists(base + ['ignore', 'rule']):
+ config.set(base + ['ignore', 'ipv4'])
+ config.copy(base + ['ignore', 'rule'], base + ['ignore', 'ipv4', 'rule'])
+ config.delete(base + ['ignore', 'rule'])
+
+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/policy/5-to-6 b/src/migration-scripts/policy/5-to-6
new file mode 100755
index 000000000..f1545cddb
--- /dev/null
+++ b/src/migration-scripts/policy/5-to-6
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T5165: Migrate policy local-route rule <tag> destination|source
+
+import re
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+from vyos.ifconfig import Section
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base4 = ['policy', 'local-route']
+base6 = ['policy', 'local-route6']
+config = ConfigTree(config_file)
+
+if not config.exists(base4) and not config.exists(base6):
+ # Nothing to do
+ exit(0)
+
+# replace 'policy local-route{v6} rule <tag> destination|source <x.x.x.x>'
+# => 'policy local-route{v6} rule <tag> destination|source address <x.x.x.x>'
+for base in [base4, base6]:
+ if config.exists(base + ['rule']):
+ for rule in config.list_nodes(base + ['rule']):
+ dst_path = base + ['rule', rule, 'destination']
+ src_path = base + ['rule', rule, 'source']
+ # Destination
+ if config.exists(dst_path):
+ for dst_addr in config.return_values(dst_path):
+ config.set(dst_path + ['address'], value=dst_addr, replace=False)
+ # Source
+ if config.exists(src_path):
+ for src_addr in config.return_values(src_path):
+ config.set(src_path + ['address'], value=src_addr, replace=False)
+
+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/system/13-to-14 b/src/migration-scripts/system/13-to-14
index 1fa781869..5b781158b 100755
--- a/src/migration-scripts/system/13-to-14
+++ b/src/migration-scripts/system/13-to-14
@@ -34,7 +34,7 @@ else:
# retrieve all valid timezones
try:
- tz_datas = cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::')
+ tz_datas = cmd('timedatectl list-timezones')
except OSError:
tz_datas = ''
tz_data = tz_datas.split('\n')
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index f558c18b7..77f38992b 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -338,10 +338,12 @@ def _get_formatted_client_leases(lease_data, family):
from time import localtime
from time import strftime
- from vyos.validate import is_intf_addr_assigned
+ from vyos.utils.network import is_intf_addr_assigned
data_entries = []
for lease in lease_data:
+ if not lease.get('new_ip_address'):
+ continue
data_entries.append(["Interface", lease['interface']])
if 'new_ip_address' in lease:
tmp = '[Active]' if is_intf_addr_assigned(lease['interface'], lease['new_ip_address']) else '[Inactive]'
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index 581710b31..3434707ec 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -24,27 +24,39 @@ from vyos.config import Config
from vyos.utils.process import cmd
from vyos.utils.dict import dict_search_args
-def get_config_firewall(conf, hook=None, priority=None, ipv6=False):
+def get_config_firewall(conf, family=None, hook=None, priority=None):
config_path = ['firewall']
- if hook:
- config_path += ['ipv6' if ipv6 else 'ipv4', hook]
- if priority:
- config_path += [priority]
+ if family:
+ config_path += [family]
+ if hook:
+ config_path += [hook]
+ if priority:
+ config_path += [priority]
firewall = conf.get_config_dict(config_path, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
return firewall
-def get_nftables_details(hook, priority, ipv6=False):
- suffix = '6' if ipv6 else ''
- aux = 'IPV6_' if ipv6 else ''
- name_prefix = 'NAME6_' if ipv6 else 'NAME_'
+def get_nftables_details(family, hook, priority):
+ if family == 'ipv6':
+ suffix = 'ip6'
+ name_prefix = 'NAME6_'
+ aux='IPV6_'
+ elif family == 'ipv4':
+ suffix = 'ip'
+ name_prefix = 'NAME_'
+ aux=''
+ else:
+ suffix = 'bridge'
+ name_prefix = 'NAME_'
+ aux=''
+
if hook == 'name' or hook == 'ipv6-name':
- command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{priority}'
+ command = f'sudo nft list chain {suffix} vyos_filter {name_prefix}{priority}'
else:
up_hook = hook.upper()
- command = f'sudo nft list chain ip{suffix} vyos_filter VYOS_{aux}{up_hook}_{priority}'
+ command = f'sudo nft list chain {suffix} vyos_filter VYOS_{aux}{up_hook}_{priority}'
try:
results = cmd(command)
@@ -68,11 +80,10 @@ def get_nftables_details(hook, priority, ipv6=False):
out[rule_id] = rule
return out
-def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_id=None):
- ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {priority}"\n')
+def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=None):
+ print(f'\n---------------------------------\n{family} Firewall "{hook} {priority}"\n')
- details = get_nftables_details(hook, priority, ipv6)
+ details = get_nftables_details(family, hook, priority)
rows = []
if 'rule' in firewall_conf:
@@ -103,11 +114,10 @@ def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_
header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
print(tabulate.tabulate(rows, header) + '\n')
-def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_rule_id=None):
- ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {prior}"\n')
+def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule_id=None):
+ print(f'\n---------------------------------\n{family} Firewall "{hook} {prior}"\n')
- details = get_nftables_details(hook, prior, ipv6)
+ details = get_nftables_details(family, hook, prior)
rows = []
if 'rule' in prior_conf:
@@ -127,7 +137,15 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
if not source_addr:
source_addr = dict_search_args(rule_conf, 'source', 'group', 'domain_group')
if not source_addr:
- source_addr = '::/0' if ipv6 else '0.0.0.0/0'
+ source_addr = dict_search_args(rule_conf, 'source', 'fqdn')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'geoip', 'country_code')
+ if source_addr:
+ source_addr = str(source_addr)[1:-1].replace('\'','')
+ if 'inverse_match' in dict_search_args(rule_conf, 'source', 'geoip'):
+ source_addr = 'NOT ' + str(source_addr)
+ if not source_addr:
+ source_addr = 'any'
# Get destination
dest_addr = dict_search_args(rule_conf, 'destination', 'address')
@@ -138,7 +156,15 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
if not dest_addr:
dest_addr = dict_search_args(rule_conf, 'destination', 'group', 'domain_group')
if not dest_addr:
- dest_addr = '::/0' if ipv6 else '0.0.0.0/0'
+ dest_addr = dict_search_args(rule_conf, 'destination', 'fqdn')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'geoip', 'country_code')
+ if dest_addr:
+ dest_addr = str(dest_addr)[1:-1].replace('\'','')
+ if 'inverse_match' in dict_search_args(rule_conf, 'destination', 'geoip'):
+ dest_addr = 'NOT ' + str(dest_addr)
+ if not dest_addr:
+ dest_addr = 'any'
# Get inbound interface
iiface = dict_search_args(rule_conf, 'inbound_interface', 'interface_name')
@@ -169,7 +195,22 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
row.append(oiface)
rows.append(row)
- if 'default_action' in prior_conf and not single_rule_id:
+
+ if hook in ['input', 'forward', 'output']:
+ row = ['default']
+ row.append('N/A')
+ row.append('N/A')
+ if 'default_action' in prior_conf:
+ row.append(prior_conf['default_action'])
+ else:
+ row.append('accept')
+ row.append('any')
+ row.append('any')
+ row.append('any')
+ row.append('any')
+ rows.append(row)
+
+ elif 'default_action' in prior_conf and not single_rule_id:
row = ['default']
if 'default-action' in details:
rule_details = details['default-action']
@@ -179,8 +220,10 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
row.append('0')
row.append('0')
row.append(prior_conf['default_action'])
- row.append('0.0.0.0/0') # Source
- row.append('0.0.0.0/0') # Dest
+ row.append('any') # Source
+ row.append('any') # Dest
+ row.append('any') # inbound-interface
+ row.append('any') # outbound-interface
rows.append(row)
if rows:
@@ -196,15 +239,11 @@ def show_firewall():
if not firewall:
return
- if 'ipv4' in firewall:
- for hook, hook_conf in firewall['ipv4'].items():
- for prior, prior_conf in firewall['ipv4'][hook].items():
- output_firewall_name(hook, prior, prior_conf, ipv6=False)
-
- if 'ipv6' in firewall:
- for hook, hook_conf in firewall['ipv6'].items():
- for prior, prior_conf in firewall['ipv6'][hook].items():
- output_firewall_name(hook, prior, prior_conf, ipv6=True)
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if family in firewall:
+ for hook, hook_conf in firewall[family].items():
+ for prior, prior_conf in firewall[family][hook].items():
+ output_firewall_name(family, hook, prior, prior_conf)
def show_firewall_family(family):
print(f'Rulesets {family} Information')
@@ -212,31 +251,28 @@ def show_firewall_family(family):
conf = Config()
firewall = get_config_firewall(conf)
- if not firewall:
+ if not firewall or family not in firewall:
return
for hook, hook_conf in firewall[family].items():
for prior, prior_conf in firewall[family][hook].items():
- if family == 'ipv6':
- output_firewall_name(hook, prior, prior_conf, ipv6=True)
- else:
- output_firewall_name(hook, prior, prior_conf, ipv6=False)
+ output_firewall_name(family, hook, prior, prior_conf)
-def show_firewall_name(hook, priority, ipv6=False):
+def show_firewall_name(family, hook, priority):
print('Ruleset Information')
conf = Config()
- firewall = get_config_firewall(conf, hook, priority, ipv6)
+ firewall = get_config_firewall(conf, family, hook, priority)
if firewall:
- output_firewall_name(hook, priority, firewall, ipv6)
+ output_firewall_name(family, hook, priority, firewall)
-def show_firewall_rule(hook, priority, rule_id, ipv6=False):
+def show_firewall_rule(family, hook, priority, rule_id):
print('Rule Information')
conf = Config()
- firewall = get_config_firewall(conf, hook, priority, ipv6)
+ firewall = get_config_firewall(conf, family, hook, priority)
if firewall:
- output_firewall_name(hook, priority, firewall, ipv6, rule_id)
+ output_firewall_name(family, hook, priority, firewall, rule_id)
def show_firewall_group(name=None):
conf = Config()
@@ -267,6 +303,8 @@ def show_firewall_group(name=None):
for priority, priority_conf in firewall[item][name_type].items():
if priority not in firewall[item][name_type]:
continue
+ if 'rule' not in priority_conf:
+ continue
for rule_id, rule_conf in priority_conf['rule'].items():
source_group = dict_search_args(rule_conf, 'source', 'group', group_type)
dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type)
@@ -303,7 +341,7 @@ def show_firewall_group(name=None):
continue
references = find_references(group_type, group_name)
- row = [group_name, group_type, '\n'.join(references) or 'N/A']
+ row = [group_name, group_type, '\n'.join(references) or 'N/D']
if 'address' in group_conf:
row.append("\n".join(sorted(group_conf['address'])))
elif 'network' in group_conf:
@@ -315,7 +353,7 @@ def show_firewall_group(name=None):
elif 'interface' in group_conf:
row.append("\n".join(sorted(group_conf['interface'])))
else:
- row.append('N/A')
+ row.append('N/D')
rows.append(row)
if rows:
@@ -334,6 +372,7 @@ def show_summary():
header = ['Ruleset Hook', 'Ruleset Priority', 'Description', 'References']
v4_out = []
v6_out = []
+ br_out = []
if 'ipv4' in firewall:
for hook, hook_conf in firewall['ipv4'].items():
@@ -347,6 +386,12 @@ def show_summary():
description = prior_conf.get('description', '')
v6_out.append([hook, prior, description])
+ if 'bridge' in firewall:
+ for hook, hook_conf in firewall['bridge'].items():
+ for prior, prior_conf in firewall['bridge'][hook].items():
+ description = prior_conf.get('description', '')
+ br_out.append([hook, prior, description])
+
if v6_out:
print('\nIPv6 Ruleset:\n')
print(tabulate.tabulate(v6_out, header) + '\n')
@@ -355,6 +400,10 @@ def show_summary():
print('\nIPv4 Ruleset:\n')
print(tabulate.tabulate(v4_out, header) + '\n')
+ if br_out:
+ print('\nBridge Ruleset:\n')
+ print(tabulate.tabulate(br_out, header) + '\n')
+
show_firewall_group()
def show_statistics():
@@ -366,15 +415,11 @@ def show_statistics():
if not firewall:
return
- if 'ipv4' in firewall:
- for hook, hook_conf in firewall['ipv4'].items():
- for prior, prior_conf in firewall['ipv4'][hook].items():
- output_firewall_name_statistics(hook,prior, prior_conf, ipv6=False)
-
- if 'ipv6' in firewall:
- for hook, hook_conf in firewall['ipv6'].items():
- for prior, prior_conf in firewall['ipv6'][hook].items():
- output_firewall_name_statistics(hook,prior, prior_conf, ipv6=True)
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if family in firewall:
+ for hook, hook_conf in firewall[family].items():
+ for prior, prior_conf in firewall[family][hook].items():
+ output_firewall_name_statistics(family, hook,prior, prior_conf)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@@ -390,9 +435,9 @@ if __name__ == '__main__':
if args.action == 'show':
if not args.rule:
- show_firewall_name(args.hook, args.priority, args.ipv6)
+ show_firewall_name(args.family, args.hook, args.priority)
else:
- show_firewall_rule(args.hook, args.priority, args.rule, args.ipv6)
+ show_firewall_rule(args.family, args.hook, args.priority, args.rule)
elif args.action == 'show_all':
show_firewall()
elif args.action == 'show_family':
diff --git a/src/op_mode/format_disk.py b/src/op_mode/format_disk.py
index 31ceb196a..dc3c96322 100755
--- a/src/op_mode/format_disk.py
+++ b/src/op_mode/format_disk.py
@@ -24,6 +24,7 @@ from vyos.utils.io import ask_yes_no
from vyos.utils.process import call
from vyos.utils.process import cmd
from vyos.utils.process import DEVNULL
+from vyos.utils.disk import device_from_id
def list_disks():
disks = set()
@@ -77,12 +78,18 @@ if __name__ == '__main__':
group = parser.add_argument_group()
group.add_argument('-t', '--target', type=str, required=True, help='Target device to format')
group.add_argument('-p', '--proto', type=str, required=True, help='Prototype device to use as reference')
+ parser.add_argument('--by-id', action='store_true', help='Specify device by disk id')
args = parser.parse_args()
+ target = args.target
+ proto = args.proto
+ if args.by_id:
+ target = device_from_id(target)
+ proto = device_from_id(proto)
- target_disk = args.target
+ target_disk = target
eligible_target_disks = list_disks()
- proto_disk = args.proto
+ proto_disk = proto
eligible_proto_disks = eligible_target_disks.copy()
eligible_proto_disks.remove(target_disk)
diff --git a/src/op_mode/generate_firewall_rule-resequence.py b/src/op_mode/generate_firewall_rule-resequence.py
new file mode 100755
index 000000000..eb82a1a0a
--- /dev/null
+++ b/src/op_mode/generate_firewall_rule-resequence.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import argparse
+from vyos.configquery import ConfigTreeQuery
+
+
+def convert_to_set_commands(config_dict, parent_key=''):
+ """
+ Converts a configuration dictionary into a list of set commands.
+
+ Args:
+ config_dict (dict): The configuration dictionary.
+ parent_key (str): The parent key for nested dictionaries.
+
+ Returns:
+ list: A list of set commands.
+ """
+ commands = []
+ for key, value in config_dict.items():
+ current_key = parent_key + key if parent_key else key
+
+ if isinstance(value, dict):
+ if not value:
+ commands.append(f"set {current_key}")
+ else:
+ commands.extend(
+ convert_to_set_commands(value, f"{current_key} "))
+
+ elif isinstance(value, str):
+ commands.append(f"set {current_key} '{value}'")
+
+ return commands
+
+
+def change_rule_numbers(config_dict, start, step):
+ """
+ Changes rule numbers in the configuration dictionary.
+
+ Args:
+ config_dict (dict): The configuration dictionary.
+ start (int): The starting rule number.
+ step (int): The step to increment the rule numbers.
+
+ Returns:
+ None
+ """
+ if 'rule' in config_dict:
+ rule_dict = config_dict['rule']
+ updated_rule_dict = {}
+ rule_num = start
+ for rule_key in sorted(rule_dict.keys()):
+ updated_rule_dict[str(rule_num)] = rule_dict[rule_key]
+ rule_num += step
+ config_dict['rule'] = updated_rule_dict
+
+ for key in config_dict:
+ if isinstance(config_dict[key], dict):
+ change_rule_numbers(config_dict[key], start, step)
+
+
+def convert_rule_keys_to_int(config_dict):
+ """
+ Converts rule keys in the configuration dictionary to integers.
+
+ Args:
+ config_dict (dict or list): The configuration dictionary or list.
+
+ Returns:
+ dict or list: The modified dictionary or list.
+ """
+ if isinstance(config_dict, dict):
+ new_dict = {}
+ for key, value in config_dict.items():
+ # Convert key to integer if possible
+ new_key = int(key) if key.isdigit() else key
+
+ # Recur for nested dictionaries
+ if isinstance(value, dict):
+ new_value = convert_rule_keys_to_int(value)
+ else:
+ new_value = value
+
+ new_dict[new_key] = new_value
+
+ return new_dict
+ elif isinstance(config_dict, list):
+ return [convert_rule_keys_to_int(item) for item in config_dict]
+ else:
+ return config_dict
+
+
+if __name__ == "__main__":
+ # Parse command-line arguments
+ parser = argparse.ArgumentParser(description='Convert dictionary to set commands with rule number modifications.')
+ parser.add_argument('--start', type=int, default=100, help='Start rule number')
+ parser.add_argument('--step', type=int, default=10, help='Step for rule numbers (default: 10)')
+ args = parser.parse_args()
+
+ config = ConfigTreeQuery()
+ if not config.exists('firewall'):
+ print('Firewall is not configured')
+ exit(1)
+
+ config_dict = config.get_config_dict('firewall')
+
+ # Remove global-options, group and flowtable as they don't need sequencing
+ if 'global-options' in config_dict['firewall']:
+ del config_dict['firewall']['global-options']
+
+ if 'group' in config_dict['firewall']:
+ del config_dict['firewall']['group']
+
+ if 'flowtable' in config_dict['firewall']:
+ del config_dict['firewall']['flowtable']
+
+ # Convert rule keys to integers, rule "10" -> rule 10
+ # This is necessary for sorting the rules
+ config_dict = convert_rule_keys_to_int(config_dict)
+
+ # Apply rule number modifications
+ change_rule_numbers(config_dict, start=args.start, step=args.step)
+
+ # Convert to 'set' commands
+ set_commands = convert_to_set_commands(config_dict)
+
+ print()
+ for command in set_commands:
+ print(command)
+ print()
diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py
index 57d3cfed9..44d41219e 100755
--- a/src/op_mode/ipsec.py
+++ b/src/op_mode/ipsec.py
@@ -779,6 +779,45 @@ def show_ra_summary(raw: bool):
return _get_formatted_output_ra_summary(list_sa)
+# PSK block
+def _get_raw_psk():
+ conf: ConfigTreeQuery = ConfigTreeQuery()
+ config_path = ['vpn', 'ipsec', 'authentication', 'psk']
+ psk_config = conf.get_config_dict(config_path, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ psk_list = []
+ for psk, psk_data in psk_config.items():
+ psk_data['psk'] = psk
+ psk_list.append(psk_data)
+
+ return psk_list
+
+
+def _get_formatted_psk(psk_list):
+ headers = ["PSK", "Id", "Secret"]
+ formatted_data = []
+
+ for psk_data in psk_list:
+ formatted_data.append([psk_data["psk"], "\n".join(psk_data["id"]), psk_data["secret"]])
+
+ return tabulate(formatted_data, headers=headers)
+
+
+def show_psk(raw: bool):
+ config = ConfigTreeQuery()
+ if not config.exists('vpn ipsec authentication psk'):
+ raise vyos.opmode.UnconfiguredSubsystem('VPN ipsec psk authentication is not configured')
+
+ psk = _get_raw_psk()
+ if raw:
+ return psk
+ return _get_formatted_psk(psk)
+
+# PSK block end
+
+
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
diff --git a/src/op_mode/otp.py b/src/op_mode/otp.py
new file mode 100755
index 000000000..6d4298894
--- /dev/null
+++ b/src/op_mode/otp.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+
+# Copyright 2017, 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/>.
+
+
+import sys
+import os
+import vyos.opmode
+from jinja2 import Template
+from vyos.configquery import ConfigTreeQuery
+from vyos.xml import defaults
+from vyos.configdict import dict_merge
+from vyos.utils.process import popen
+
+
+users_otp_template = Template("""
+{% if info == "full" %}
+# You can share it with the user, he just needs to scan the QR in his OTP app
+# username: {{username}}
+# OTP KEY: {{key_base32}}
+# OTP URL: {{otp_url}}
+{{qrcode}}
+# To add this OTP key to configuration, run the following commands:
+set system login user {{username}} authentication otp key '{{key_base32}}'
+{% if rate_limit != "3" %}
+set system login user {{username}} authentication otp rate-limit '{{rate_limit}}'
+{% endif %}
+{% if rate_time != "30" %}
+set system login user {{username}} authentication otp rate-time '{{rate_time}}'
+{% endif %}
+{% if window_size != "3" %}
+set system login user {{username}} authentication otp window-size '{{window_size}}'
+{% endif %}
+{% elif info == "key-b32" %}
+# OTP key in Base32 for system user {{username}}:
+{{key_base32}}
+{% elif info == "qrcode" %}
+# QR code for system user '{{username}}'
+{{qrcode}}
+{% elif info == "uri" %}
+# URI for system user '{{username}}'
+{{otp_url}}
+{% endif %}
+""", trim_blocks=True, lstrip_blocks=True)
+
+
+def _check_uname_otp(username:str):
+ """
+ Check if "username" exists and have an OTP key
+ """
+ config = ConfigTreeQuery()
+ base_key = ['system', 'login', 'user', username, 'authentication', 'otp', 'key']
+ if not config.exists(base_key):
+ return None
+ return True
+
+def _get_login_otp(username: str, info:str):
+ """
+ Retrieve user settings from configuration and set some defaults
+ """
+ config = ConfigTreeQuery()
+ base = ['system', 'login', 'user', username]
+ if not config.exists(base):
+ return None
+ user_otp = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # 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(['system', 'login', 'user'])
+ user_otp = dict_merge(default_values, user_otp)
+ result = user_otp['authentication']['otp']
+ # Filling in the system and default options
+ result['info'] = info
+ result['hostname'] = os.uname()[1]
+ result['username'] = username
+ result['key_base32'] = result['key']
+ result['otp_length'] = '6'
+ result['interval'] = '30'
+ result['token_type'] = 'hotp-time'
+ if result['token_type'] == 'hotp-time':
+ token_type_acrn = 'totp'
+ result['otp_url'] = ''.join(["otpauth://",token_type_acrn,"/",username,"@",\
+ result['hostname'],"?secret=",result['key_base32'],"&digits=",\
+ result['otp_length'],"&period=",result['interval']])
+ result['qrcode'],err = popen('qrencode -t ansiutf8', input=result['otp_url'])
+ return result
+
+def show_login(raw: bool, username: str, info:str):
+ '''
+ Display OTP parameters for <username>
+ '''
+ check_otp = _check_uname_otp(username)
+ if check_otp:
+ user_otp_params = _get_login_otp(username, info)
+ else:
+ print(f'There is no such user ("{username}") with an OTP key configured')
+ print('You can use the following command to generate a key for a user:\n')
+ print(f'generate system login username {username} otp-key hotp-time')
+ sys.exit(0)
+ if raw:
+ return user_otp_params
+ return users_otp_template.render(user_otp_params)
+
+
+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/raid.py b/src/op_mode/raid.py
new file mode 100755
index 000000000..fed8ae2c3
--- /dev/null
+++ b/src/op_mode/raid.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+import sys
+
+import vyos.opmode
+from vyos.raid import add_raid_member
+from vyos.raid import delete_raid_member
+
+def add(raid_set_name: str, member: str, by_id: bool = False):
+ try:
+ add_raid_member(raid_set_name, member, by_id)
+ except ValueError as e:
+ raise vyos.opmode.IncorrectValue(str(e))
+
+def delete(raid_set_name: str, member: str, by_id: bool = False):
+ try:
+ delete_raid_member(raid_set_name, member, by_id)
+ except ValueError as e:
+ raise vyos.opmode.IncorrectValue(str(e))
+
+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/restart_frr.py b/src/op_mode/restart_frr.py
index 5cce377eb..820a3846c 100755
--- a/src/op_mode/restart_frr.py
+++ b/src/op_mode/restart_frr.py
@@ -139,7 +139,9 @@ def _reload_config(daemon):
# define program arguments
cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons')
cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons')
-cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ldpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra', 'babeld'], required=False, nargs='*', help='select single or multiple daemons')
+# Full list of FRR 9.0/stable daemons for reference
+#cmd_args_parser.add_argument('--daemon', choices=['zebra', 'staticd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd', 'isisd', 'pim6d', 'ldpd', 'eigrpd', 'babeld', 'sharpd', 'bfdd', 'fabricd', 'pathd'], required=False, nargs='*', help='select single or multiple daemons')
+cmd_args_parser.add_argument('--daemon', choices=['zebra', 'staticd', 'bgpd', 'ospfd', 'ospf6d', 'ripd', 'ripngd', 'isisd', 'pim6d', 'ldpd', 'babeld', 'bfdd'], required=False, nargs='*', help='select single or multiple daemons')
# parse arguments
cmd_args = cmd_args_parser.parse_args()
diff --git a/src/op_mode/show_users.py b/src/op_mode/show_users.py
index 8e4f12851..82bd585c9 100755
--- a/src/op_mode/show_users.py
+++ b/src/op_mode/show_users.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -15,7 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
import pwd
-import spwd
import struct
import sys
from time import ctime
@@ -48,6 +47,10 @@ def is_locked(user_name: str) -> bool:
"""Check if a given user has password in shadow db"""
try:
+ import warnings
+ with warnings.catch_warnings():
+ warnings.filterwarnings("ignore",category=DeprecationWarning)
+ import spwd
encrypted_password = spwd.getspnam(user_name)[1]
return encrypted_password == '*' or encrypted_password.startswith('!')
except (KeyError, PermissionError):
diff --git a/src/op_mode/vyos-op-cmd-wrapper.sh b/src/op_mode/vyos-op-cmd-wrapper.sh
new file mode 100755
index 000000000..a89211b2b
--- /dev/null
+++ b/src/op_mode/vyos-op-cmd-wrapper.sh
@@ -0,0 +1,6 @@
+#!/bin/vbash
+shopt -s expand_aliases
+source /etc/default/vyatta
+source /etc/bash_completion.d/vyatta-op
+_vyatta_op_init
+_vyatta_op_run "$@"
diff --git a/src/op_mode/zone.py b/src/op_mode/zone.py
deleted file mode 100755
index 17ce90396..000000000
--- a/src/op_mode/zone.py
+++ /dev/null
@@ -1,215 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2023 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import typing
-import sys
-import vyos.opmode
-
-import tabulate
-from vyos.configquery import ConfigTreeQuery
-from vyos.utils.dict import dict_search_args
-from vyos.utils.dict import dict_search
-
-
-def get_config_zone(conf, name=None):
- config_path = ['firewall', 'zone']
- if name:
- config_path += [name]
-
- zone_policy = conf.get_config_dict(config_path, key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
- return zone_policy
-
-
-def _convert_one_zone_data(zone: str, zone_config: dict) -> dict:
- """
- Convert config dictionary of one zone to API dictionary
- :param zone: Zone name
- :type zone: str
- :param zone_config: config dictionary
- :type zone_config: dict
- :return: AP dictionary
- :rtype: dict
- """
- list_of_rules = []
- intrazone_dict = {}
- if dict_search('from', zone_config):
- for from_zone, from_zone_config in zone_config['from'].items():
- from_zone_dict = {'name': from_zone}
- if dict_search('firewall.name', from_zone_config):
- from_zone_dict['firewall'] = dict_search('firewall.name',
- from_zone_config)
- if dict_search('firewall.ipv6_name', from_zone_config):
- from_zone_dict['firewall_v6'] = dict_search(
- 'firewall.ipv6_name', from_zone_config)
- list_of_rules.append(from_zone_dict)
-
- zone_dict = {
- 'name': zone,
- 'interface': dict_search('interface', zone_config),
- 'type': 'LOCAL' if dict_search('local_zone',
- zone_config) is not None else None,
- }
- if list_of_rules:
- zone_dict['from'] = list_of_rules
- if dict_search('intra_zone_filtering.firewall.name', zone_config):
- intrazone_dict['firewall'] = dict_search(
- 'intra_zone_filtering.firewall.name', zone_config)
- if dict_search('intra_zone_filtering.firewall.ipv6_name', zone_config):
- intrazone_dict['firewall_v6'] = dict_search(
- 'intra_zone_filtering.firewall.ipv6_name', zone_config)
- if intrazone_dict:
- zone_dict['intrazone'] = intrazone_dict
- return zone_dict
-
-
-def _convert_zones_data(zone_policies: dict) -> list:
- """
- Convert all config dictionary to API list of zone dictionaries
- :param zone_policies: config dictionary
- :type zone_policies: dict
- :return: API list
- :rtype: list
- """
- zone_list = []
- for zone, zone_config in zone_policies.items():
- zone_list.append(_convert_one_zone_data(zone, zone_config))
- return zone_list
-
-
-def _convert_config(zones_config: dict, zone: str = None) -> list:
- """
- convert config to API list
- :param zones_config: zones config
- :type zones_config:
- :param zone: zone name
- :type zone: str
- :return: API list
- :rtype: list
- """
- if zone:
- if zones_config:
- output = [_convert_one_zone_data(zone, zones_config)]
- else:
- raise vyos.opmode.DataUnavailable(f'Zone {zone} not found')
- else:
- if zones_config:
- output = _convert_zones_data(zones_config)
- else:
- raise vyos.opmode.UnconfiguredSubsystem(
- 'Zone entries are not configured')
- return output
-
-
-def output_zone_list(zone_conf: dict) -> list:
- """
- Format one zone row
- :param zone_conf: zone config
- :type zone_conf: dict
- :return: formatted list of zones
- :rtype: list
- """
- zone_info = [zone_conf['name']]
- if zone_conf['type'] == 'LOCAL':
- zone_info.append('LOCAL')
- else:
- zone_info.append("\n".join(zone_conf['interface']))
-
- from_zone = []
- firewall = []
- firewall_v6 = []
- if 'intrazone' in zone_conf:
- from_zone.append(zone_conf['name'])
-
- v4_name = dict_search_args(zone_conf['intrazone'], 'firewall')
- v6_name = dict_search_args(zone_conf['intrazone'], 'firewall_v6')
- if v4_name:
- firewall.append(v4_name)
- else:
- firewall.append('')
- if v6_name:
- firewall_v6.append(v6_name)
- else:
- firewall_v6.append('')
-
- if 'from' in zone_conf:
- for from_conf in zone_conf['from']:
- from_zone.append(from_conf['name'])
-
- v4_name = dict_search_args(from_conf, 'firewall')
- v6_name = dict_search_args(from_conf, 'firewall_v6')
- if v4_name:
- firewall.append(v4_name)
- else:
- firewall.append('')
- if v6_name:
- firewall_v6.append(v6_name)
- else:
- firewall_v6.append('')
-
- zone_info.append("\n".join(from_zone))
- zone_info.append("\n".join(firewall))
- zone_info.append("\n".join(firewall_v6))
- return zone_info
-
-
-def get_formatted_output(zone_policy: list) -> str:
- """
- Formatted output of all zones
- :param zone_policy: list of zones
- :type zone_policy: list
- :return: formatted table with zones
- :rtype: str
- """
- headers = ["Zone",
- "Interfaces",
- "From Zone",
- "Firewall IPv4",
- "Firewall IPv6"
- ]
- formatted_list = []
- for zone_conf in zone_policy:
- formatted_list.append(output_zone_list(zone_conf))
- tabulate.PRESERVE_WHITESPACE = True
- output = tabulate.tabulate(formatted_list, headers, numalign="left")
- return output
-
-
-def show(raw: bool, zone: typing.Optional[str]):
- """
- Show zone-policy command
- :param raw: if API
- :type raw: bool
- :param zone: zone name
- :type zone: str
- """
- conf: ConfigTreeQuery = ConfigTreeQuery()
- zones_config: dict = get_config_zone(conf, zone)
- zone_policy_api: list = _convert_config(zones_config, zone)
- if raw:
- return zone_policy_api
- else:
- return get_formatted_output(zone_policy_api)
-
-
-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/pam-configs/radius b/src/pam-configs/radius
deleted file mode 100644
index 08247f77c..000000000
--- a/src/pam-configs/radius
+++ /dev/null
@@ -1,17 +0,0 @@
-Name: RADIUS authentication
-Default: no
-Priority: 257
-Auth-Type: Primary
-Auth:
- [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
- [authinfo_unavail=ignore success=end default=ignore] pam_radius_auth.so
-
-Account-Type: Primary
-Account:
- [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
- [authinfo_unavail=ignore success=end perm_denied=bad default=ignore] pam_radius_auth.so
-
-Session-Type: Additional
-Session:
- [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
- [authinfo_unavail=ignore success=ok default=ignore] pam_radius_auth.so
diff --git a/src/pam-configs/radius-mandatory b/src/pam-configs/radius-mandatory
new file mode 100644
index 000000000..3368fe7ff
--- /dev/null
+++ b/src/pam-configs/radius-mandatory
@@ -0,0 +1,19 @@
+Name: RADIUS authentication (mandatory mode)
+Default: no
+Priority: 576
+
+Auth-Type: Primary
+Auth-Initial:
+ [default=ignore success=end auth_err=die perm_denied=die user_unknown=die] pam_radius_auth.so
+Auth:
+ [default=ignore success=end auth_err=die perm_denied=die user_unknown=die] pam_radius_auth.so use_first_pass
+
+Account-Type: Primary
+Account:
+ [default=ignore success=1] pam_succeed_if.so user notingroup radius quiet
+ [default=ignore success=end] pam_radius_auth.so
+
+Session-Type: Additional
+Session:
+ [default=ignore success=1] pam_succeed_if.so user notingroup radius quiet
+ [default=bad success=ok] pam_radius_auth.so
diff --git a/src/pam-configs/radius-optional b/src/pam-configs/radius-optional
new file mode 100644
index 000000000..73085061d
--- /dev/null
+++ b/src/pam-configs/radius-optional
@@ -0,0 +1,19 @@
+Name: RADIUS authentication (optional mode)
+Default: no
+Priority: 576
+
+Auth-Type: Primary
+Auth-Initial:
+ [default=ignore success=end] pam_radius_auth.so
+Auth:
+ [default=ignore success=end] pam_radius_auth.so use_first_pass
+
+Account-Type: Primary
+Account:
+ [default=ignore success=1] pam_succeed_if.so user notingroup radius quiet
+ [default=ignore success=end] pam_radius_auth.so
+
+Session-Type: Additional
+Session:
+ [default=ignore success=1] pam_succeed_if.so user notingroup radius quiet
+ [default=ignore success=ok perm_denied=bad user_unknown=bad] pam_radius_auth.so
diff --git a/src/pam-configs/tacplus b/src/pam-configs/tacplus
deleted file mode 100644
index 66a1eaa4c..000000000
--- a/src/pam-configs/tacplus
+++ /dev/null
@@ -1,17 +0,0 @@
-Name: TACACS+ authentication
-Default: no
-Priority: 257
-Auth-Type: Primary
-Auth:
- [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
- [authinfo_unavail=ignore success=end auth_err=bad default=ignore] pam_tacplus.so include=/etc/tacplus_servers login=login
-
-Account-Type: Primary
-Account:
- [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
- [authinfo_unavail=ignore success=end perm_denied=bad default=ignore] pam_tacplus.so include=/etc/tacplus_servers login=login
-
-Session-Type: Additional
-Session:
- [default=ignore success=ignore] pam_succeed_if.so user ingroup aaa quiet
- [authinfo_unavail=ignore success=ok default=ignore] pam_tacplus.so include=/etc/tacplus_servers login=login
diff --git a/src/pam-configs/tacplus-mandatory b/src/pam-configs/tacplus-mandatory
new file mode 100644
index 000000000..ffccece19
--- /dev/null
+++ b/src/pam-configs/tacplus-mandatory
@@ -0,0 +1,17 @@
+Name: TACACS+ authentication (mandatory mode)
+Default: no
+Priority: 576
+
+Auth-Type: Primary
+Auth:
+ [default=ignore success=end auth_err=die perm_denied=die user_unknown=die] pam_tacplus.so include=/etc/tacplus_servers login=login
+
+Account-Type: Primary
+Account:
+ [default=ignore success=1] pam_succeed_if.so user notingroup tacacs quiet
+ [default=bad success=end] pam_tacplus.so include=/etc/tacplus_servers login=login
+
+Session-Type: Additional
+Session:
+ [default=ignore success=1] pam_succeed_if.so user notingroup tacacs quiet
+ [default=bad success=ok] pam_tacplus.so include=/etc/tacplus_servers login=login
diff --git a/src/pam-configs/tacplus-optional b/src/pam-configs/tacplus-optional
new file mode 100644
index 000000000..095c3a164
--- /dev/null
+++ b/src/pam-configs/tacplus-optional
@@ -0,0 +1,17 @@
+Name: TACACS+ authentication (optional mode)
+Default: no
+Priority: 576
+
+Auth-Type: Primary
+Auth:
+ [default=ignore success=end] pam_tacplus.so include=/etc/tacplus_servers login=login
+
+Account-Type: Primary
+Account:
+ [default=ignore success=1] pam_succeed_if.so user notingroup tacacs quiet
+ [default=ignore success=end auth_err=bad perm_denied=bad user_unknown=bad] pam_tacplus.so include=/etc/tacplus_servers login=login
+
+Session-Type: Additional
+Session:
+ [default=ignore success=1] pam_succeed_if.so user notingroup tacacs quiet
+ [default=ignore success=ok session_err=bad user_unknown=bad] pam_tacplus.so include=/etc/tacplus_servers login=login
diff --git a/src/systemd/aws-gwlbtun.service b/src/systemd/aws-gwlbtun.service
new file mode 100644
index 000000000..97d772dec
--- /dev/null
+++ b/src/systemd/aws-gwlbtun.service
@@ -0,0 +1,11 @@
+[Unit]
+Description=Description=AWS Gateway Load Balancer Tunnel Handler
+Documentation=https://github.com/aws-samples/aws-gateway-load-balancer-tunnel-handler
+After=network.target
+
+[Service]
+ExecStart=
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/systemd/vyos-router.service b/src/systemd/vyos-router.service
index 6f683cebb..7a1638f11 100644
--- a/src/systemd/vyos-router.service
+++ b/src/systemd/vyos-router.service
@@ -1,7 +1,6 @@
[Unit]
Description=VyOS Router
After=systemd-journald-dev-log.socket time-sync.target local-fs.target cloud-config.service
-Requires=frr.service
Conflicts=shutdown.target
Before=systemd-user-sessions.service
diff --git a/src/tests/test_dependency_graph.py b/src/tests/test_dependency_graph.py
new file mode 100644
index 000000000..f682e87bb
--- /dev/null
+++ b/src/tests/test_dependency_graph.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+from vyos.configdep import check_dependency_graph
+
+_here = os.path.dirname(__file__)
+ddir = os.path.join(_here, '../../data/config-mode-dependencies')
+
+from unittest import TestCase
+
+class TestDependencyGraph(TestCase):
+ def setUp(self):
+ pass
+
+ def test_acyclic(self):
+ res = check_dependency_graph(dependency_dir=ddir)
+ self.assertTrue(res)
diff --git a/src/validators/ddclient-protocol b/src/validators/ddclient-protocol
index 6f927927b..bc6826120 100755
--- a/src/validators/ddclient-protocol
+++ b/src/validators/ddclient-protocol
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-ddclient -list-protocols | grep -qw $1
+ddclient -list-protocols | grep -vE 'nsupdate|cloudns' | grep -qw $1
if [ $? -gt 0 ]; then
echo "Error: $1 is not a valid protocol, please choose from the supported list of protocols"
diff --git a/src/validators/numeric-exclude b/src/validators/numeric-exclude
new file mode 100644
index 000000000..676a240b6
--- /dev/null
+++ b/src/validators/numeric-exclude
@@ -0,0 +1,8 @@
+#!/bin/sh
+path=$(dirname "$0")
+num="${@: -1}"
+if [ "${num:0:1}" != "!" ]; then
+ ${path}/numeric $@
+else
+ ${path}/numeric ${@:1:$#-1} ${num:1}
+fi