summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/frr/bgpd.frr.j28
-rw-r--r--data/templates/ssh/override.conf.j214
-rw-r--r--debian/vyos-1x.postinst7
-rw-r--r--interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i2
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i6
-rw-r--r--interface-definitions/include/constraint/vrf.xml.i6
-rw-r--r--interface-definitions/include/interface/vrf.xml.i1
-rw-r--r--interface-definitions/include/vrf-multi.xml.i22
-rw-r--r--interface-definitions/service_ssh.xml.in2
-rw-r--r--interface-definitions/vrf.xml.in5
-rw-r--r--python/vyos/configverify.py24
-rw-r--r--python/vyos/firewall.py6
-rw-r--r--python/vyos/ifconfig/interface.py2
-rw-r--r--python/vyos/template.py3
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py15
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py38
-rwxr-xr-xsrc/conf_mode/container.py3
-rwxr-xr-xsrc/conf_mode/firewall.py9
-rwxr-xr-xsrc/conf_mode/nat.py8
-rwxr-xr-xsrc/conf_mode/nat66.py6
-rwxr-xr-xsrc/conf_mode/policy_route.py4
-rwxr-xr-xsrc/conf_mode/protocols_nhrp.py4
-rwxr-xr-xsrc/conf_mode/qos.py5
-rwxr-xr-xsrc/conf_mode/service_ssh.py16
-rwxr-xr-xsrc/conf_mode/system_conntrack.py7
-rwxr-xr-xsrc/conf_mode/vrf.py11
-rw-r--r--src/etc/systemd/system/ssh@.service.d/vrf-override.conf13
-rwxr-xr-xsrc/helpers/vyos-domain-resolver.py7
-rwxr-xr-xsrc/init/vyos-router2
-rw-r--r--src/tests/test_template.py6
30 files changed, 165 insertions, 97 deletions
diff --git a/data/templates/frr/bgpd.frr.j2 b/data/templates/frr/bgpd.frr.j2
index e9422b257..e5bfad59d 100644
--- a/data/templates/frr/bgpd.frr.j2
+++ b/data/templates/frr/bgpd.frr.j2
@@ -290,10 +290,7 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% endif %}
{% if afi_config.aggregate_address is vyos_defined %}
{% for aggregate, aggregate_config in afi_config.aggregate_address.items() %}
- aggregate-address {{ aggregate }}{{ ' as-set' if aggregate_config.as_set is vyos_defined }}{{ ' summary-only' if aggregate_config.summary_only is vyos_defined }}
-{% if aggregate_config.route_map is vyos_defined %}
- aggregate-address {{ aggregate }} route-map {{ aggregate_config.route_map }}
-{% endif %}
+ aggregate-address {{ aggregate }} {{ 'as-set' if aggregate_config.as_set is vyos_defined }} {{ 'summary-only' if aggregate_config.summary_only is vyos_defined }} {{ 'route-map ' ~ aggregate_config.route_map if aggregate_config.route_map is vyos_defined }}
{% endfor %}
{% endif %}
{% if afi_config.maximum_paths.ebgp is vyos_defined %}
@@ -537,6 +534,9 @@ router bgp {{ system_as }} {{ 'vrf ' ~ vrf if vrf is vyos_defined }}
{% if parameters.allow_martian_nexthop is vyos_defined %}
bgp allow-martian-nexthop
{% endif %}
+{% if parameters.disable_ebgp_connected_route_check is vyos_defined %}
+ bgp disable-ebgp-connected-route-check
+{% endif %}
{% if parameters.always_compare_med is vyos_defined %}
bgp always-compare-med
{% endif %}
diff --git a/data/templates/ssh/override.conf.j2 b/data/templates/ssh/override.conf.j2
deleted file mode 100644
index 4454ad1b8..000000000
--- a/data/templates/ssh/override.conf.j2
+++ /dev/null
@@ -1,14 +0,0 @@
-{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %}
-[Unit]
-StartLimitIntervalSec=0
-After=vyos-router.service
-ConditionPathExists={{ config_file }}
-
-[Service]
-EnvironmentFile=
-ExecStart=
-ExecStart={{ vrf_command }}/usr/sbin/sshd -f {{ config_file }}
-Restart=always
-RestartPreventExitStatus=
-RestartSec=10
-RuntimeDirectoryPreserve=yes
diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst
index 0e6e3c863..78e895d6e 100644
--- a/debian/vyos-1x.postinst
+++ b/debian/vyos-1x.postinst
@@ -194,3 +194,10 @@ systemctl enable vyos-config-cloud-init.service
# Update XML cache
python3 /usr/lib/python3/dist-packages/vyos/xml_ref/update_cache.py
+
+# Generate hardlinks for systemd units for multi VRF support
+# as softlinks will fail in systemd:
+# symlink target name type "ssh.service" does not match source, rejecting.
+if [ ! -f /lib/systemd/system/ssh@.service ]; then
+ ln /lib/systemd/system/ssh.service /lib/systemd/system/ssh@.service
+fi
diff --git a/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i b/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i
index cb8b610b4..aef5a55e9 100644
--- a/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i
+++ b/interface-definitions/include/bgp/neighbor-disable-connected-check.xml.i
@@ -1,7 +1,7 @@
<!-- include start from bgp/neighbor-disable-connected-check.xml.i -->
<leafNode name="disable-connected-check">
<properties>
- <help>Disable check to see if eBGP peer address is a connected route</help>
+ <help>Allow peerings between eBGP peer using loopback/dummy address</help>
<valueless/>
</properties>
</leafNode>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index ca67eaf3c..0f05625a7 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1249,6 +1249,12 @@
<valueless/>
</properties>
</leafNode>
+ <leafNode name="disable-ebgp-connected-route-check">
+ <properties>
+ <help>Disable checking if nexthop is connected on eBGP session</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="always-compare-med">
<properties>
<help>Always compare MEDs from different neighbors</help>
diff --git a/interface-definitions/include/constraint/vrf.xml.i b/interface-definitions/include/constraint/vrf.xml.i
new file mode 100644
index 000000000..a1922bb6d
--- /dev/null
+++ b/interface-definitions/include/constraint/vrf.xml.i
@@ -0,0 +1,6 @@
+<!-- include start from constraint/vrf.xml.i -->
+<constraint>
+ <validator name="vrf-name"/>
+</constraint>
+<constraintErrorMessage>VRF instance name must be 15 characters or less and can not\nbe named as regular network interfaces.\nA name must starts from a letter.\n</constraintErrorMessage>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/vrf.xml.i b/interface-definitions/include/interface/vrf.xml.i
index 8605f56e8..ef0058f86 100644
--- a/interface-definitions/include/interface/vrf.xml.i
+++ b/interface-definitions/include/interface/vrf.xml.i
@@ -9,6 +9,7 @@
<completionHelp>
<path>vrf name</path>
</completionHelp>
+ #include <include/constraint/vrf.xml.i>
</properties>
</leafNode>
<!-- include end -->
diff --git a/interface-definitions/include/vrf-multi.xml.i b/interface-definitions/include/vrf-multi.xml.i
new file mode 100644
index 000000000..0b22894e4
--- /dev/null
+++ b/interface-definitions/include/vrf-multi.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from interface/vrf.xml.i -->
+<leafNode name="vrf">
+ <properties>
+ <help>VRF instance name</help>
+ <completionHelp>
+ <path>vrf name</path>
+ <list>default</list>
+ </completionHelp>
+ <valueHelp>
+ <format>default</format>
+ <description>Explicitly start in default VRF</description>
+ </valueHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>VRF instance name</description>
+ </valueHelp>
+ #include <include/constraint/vrf.xml.i>
+ <multi/>
+ </properties>
+ <defaultValue>default</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/service_ssh.xml.in b/interface-definitions/service_ssh.xml.in
index 5c893bd35..d9eee1ab8 100644
--- a/interface-definitions/service_ssh.xml.in
+++ b/interface-definitions/service_ssh.xml.in
@@ -262,7 +262,7 @@
</constraint>
</properties>
</leafNode>
- #include <include/interface/vrf.xml.i>
+ #include <include/vrf-multi.xml.i>
</children>
</node>
</children>
diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in
index 25f26d0cc..94ed96e4b 100644
--- a/interface-definitions/vrf.xml.in
+++ b/interface-definitions/vrf.xml.in
@@ -16,10 +16,7 @@
<tagNode name="name">
<properties>
<help>Virtual Routing and Forwarding instance</help>
- <constraint>
- <validator name="vrf-name"/>
- </constraint>
- <constraintErrorMessage>VRF instance name must be 15 characters or less and can not\nbe named as regular network interfaces.\nA name must starts from a letter.\n</constraintErrorMessage>
+ #include <include/constraint/vrf.xml.i>
<valueHelp>
<format>txt</format>
<description>VRF instance name</description>
diff --git a/python/vyos/configverify.py b/python/vyos/configverify.py
index 2a5452e7b..300647d21 100644
--- a/python/vyos/configverify.py
+++ b/python/vyos/configverify.py
@@ -99,10 +99,17 @@ def verify_vrf(config):
Common helper function used by interface implementations to perform
recurring validation of VRF configuration.
"""
- from netifaces import interfaces
- if 'vrf' in config and config['vrf'] != 'default':
- if config['vrf'] not in interfaces():
- raise ConfigError('VRF "{vrf}" does not exist'.format(**config))
+ from vyos.utils.network import interface_exists
+ if 'vrf' in config:
+ vrfs = config['vrf']
+ if isinstance(vrfs, str):
+ vrfs = [vrfs]
+
+ for vrf in vrfs:
+ if vrf == 'default':
+ continue
+ if not interface_exists(vrf):
+ raise ConfigError(f'VRF "{vrf}" does not exist!')
if 'is_bridge_member' in config:
raise ConfigError(
@@ -169,13 +176,13 @@ def verify_mirror_redirect(config):
It makes no sense to mirror traffic back at yourself!
"""
- import os
+ from vyos.utils.network import interface_exists
if {'mirror', 'redirect'} <= set(config):
raise ConfigError('Mirror and redirect can not be enabled at the same time!')
if 'mirror' in config:
for direction, mirror_interface in config['mirror'].items():
- if not os.path.exists(f'/sys/class/net/{mirror_interface}'):
+ if not interface_exists(mirror_interface):
raise ConfigError(f'Requested mirror interface "{mirror_interface}" '\
'does not exist!')
@@ -185,7 +192,7 @@ def verify_mirror_redirect(config):
if 'redirect' in config:
redirect_ifname = config['redirect']
- if not os.path.exists(f'/sys/class/net/{redirect_ifname}'):
+ if not interface_exists(redirect_ifname):
raise ConfigError(f'Requested redirect interface "{redirect_ifname}" '\
'does not exist!')
@@ -243,6 +250,7 @@ def verify_interface_exists(ifname, warning_only=False):
from vyos.base import Warning
from vyos.configquery import ConfigTreeQuery
from vyos.utils.dict import dict_search_recursive
+ from vyos.utils.network import interface_exists
# Check if interface is present in CLI config
config = ConfigTreeQuery()
@@ -251,7 +259,7 @@ def verify_interface_exists(ifname, warning_only=False):
return True
# Interface not found on CLI, try Linux Kernel
- if os.path.exists(f'/sys/class/net/{ifname}'):
+ if interface_exists(ifname):
return True
message = f'Interface "{ifname}" does not exist!'
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index e70b4f0d9..e29aeb0c6 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -66,7 +66,7 @@ def fqdn_config_parse(firewall):
rule = path[4]
suffix = path[5][0]
set_name = f'{hook_name}_{priority}_{rule}_{suffix}'
-
+
if (path[0] == 'ipv4') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'):
firewall['ip_fqdn'][set_name] = domain
elif (path[0] == 'ipv6') and (path[1] == 'forward' or path[1] == 'input' or path[1] == 'output' or path[1] == 'name'):
@@ -85,7 +85,7 @@ def fqdn_resolve(fqdn, ipv6=False):
def find_nftables_rule(table, chain, rule_matches=[]):
# Find rule in table/chain that matches all criteria and return the handle
- results = cmd(f'sudo nft -a list chain {table} {chain}').split("\n")
+ results = cmd(f'sudo nft --handle list chain {table} {chain}').split("\n")
for line in results:
if all(rule_match in line for rule_match in rule_matches):
handle_search = re.search('handle (\d+)', line)
@@ -655,7 +655,7 @@ def geoip_update(firewall, force=False):
'ipv6_sets': ipv6_sets
})
- result = run(f'nft -f {nftables_geoip_conf}')
+ result = run(f'nft --file {nftables_geoip_conf}')
if result != 0:
print('Error: GeoIP failed to update firewall')
return False
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index c87fb9c71..b2cb621bc 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -415,7 +415,7 @@ class Interface(Control):
else:
nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{self.ifname}" }}'
# Check if deleting is possible first to avoid raising errors
- _, err = self._popen(f'nft -c {nft_del_element}')
+ _, err = self._popen(f'nft --check {nft_del_element}')
if not err:
# Remove map element
self._cmd(f'nft {nft_del_element}')
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 1aa9ace8b..3e468eb82 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -307,7 +307,8 @@ def network_from_ipv4(address):
@register_filter('is_interface')
def is_interface(interface):
""" Check if parameter is a valid local interface name """
- return os.path.exists(f'/sys/class/net/{interface}')
+ from vyos.utils.network import interface_exists
+ return interface_exists(interface)
@register_filter('is_ip')
def is_ip(addr):
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index e8556cf44..60c49b8b4 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -321,6 +321,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
tcp_keepalive_probes = '22'
self.cli_set(base_path + ['parameters', 'allow-martian-nexthop'])
+ self.cli_set(base_path + ['parameters', 'disable-ebgp-connected-route-check'])
self.cli_set(base_path + ['parameters', 'no-hard-administrative-reset'])
self.cli_set(base_path + ['parameters', 'log-neighbor-changes'])
self.cli_set(base_path + ['parameters', 'labeled-unicast', 'explicit-null'])
@@ -372,6 +373,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' bgp router-id {router_id}', frrconfig)
self.assertIn(f' bgp allow-martian-nexthop', frrconfig)
+ self.assertIn(f' bgp disable-ebgp-connected-route-check', frrconfig)
self.assertIn(f' bgp log-neighbor-changes', frrconfig)
self.assertIn(f' bgp default local-preference {local_pref}', frrconfig)
self.assertIn(f' bgp conditional-advertisement timer {cond_adv_timer}', frrconfig)
@@ -628,6 +630,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
networks = {
'10.0.0.0/8' : {
'as_set' : '',
+ 'summary_only' : '',
+ 'route_map' : route_map_in,
},
'100.64.0.0/10' : {
'as_set' : '',
@@ -652,6 +656,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'summary_only' in network_config:
self.cli_set(base_path + ['address-family', 'ipv4-unicast',
'aggregate-address', network, 'summary-only'])
+ if 'route_map' in network_config:
+ self.cli_set(base_path + ['address-family', 'ipv4-unicast',
+ 'aggregate-address', network, 'route-map', network_config['route_map']])
# commit changes
self.cli_commit()
@@ -666,10 +673,14 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
for network, network_config in networks.items():
self.assertIn(f' network {network}', frrconfig)
+ command = f'aggregate-address {network}'
if 'as_set' in network_config:
- self.assertIn(f' aggregate-address {network} as-set', frrconfig)
+ command = f'{command} as-set'
if 'summary_only' in network_config:
- self.assertIn(f' aggregate-address {network} summary-only', frrconfig)
+ command = f'{command} summary-only'
+ if 'route_map' in network_config:
+ command = f'{command} route-map {network_config["route_map"]}'
+ self.assertIn(command, frrconfig)
def test_bgp_05_afi_ipv6(self):
networks = {
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index 947d7d568..031897c26 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -32,7 +32,6 @@ from vyos.utils.file import read_file
PROCESS_NAME = 'sshd'
SSHD_CONF = '/run/sshd/sshd_config'
base_path = ['service', 'ssh']
-vrf = 'mgmt'
key_rsa = '/etc/ssh/ssh_host_rsa_key'
key_dsa = '/etc/ssh/ssh_host_dsa_key'
@@ -51,6 +50,7 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
+ cls.cli_delete(cls, ['vrf'])
def tearDown(self):
# Check for running process
@@ -58,6 +58,7 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# delete testing SSH config
self.cli_delete(base_path)
+ self.cli_delete(['vrf'])
self.cli_commit()
self.assertTrue(os.path.isfile(key_rsa))
@@ -79,7 +80,7 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# Check configured port
port = get_config_value('Port')[0]
- self.assertEqual('22', port)
+ self.assertEqual('22', port) # default value
def test_ssh_single_listen_address(self):
# Check if SSH service can be configured and runs
@@ -141,10 +142,9 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
for address in addresses:
self.assertIn(address, tmp)
- def test_ssh_vrf(self):
+ def test_ssh_vrf_single(self):
+ vrf = 'mgmt'
# Check if SSH service can be bound to given VRF
- port = '22'
- self.cli_set(base_path + ['port', port])
self.cli_set(base_path + ['vrf', vrf])
# VRF does yet not exist - an error must be thrown
@@ -156,16 +156,32 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- # Check configured port
- tmp = get_config_value('Port')
- self.assertIn(port, tmp)
-
# Check for process in VRF
tmp = cmd(f'ip vrf pids {vrf}')
self.assertIn(PROCESS_NAME, tmp)
- # delete VRF
- self.cli_delete(['vrf', 'name', vrf])
+ def test_ssh_vrf_multi(self):
+ # Check if SSH service can be bound to multiple VRFs
+ vrfs = ['red', 'blue', 'green']
+ for vrf in vrfs:
+ self.cli_set(base_path + ['vrf', vrf])
+
+ # VRF does yet not exist - an error must be thrown
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ table = 12345
+ for vrf in vrfs:
+ self.cli_set(['vrf', 'name', vrf, 'table', str(table)])
+ table += 1
+
+ # commit changes
+ self.cli_commit()
+
+ # Check for process in VRF
+ for vrf in vrfs:
+ tmp = cmd(f'ip vrf pids {vrf}')
+ self.assertIn(PROCESS_NAME, tmp)
def test_ssh_login(self):
# Perform SSH login and command execution with a predefined user. The
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index e967bee71..910a92a7c 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -32,6 +32,7 @@ from vyos.utils.file import write_file
from vyos.utils.process import call
from vyos.utils.process import cmd
from vyos.utils.process import run
+from vyos.utils.network import interface_exists
from vyos.template import bracketize_ipv6
from vyos.template import inc_ip
from vyos.template import is_ipv4
@@ -471,7 +472,7 @@ def apply(container):
# T5147: Networks are started only as soon as there is a consumer.
# If only a network is created in the first place, no need to assign
# it to a VRF as there's no consumer, yet.
- if os.path.exists(f'/sys/class/net/{network_name}'):
+ if interface_exists(network_name):
tmp = Interface(network_name)
tmp.add_ipv6_eui64_address('fe80::/64')
tmp.set_vrf(network_config.get('vrf', ''))
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 810437dda..3cf618363 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,7 +18,6 @@ import os
import re
from glob import glob
-from json import loads
from sys import exit
from vyos.base import Warning
@@ -31,11 +30,9 @@ from vyos.ethtool import Ethtool
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
from vyos.template import render
-from vyos.utils.process import call
-from vyos.utils.process import cmd
from vyos.utils.dict import dict_search_args
from vyos.utils.dict import dict_search_recursive
-from vyos.utils.process import process_named_running
+from vyos.utils.process import call
from vyos.utils.process import rc_cmd
from vyos import ConfigError
from vyos import airbag
@@ -491,7 +488,7 @@ def apply_sysfs(firewall):
f.write(value)
def apply(firewall):
- install_result, output = rc_cmd(f'nft -f {nftables_conf}')
+ install_result, output = rc_cmd(f'nft --file {nftables_conf}')
if install_result == 1:
raise ConfigError(f'Failed to apply firewall: {output}')
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index b3f38c04a..76c07a9ec 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -223,19 +223,19 @@ def generate(nat):
render(nftables_static_nat_conf, 'firewall/nftables-static-nat.j2', nat)
# dry-run newly generated configuration
- tmp = run(f'nft -c -f {nftables_nat_config}')
+ tmp = run(f'nft --check --file {nftables_nat_config}')
if tmp > 0:
raise ConfigError('Configuration file errors encountered!')
- tmp = run(f'nft -c -f {nftables_static_nat_conf}')
+ tmp = run(f'nft --check --file {nftables_static_nat_conf}')
if tmp > 0:
raise ConfigError('Configuration file errors encountered!')
return None
def apply(nat):
- cmd(f'nft -f {nftables_nat_config}')
- cmd(f'nft -f {nftables_static_nat_conf}')
+ cmd(f'nft --file {nftables_nat_config}')
+ cmd(f'nft --file {nftables_static_nat_conf}')
if not nat or 'deleted' in nat:
os.unlink(nftables_nat_config)
diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py
index 4c1ead258..fe017527d 100755
--- a/src/conf_mode/nat66.py
+++ b/src/conf_mode/nat66.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -14,8 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import jmespath
-import json
import os
from sys import exit
@@ -106,7 +104,7 @@ def apply(nat):
if not nat:
return None
- cmd(f'nft -f {nftables_nat66_config}')
+ cmd(f'nft --file {nftables_nat66_config}')
call_dependents()
return None
diff --git a/src/conf_mode/policy_route.py b/src/conf_mode/policy_route.py
index 6d7a06714..c58fe1bce 100755
--- a/src/conf_mode/policy_route.py
+++ b/src/conf_mode/policy_route.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -177,7 +177,7 @@ def cleanup_table_marks():
cmd(f'{cmd_str} rule del fwmark {fwmark} table {table}')
def apply(policy):
- install_result = run(f'nft -f {nftables_conf}')
+ install_result = run(f'nft --file {nftables_conf}')
if install_result == 1:
raise ConfigError('Failed to apply policy based routing')
diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py
index c339c6391..9f66407f2 100755
--- a/src/conf_mode/protocols_nhrp.py
+++ b/src/conf_mode/protocols_nhrp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 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
@@ -93,7 +93,7 @@ def generate(nhrp):
return None
def apply(nhrp):
- nft_rc = run(f'nft -f {nhrp_nftables_conf}')
+ nft_rc = run(f'nft --file {nhrp_nftables_conf}')
if nft_rc != 0:
raise ConfigError('Failed to apply NHRP tunnel firewall rules')
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
index 4a0b4d0c5..2b4fcc1bf 100755
--- a/src/conf_mode/qos.py
+++ b/src/conf_mode/qos.py
@@ -36,8 +36,9 @@ from vyos.qos import RateLimiter
from vyos.qos import RoundRobin
from vyos.qos import TrafficShaper
from vyos.qos import TrafficShaperHFSC
-from vyos.utils.process import run
from vyos.utils.dict import dict_search_recursive
+from vyos.utils.network import interface_exists
+from vyos.utils.process import run
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -214,7 +215,7 @@ def apply(qos):
return None
for interface, interface_config in qos['interface'].items():
- if not os.path.exists(f'/sys/class/net/{interface}'):
+ if not interface_exists(interface):
# When shaper is bound to a dialup (e.g. PPPoE) interface it is
# possible that it is yet not availbale when to QoS code runs.
# Skip the configuration and inform the user
diff --git a/src/conf_mode/service_ssh.py b/src/conf_mode/service_ssh.py
index ee5e1eca2..9abdd33dc 100755
--- a/src/conf_mode/service_ssh.py
+++ b/src/conf_mode/service_ssh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 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
@@ -30,7 +30,6 @@ from vyos import airbag
airbag.enable()
config_file = r'/run/sshd/sshd_config'
-systemd_override = r'/run/systemd/system/ssh.service.d/override.conf'
sshguard_config_file = '/etc/sshguard/sshguard.conf'
sshguard_whitelist = '/etc/sshguard/whitelist'
@@ -81,8 +80,6 @@ def generate(ssh):
if not ssh:
if os.path.isfile(config_file):
os.unlink(config_file)
- if os.path.isfile(systemd_override):
- os.unlink(systemd_override)
return None
@@ -99,13 +96,10 @@ def generate(ssh):
call(f'ssh-keygen -q -N "" -t ed25519 -f {key_ed25519}')
render(config_file, 'ssh/sshd_config.j2', ssh)
- render(systemd_override, 'ssh/override.conf.j2', ssh)
if 'dynamic_protection' in ssh:
render(sshguard_config_file, 'ssh/sshguard_config.j2', ssh)
render(sshguard_whitelist, 'ssh/sshguard_whitelist.j2', ssh)
- # Reload systemd manager configuration
- call('systemctl daemon-reload')
return None
@@ -114,7 +108,7 @@ def apply(ssh):
systemd_service_sshguard = 'sshguard.service'
if not ssh:
# SSH access is removed in the commit
- call(f'systemctl stop {systemd_service_ssh}')
+ call(f'systemctl stop ssh@*.service')
call(f'systemctl stop {systemd_service_sshguard}')
return None
@@ -126,9 +120,13 @@ def apply(ssh):
# we need to restart the service if e.g. the VRF name changed
systemd_action = 'reload-or-restart'
if 'restart_required' in ssh:
+ # this is only true if something for the VRFs changed, thus we
+ # stop all VRF services and only restart then new ones
+ call(f'systemctl stop ssh@*.service')
systemd_action = 'restart'
- call(f'systemctl {systemd_action} {systemd_service_ssh}')
+ for vrf in ssh['vrf']:
+ call(f'systemctl {systemd_action} ssh@{vrf}.service')
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py
index 3d42389f6..031fe63b0 100755
--- a/src/conf_mode/system_conntrack.py
+++ b/src/conf_mode/system_conntrack.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 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,19 +15,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-import re
from sys import exit
from vyos.config import Config
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
from vyos import airbag
@@ -223,7 +220,7 @@ def apply(conntrack):
cmd(f'modprobe -a {module_str}')
# Load new nftables ruleset
- install_result, output = rc_cmd(f'nft -f {nftables_ct_file}')
+ install_result, output = rc_cmd(f'nft --file {nftables_ct_file}')
if install_result == 1:
raise ConfigError(f'Failed to apply configuration: {output}')
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 16908100f..1fc813189 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -14,8 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
-
from sys import exit
from json import loads
@@ -33,6 +31,7 @@ from vyos.utils.network import get_vrf_members
from vyos.utils.network import interface_exists
from vyos.utils.process import call
from vyos.utils.process import cmd
+from vyos.utils.process import popen
from vyos.utils.system import sysctl_write
from vyos import ConfigError
from vyos import frr
@@ -227,7 +226,11 @@ def apply(vrf):
# Remove nftables conntrack zone map item
nft_del_element = f'delete element inet vrf_zones ct_iface_map {{ "{tmp}" }}'
- cmd(f'nft {nft_del_element}')
+ # Check if deleting is possible first to avoid raising errors
+ _, err = popen(f'nft --check {nft_del_element}')
+ if not err:
+ # Remove map element
+ cmd(f'nft {nft_del_element}')
# Delete the VRF Kernel interface
call(f'ip link delete dev {tmp}')
@@ -307,7 +310,7 @@ def apply(vrf):
if vrf['conntrack']:
for chain, rule in nftables_rules.items():
cmd(f'nft add rule inet vrf_zones {chain} {rule}')
-
+
if 'name' not in vrf or not vrf['conntrack']:
for chain, rule in nftables_rules.items():
cmd(f'nft flush chain inet vrf_zones {chain}')
diff --git a/src/etc/systemd/system/ssh@.service.d/vrf-override.conf b/src/etc/systemd/system/ssh@.service.d/vrf-override.conf
new file mode 100644
index 000000000..b8952d86c
--- /dev/null
+++ b/src/etc/systemd/system/ssh@.service.d/vrf-override.conf
@@ -0,0 +1,13 @@
+[Unit]
+StartLimitIntervalSec=0
+After=vyos-router.service
+ConditionPathExists=/run/sshd/sshd_config
+
+[Service]
+EnvironmentFile=
+ExecStart=
+ExecStart=ip vrf exec %i /usr/sbin/sshd -f /run/sshd/sshd_config
+Restart=always
+RestartPreventExitStatus=
+RestartSec=10
+RuntimeDirectoryPreserve=yes
diff --git a/src/helpers/vyos-domain-resolver.py b/src/helpers/vyos-domain-resolver.py
index eac3d37af..57cfcabd7 100755
--- a/src/helpers/vyos-domain-resolver.py
+++ b/src/helpers/vyos-domain-resolver.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-2024 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 json
-import os
import time
from vyos.configdict import dict_merge
@@ -95,7 +94,7 @@ def nft_output(table, set_name, ip_list):
def nft_valid_sets():
try:
valid_sets = []
- sets_json = cmd('nft -j list sets')
+ sets_json = cmd('nft --json list sets')
sets_obj = json.loads(sets_json)
for obj in sets_obj['nftables']:
@@ -155,7 +154,7 @@ def update(firewall):
count += 1
nft_conf_str = "\n".join(conf_lines) + "\n"
- code = run(f'nft -f -', input=nft_conf_str)
+ code = run(f'nft --file -', input=nft_conf_str)
print(f'Updated {count} sets - result: {code}')
diff --git a/src/init/vyos-router b/src/init/vyos-router
index adf892371..06fea140d 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -430,7 +430,7 @@ start ()
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"
+ nft --file /usr/share/vyos/vyos-firewall-init.conf || log_failure_msg "could not initiate firewall rules"
# 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
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
index aba97015e..dbb86b40b 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -14,9 +14,9 @@
# 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 vyos.template
+from vyos.utils.network import interface_exists
from ipaddress import ip_network
from unittest import TestCase
@@ -26,7 +26,7 @@ class TestVyOSTemplate(TestCase):
def test_is_interface(self):
for interface in ['lo', 'eth0']:
- if os.path.exists(f'/sys/class/net/{interface}'):
+ if interface_exists(interface):
self.assertTrue(vyos.template.is_interface(interface))
else:
self.assertFalse(vyos.template.is_interface(interface))