summaryrefslogtreecommitdiff
path: root/smoketest/scripts/cli
diff options
context:
space:
mode:
Diffstat (limited to 'smoketest/scripts/cli')
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py379
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py35
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py5
-rwxr-xr-xsmoketest/scripts/cli/test_component_version.py50
-rwxr-xr-xsmoketest/scripts/cli/test_container.py112
-rwxr-xr-xsmoketest/scripts/cli/test_dependency_graph.py54
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py271
-rwxr-xr-xsmoketest/scripts/cli/test_high-availability_virtual-server.py (renamed from smoketest/scripts/cli/test_ha_virtual_server.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_high-availability_vrrp.py (renamed from smoketest/scripts/cli/test_ha_vrrp.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py40
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py35
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py76
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pppoe.py60
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pseudo-ethernet.py (renamed from smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py16
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_virtual-ethernet.py (renamed from smoketest/scripts/cli/test_interfaces_virtual_ethernet.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py186
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireguard.py48
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_reverse-proxy.py (renamed from smoketest/scripts/cli/test_load_balancing_reverse_proxy.py)2
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_wan.py (renamed from smoketest/scripts/cli/test_load_balancing_wan.py)7
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py53
-rwxr-xr-xsmoketest/scripts/cli/test_nat64.py102
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py22
-rwxr-xr-xsmoketest/scripts/cli/test_netns.py (renamed from smoketest/scripts/cli/test_interfaces_netns.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_pki.py78
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py135
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py33
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py18
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py168
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_igmp-proxy.py29
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py81
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_mpls.py10
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py10
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py10
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_pim.py192
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_pim6.py146
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rip.py9
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ripng.py51
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rpki.py16
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_segment-routing.py112
-rwxr-xr-xsmoketest/scripts/cli/test_qos.py57
-rwxr-xr-xsmoketest/scripts/cli/test_service_broadcast-relay.py (renamed from smoketest/scripts/cli/test_service_bcast-relay.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_dynamic.py284
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_forwarding.py42
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py309
-rwxr-xr-xsmoketest/scripts/cli/test_service_ids_ddos-protection.py (renamed from smoketest/scripts/cli/test_service_ids.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_service_ipoe-server.py211
-rwxr-xr-xsmoketest/scripts/cli/test_service_lldp.py16
-rwxr-xr-xsmoketest/scripts/cli/test_service_mdns-repeater.py74
-rwxr-xr-xsmoketest/scripts/cli/test_service_mdns_repeater.py134
-rwxr-xr-xsmoketest/scripts/cli/test_service_ndp-proxy.py70
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py73
-rwxr-xr-xsmoketest/scripts/cli/test_service_salt-minion.py (renamed from smoketest/scripts/cli/test_service_salt.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_system_frr.py26
-rwxr-xr-xsmoketest/scripts/cli/test_system_nameserver.py63
-rwxr-xr-xsmoketest/scripts/cli/test_system_resolvconf.py112
-rwxr-xr-xsmoketest/scripts/cli/test_system_syslog.py85
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py488
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_l2tp.py135
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py21
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_pptp.py223
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_sstp.py13
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py23
63 files changed, 4093 insertions, 1017 deletions
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index 989028f64..1ea5db898 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -11,10 +11,10 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
import re
import unittest
+
from base_vyostest_shim import VyOSUnitTestSHIM
from configparser import ConfigParser
@@ -25,12 +25,12 @@ from vyos.utils.system import get_half_cpus
from vyos.utils.process import process_named_running
from vyos.utils.process import cmd
+
class BasicAccelPPPTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
-
@classmethod
def setUpClass(cls):
- cls._process_name = 'accel-pppd'
+ cls._process_name = "accel-pppd"
super(BasicAccelPPPTest.TestCase, cls).setUpClass()
@@ -39,7 +39,7 @@ class BasicAccelPPPTest:
cls.cli_delete(cls, cls._base_path)
def setUp(self):
- self._gateway = '192.0.2.1'
+ self._gateway = "192.0.2.1"
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
self.cli_delete(self._base_path)
@@ -60,84 +60,188 @@ class BasicAccelPPPTest:
def delete(self, path):
self.cli_delete(self._base_path + path)
- def basic_config(self):
- # PPPoE local auth mode requires local users to be configured!
- self.set(['authentication', 'local-users', 'username', 'vyos', 'password', 'vyos'])
- self.set(['authentication', 'mode', 'local'])
- self.set(['gateway-address', self._gateway])
+ def basic_protocol_specific_config(self):
+ """
+ An astract method.
+ Initialize protocol scpecific configureations.
+ """
+ self.assertFalse(True, msg="Function must be defined")
+
+ def initial_auth_config(self):
+ """
+ Initialization of default authentication for all protocols
+ """
+ self.set(
+ [
+ "authentication",
+ "local-users",
+ "username",
+ "vyos",
+ "password",
+ "vyos",
+ ]
+ )
+ self.set(["authentication", "mode", "local"])
+
+ def initial_gateway_config(self):
+ """
+ Initialization of default gateway
+ """
+ self.set(["gateway-address", self._gateway])
+
+ def initial_pool_config(self):
+ """
+ Initialization of default client ip pool
+ """
+ first_pool = "SIMPLE-POOL"
+ self.set(["client-ip-pool", first_pool, "range", "192.0.2.0/24"])
+ self.set(["default-pool", first_pool])
+
+ def basic_config(self, is_auth=True, is_gateway=True, is_client_pool=True):
+ """
+ Initialization of basic configuration
+ :param is_auth: authentication initialization
+ :type is_auth: bool
+ :param is_gateway: gateway initialization
+ :type is_gateway: bool
+ :param is_client_pool: client ip pool initialization
+ :type is_client_pool: bool
+ """
+ self.basic_protocol_specific_config()
+ if is_auth:
+ self.initial_auth_config()
+ if is_gateway:
+ self.initial_gateway_config()
+ if is_client_pool:
+ self.initial_pool_config()
+
+ def getConfig(self, start, end="cli"):
+ """
+ Return part of configuration from line
+ where the first injection of start keyword to the line
+ where the first injection of end keyowrd
+ :param start: start keyword
+ :type start: str
+ :param end: end keyword
+ :type end: str
+ :return: part of config
+ :rtype: str
+ """
+ command = f'cat {self._config_file} | sed -n "/^\[{start}/,/^\[{end}/p"'
+ out = cmd(command)
+ return out
def verify(self, conf):
- self.assertEqual(conf['core']['thread-count'], str(get_half_cpus()))
+ self.assertEqual(conf["core"]["thread-count"], str(get_half_cpus()))
def test_accel_name_servers(self):
# Verify proper Name-Server configuration for IPv4 and IPv6
self.basic_config()
- nameserver = ['192.0.2.1', '192.0.2.2', '2001:db8::1']
+ nameserver = ["192.0.2.1", "192.0.2.2", "2001:db8::1"]
for ns in nameserver:
- self.set(['name-server', ns])
+ self.set(["name-server", ns])
# commit changes
self.cli_commit()
# Validate configuration values
- conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
conf.read(self._config_file)
# IPv4 and IPv6 nameservers must be checked individually
for ns in nameserver:
if is_ipv4(ns):
- self.assertIn(ns, [conf['dns']['dns1'], conf['dns']['dns2']])
+ self.assertIn(ns, [conf["dns"]["dns1"], conf["dns"]["dns2"]])
else:
- self.assertEqual(conf['ipv6-dns'][ns], None)
+ self.assertEqual(conf["ipv6-dns"][ns], None)
def test_accel_local_authentication(self):
# Test configuration of local authentication
self.basic_config()
# upload / download limit
- user = 'test'
- password = 'test2'
- static_ip = '100.100.100.101'
- upload = '5000'
- download = '10000'
-
- self.set(['authentication', 'local-users', 'username', user, 'password', password])
- self.set(['authentication', 'local-users', 'username', user, 'static-ip', static_ip])
- self.set(['authentication', 'local-users', 'username', user, 'rate-limit', 'upload', upload])
+ user = "test"
+ password = "test2"
+ static_ip = "100.100.100.101"
+ upload = "5000"
+ download = "10000"
+ self.set(
+ [
+ "authentication",
+ "local-users",
+ "username",
+ user,
+ "password",
+ password,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "local-users",
+ "username",
+ user,
+ "static-ip",
+ static_ip,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "local-users",
+ "username",
+ user,
+ "rate-limit",
+ "upload",
+ upload,
+ ]
+ )
# upload rate-limit requires also download rate-limit
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.set(['authentication', 'local-users', 'username', user, 'rate-limit', 'download', download])
+ self.set(
+ [
+ "authentication",
+ "local-users",
+ "username",
+ user,
+ "rate-limit",
+ "download",
+ download,
+ ]
+ )
# commit changes
self.cli_commit()
# Validate configuration values
- conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
conf.read(self._config_file)
# check proper path to chap-secrets file
- self.assertEqual(conf['chap-secrets']['chap-secrets'], self._chap_secrets)
+ self.assertEqual(conf["chap-secrets"]["chap-secrets"], self._chap_secrets)
# basic verification
self.verify(conf)
# check local users
- tmp = cmd(f'sudo cat {self._chap_secrets}')
- regex = f'{user}\s+\*\s+{password}\s+{static_ip}\s+{download}/{upload}'
+ tmp = cmd(f"sudo cat {self._chap_secrets}")
+ regex = f"{user}\s+\*\s+{password}\s+{static_ip}\s+{download}/{upload}"
tmp = re.findall(regex, tmp)
self.assertTrue(tmp)
# Check local-users default value(s)
- self.delete(['authentication', 'local-users', 'username', user, 'static-ip'])
+ self.delete(
+ ["authentication", "local-users", "username", user, "static-ip"]
+ )
# commit changes
self.cli_commit()
# check local users
- tmp = cmd(f'sudo cat {self._chap_secrets}')
- regex = f'{user}\s+\*\s+{password}\s+\*\s+{download}/{upload}'
+ tmp = cmd(f"sudo cat {self._chap_secrets}")
+ regex = f"{user}\s+\*\s+{password}\s+\*\s+{download}/{upload}"
tmp = re.findall(regex, tmp)
self.assertTrue(tmp)
@@ -145,74 +249,195 @@ class BasicAccelPPPTest:
# Test configuration of RADIUS authentication for PPPoE server
self.basic_config()
- radius_server = '192.0.2.22'
- radius_key = 'secretVyOS'
- radius_port = '2000'
- radius_port_acc = '3000'
-
- self.set(['authentication', 'mode', 'radius'])
- self.set(['authentication', 'radius', 'server', radius_server, 'key', radius_key])
- self.set(['authentication', 'radius', 'server', radius_server, 'port', radius_port])
- self.set(['authentication', 'radius', 'server', radius_server, 'acct-port', radius_port_acc])
-
- coa_server = '4.4.4.4'
- coa_key = 'testCoA'
- self.set(['authentication', 'radius', 'dynamic-author', 'server', coa_server])
- self.set(['authentication', 'radius', 'dynamic-author', 'key', coa_key])
-
- nas_id = 'VyOS-PPPoE'
- nas_ip = '7.7.7.7'
- self.set(['authentication', 'radius', 'nas-identifier', nas_id])
- self.set(['authentication', 'radius', 'nas-ip-address', nas_ip])
-
- source_address = '1.2.3.4'
- self.set(['authentication', 'radius', 'source-address', source_address])
+ radius_server = "192.0.2.22"
+ radius_key = "secretVyOS"
+ radius_port = "2000"
+ radius_port_acc = "3000"
+ acct_interim_jitter = '10'
+ acct_interim_interval = '10'
+ acct_timeout = '30'
+
+ self.set(["authentication", "mode", "radius"])
+ self.set(
+ ["authentication", "radius", "server", radius_server, "key", radius_key]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "server",
+ radius_server,
+ "port",
+ radius_port,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "server",
+ radius_server,
+ "acct-port",
+ radius_port_acc,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "acct-interim-jitter",
+ acct_interim_jitter,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "accounting-interim-interval",
+ acct_interim_interval,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "acct-timeout",
+ acct_timeout,
+ ]
+ )
+
+ coa_server = "4.4.4.4"
+ coa_key = "testCoA"
+ self.set(
+ ["authentication", "radius", "dynamic-author", "server", coa_server]
+ )
+ self.set(["authentication", "radius", "dynamic-author", "key", coa_key])
+
+ nas_id = "VyOS-PPPoE"
+ nas_ip = "7.7.7.7"
+ self.set(["authentication", "radius", "nas-identifier", nas_id])
+ self.set(["authentication", "radius", "nas-ip-address", nas_ip])
+
+ source_address = "1.2.3.4"
+ self.set(["authentication", "radius", "source-address", source_address])
# commit changes
self.cli_commit()
# Validate configuration values
- conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
conf.read(self._config_file)
# basic verification
self.verify(conf)
# check auth
- self.assertTrue(conf['radius'].getboolean('verbose'))
- self.assertEqual(conf['radius']['acct-timeout'], '3')
- self.assertEqual(conf['radius']['timeout'], '3')
- self.assertEqual(conf['radius']['max-try'], '3')
-
- self.assertEqual(conf['radius']['dae-server'], f'{coa_server}:1700,{coa_key}')
- self.assertEqual(conf['radius']['nas-identifier'], nas_id)
- self.assertEqual(conf['radius']['nas-ip-address'], nas_ip)
- self.assertEqual(conf['radius']['bind'], source_address)
-
- server = conf['radius']['server'].split(',')
+ self.assertTrue(conf["radius"].getboolean("verbose"))
+ self.assertEqual(conf["radius"]["acct-timeout"], acct_timeout)
+ self.assertEqual(conf["radius"]["acct-interim-interval"], acct_interim_interval)
+ self.assertEqual(conf["radius"]["acct-interim-jitter"], acct_interim_jitter)
+ self.assertEqual(conf["radius"]["timeout"], "3")
+ self.assertEqual(conf["radius"]["max-try"], "3")
+
+ self.assertEqual(
+ conf["radius"]["dae-server"], f"{coa_server}:1700,{coa_key}"
+ )
+ self.assertEqual(conf["radius"]["nas-identifier"], nas_id)
+ self.assertEqual(conf["radius"]["nas-ip-address"], nas_ip)
+ self.assertEqual(conf["radius"]["bind"], source_address)
+
+ server = conf["radius"]["server"].split(",")
self.assertEqual(radius_server, server[0])
self.assertEqual(radius_key, server[1])
- self.assertEqual(f'auth-port={radius_port}', server[2])
- self.assertEqual(f'acct-port={radius_port_acc}', server[3])
- self.assertEqual(f'req-limit=0', server[4])
- self.assertEqual(f'fail-time=0', server[5])
+ self.assertEqual(f"auth-port={radius_port}", server[2])
+ self.assertEqual(f"acct-port={radius_port_acc}", server[3])
+ self.assertEqual(f"req-limit=0", server[4])
+ self.assertEqual(f"fail-time=0", server[5])
#
# Disable Radius Accounting
#
- self.delete(['authentication', 'radius', 'server', radius_server, 'acct-port'])
- self.set(['authentication', 'radius', 'server', radius_server, 'disable-accounting'])
+ self.delete(
+ ["authentication", "radius", "server", radius_server, "acct-port"]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "server",
+ radius_server,
+ "disable-accounting",
+ ]
+ )
# commit changes
self.cli_commit()
conf.read(self._config_file)
- server = conf['radius']['server'].split(',')
+ server = conf["radius"]["server"].split(",")
self.assertEqual(radius_server, server[0])
self.assertEqual(radius_key, server[1])
- self.assertEqual(f'auth-port={radius_port}', server[2])
- self.assertEqual(f'acct-port=0', server[3])
- self.assertEqual(f'req-limit=0', server[4])
- self.assertEqual(f'fail-time=0', server[5])
+ self.assertEqual(f"auth-port={radius_port}", server[2])
+ self.assertEqual(f"acct-port=0", server[3])
+ self.assertEqual(f"req-limit=0", server[4])
+ self.assertEqual(f"fail-time=0", server[5])
+
+ def test_accel_ipv4_pool(self):
+ self.basic_config(is_gateway=False, is_client_pool=False)
+ gateway = "192.0.2.1"
+ subnet = "172.16.0.0/24"
+ first_pool = "POOL1"
+ second_pool = "POOL2"
+ range = "192.0.2.10-192.0.2.20"
+ range_config = "192.0.2.10-20"
+
+ self.set(["gateway-address", gateway])
+ self.set(["client-ip-pool", first_pool, "range", subnet])
+ self.set(["client-ip-pool", first_pool, "next-pool", second_pool])
+ self.set(["client-ip-pool", second_pool, "range", range])
+ self.set(["default-pool", first_pool])
+ # commit changes
+
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
+ conf.read(self._config_file)
+
+ self.assertEqual(
+ f"{first_pool},next={second_pool}", conf["ip-pool"][f"{subnet},name"]
+ )
+ self.assertEqual(second_pool, conf["ip-pool"][f"{range_config},name"])
+ self.assertEqual(gateway, conf["ip-pool"]["gw-ip-address"])
+ self.assertEqual(first_pool, conf[self._protocol_section]["ip-pool"])
+
+ def test_accel_next_pool(self):
+ # T5099 required specific order
+ self.basic_config(is_gateway=False, is_client_pool=False)
+
+ gateway = "192.0.2.1"
+ first_pool = "VyOS-pool1"
+ first_subnet = "192.0.2.0/25"
+ second_pool = "Vyos-pool2"
+ second_subnet = "203.0.113.0/25"
+ third_pool = "Vyos-pool3"
+ third_subnet = "198.51.100.0/24"
+
+ self.set(["gateway-address", gateway])
+ self.set(["client-ip-pool", first_pool, "range", first_subnet])
+ self.set(["client-ip-pool", first_pool, "next-pool", second_pool])
+ self.set(["client-ip-pool", second_pool, "range", second_subnet])
+ self.set(["client-ip-pool", second_pool, "next-pool", third_pool])
+ self.set(["client-ip-pool", third_pool, "range", third_subnet])
+
+ # commit changes
+ self.cli_commit()
+
+ config = self.getConfig("ip-pool")
+ pool_config = f"""gw-ip-address={gateway}
+{third_subnet},name={third_pool}
+{second_subnet},name={second_pool},next={third_pool}
+{first_subnet},name={first_pool},next={second_pool}"""
+ self.assertIn(pool_config, config)
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 51ccbc9e6..a40b762a8 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -12,9 +12,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
-import unittest
-
from binascii import hexlify
from netifaces import AF_INET
@@ -24,7 +21,6 @@ from netifaces import interfaces
from base_vyostest_shim import VyOSUnitTestSHIM
-from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.defaults import directories
from vyos.ifconfig import Interface
@@ -162,25 +158,37 @@ class BasicInterfaceTest:
if not self._test_dhcp or not self._test_vrf:
self.skipTest('not supported')
+ client_id = 'VyOS-router'
distance = '100'
+ hostname = 'vyos'
+ vendor_class_id = 'vyos-vendor'
+ user_class = 'vyos'
for interface in self._interfaces:
for option in self._options.get(interface, []):
self.cli_set(self._base_path + [interface] + option.split())
self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
+ self.cli_set(self._base_path + [interface, 'dhcp-options', 'client-id', client_id])
self.cli_set(self._base_path + [interface, 'dhcp-options', 'default-route-distance', distance])
+ self.cli_set(self._base_path + [interface, 'dhcp-options', 'host-name', hostname])
+ self.cli_set(self._base_path + [interface, 'dhcp-options', 'vendor-class-id', vendor_class_id])
+ self.cli_set(self._base_path + [interface, 'dhcp-options', 'user-class', user_class])
self.cli_commit()
for interface in self._interfaces:
# Check if dhclient process runs
- dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface)
+ dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface, timeout=10)
self.assertTrue(dhclient_pid)
dhclient_config = read_file(f'{dhclient_base_dir}/dhclient_{interface}.conf')
- self.assertIn('request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config)
- self.assertIn('require subnet-mask;', dhclient_config)
+ self.assertIn(f'request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config)
+ self.assertIn(f'require subnet-mask;', dhclient_config)
+ self.assertIn(f'send host-name "{hostname}";', dhclient_config)
+ self.assertIn(f'send dhcp-client-identifier "{client_id}";', dhclient_config)
+ self.assertIn(f'send vendor-class-identifier "{vendor_class_id}";', dhclient_config)
+ self.assertIn(f'send user-class "{user_class}";', dhclient_config)
# and the commandline has the appropriate options
cmdline = read_file(f'/proc/{dhclient_pid}/cmdline')
@@ -208,7 +216,7 @@ class BasicInterfaceTest:
self.assertEqual(tmp, vrf_name)
# Check if dhclient process runs
- dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface)
+ dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface, timeout=10)
self.assertTrue(dhclient_pid)
# .. inside the appropriate VRF instance
vrf_pids = cmd(f'ip vrf pids {vrf_name}')
@@ -243,7 +251,7 @@ class BasicInterfaceTest:
self.assertEqual(tmp, vrf_name)
# Check if dhclient process runs
- tmp = process_named_running(dhcp6c_process_name, cmdline=interface)
+ tmp = process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10)
self.assertTrue(tmp)
# .. inside the appropriate VRF instance
vrf_pids = cmd(f'ip vrf pids {vrf_name}')
@@ -404,10 +412,9 @@ class BasicInterfaceTest:
for intf in self._interfaces:
base = self._base_path + [intf]
- self.cli_set(base + ['mtu', self._mtu])
-
for option in self._options.get(intf, []):
self.cli_set(base + option.split())
+ self.cli_set(base + ['mtu', self._mtu])
# check validate() - can not set low MTU if 'no-default-link-local'
# is not set on CLI
@@ -938,7 +945,7 @@ class BasicInterfaceTest:
duid_base += 1
# Better ask the process about it's commandline in the future
- pid = process_named_running(dhcp6c_process_name, cmdline=interface)
+ pid = process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10)
self.assertTrue(pid)
dhcp6c_options = read_file(f'/proc/{pid}/cmdline')
@@ -997,7 +1004,7 @@ class BasicInterfaceTest:
address = str(int(address) + 1)
# Check for running process
- self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface))
+ self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10))
for delegatee in delegatees:
# we can already cleanup the test delegatee interface here
@@ -1063,7 +1070,7 @@ class BasicInterfaceTest:
address = str(int(address) + 1)
# Check for running process
- self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface))
+ self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface, timeout=10))
for delegatee in delegatees:
# we can already cleanup the test delegatee interface here
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index f694f539d..140642806 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -78,9 +78,10 @@ class VyOSUnitTestSHIM:
while run(f'sudo lsof -nP {commit_lock}') == 0:
sleep(0.250)
- def getFRRconfig(self, string, end='$', endsection='^!', daemon=''):
+ def getFRRconfig(self, string=None, end='$', endsection='^!', daemon=''):
""" Retrieve current "running configuration" from FRR """
- command = f'vtysh -c "show run {daemon} no-header" | sed -n "/^{string}{end}/,/{endsection}/p"'
+ command = f'vtysh -c "show run {daemon} no-header"'
+ if string: command += f' | sed -n "/^{string}{end}/,/{endsection}/p"'
out = cmd(command)
if self.debug:
import pprint
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_container.py b/smoketest/scripts/cli/test_container.py
index b43c05fae..cdf46a6e1 100755
--- a/smoketest/scripts/cli/test_container.py
+++ b/smoketest/scripts/cli/test_container.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
@@ -19,6 +19,7 @@ import glob
import json
from base_vyostest_shim import VyOSUnitTestSHIM
+from ipaddress import ip_interface
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
@@ -27,8 +28,6 @@ from vyos.utils.file import read_file
base_path = ['container']
cont_image = 'busybox:stable' # busybox is included in vyos-build
-prefix = '192.168.205.0/24'
-net_name = 'NET01'
PROCESS_NAME = 'conmon'
PROCESS_PIDFILE = '/run/vyos-container-{0}.service.pid'
@@ -37,10 +36,8 @@ busybox_image_path = '/usr/share/vyos/busybox-stable.tar'
def cmd_to_json(command):
c = cmd(command + ' --format=json')
data = json.loads(c)[0]
-
return data
-
class TestContainer(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
@@ -52,6 +49,10 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
except:
cls.skipTest(cls, reason='busybox image not available')
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
@classmethod
def tearDownClass(cls):
super(TestContainer, cls).tearDownClass()
@@ -70,7 +71,7 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
units = glob.glob('/run/systemd/system/vyos-container-*')
self.assertEqual(units, [])
- def test_01_basic_container(self):
+ def test_basic(self):
cont_name = 'c1'
self.cli_set(['interfaces', 'ethernet', 'eth0', 'address', '10.0.2.15/24'])
@@ -91,24 +92,101 @@ class TestContainer(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertEqual(process_named_running(PROCESS_NAME), pid)
- def test_02_container_network(self):
- cont_name = 'c2'
- cont_ip = '192.168.205.25'
+ def test_ipv4_network(self):
+ prefix = '192.0.2.0/24'
+ base_name = 'ipv4'
+ net_name = 'NET01'
+
self.cli_set(base_path + ['network', net_name, 'prefix', prefix])
- self.cli_set(base_path + ['name', cont_name, 'image', cont_image])
- self.cli_set(base_path + ['name', cont_name, 'network', net_name, 'address', cont_ip])
- # commit changes
+ for ii in range(1, 6):
+ name = f'{base_name}-{ii}'
+ self.cli_set(base_path + ['name', name, 'image', cont_image])
+ self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + ii)])
+
+ # verify() - first IP address of a prefix can not be used by a container
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ tmp = f'{base_name}-1'
+ self.cli_delete(base_path + ['name', tmp])
self.cli_commit()
n = cmd_to_json(f'sudo podman network inspect {net_name}')
- json_subnet = n['subnets'][0]['subnet']
+ self.assertEqual(n['subnets'][0]['subnet'], prefix)
- c = cmd_to_json(f'sudo podman container inspect {cont_name}')
- json_ip = c['NetworkSettings']['Networks'][net_name]['IPAddress']
+ # skipt first container, it was never created
+ for ii in range(2, 6):
+ name = f'{base_name}-{ii}'
+ c = cmd_to_json(f'sudo podman container inspect {name}')
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['Gateway'] , str(ip_interface(prefix).ip + 1))
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPAddress'], str(ip_interface(prefix).ip + ii))
- self.assertEqual(json_subnet, prefix)
- self.assertEqual(json_ip, cont_ip)
+ def test_ipv6_network(self):
+ prefix = '2001:db8::/64'
+ base_name = 'ipv6'
+ net_name = 'NET02'
+
+ self.cli_set(base_path + ['network', net_name, 'prefix', prefix])
+
+ for ii in range(1, 6):
+ name = f'{base_name}-{ii}'
+ self.cli_set(base_path + ['name', name, 'image', cont_image])
+ self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix).ip + ii)])
+
+ # verify() - first IP address of a prefix can not be used by a container
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ tmp = f'{base_name}-1'
+ self.cli_delete(base_path + ['name', tmp])
+ self.cli_commit()
+
+ n = cmd_to_json(f'sudo podman network inspect {net_name}')
+ self.assertEqual(n['subnets'][0]['subnet'], prefix)
+
+ # skipt first container, it was never created
+ for ii in range(2, 6):
+ name = f'{base_name}-{ii}'
+ c = cmd_to_json(f'sudo podman container inspect {name}')
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPv6Gateway'] , str(ip_interface(prefix).ip + 1))
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['GlobalIPv6Address'], str(ip_interface(prefix).ip + ii))
+
+ def test_dual_stack_network(self):
+ prefix4 = '192.0.2.0/24'
+ prefix6 = '2001:db8::/64'
+ base_name = 'dual-stack'
+ net_name = 'net-4-6'
+
+ self.cli_set(base_path + ['network', net_name, 'prefix', prefix4])
+ self.cli_set(base_path + ['network', net_name, 'prefix', prefix6])
+
+ for ii in range(1, 6):
+ name = f'{base_name}-{ii}'
+ self.cli_set(base_path + ['name', name, 'image', cont_image])
+ self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix4).ip + ii)])
+ self.cli_set(base_path + ['name', name, 'network', net_name, 'address', str(ip_interface(prefix6).ip + ii)])
+
+ # verify() - first IP address of a prefix can not be used by a container
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ tmp = f'{base_name}-1'
+ self.cli_delete(base_path + ['name', tmp])
+ self.cli_commit()
+
+ n = cmd_to_json(f'sudo podman network inspect {net_name}')
+ self.assertEqual(n['subnets'][0]['subnet'], prefix4)
+ self.assertEqual(n['subnets'][1]['subnet'], prefix6)
+
+ # skipt first container, it was never created
+ for ii in range(2, 6):
+ name = f'{base_name}-{ii}'
+ c = cmd_to_json(f'sudo podman container inspect {name}')
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPv6Gateway'] , str(ip_interface(prefix6).ip + 1))
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['GlobalIPv6Address'], str(ip_interface(prefix6).ip + ii))
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['Gateway'] , str(ip_interface(prefix4).ip + 1))
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPAddress'] , str(ip_interface(prefix4).ip + ii))
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 6f9093f4d..f74a33566 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -148,7 +148,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'source', 'group', 'domain-group', 'smoketest_domain'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'action', 'accept'])
- self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'outbound-interface', 'interface-group', '!smoketest_interface'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'outbound-interface', 'group', '!smoketest_interface'])
self.cli_commit()
@@ -209,28 +209,29 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
conn_mark = '555'
self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'default-log'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'log'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'log-options', 'level', 'debug'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'ttl', 'eq', '15'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'action', 'reject'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'protocol', 'tcp'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'destination', 'port', '8888'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'log'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'log-options', 'level', 'err'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'ttl', 'gt', '102'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'default-log'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'protocol', 'tcp'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'destination', 'port', '22'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'limit', 'rate', '5/minute'])
- self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'log', 'disable'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '3', 'log'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'action', 'drop'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'protocol', 'tcp'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '4', 'destination', 'port', '22'])
@@ -243,15 +244,16 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'tcp', 'flags', 'syn'])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'tcp', 'mss', mss_range])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'packet-type', 'broadcast'])
- self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'inbound-interface', 'interface-name', interface_wc])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '5', 'inbound-interface', 'name', interface_wc])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '6', 'action', 'return'])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '6', 'protocol', 'gre'])
self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '6', 'connection-mark', conn_mark])
- self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'default-action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'default-log'])
self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'action', 'drop'])
self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'protocol', 'gre'])
- self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'outbound-interface', 'interface-name', interface_inv])
+ self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '5', 'outbound-interface', 'name', interface_inv])
self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '6', 'action', 'return'])
self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '6', 'protocol', 'icmp'])
self.cli_set(['firewall', 'ipv4', 'output', 'filter', 'rule', '6', 'connection-mark', conn_mark])
@@ -262,21 +264,23 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
nftables_search = [
['chain VYOS_FORWARD_filter'],
- ['type filter hook forward priority filter; policy drop;'],
+ ['type filter hook forward priority filter; policy accept;'],
['tcp dport 22', 'limit rate 5/minute', 'accept'],
['tcp dport 22', 'add @RECENT_FWD_filter_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'meta pkttype host', 'drop'],
+ ['log prefix "[ipv4-FWD-filter-default-D]"','FWD-filter default-action drop', 'drop'],
['chain VYOS_INPUT_filter'],
['type filter hook input priority filter; policy accept;'],
['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface_wc}"', 'meta pkttype broadcast', 'accept'],
['meta l4proto gre', f'ct mark {mark_hex}', 'return'],
+ ['INP-filter default-action accept', 'accept'],
['chain VYOS_OUTPUT_filter'],
['type filter hook output priority filter; policy accept;'],
['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'],
- ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop']
+ ['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 "[ipv4-smoketest-default-D]"','smoketest default-action', 'drop']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
@@ -287,7 +291,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
interface = 'eth0'
self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'default-log'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'packet-length', '64'])
@@ -295,7 +299,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'packet-length', '1024'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'dscp', '17'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'dscp', '52'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log-options', 'group', '66'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log-options', 'snapshot-length', '6666'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '6', 'log-options', 'queue-threshold','32000'])
@@ -308,10 +312,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'])
@@ -324,16 +330,18 @@ 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}'],
+ ['type filter hook forward priority filter; policy accept;'],
+ ['ip saddr 198.51.100.1', 'meta mark 0x000003f2', f'jump NAME_{name}'],
+ ['FWD-filter default-action drop', 'drop'],
['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'],
+ ['INP-filter default-action accept', 'accept'],
[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']
+ [f'log prefix "[ipv4-{name}-default-D]"', 'drop']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
@@ -345,7 +353,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'address-group', 'mask_group', 'address', '1.1.1.1'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'default-log'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'drop'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2'])
@@ -374,30 +382,36 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
name = 'v6-smoketest'
interface = 'eth0'
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'related', 'action', 'accept'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'drop'])
+
self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'default-log'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'action', 'accept'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'source', 'address', '2002::1'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'destination', 'address', '2002::1:1'])
- self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'log', 'enable'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'log'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'log-options', 'level', 'crit'])
self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'default-action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'default-log'])
self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'action', 'reject'])
self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'protocol', 'tcp_udp'])
self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'destination', 'port', '8888'])
- self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'inbound-interface', 'interface-name', interface])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '2', 'inbound-interface', 'name', interface])
self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'default-log'])
self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'action', 'return'])
self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'protocol', 'gre'])
- self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'outbound-interface', 'interface-name', interface])
+ self.cli_set(['firewall', 'ipv6', 'output', 'filter', 'rule', '3', 'outbound-interface', 'name', interface])
self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'action', 'accept'])
self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'protocol', 'udp'])
self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'source', 'address', '2002::1:2'])
- self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'inbound-interface', 'interface-name', interface])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '3', 'inbound-interface', 'name', interface])
self.cli_commit()
@@ -405,15 +419,23 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['chain VYOS_IPV6_FORWARD_filter'],
['type filter hook forward priority filter; policy accept;'],
['meta l4proto { tcp, udp }', 'th dport 8888', f'iifname "{interface}"', 'reject'],
+ ['log prefix "[ipv6-FWD-filter-default-A]"','FWD-filter default-action accept', 'accept'],
['chain VYOS_IPV6_INPUT_filter'],
['type filter hook input priority filter; policy accept;'],
['meta l4proto udp', 'ip6 saddr 2002::1:2', f'iifname "{interface}"', 'accept'],
+ ['INP-filter default-action accept', 'accept'],
['chain VYOS_IPV6_OUTPUT_filter'],
- ['type filter hook output priority filter; policy drop;'],
+ ['type filter hook output priority filter; policy accept;'],
['meta l4proto gre', f'oifname "{interface}"', 'return'],
+ ['log prefix "[ipv6-OUT-filter-default-D]"','OUT-filter default-action drop', 'drop'],
[f'chain NAME6_{name}'],
- ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'accept'],
- [f'"{name} default-action drop"', f'log prefix "[{name}-default-D]"', 'drop']
+ ['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 "[ipv6-{name}-default-D]"', 'drop'],
+ ['jump VYOS_STATE_POLICY6'],
+ ['chain VYOS_STATE_POLICY6'],
+ ['ct state established', 'accept'],
+ ['ct state invalid', 'drop'],
+ ['ct state related', 'accept']
]
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
@@ -424,7 +446,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
interface = 'eth0'
self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'default-log'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'action', 'accept'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '3', 'packet-length', '65'])
@@ -455,7 +477,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['ip6 saddr 2001:db8::/64', 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']
+ [f'log prefix "[ipv6-{name}-default-D]"', 'drop']
]
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
@@ -467,7 +489,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'group', 'ipv6-address-group', 'mask_group', 'address', '::beef'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'default-action', 'drop'])
- self.cli_set(['firewall', 'ipv6', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'ipv6', 'name', name, 'default-log'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'action', 'drop'])
self.cli_set(['firewall', 'ipv6', 'name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444'])
@@ -495,20 +517,27 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
name = 'smoketest-state'
interface = 'eth0'
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'related', 'action', 'accept'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'drop'])
+
self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'accept'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'state', 'established', 'enable'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'state', 'related', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'state', 'established'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'state', 'related'])
self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'action', 'reject'])
- self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'state', 'invalid', 'enable'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '2', 'state', 'invalid'])
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', 'state', 'new'])
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', 'state', 'new'])
+ self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '4', 'state', 'established'])
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'])
+ 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,7 +546,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['ct state invalid', 'reject'],
['ct state new', 'ct status dnat', 'accept'],
['ct state { established, new }', 'ct status snat', 'accept'],
- ['drop', f'comment "{name} default-action drop"']
+ ['ct state related', 'ct helper { "ftp", "pptp" }', 'accept'],
+ ['drop', f'comment "{name} default-action drop"'],
+ ['jump VYOS_STATE_POLICY'],
+ ['chain VYOS_STATE_POLICY'],
+ ['ct state established', 'accept'],
+ ['ct state invalid', 'drop'],
+ ['ct state related', 'accept']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
@@ -526,6 +561,42 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables_chain([['accept']], 'raw', 'FW_CONNTRACK')
self.verify_nftables_chain([['return']], 'ip6 raw', '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, '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', 'name', interface_in])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log'])
+ 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', 'default-log'])
+ 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'])
@@ -568,5 +639,125 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with open(path, 'r') as f:
self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
+### Zone
+ def test_zone_basic(self):
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
+ self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
+ self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
+ self.cli_set(['firewall', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'log'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'related', 'action', 'accept'])
+ self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'drop'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['chain VYOS_ZONE_FORWARD'],
+ ['type filter hook forward priority filter + 1'],
+ ['chain VYOS_ZONE_OUTPUT'],
+ ['type filter hook output priority filter + 1'],
+ ['chain VYOS_ZONE_LOCAL'],
+ ['type filter hook input priority filter + 1'],
+ ['chain VZONE_smoketest-eth0'],
+ ['chain VZONE_smoketest-local_IN'],
+ ['chain VZONE_smoketest-local_OUT'],
+ ['oifname "eth0"', 'jump VZONE_smoketest-eth0'],
+ ['jump VZONE_smoketest-local_IN'],
+ ['jump VZONE_smoketest-local_OUT'],
+ ['iifname "eth0"', 'jump NAME_smoketest'],
+ ['oifname "eth0"', 'jump NAME_smoketest'],
+ ['jump VYOS_STATE_POLICY'],
+ ['chain VYOS_STATE_POLICY'],
+ ['ct state established', 'log prefix "[STATE-POLICY-EST-A]"', 'accept'],
+ ['ct state invalid', 'drop'],
+ ['ct state related', 'accept']
+ ]
+
+ nftables_output = cmd('sudo nft list table ip vyos_filter')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
+ 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'])
+ self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'related'])
+
+ 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'])
+ self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'state', 'related'])
+
+ 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')
+
+ def test_zone_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', 'name', 'smoketest', 'rule', '1', 'action', 'offload'])
+ self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'rule', '1', 'offload-target', 'smoketest'])
+
+ self.cli_set(['firewall', 'ipv6', 'name', 'smoketest', 'rule', '1', 'action', 'offload'])
+ self.cli_set(['firewall', 'ipv6', 'name', 'smoketest', 'rule', '1', 'offload-target', 'smoketest'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['chain NAME_smoketest'],
+ ['flow add @VYOS_FLOWTABLE_smoketest']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+
+ nftables_search = [
+ ['chain NAME6_smoketest'],
+ ['flow add @VYOS_FLOWTABLE_smoketest']
+ ]
+
+ 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_ha_virtual_server.py b/smoketest/scripts/cli/test_high-availability_virtual-server.py
index 51ccfa4df..51ccfa4df 100755
--- a/smoketest/scripts/cli/test_ha_virtual_server.py
+++ b/smoketest/scripts/cli/test_high-availability_virtual-server.py
diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_high-availability_vrrp.py
index 98259d830..98259d830 100755
--- a/smoketest/scripts/cli/test_ha_vrrp.py
+++ b/smoketest/scripts/cli/test_high-availability_vrrp.py
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index 8867cb427..419de774a 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -241,5 +241,45 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
for member in self._members:
self.assertIn(member, slaves)
+ def test_bonding_evpn_multihoming(self):
+ id = '5'
+ for interface in self._interfaces:
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_set(self._base_path + [interface, 'evpn', 'es-id', id])
+ self.cli_set(self._base_path + [interface, 'evpn', 'es-df-pref', id])
+ self.cli_set(self._base_path + [interface, 'evpn', 'es-sys-mac', f'00:12:34:56:78:0{id}'])
+ self.cli_set(self._base_path + [interface, 'evpn', 'uplink'])
+
+ id = int(id) + 1
+
+ self.cli_commit()
+
+ id = '5'
+ for interface in self._interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}', daemon='zebra')
+
+ self.assertIn(f' evpn mh es-id {id}', frrconfig)
+ self.assertIn(f' evpn mh es-df-pref {id}', frrconfig)
+ self.assertIn(f' evpn mh es-sys-mac 00:12:34:56:78:0{id}', frrconfig)
+ self.assertIn(f' evpn mh uplink', frrconfig)
+
+ id = int(id) + 1
+
+ for interface in self._interfaces:
+ self.cli_delete(self._base_path + [interface, 'evpn', 'es-id'])
+ self.cli_delete(self._base_path + [interface, 'evpn', 'es-df-pref'])
+
+ self.cli_commit()
+
+ id = '5'
+ for interface in self._interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}', daemon='zebra')
+ self.assertIn(f' evpn mh es-sys-mac 00:12:34:56:78:0{id}', frrconfig)
+ self.assertIn(f' evpn mh uplink', frrconfig)
+
+ id = int(id) + 1
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index ea0f00071..d8d564792 100755
--- a/smoketest/scripts/cli/test_interfaces_macsec.py
+++ b/smoketest/scripts/cli/test_interfaces_macsec.py
@@ -14,7 +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
import re
import unittest
@@ -23,9 +22,10 @@ from netifaces import interfaces
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
-from vyos.utils.process import cmd
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
+from vyos.utils.network import interface_exists
+from vyos.utils.process import cmd
from vyos.utils.process import process_named_running
PROCESS_NAME = 'wpa_supplicant'
@@ -35,10 +35,6 @@ def get_config_value(interface, key):
tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp)
return tmp[0]
-def get_cipher(interface):
- tmp = get_interface_config(interface)
- return tmp['linkinfo']['info_data']['cipher_suite'].lower()
-
class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
@@ -117,6 +113,10 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
tmp = read_file(f'/sys/class/net/{interface}/mtu')
self.assertEqual(tmp, '1460')
+ # Encryption enabled?
+ tmp = get_interface_config(interface)
+ self.assertTrue(tmp['linkinfo']['info_data']['encrypt'])
+
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -138,10 +138,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
+ self.assertTrue(interface_exists(interface))
# Verify proper cipher suite (T4537)
- self.assertEqual(cipher, get_cipher(interface))
+ tmp = get_interface_config(interface)
+ self.assertEqual(cipher, tmp['linkinfo']['info_data']['cipher_suite'].lower())
def test_macsec_gcm_aes_256(self):
src_interface = 'eth0'
@@ -161,10 +162,11 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
+ self.assertTrue(interface_exists(interface))
# Verify proper cipher suite (T4537)
- self.assertEqual(cipher, get_cipher(interface))
+ tmp = get_interface_config(interface)
+ self.assertEqual(cipher, tmp['linkinfo']['info_data']['cipher_suite'].lower())
def test_macsec_source_interface(self):
# Ensure source-interface can bot be part of any other bond or bridge
@@ -191,7 +193,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
+ self.assertTrue(interface_exists(interface))
def test_macsec_static_keys(self):
src_interface = 'eth0'
@@ -205,7 +207,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
peer_mac = '00:11:22:33:44:55'
self.cli_set(self._base_path + [interface])
- # Encrypt link
+ # Encrypt link
self.cli_set(self._base_path + [interface, 'security', 'encrypt'])
# check validate() - source interface is mandatory
@@ -261,9 +263,12 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
# final commit and verify
self.cli_commit()
- self.assertIn(interface, interfaces())
- self.assertEqual(cipher2, get_cipher(interface))
- self.assertTrue(os.path.isdir(f'/sys/class/net/{interface}'))
+
+ self.assertTrue(interface_exists(interface))
+ tmp = get_interface_config(interface)
+ self.assertEqual(cipher2, tmp['linkinfo']['info_data']['cipher_suite'].lower())
+ # Encryption enabled?
+ self.assertTrue(tmp['linkinfo']['info_data']['encrypt'])
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..e99d8b3d1 100755
--- a/smoketest/scripts/cli/test_interfaces_pppoe.py
+++ b/smoketest/scripts/cli/test_interfaces_pppoe.py
@@ -36,6 +36,9 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(PPPoEInterfaceTest, cls).setUpClass()
+ # 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._interfaces = ['pppoe10', 'pppoe20', 'pppoe30']
cls._source_interface = 'eth0'
@@ -53,7 +56,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- def test_01_pppoe_client(self):
+ def test_pppoe_client(self):
# Check if PPPoE dialer can be configured and runs
for interface in self._interfaces:
user = f'VyOS-user-{interface}'
@@ -80,6 +83,9 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value(interface, 'mtu')[1]
self.assertEqual(tmp, mtu)
+ # MRU must default to MTU if not specified on CLI
+ tmp = get_config_value(interface, 'mru')[1]
+ self.assertEqual(tmp, mtu)
tmp = get_config_value(interface, 'user')[1].replace('"', '')
self.assertEqual(tmp, user)
tmp = get_config_value(interface, 'password')[1].replace('"', '')
@@ -87,7 +93,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value(interface, 'ifname')[1]
self.assertEqual(tmp, interface)
- def test_02_pppoe_client_disabled_interface(self):
+ def test_pppoe_client_disabled_interface(self):
# Check if PPPoE Client can be disabled
for interface in self._interfaces:
user = f'VyOS-user-{interface}'
@@ -116,16 +122,16 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- def test_03_pppoe_authentication(self):
+ def test_pppoe_authentication(self):
# When username or password is set - so must be the other
for interface in self._interfaces:
user = f'VyOS-user-{interface}'
passwd = f'VyOS-passwd-{interface}'
- self.cli_set(base_path + [interface, 'authentication', 'username', user])
self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf'])
+ self.cli_set(base_path + [interface, 'authentication', 'username', user])
# check validate() - if user is set, so must be the password
with self.assertRaises(ConfigSessionError):
self.cli_commit()
@@ -134,7 +140,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- def test_04_pppoe_dhcpv6pd(self):
+ def test_pppoe_dhcpv6pd(self):
# Check if PPPoE dialer can be configured with DHCPv6-PD
address = '1'
sla_id = '0'
@@ -174,7 +180,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value(interface, '+ipv6 ipv6cp-use-ipaddr')
self.assertListEqual(tmp, ['+ipv6', 'ipv6cp-use-ipaddr'])
- def test_05_pppoe_options(self):
+ def test_pppoe_options(self):
# Check if PPPoE dialer can be configured with DHCPv6-PD
for interface in self._interfaces:
user = f'VyOS-user-{interface}'
@@ -206,5 +212,47 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value(interface, 'pppoe-host-uniq')[1]
self.assertEqual(tmp, f'"{host_uniq}"')
+ def test_pppoe_mtu_mru(self):
+ # Check if PPPoE dialer can be configured and runs
+ for interface in self._interfaces:
+ 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'])
+
+ # check validate() - a source-interface is required
+ with self.assertRaises(ConfigSessionError):
+ 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()
+
+ # verify configuration file(s)
+ for interface in self._interfaces:
+ user = f'VyOS-user-{interface}'
+ passwd = f'VyOS-passwd-{interface}'
+
+ 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('"', '')
+ self.assertEqual(tmp, passwd)
+ tmp = get_config_value(interface, 'ifname')[1]
+ self.assertEqual(tmp, interface)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py b/smoketest/scripts/cli/test_interfaces_pseudo-ethernet.py
index 0d6f5bc1f..0d6f5bc1f 100755
--- a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_pseudo-ethernet.py
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index 2a7a519fd..dd9f1d2d1 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -393,5 +393,21 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(tunnel_config['encapsulation'], conf['linkinfo']['info_kind'])
self.assertEqual(tunnel_config['remote'], conf['linkinfo']['info_data']['remote'])
+ def test_tunnel_invalid_source_interface(self):
+ encapsulation = 'gre'
+ remote = '192.0.2.1'
+ interface = 'tun7543'
+
+ self.cli_set(self._base_path + [interface, 'encapsulation', encapsulation])
+ self.cli_set(self._base_path + [interface, 'remote', remote])
+
+ for dynamic_interface in ['l2tp0', 'ppp4220', 'sstpc0', 'ipoe654']:
+ self.cli_set(self._base_path + [interface, 'source-interface', dynamic_interface])
+ # verify() - we can not source from dynamic interfaces
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'source-interface', 'eth0'])
+ self.cli_commit()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_virtual_ethernet.py b/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py
index 7874589ca..7874589ca 100755
--- a/smoketest/scripts/cli/test_interfaces_virtual_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_virtual-ethernet.py
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index f6b203de4..18676491b 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -18,8 +18,12 @@ import unittest
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
+from vyos.ifconfig import Section
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.utils.network import get_vxlan_vni_filter
from vyos.template import is_ipv6
from base_interfaces_test import BasicInterfaceTest
@@ -29,12 +33,13 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
cls._base_path = ['interfaces', 'vxlan']
cls._options = {
'vxlan10': ['vni 10', 'remote 127.0.0.2'],
- 'vxlan20': ['vni 20', 'group 239.1.1.1', 'source-interface eth0'],
+ 'vxlan20': ['vni 20', 'group 239.1.1.1', 'source-interface eth0', 'mtu 1450'],
'vxlan30': ['vni 30', 'remote 2001:db8:2000::1', 'source-address 2001:db8:1000::1', 'parameters ipv6 flowlabel 0x1000'],
'vxlan40': ['vni 40', 'remote 127.0.0.2', 'remote 127.0.0.3'],
'vxlan50': ['vni 50', 'remote 2001:db8:2000::1', 'remote 2001:db8:2000::2', 'parameters ipv6 flowlabel 0x1000'],
}
cls._interfaces = list(cls._options)
+ cls._mtu = '1450'
# call base-classes classmethod
super(VXLANInterfaceTest, cls).setUpClass()
@@ -105,7 +110,7 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
def test_vxlan_external(self):
interface = 'vxlan0'
source_address = '192.0.2.1'
- self.cli_set(self._base_path + [interface, 'external'])
+ self.cli_set(self._base_path + [interface, 'parameters', 'external'])
self.cli_set(self._base_path + [interface, 'source-address', source_address])
# Both 'VNI' and 'external' can not be specified at the same time.
@@ -133,5 +138,182 @@ 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_address = '192.0.2.99'
+
+ vlan_to_vni = {
+ '10': '10010',
+ '11': '10011',
+ '12': '10012',
+ '13': '10013',
+ '20': '10020',
+ '30': '10030',
+ '31': '10031',
+ }
+
+ self.cli_set(self._base_path + [interface, 'parameters', 'external'])
+ self.cli_set(self._base_path + [interface, 'source-address', source_address])
+
+ 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)
+ self.assertFalse(tmp['linkinfo']['info_slave_data']['neigh_suppress'])
+
+ tmp = get_vxlan_vlan_tunnels('vxlan0')
+ self.assertEqual(tmp, list(vlan_to_vni))
+
+ self.cli_delete(['interfaces', 'bridge', bridge])
+
+ def test_vxlan_neighbor_suppress(self):
+ bridge = 'br555'
+ interface = 'vxlan555'
+ source_interface = 'dum0'
+
+ self.cli_set(['interfaces', Section.section(source_interface), source_interface, 'mtu', '9000'])
+
+ self.cli_set(self._base_path + [interface, 'parameters', 'external'])
+ self.cli_set(self._base_path + [interface, 'source-interface', source_interface])
+ self.cli_set(self._base_path + [interface, 'parameters', 'neighbor-suppress'])
+
+ # 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])
+
+ # 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)
+ self.assertTrue(tmp['linkinfo']['info_slave_data']['neigh_suppress'])
+ self.assertFalse(tmp['linkinfo']['info_slave_data']['learning'])
+
+ # Remove neighbor suppress configuration and re-test
+ self.cli_delete(self._base_path + [interface, 'parameters', 'neighbor-suppress'])
+ # commit configuration
+ self.cli_commit()
+
+ tmp = get_interface_config(interface)
+ self.assertEqual(tmp['master'], bridge)
+ self.assertFalse(tmp['linkinfo']['info_slave_data']['neigh_suppress'])
+ self.assertTrue(tmp['linkinfo']['info_slave_data']['learning'])
+
+ self.cli_delete(['interfaces', 'bridge', bridge])
+ self.cli_delete(['interfaces', Section.section(source_interface), source_interface])
+
+ def test_vxlan_vni_filter(self):
+ interfaces = ['vxlan987', 'vxlan986', 'vxlan985']
+ source_address = '192.0.2.77'
+
+ for interface in interfaces:
+ self.cli_set(self._base_path + [interface, 'parameters', 'external'])
+ self.cli_set(self._base_path + [interface, 'source-address', source_address])
+
+ # This must fail as there can only be one "external" VXLAN device unless "vni-filter" is defined
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Enable "vni-filter" on the first VXLAN interface
+ self.cli_set(self._base_path + [interfaces[0], 'parameters', 'vni-filter'])
+
+ # This must fail as if it's enabled on one VXLAN interface, it must be enabled on all
+ # VXLAN interfaces
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in interfaces:
+ self.cli_set(self._base_path + [interface, 'parameters', 'vni-filter'])
+
+ # commit configuration
+ self.cli_commit()
+
+ for interface in interfaces:
+ self.assertTrue(interface_exists(interface))
+
+ tmp = get_interface_config(interface)
+ self.assertTrue(tmp['linkinfo']['info_data']['vnifilter'])
+
+ def test_vxlan_vni_filter_add_remove(self):
+ interface = 'vxlan987'
+ source_address = '192.0.2.66'
+ bridge = 'br0'
+
+ self.cli_set(self._base_path + [interface, 'parameters', 'external'])
+ self.cli_set(self._base_path + [interface, 'source-address', source_address])
+ self.cli_set(self._base_path + [interface, 'parameters', 'vni-filter'])
+
+ # commit configuration
+ self.cli_commit()
+
+ # Check if VXLAN interface got created
+ self.assertTrue(interface_exists(interface))
+
+ # VNI filter configured?
+ tmp = get_interface_config(interface)
+ self.assertTrue(tmp['linkinfo']['info_data']['vnifilter'])
+
+ # Now create some VLAN mappings and VNI filter
+ vlan_to_vni = {
+ '50': '10050',
+ '51': '10051',
+ '52': '10052',
+ '53': '10053',
+ '54': '10054',
+ '60': '10060',
+ '69': '10069',
+ }
+ for vlan, vni in vlan_to_vni.items():
+ self.cli_set(self._base_path + [interface, 'vlan-to-vni', vlan, 'vni', vni])
+ # we need a bridge ...
+ self.cli_set(['interfaces', 'bridge', bridge, 'member', 'interface', interface])
+ # commit configuration
+ self.cli_commit()
+
+ # All VNIs configured?
+ tmp = get_vxlan_vni_filter(interface)
+ self.assertListEqual(list(vlan_to_vni.values()), tmp)
+
+ #
+ # Delete a VLAN mappings and check if all VNIs are properly set up
+ #
+ vlan_to_vni.popitem()
+ self.cli_delete(self._base_path + [interface, 'vlan-to-vni'])
+ 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()
+
+ # All VNIs configured?
+ tmp = get_vxlan_vni_filter(interface)
+ self.assertListEqual(list(vlan_to_vni.values()), tmp)
+
+ self.cli_delete(['interfaces', 'bridge', bridge])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_wireguard.py b/smoketest/scripts/cli/test_interfaces_wireguard.py
index 48c7cb6a1..4b994a659 100755
--- a/smoketest/scripts/cli/test_interfaces_wireguard.py
+++ b/smoketest/scripts/cli/test_interfaces_wireguard.py
@@ -20,6 +20,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.file import read_file
+from vyos.utils.process import cmd
base_path = ['interfaces', 'wireguard']
@@ -152,5 +153,52 @@ class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
tmp = read_file(f'/sys/class/net/{interface}/threaded')
self.assertTrue(tmp, "1")
+ def test_05_wireguard_peer_pubkey_change(self):
+ # T5707 changing WireGuard CLI public key of a peer - it's not removed
+
+ def get_peers(interface) -> list:
+ tmp = cmd(f'sudo wg show {interface} dump')
+ first_line = True
+ peers = []
+ for line in tmp.split('\n'):
+ if not line:
+ continue # Skip empty lines and last line
+ items = line.split('\t')
+ if first_line:
+ self.assertEqual(privkey, items[0])
+ first_line = False
+ else:
+ peers.append(items[0])
+ return peers
+
+
+ interface = 'wg1337'
+ port = '1337'
+ privkey = 'iJi4lb2HhkLx2KSAGOjji2alKkYsJjSPkHkrcpxgEVU='
+ pubkey_1 = 'srQ8VF6z/LDjKCzpxBzFpmaNUOeuHYzIfc2dcmoc/h4='
+ pubkey_2 = '8pbMHiQ7NECVP7F65Mb2W8+4ldGG2oaGvDSpSEsOBn8='
+
+ self.cli_set(base_path + [interface, 'address', '172.16.0.1/24'])
+ self.cli_set(base_path + [interface, 'port', port])
+ self.cli_set(base_path + [interface, 'private-key', privkey])
+
+ self.cli_set(base_path + [interface, 'peer', 'VyOS', 'public-key', pubkey_1])
+ self.cli_set(base_path + [interface, 'peer', 'VyOS', 'allowed-ips', '10.205.212.10/32'])
+
+ self.cli_commit()
+
+ peers = get_peers(interface)
+ self.assertIn(pubkey_1, peers)
+ self.assertNotIn(pubkey_2, peers)
+
+ # Now change the public key of our peer
+ self.cli_set(base_path + [interface, 'peer', 'VyOS', 'public-key', pubkey_2])
+ self.cli_commit()
+
+ # Verify config
+ peers = get_peers(interface)
+ self.assertNotIn(pubkey_1, peers)
+ self.assertIn(pubkey_2, peers)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
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..1e6435df8 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -82,12 +82,12 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
# or configured destination address for NAT
if int(rule) < 200:
self.cli_set(src_path + ['rule', rule, 'source', 'address', network])
- self.cli_set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_100])
+ self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'name', outbound_iface_100])
self.cli_set(src_path + ['rule', rule, 'translation', 'address', 'masquerade'])
nftables_search.append([f'saddr {network}', f'oifname "{outbound_iface_100}"', 'masquerade'])
else:
self.cli_set(src_path + ['rule', rule, 'destination', 'address', network])
- self.cli_set(src_path + ['rule', rule, 'outbound-interface', outbound_iface_200])
+ self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'name', outbound_iface_200])
self.cli_set(src_path + ['rule', rule, 'exclude'])
nftables_search.append([f'daddr {network}', f'oifname "{outbound_iface_200}"', 'return'])
@@ -98,13 +98,15 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
def test_snat_groups(self):
address_group = 'smoketest_addr'
address_group_member = '192.0.2.1'
+ interface_group = 'smoketest_ifaces'
+ interface_group_member = 'bond.99'
rule = '100'
- outbound_iface = 'eth0'
self.cli_set(['firewall', 'group', 'address-group', address_group, 'address', address_group_member])
+ self.cli_set(['firewall', 'group', 'interface-group', interface_group, 'interface', interface_group_member])
self.cli_set(src_path + ['rule', rule, 'source', 'group', 'address-group', address_group])
- self.cli_set(src_path + ['rule', rule, 'outbound-interface', outbound_iface])
+ self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'group', interface_group])
self.cli_set(src_path + ['rule', rule, 'translation', 'address', 'masquerade'])
self.cli_commit()
@@ -112,7 +114,7 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
nftables_search = [
[f'set A_{address_group}'],
[f'elements = {{ {address_group_member} }}'],
- [f'ip saddr @A_{address_group}', f'oifname "{outbound_iface}"', 'masquerade']
+ [f'ip saddr @A_{address_group}', f'oifname @I_{interface_group}', 'masquerade']
]
self.verify_nftables(nftables_search, 'ip vyos_nat')
@@ -136,12 +138,12 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
rule_search = [f'dnat to 192.0.2.1:{port}']
if int(rule) < 200:
self.cli_set(dst_path + ['rule', rule, 'protocol', inbound_proto_100])
- self.cli_set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_100])
+ self.cli_set(dst_path + ['rule', rule, 'inbound-interface', 'name', inbound_iface_100])
rule_search.append(f'{inbound_proto_100} sport {port}')
rule_search.append(f'iifname "{inbound_iface_100}"')
else:
self.cli_set(dst_path + ['rule', rule, 'protocol', inbound_proto_200])
- self.cli_set(dst_path + ['rule', rule, 'inbound-interface', inbound_iface_200])
+ self.cli_set(dst_path + ['rule', rule, 'inbound-interface', 'name', inbound_iface_200])
rule_search.append(f'iifname "{inbound_iface_200}"')
nftables_search.append(rule_search)
@@ -155,11 +157,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()
@@ -172,7 +169,7 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
rule = '1000'
self.cli_set(dst_path + ['rule', rule, 'destination', 'address', '!192.0.2.1'])
self.cli_set(dst_path + ['rule', rule, 'destination', 'port', '53'])
- self.cli_set(dst_path + ['rule', rule, 'inbound-interface', 'eth0'])
+ self.cli_set(dst_path + ['rule', rule, 'inbound-interface', 'name', 'eth0'])
self.cli_set(dst_path + ['rule', rule, 'protocol', 'tcp_udp'])
self.cli_set(dst_path + ['rule', rule, 'source', 'address', '!192.0.2.1'])
self.cli_set(dst_path + ['rule', rule, 'translation', 'address', '192.0.2.1'])
@@ -191,7 +188,7 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
def test_dnat_without_translation_address(self):
- self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'name', 'eth1'])
self.cli_set(dst_path + ['rule', '1', 'destination', 'port', '443'])
self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp'])
self.cli_set(dst_path + ['rule', '1', 'packet-type', 'host'])
@@ -241,13 +238,13 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.cli_set(dst_path + ['rule', '10', 'destination', 'address', dst_addr_1])
self.cli_set(dst_path + ['rule', '10', 'destination', 'port', dest_port])
self.cli_set(dst_path + ['rule', '10', 'protocol', protocol])
- self.cli_set(dst_path + ['rule', '10', 'inbound-interface', ifname])
+ self.cli_set(dst_path + ['rule', '10', 'inbound-interface', 'name', ifname])
self.cli_set(dst_path + ['rule', '10', 'translation', 'redirect', 'port', redirected_port])
self.cli_set(dst_path + ['rule', '20', 'destination', 'address', dst_addr_1])
self.cli_set(dst_path + ['rule', '20', 'destination', 'port', dest_port])
self.cli_set(dst_path + ['rule', '20', 'protocol', protocol])
- self.cli_set(dst_path + ['rule', '20', 'inbound-interface', ifname])
+ self.cli_set(dst_path + ['rule', '20', 'inbound-interface', 'name', ifname])
self.cli_set(dst_path + ['rule', '20', 'translation', 'redirect'])
self.cli_commit()
@@ -271,7 +268,7 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
weight_4 = '65'
dst_port = '443'
- self.cli_set(dst_path + ['rule', '1', 'inbound-interface', ifname])
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'name', ifname])
self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp'])
self.cli_set(dst_path + ['rule', '1', 'destination', 'port', dst_port])
self.cli_set(dst_path + ['rule', '1', 'load-balance', 'hash', 'source-address'])
@@ -281,7 +278,7 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.cli_set(dst_path + ['rule', '1', 'load-balance', 'backend', member_1, 'weight', weight_1])
self.cli_set(dst_path + ['rule', '1', 'load-balance', 'backend', member_2, 'weight', weight_2])
- self.cli_set(src_path + ['rule', '1', 'outbound-interface', ifname])
+ self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'name', ifname])
self.cli_set(src_path + ['rule', '1', 'load-balance', 'hash', 'random'])
self.cli_set(src_path + ['rule', '1', 'load-balance', 'backend', member_3, 'weight', weight_3])
self.cli_set(src_path + ['rule', '1', 'load-balance', 'backend', member_4, 'weight', weight_4])
@@ -295,5 +292,25 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_nat')
+ def test_snat_net_port_map(self):
+ self.cli_set(src_path + ['rule', '10', 'protocol', 'tcp_udp'])
+ self.cli_set(src_path + ['rule', '10', 'source', 'address', '100.64.0.0/25'])
+ self.cli_set(src_path + ['rule', '10', 'translation', 'address', '203.0.113.0/25'])
+ self.cli_set(src_path + ['rule', '10', 'translation', 'port', '1025-3072'])
+
+ self.cli_set(src_path + ['rule', '20', 'protocol', 'tcp_udp'])
+ self.cli_set(src_path + ['rule', '20', 'source', 'address', '100.64.0.128/25'])
+ self.cli_set(src_path + ['rule', '20', 'translation', 'address', '203.0.113.128/25'])
+ self.cli_set(src_path + ['rule', '20', 'translation', 'port', '1025-3072'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['meta l4proto { tcp, udp }', 'snat ip prefix to ip saddr map { 100.64.0.0/25 : 203.0.113.0/25 . 1025-3072 }', 'comment "SRC-NAT-10"'],
+ ['meta l4proto { tcp, udp }', 'snat ip prefix to ip saddr map { 100.64.0.128/25 : 203.0.113.128/25 . 1025-3072 }', 'comment "SRC-NAT-20"']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_nat')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_nat64.py b/smoketest/scripts/cli/test_nat64.py
new file mode 100755
index 000000000..b5723ac7e
--- /dev/null
+++ b/smoketest/scripts/cli/test_nat64.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/>.
+
+import json
+import os
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
+from vyos.utils.process import cmd
+from vyos.utils.dict import dict_search
+
+base_path = ['nat64']
+src_path = base_path + ['source']
+
+jool_nat64_config = '/run/jool/instance-100.json'
+
+
+class TestNAT64(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestNAT64, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+ self.assertFalse(os.path.exists(jool_nat64_config))
+
+ def test_snat64(self):
+ rule = '100'
+ translation_rule = '10'
+ prefix_v6 = '64:ff9b::/96'
+ pool = '192.0.2.10'
+ pool_port = '1-65535'
+
+ self.cli_set(src_path + ['rule', rule, 'source', 'prefix', prefix_v6])
+ self.cli_set(
+ src_path
+ + ['rule', rule, 'translation', 'pool', translation_rule, 'address', pool]
+ )
+ self.cli_set(
+ src_path
+ + ['rule', rule, 'translation', 'pool', translation_rule, 'port', pool_port]
+ )
+ self.cli_commit()
+
+ # Load the JSON file
+ with open(f'/run/jool/instance-{rule}.json', 'r') as json_file:
+ config_data = json.load(json_file)
+
+ # Assertions based on the content of the JSON file
+ self.assertEqual(config_data['instance'], f'instance-{rule}')
+ self.assertEqual(config_data['framework'], 'netfilter')
+ self.assertEqual(config_data['global']['pool6'], prefix_v6)
+ self.assertTrue(config_data['global']['manually-enabled'])
+
+ # Check the pool4 entries
+ pool4_entries = config_data.get('pool4', [])
+ self.assertIsInstance(pool4_entries, list)
+ self.assertGreater(len(pool4_entries), 0)
+
+ for entry in pool4_entries:
+ self.assertIn('protocol', entry)
+ self.assertIn('prefix', entry)
+ self.assertIn('port range', entry)
+
+ protocol = entry['protocol']
+ prefix = entry['prefix']
+ port_range = entry['port range']
+
+ if protocol == 'ICMP':
+ self.assertEqual(prefix, pool)
+ self.assertEqual(port_range, pool_port)
+ elif protocol == 'UDP':
+ self.assertEqual(prefix, pool)
+ self.assertEqual(port_range, pool_port)
+ elif protocol == 'TCP':
+ self.assertEqual(prefix, pool)
+ self.assertEqual(port_range, pool_port)
+ else:
+ self.fail(f'Unexpected protocol: {protocol}')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index e062f28a6..0607f6616 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -56,15 +56,15 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
def test_source_nat66(self):
source_prefix = 'fc00::/64'
translation_prefix = 'fc01::/64'
- self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'eth1'])
+ self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'name', 'eth1'])
self.cli_set(src_path + ['rule', '1', 'source', 'prefix', source_prefix])
self.cli_set(src_path + ['rule', '1', 'translation', 'address', translation_prefix])
- self.cli_set(src_path + ['rule', '2', 'outbound-interface', 'eth1'])
+ self.cli_set(src_path + ['rule', '2', 'outbound-interface', 'name', 'eth1'])
self.cli_set(src_path + ['rule', '2', 'source', 'prefix', source_prefix])
self.cli_set(src_path + ['rule', '2', 'translation', 'address', 'masquerade'])
- self.cli_set(src_path + ['rule', '3', 'outbound-interface', 'eth1'])
+ self.cli_set(src_path + ['rule', '3', 'outbound-interface', 'name', 'eth1'])
self.cli_set(src_path + ['rule', '3', 'source', 'prefix', source_prefix])
self.cli_set(src_path + ['rule', '3', 'exclude'])
@@ -81,7 +81,7 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
def test_source_nat66_address(self):
source_prefix = 'fc00::/64'
translation_address = 'fc00::1'
- self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'eth1'])
+ self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'name', 'eth1'])
self.cli_set(src_path + ['rule', '1', 'source', 'prefix', source_prefix])
self.cli_set(src_path + ['rule', '1', 'translation', 'address', translation_address])
@@ -98,11 +98,11 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
destination_address = 'fc00::1'
translation_address = 'fc01::1'
source_address = 'fc02::1'
- self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'name', 'eth1'])
self.cli_set(dst_path + ['rule', '1', 'destination', 'address', destination_address])
self.cli_set(dst_path + ['rule', '1', 'translation', 'address', translation_address])
- self.cli_set(dst_path + ['rule', '2', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '2', 'inbound-interface', 'name', 'eth1'])
self.cli_set(dst_path + ['rule', '2', 'destination', 'address', destination_address])
self.cli_set(dst_path + ['rule', '2', 'source', 'address', source_address])
self.cli_set(dst_path + ['rule', '2', 'exclude'])
@@ -124,7 +124,7 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
sport = '8080'
tport = '5555'
proto = 'tcp'
- self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'name', 'eth1'])
self.cli_set(dst_path + ['rule', '1', 'destination', 'port', dport])
self.cli_set(dst_path + ['rule', '1', 'source', 'address', source_prefix])
self.cli_set(dst_path + ['rule', '1', 'source', 'port', sport])
@@ -144,7 +144,7 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
def test_destination_nat66_prefix(self):
destination_prefix = 'fc00::/64'
translation_prefix = 'fc01::/64'
- self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'name', 'eth1'])
self.cli_set(dst_path + ['rule', '1', 'destination', 'address', destination_prefix])
self.cli_set(dst_path + ['rule', '1', 'translation', 'address', translation_prefix])
@@ -158,7 +158,7 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip6 vyos_nat')
def test_destination_nat66_without_translation_address(self):
- self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1'])
+ self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'name', 'eth1'])
self.cli_set(dst_path + ['rule', '1', 'destination', 'port', '443'])
self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp'])
self.cli_set(dst_path + ['rule', '1', 'translation', 'port', '443'])
@@ -180,7 +180,7 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
# check validate() - outbound-interface must be defined
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'eth0'])
+ self.cli_set(src_path + ['rule', rule, 'outbound-interface', 'name', 'eth0'])
# check validate() - translation address not specified
with self.assertRaises(ConfigSessionError):
@@ -196,7 +196,7 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
sport = '8080'
tport = '80'
proto = 'tcp'
- self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'eth1'])
+ self.cli_set(src_path + ['rule', '1', 'outbound-interface', 'name', 'eth1'])
self.cli_set(src_path + ['rule', '1', 'destination', 'port', dport])
self.cli_set(src_path + ['rule', '1', 'source', 'prefix', source_prefix])
self.cli_set(src_path + ['rule', '1', 'source', 'port', sport])
diff --git a/smoketest/scripts/cli/test_interfaces_netns.py b/smoketest/scripts/cli/test_netns.py
index b8bebb221..b8bebb221 100755
--- a/smoketest/scripts/cli/test_interfaces_netns.py
+++ b/smoketest/scripts/cli/test_netns.py
diff --git a/smoketest/scripts/cli/test_pki.py b/smoketest/scripts/cli/test_pki.py
index b18b0b039..02beafb26 100755
--- a/smoketest/scripts/cli/test_pki.py
+++ b/smoketest/scripts/cli/test_pki.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 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
@@ -19,6 +19,8 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.utils.file import read_file
+
base_path = ['pki']
valid_ca_cert = """
@@ -153,10 +155,10 @@ class TestPKI(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestPKI, cls).setUpClass()
-
# 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, ['service', 'https'])
def tearDown(self):
self.cli_delete(base_path)
@@ -181,92 +183,72 @@ class TestPKI(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
def test_invalid_ca_valid_certificate(self):
- self.cli_set(base_path + ['ca', 'smoketest', 'certificate', valid_cert.replace('\n','')])
-
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- def test_invalid_certificate(self):
- self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', 'invalidcertdata'])
-
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- def test_invalid_public_key(self):
- self.cli_set(base_path + ['key-pair', 'smoketest', 'public', 'key', 'invalidkeydata'])
-
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- def test_invalid_private_key(self):
- self.cli_set(base_path + ['key-pair', 'smoketest', 'private', 'key', 'invalidkeydata'])
-
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- def test_invalid_dh_parameters(self):
- self.cli_set(base_path + ['dh', 'smoketest', 'parameters', 'thisisinvalid'])
+ self.cli_set(base_path + ['ca', 'invalid-ca', 'certificate', valid_cert.replace('\n','')])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
def test_certificate_in_use(self):
- self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', valid_ca_cert.replace('\n','')])
- self.cli_set(base_path + ['certificate', 'smoketest', 'private', 'key', valid_ca_private_key.replace('\n','')])
+ cert_name = 'smoketest'
+
+ self.cli_set(base_path + ['certificate', cert_name, 'certificate', valid_ca_cert.replace('\n','')])
+ self.cli_set(base_path + ['certificate', cert_name, 'private', 'key', valid_ca_private_key.replace('\n','')])
self.cli_commit()
- self.cli_set(['service', 'https', 'certificates', 'certificate', 'smoketest'])
+ self.cli_set(['service', 'https', 'certificates', 'certificate', cert_name])
self.cli_commit()
- self.cli_delete(base_path + ['certificate', 'smoketest'])
+ self.cli_delete(base_path + ['certificate', cert_name])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(['service', 'https', 'certificates', 'certificate'])
def test_certificate_https_update(self):
- self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', valid_ca_cert.replace('\n','')])
- self.cli_set(base_path + ['certificate', 'smoketest', 'private', 'key', valid_ca_private_key.replace('\n','')])
+ cert_name = 'smoke-test_foo'
+ cert_path = f'/run/nginx/certs/{cert_name}_cert.pem'
+ self.cli_set(base_path + ['certificate', cert_name, 'certificate', valid_ca_cert.replace('\n','')])
+ self.cli_set(base_path + ['certificate', cert_name, 'private', 'key', valid_ca_private_key.replace('\n','')])
self.cli_commit()
- self.cli_set(['service', 'https', 'certificates', 'certificate', 'smoketest'])
+ self.cli_set(['service', 'https', 'certificates', 'certificate', cert_name])
self.cli_commit()
cert_data = None
- with open('/etc/ssl/certs/smoketest.pem') as f:
- cert_data = f.read()
+ cert_data = read_file(cert_path)
- self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', valid_update_cert.replace('\n','')])
- self.cli_set(base_path + ['certificate', 'smoketest', 'private', 'key', valid_update_private_key.replace('\n','')])
+ self.cli_set(base_path + ['certificate', cert_name, 'certificate', valid_update_cert.replace('\n','')])
+ self.cli_set(base_path + ['certificate', cert_name, 'private', 'key', valid_update_private_key.replace('\n','')])
self.cli_commit()
- with open('/etc/ssl/certs/smoketest.pem') as f:
- self.assertNotEqual(cert_data, f.read())
+ self.assertNotEqual(cert_data, read_file(cert_path))
self.cli_delete(['service', 'https', 'certificates', 'certificate'])
def test_certificate_eapol_update(self):
- self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', valid_ca_cert.replace('\n','')])
- self.cli_set(base_path + ['certificate', 'smoketest', 'private', 'key', valid_ca_private_key.replace('\n','')])
+ cert_name = 'eapol'
+ interface = 'eth1'
+ self.cli_set(base_path + ['certificate', cert_name, 'certificate', valid_ca_cert.replace('\n','')])
+ self.cli_set(base_path + ['certificate', cert_name, 'private', 'key', valid_ca_private_key.replace('\n','')])
self.cli_commit()
- self.cli_set(['interfaces', 'ethernet', 'eth1', 'eapol', 'certificate', 'smoketest'])
+ self.cli_set(['interfaces', 'ethernet', interface, 'eapol', 'certificate', cert_name])
self.cli_commit()
cert_data = None
- with open('/run/wpa_supplicant/eth1_cert.pem') as f:
+ with open(f'/run/wpa_supplicant/{interface}_cert.pem') as f:
cert_data = f.read()
- self.cli_set(base_path + ['certificate', 'smoketest', 'certificate', valid_update_cert.replace('\n','')])
- self.cli_set(base_path + ['certificate', 'smoketest', 'private', 'key', valid_update_private_key.replace('\n','')])
+ self.cli_set(base_path + ['certificate', cert_name, 'certificate', valid_update_cert.replace('\n','')])
+ self.cli_set(base_path + ['certificate', cert_name, 'private', 'key', valid_update_private_key.replace('\n','')])
self.cli_commit()
- with open('/run/wpa_supplicant/eth1_cert.pem') as f:
+ with open(f'/run/wpa_supplicant/{interface}_cert.pem') as f:
self.assertNotEqual(cert_data, f.read())
- self.cli_delete(['interfaces', 'ethernet', 'eth1', 'eapol'])
+ self.cli_delete(['interfaces', 'ethernet', interface, 'eapol'])
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..c21d8af4e 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -1107,6 +1107,33 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
'metric' : '-20',
},
},
+ '30': {
+ 'action': 'permit',
+ 'match': {
+ 'ip-nexthop-addr': ipv4_nexthop_address,
+ },
+ 'set': {
+ 'metric': 'rtt',
+ },
+ },
+ '40': {
+ 'action': 'permit',
+ 'match': {
+ 'ip-nexthop-addr': ipv4_nexthop_address,
+ },
+ 'set': {
+ 'metric': '+rtt',
+ },
+ },
+ '50': {
+ 'action': 'permit',
+ 'match': {
+ 'ip-nexthop-addr': ipv4_nexthop_address,
+ },
+ 'set': {
+ 'metric': '-rtt',
+ },
+ },
},
},
}
@@ -1467,7 +1494,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 +1535,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 +1546,78 @@ 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 destination, source, protocol, fwmark and port
+ def test_protocol_port_address_fwmark_table_id(self):
+ path = base_path + ['local-route']
+
+ dst = '203.0.113.5'
+ src_list = ['203.0.113.1', '203.0.113.2']
+ rule = '23'
+ fwmark = '123456'
+ table = '123'
+ new_table = '111'
+ proto = 'udp'
+ new_proto = 'tcp'
+ src_port = '5555'
+ dst_port = '8888'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'destination', 'address', dst])
+ self.cli_set(path + ['rule', rule, 'source', 'port', src_port])
+ self.cli_set(path + ['rule', rule, 'protocol', proto])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmark])
+ self.cli_set(path + ['rule', rule, 'destination', 'port', dst_port])
+ for src in src_list:
+ self.cli_set(path + ['rule', rule, 'source', 'address', src])
+
+ self.cli_commit()
+
+ original = """
+ 23: from 203.0.113.1 to 203.0.113.5 fwmark 0x1e240 ipproto udp sport 5555 dport 8888 lookup 123
+ 23: from 203.0.113.2 to 203.0.113.5 fwmark 0x1e240 ipproto udp sport 5555 dport 8888 lookup 123
+ """
+ tmp = cmd(f'ip rule show prio {rule}')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Change table and protocol, delete fwmark and source port
+ self.cli_delete(path + ['rule', rule, 'fwmark'])
+ self.cli_delete(path + ['rule', rule, 'source', 'port'])
+ self.cli_set(path + ['rule', rule, 'set', 'table', new_table])
+ self.cli_set(path + ['rule', rule, 'protocol', new_proto])
+
+ self.cli_commit()
+
+ original = """
+ 23: from 203.0.113.1 to 203.0.113.5 ipproto tcp dport 8888 lookup 111
+ 23: from 203.0.113.2 to 203.0.113.5 ipproto tcp dport 8888 lookup 111
+ """
+ tmp = cmd(f'ip rule show prio {rule}')
+
+ 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 +1628,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 +1653,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 +1679,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 +1704,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 +1745,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 +1766,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 +1789,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 +1816,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 +1847,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 +1898,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 +1911,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..c0b7c1fe7 100755
--- a/smoketest/scripts/cli/test_policy_route.py
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -33,6 +33,9 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestPolicyRoute, cls).setUpClass()
+ # Clear out current configuration to allow running this test on a live system
+ cls.cli_delete(cls, ['policy', 'route'])
+ cls.cli_delete(cls, ['policy', 'route6'])
cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip])
cls.cli_set(cls, ['protocols', 'static', 'table', table_id, 'route', '0.0.0.0/0', 'interface', interface])
@@ -189,29 +192,35 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
def test_pbr_matching_criteria(self):
+ self.cli_set(['policy', 'route', 'smoketest', 'default-log'])
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', 'state', 'new'])
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'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '128'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '1024-2048'])
self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-type', 'other'])
- self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'log', 'enable'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'log'])
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', 'default-log'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'udp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'action', 'drop'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '2', 'protocol', 'tcp'])
@@ -221,7 +230,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'source', 'address', '2001:db8::0/64'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'protocol', 'tcp'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'destination', 'port', '22'])
- self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'state', 'new', 'enable'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'state', 'new'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'hop-limit', 'gt', '2'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '3', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'protocol', 'icmpv6'])
@@ -230,7 +239,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '1024-2048'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-type', 'multicast'])
- self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'log', 'enable'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'log'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'set', 'table', table_id])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '61'])
self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '14-19'])
@@ -247,11 +256,12 @@ 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],
+ ['log prefix "[ipv4-smoketest-default]"']
]
self.verify_nftables(nftables_search, 'ip vyos_mangle')
@@ -262,8 +272,9 @@ 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],
- ['ip6 dscp != { 0x0e-0x13, 0x3d }', '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],
+ ['log prefix "[ipv6-smoketest6-default]"']
]
self.verify_nftables(nftables6_search, 'ip6 vyos_mangle')
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index 451565664..f209eae3a 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.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
@@ -77,11 +77,23 @@ profiles = {
}
class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestProtocolsBFD, cls).setUpClass()
+
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def test_bfd_peer(self):
self.cli_set(['vrf', 'name', vrf_name, 'table', '1000'])
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 77952d8d9..d5efae12c 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -16,12 +16,15 @@
import unittest
+from time import sleep
+
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.ifconfig import Section
from vyos.configsession import ConfigSessionError
from vyos.template import is_ipv6
from vyos.utils.process import process_named_running
+from vyos.utils.process import cmd
PROCESS_NAME = 'bgpd'
ASN = '64512'
@@ -44,6 +47,7 @@ neighbor_config = {
'bfd' : '',
'cap_dynamic' : '',
'cap_ext_next' : '',
+ 'cap_ext_sver' : '',
'remote_as' : '100',
'adv_interv' : '400',
'passive' : '',
@@ -71,6 +75,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 +92,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 +143,7 @@ peer_group_config = {
'remote_as' : '111',
'graceful_rst_no' : '',
'port' : '667',
+ 'p_attr_taw' : '126',
},
'foo-bar' : {
'advertise_map' : route_map_in,
@@ -170,9 +177,16 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def setUpClass(cls):
super(TestProtocolsBGP, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+
# 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, ['policy', 'route-map'])
+ cls.cli_delete(cls, ['policy', 'prefix-list'])
+ cls.cli_delete(cls, ['policy', 'prefix-list6'])
+ cls.cli_delete(cls, ['vrf'])
cls.cli_set(cls, ['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit'])
@@ -188,18 +202,23 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
@classmethod
def tearDownClass(cls):
- cls.cli_delete(cls, ['policy'])
+ cls.cli_delete(cls, ['policy', 'route-map'])
+ cls.cli_delete(cls, ['policy', 'prefix-list'])
+ cls.cli_delete(cls, ['policy', 'prefix-list6'])
def setUp(self):
self.cli_set(base_path + ['system-as', ASN])
def tearDown(self):
+ # cleanup any possible VRF mess
self.cli_delete(['vrf'])
+ # always destrox the entire bgpd configuration to make the processes
+ # life as hard as possible
self.cli_delete(base_path)
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def create_bgp_instances_for_import_test(self):
table = '1000'
@@ -217,6 +236,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 +285,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:
@@ -332,6 +355,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
# AFI maximum path support
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4])
self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp])
+ self.cli_set(base_path + ['address-family', 'ipv4-labeled-unicast', 'maximum-paths', 'ebgp', max_path_v4])
+ self.cli_set(base_path + ['address-family', 'ipv4-labeled-unicast', 'maximum-paths', 'ibgp', max_path_v4ibgp])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ebgp', max_path_v6])
self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'maximum-paths', 'ibgp', max_path_v6ibgp])
@@ -365,6 +390,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
self.assertIn(f' maximum-paths ibgp {max_path_v4ibgp}', afiv4_config)
+ afiv4_config = self.getFRRconfig(' address-family ipv4 labeled-unicast')
+ self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
+ self.assertIn(f' maximum-paths ibgp {max_path_v4ibgp}', afiv4_config)
+
afiv6_config = self.getFRRconfig(' address-family ipv6 unicast')
self.assertIn(f' maximum-paths {max_path_v6}', afiv6_config)
self.assertIn(f' maximum-paths ibgp {max_path_v6ibgp}', afiv6_config)
@@ -390,6 +419,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 +455,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 +523,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 +579,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:
@@ -706,15 +743,25 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def test_bgp_07_l2vpn_evpn(self):
vnis = ['10010', '10020', '10030']
neighbors = ['192.0.2.10', '192.0.2.20', '192.0.2.30']
+ evi_limit = '1000'
+ route_targets = ['1.1.1.1:100', '1.1.1.1:200', '1.1.1.1:300']
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-all-vni'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'advertise-svi-ip'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'flooding', 'disable'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'default-originate', 'ipv4'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'default-originate', 'ipv6'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'disable-ead-evi-rx'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'disable-ead-evi-tx'])
for vni in vnis:
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-default-gw'])
self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'vni', vni, 'advertise-svi-ip'])
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'ead-es-frag', 'evi-limit', evi_limit])
+ for route_target in route_targets:
+ self.cli_set(base_path + ['address-family', 'l2vpn-evpn', 'ead-es-route-target', 'export', route_target])
+
# commit changes
self.cli_commit()
@@ -725,12 +772,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' advertise-all-vni', frrconfig)
self.assertIn(f' advertise-default-gw', frrconfig)
self.assertIn(f' advertise-svi-ip', frrconfig)
+ self.assertIn(f' default-originate ipv4', frrconfig)
+ self.assertIn(f' default-originate ipv6', frrconfig)
+ self.assertIn(f' disable-ead-evi-rx', frrconfig)
+ self.assertIn(f' disable-ead-evi-tx', frrconfig)
self.assertIn(f' flooding disable', frrconfig)
for vni in vnis:
vniconfig = self.getFRRconfig(f' vni {vni}')
self.assertIn(f'vni {vni}', vniconfig)
self.assertIn(f' advertise-default-gw', vniconfig)
self.assertIn(f' advertise-svi-ip', vniconfig)
+ self.assertIn(f' ead-es-frag evi-limit {evi_limit}', frrconfig)
+ for route_target in route_targets:
+ self.assertIn(f' ead-es-route-target export {route_target}', frrconfig)
+
def test_bgp_09_distance_and_flowspec(self):
distance_external = '25'
@@ -1081,5 +1136,112 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' mpls bgp forwarding', frrconfig)
self.cli_delete(['interfaces', 'ethernet', interface, 'vrf'])
+ def test_bgp_24_srv6_sid(self):
+ locator_name = 'VyOS_foo'
+ sid = 'auto'
+
+ self.cli_set(base_path + ['srv6', 'locator', locator_name])
+ self.cli_set(base_path + ['sid', 'vpn', 'per-vrf', 'export', sid])
+
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ self.assertIn(f'router bgp {ASN}', frrconfig)
+ self.assertIn(f' segment-routing srv6', frrconfig)
+ self.assertIn(f' locator {locator_name}', frrconfig)
+ self.assertIn(f' sid vpn per-vrf export {sid}', frrconfig)
+
+ def test_bgp_25_ipv4_ipv6_labeled_unicast_peer_group(self):
+ pg_ipv4 = 'foo4'
+ pg_ipv6 = 'foo6'
+
+ ipv4_max_prefix = '20'
+ ipv6_max_prefix = '200'
+ ipv4_prefix = '192.0.2.0/24'
+ ipv6_prefix = '2001:db8:1000::/64'
+
+ self.cli_set(base_path + ['listen', 'range', ipv4_prefix, 'peer-group', pg_ipv4])
+ self.cli_set(base_path + ['listen', 'range', ipv6_prefix, 'peer-group', pg_ipv6])
+
+ self.cli_set(base_path + ['peer-group', pg_ipv4, 'address-family', 'ipv4-labeled-unicast', 'maximum-prefix', ipv4_max_prefix])
+ self.cli_set(base_path + ['peer-group', pg_ipv4, 'remote-as', 'external'])
+ self.cli_set(base_path + ['peer-group', pg_ipv6, 'address-family', 'ipv6-labeled-unicast', 'maximum-prefix', ipv6_max_prefix])
+ self.cli_set(base_path + ['peer-group', pg_ipv6, 'remote-as', 'external'])
+
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ self.assertIn(f'router bgp {ASN}', frrconfig)
+ self.assertIn(f' neighbor {pg_ipv4} peer-group', frrconfig)
+ self.assertIn(f' neighbor {pg_ipv4} remote-as external', frrconfig)
+ self.assertIn(f' neighbor {pg_ipv6} peer-group', frrconfig)
+ self.assertIn(f' neighbor {pg_ipv6} remote-as external', frrconfig)
+ self.assertIn(f' bgp listen range {ipv4_prefix} peer-group {pg_ipv4}', frrconfig)
+ self.assertIn(f' bgp listen range {ipv6_prefix} peer-group {pg_ipv6}', frrconfig)
+
+ afiv4_config = self.getFRRconfig(' address-family ipv4 labeled-unicast')
+ self.assertIn(f' neighbor {pg_ipv4} activate', afiv4_config)
+ self.assertIn(f' neighbor {pg_ipv4} maximum-prefix {ipv4_max_prefix}', afiv4_config)
+
+ afiv6_config = self.getFRRconfig(' address-family ipv6 labeled-unicast')
+ self.assertIn(f' neighbor {pg_ipv6} activate', afiv6_config)
+ self.assertIn(f' neighbor {pg_ipv6} maximum-prefix {ipv6_max_prefix}', afiv6_config)
+
+ def test_bgp_99_bmp(self):
+ target_name = 'instance-bmp'
+ target_address = '127.0.0.1'
+ target_port = '5000'
+ min_retry = '1024'
+ max_retry = '2048'
+ monitor_ipv4 = 'pre-policy'
+ monitor_ipv6 = 'pre-policy'
+ mirror_buffer = '32000000'
+ bmp_path = base_path + ['bmp']
+ target_path = bmp_path + ['target', target_name]
+
+ # by default the 'bmp' module not loaded for the bgpd expect Error
+ self.cli_set(bmp_path)
+ if not process_named_running('bgpd', 'bmp'):
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # add required 'bmp' module to bgpd and restart bgpd
+ self.cli_delete(bmp_path)
+ self.cli_set(['system', 'frr', 'bmp'])
+ self.cli_commit()
+
+ # restart bgpd to apply "-M bmp" and update PID
+ cmd(f'sudo kill -9 {self.daemon_pid}')
+ # let the bgpd process recover
+ sleep(10)
+ # update daemon PID - this was a planned daemon restart
+ self.daemon_pid = process_named_running(PROCESS_NAME)
+
+ # set bmp config but not set address
+ self.cli_set(target_path + ['port', target_port])
+ # address is not set, expect Error
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # config other bmp options
+ self.cli_set(target_path + ['address', target_address])
+ self.cli_set(bmp_path + ['mirror-buffer-limit', mirror_buffer])
+ self.cli_set(target_path + ['port', target_port])
+ self.cli_set(target_path + ['min-retry', min_retry])
+ self.cli_set(target_path + ['max-retry', max_retry])
+ self.cli_set(target_path + ['mirror'])
+ self.cli_set(target_path + ['monitor', 'ipv4-unicast', monitor_ipv4])
+ self.cli_set(target_path + ['monitor', 'ipv6-unicast', monitor_ipv6])
+ self.cli_commit()
+
+ # Verify bgpd bmp configuration
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ self.assertIn(f'bmp mirror buffer-limit {mirror_buffer}', frrconfig)
+ self.assertIn(f'bmp targets {target_name}', frrconfig)
+ self.assertIn(f'bmp mirror', frrconfig)
+ self.assertIn(f'bmp monitor ipv4 unicast {monitor_ipv4}', frrconfig)
+ self.assertIn(f'bmp monitor ipv6 unicast {monitor_ipv6}', frrconfig)
+ self.assertIn(f'bmp connect {target_address} port {target_port} min-retry {min_retry} max-retry {max_retry}', frrconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_igmp-proxy.py b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
index a75003b12..df10442ea 100755
--- a/smoketest/scripts/cli/test_protocols_igmp-proxy.py
+++ b/smoketest/scripts/cli/test_protocols_igmp-proxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 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
@@ -29,14 +29,32 @@ upstream_if = 'eth1'
downstream_if = 'eth2'
class TestProtocolsIGMPProxy(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['interfaces', 'ethernet', upstream_if, 'address', '172.16.1.1/24'])
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(TestProtocolsIGMPProxy, cls).setUpClass()
+ # 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_set(cls, ['interfaces', 'ethernet', upstream_if, 'address', '172.16.1.1/24'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'ethernet', upstream_if, 'address'])
+
+ # call base-classes classmethod
+ super(TestProtocolsIGMPProxy, cls).tearDownClass()
def tearDown(self):
- self.cli_delete(['interfaces', 'ethernet', upstream_if, 'address'])
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
self.cli_delete(base_path)
self.cli_commit()
+ # Check for no longer running process
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
def test_igmpproxy(self):
threshold = '20'
altnet = '192.0.2.0/24'
@@ -74,8 +92,5 @@ class TestProtocolsIGMPProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'whitelist {whitelist}', config)
self.assertIn(f'phyint {downstream_if} downstream ratelimit 0 threshold 1', config)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index 5ab7fae14..94f4ec5c9 100755
--- a/smoketest/scripts/cli/test_protocols_isis.py
+++ b/smoketest/scripts/cli/test_protocols_isis.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
@@ -31,20 +31,25 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
cls._interfaces = Section.interfaces('ethernet')
-
# call base-classes classmethod
super(TestProtocolsISIS, cls).setUpClass()
-
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
# 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):
+ # cleanup any possible VRF mess
+ self.cli_delete(['vrf'])
+ # always destrox the entire isisd configuration to make the processes
+ # life as hard as possible
self.cli_delete(base_path)
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def isis_base_config(self):
self.cli_set(base_path + ['net', net])
@@ -100,6 +105,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 +122,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'])
@@ -320,5 +329,65 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.assertIn(f' no isis mpls ldp-sync', tmp)
+ def test_isis_09_lfa(self):
+ prefix_list = 'lfa-prefix-list-test-1'
+ prefix_list_address = '192.168.255.255/32'
+ interface = 'lo'
+
+ self.cli_set(base_path + ['net', net])
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', '1', 'action', 'permit'])
+ self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', '1', 'prefix', prefix_list_address])
+
+ # Commit main ISIS changes
+ self.cli_commit()
+
+ # Add remote portion of LFA with prefix list with validation
+ for level in ['level-1', 'level-2']:
+ self.cli_set(base_path + ['fast-reroute', 'lfa', 'remote', 'prefix-list', prefix_list, level])
+ self.cli_commit()
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
+ self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' fast-reroute remote-lfa prefix-list {prefix_list} {level}', tmp)
+ self.cli_delete(base_path + ['fast-reroute'])
+ self.cli_commit()
+
+ # Add local portion of LFA load-sharing portion with validation
+ for level in ['level-1', 'level-2']:
+ self.cli_set(base_path + ['fast-reroute', 'lfa', 'local', 'load-sharing', 'disable', level])
+ self.cli_commit()
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
+ self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' fast-reroute load-sharing disable {level}', tmp)
+ self.cli_delete(base_path + ['fast-reroute'])
+ self.cli_commit()
+
+ # Add local portion of LFA priority-limit portion with validation
+ for priority in ['critical', 'high', 'medium']:
+ for level in ['level-1', 'level-2']:
+ self.cli_set(base_path + ['fast-reroute', 'lfa', 'local', 'priority-limit', priority, level])
+ self.cli_commit()
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
+ self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' fast-reroute priority-limit {priority} {level}', tmp)
+ self.cli_delete(base_path + ['fast-reroute'])
+ self.cli_commit()
+
+ # Add local portion of LFA tiebreaker portion with validation
+ index = '100'
+ for tiebreaker in ['downstream','lowest-backup-metric','node-protecting']:
+ for level in ['level-1', 'level-2']:
+ self.cli_set(base_path + ['fast-reroute', 'lfa', 'local', 'tiebreaker', tiebreaker, 'index', index, level])
+ self.cli_commit()
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
+ self.assertIn(f' net {net}', tmp)
+ self.assertIn(f' fast-reroute lfa tiebreaker {tiebreaker} index {index} {level}', tmp)
+ self.cli_delete(base_path + ['fast-reroute'])
+ self.cli_commit()
+
+ # Clean up and remove prefix list
+ self.cli_delete(['policy', 'prefix-list', prefix_list])
+ self.cli_commit()
+
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=2) \ No newline at end of file
diff --git a/smoketest/scripts/cli/test_protocols_mpls.py b/smoketest/scripts/cli/test_protocols_mpls.py
index 06f21c6e1..0c1599f9b 100755
--- a/smoketest/scripts/cli/test_protocols_mpls.py
+++ b/smoketest/scripts/cli/test_protocols_mpls.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
@@ -70,6 +70,9 @@ class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
def setUpClass(cls):
super(TestProtocolsMPLS, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
@@ -77,8 +80,9 @@ class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def test_mpls_basic(self):
router_id = '1.2.3.4'
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index a6850db71..6bffc7c45 100755
--- a/smoketest/scripts/cli/test_protocols_ospf.py
+++ b/smoketest/scripts/cli/test_protocols_ospf.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
@@ -32,6 +32,9 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
def setUpClass(cls):
super(TestProtocolsOSPF, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
@@ -45,11 +48,12 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
super(TestProtocolsOSPF, cls).tearDownClass()
def tearDown(self):
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
self.cli_delete(base_path)
self.cli_commit()
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
+
def test_ospf_01_defaults(self):
# commit changes
self.cli_set(base_path)
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index 0d6c6c691..4ae7f05d9 100755
--- a/smoketest/scripts/cli/test_protocols_ospfv3.py
+++ b/smoketest/scripts/cli/test_protocols_ospfv3.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
@@ -35,6 +35,9 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
def setUpClass(cls):
super(TestProtocolsOSPFv3, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
@@ -48,11 +51,12 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
super(TestProtocolsOSPFv3, cls).tearDownClass()
def tearDown(self):
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
self.cli_delete(base_path)
self.cli_commit()
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
+
def test_ospfv3_01_basic(self):
seq = '10'
prefix = '2001:db8::/32'
diff --git a/smoketest/scripts/cli/test_protocols_pim.py b/smoketest/scripts/cli/test_protocols_pim.py
new file mode 100755
index 000000000..ccfced138
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_pim.py
@@ -0,0 +1,192 @@
+#!/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 = 'pimd'
+base_path = ['protocols', 'pim']
+
+class TestProtocolsPIM(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # pimd process must be running
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # pimd process must be stopped by now
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_pim_basic(self):
+ rp = '127.0.0.1'
+ group = '224.0.0.0/4'
+ hello = '100'
+ dr_priority = '64'
+
+ self.cli_set(base_path + ['rp', 'address', rp, 'group', group])
+
+ interfaces = Section.interfaces('ethernet')
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface , 'bfd'])
+ self.cli_set(base_path + ['interface', interface , 'dr-priority', dr_priority])
+ self.cli_set(base_path + ['interface', interface , 'hello', hello])
+ self.cli_set(base_path + ['interface', interface , 'no-bsm'])
+ self.cli_set(base_path + ['interface', interface , 'no-unicast-bsm'])
+ self.cli_set(base_path + ['interface', interface , 'passive'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR pimd configuration
+ frrconfig = self.getFRRconfig(daemon=PROCESS_NAME)
+ self.assertIn(f'ip pim rp {rp} {group}', frrconfig)
+
+ for interface in interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertIn(f'interface {interface}', frrconfig)
+ self.assertIn(f' ip pim', frrconfig)
+ self.assertIn(f' ip pim bfd', frrconfig)
+ self.assertIn(f' ip pim drpriority {dr_priority}', frrconfig)
+ self.assertIn(f' ip pim hello {hello}', frrconfig)
+ self.assertIn(f' no ip pim bsm', frrconfig)
+ self.assertIn(f' no ip pim unicast-bsm', frrconfig)
+ self.assertIn(f' ip pim passive', frrconfig)
+
+ self.cli_commit()
+
+ def test_02_pim_advanced(self):
+ rp = '127.0.0.2'
+ group = '224.0.0.0/4'
+ join_prune_interval = '123'
+ rp_keep_alive_timer = '190'
+ keep_alive_timer = '180'
+ packets = '10'
+ prefix_list = 'pim-test'
+ register_suppress_time = '300'
+
+ self.cli_set(base_path + ['rp', 'address', rp, 'group', group])
+ self.cli_set(base_path + ['rp', 'keep-alive-timer', rp_keep_alive_timer])
+
+ self.cli_set(base_path + ['ecmp', 'rebalance'])
+ self.cli_set(base_path + ['join-prune-interval', join_prune_interval])
+ self.cli_set(base_path + ['keep-alive-timer', keep_alive_timer])
+ self.cli_set(base_path + ['packets', packets])
+ self.cli_set(base_path + ['register-accept-list', 'prefix-list', prefix_list])
+ self.cli_set(base_path + ['register-suppress-time', register_suppress_time])
+ self.cli_set(base_path + ['no-v6-secondary'])
+ self.cli_set(base_path + ['spt-switchover', 'infinity-and-beyond', 'prefix-list', prefix_list])
+ self.cli_set(base_path + ['ssm', 'prefix-list', prefix_list])
+
+ # check validate() - PIM require defined interfaces!
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ interfaces = Section.interfaces('ethernet')
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR pimd configuration
+ frrconfig = self.getFRRconfig(daemon=PROCESS_NAME)
+ self.assertIn(f'ip pim rp {rp} {group}', frrconfig)
+ self.assertIn(f'ip pim rp keep-alive-timer {rp_keep_alive_timer}', frrconfig)
+ self.assertIn(f'ip pim ecmp rebalance', frrconfig)
+ self.assertIn(f'ip pim join-prune-interval {join_prune_interval}', frrconfig)
+ self.assertIn(f'ip pim keep-alive-timer {keep_alive_timer}', frrconfig)
+ self.assertIn(f'ip pim packets {packets}', frrconfig)
+ self.assertIn(f'ip pim register-accept-list {prefix_list}', frrconfig)
+ self.assertIn(f'ip pim register-suppress-time {register_suppress_time}', frrconfig)
+ self.assertIn(f'no ip pim send-v6-secondary', frrconfig)
+ self.assertIn(f'ip pim spt-switchover infinity-and-beyond prefix-list {prefix_list}', frrconfig)
+ self.assertIn(f'ip pim ssm prefix-list {prefix_list}', frrconfig)
+
+ def test_03_pim_igmp_proxy(self):
+ igmp_proxy = ['protocols', 'igmp-proxy']
+ rp = '127.0.0.1'
+ group = '224.0.0.0/4'
+
+ self.cli_set(base_path)
+ self.cli_set(igmp_proxy)
+
+ # check validate() - can not set both IGMP proxy and PIM
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(igmp_proxy)
+
+ self.cli_set(base_path + ['rp', 'address', rp, 'group', group])
+ interfaces = Section.interfaces('ethernet')
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface , 'bfd'])
+
+ # commit changes
+ self.cli_commit()
+
+ def test_04_igmp(self):
+ watermark_warning = '2000'
+ query_interval = '1000'
+ query_max_response_time = '200'
+ version = '2'
+
+ igmp_join = {
+ '224.1.1.1' : { 'source' : ['1.1.1.1', '2.2.2.2', '3.3.3.3'] },
+ '224.1.2.2' : { 'source' : [] },
+ '224.1.3.3' : {},
+ }
+
+ self.cli_set(base_path + ['igmp', 'watermark-warning', watermark_warning])
+ interfaces = Section.interfaces('ethernet')
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface , 'igmp', 'version', version])
+ self.cli_set(base_path + ['interface', interface , 'igmp', 'query-interval', query_interval])
+ self.cli_set(base_path + ['interface', interface , 'igmp', 'query-max-response-time', query_max_response_time])
+
+ for join, join_config in igmp_join.items():
+ self.cli_set(base_path + ['interface', interface , 'igmp', 'join', join])
+ if 'source' in join_config:
+ for source in join_config['source']:
+ self.cli_set(base_path + ['interface', interface , 'igmp', 'join', join, 'source-address', source])
+
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(daemon=PROCESS_NAME)
+ self.assertIn(f'ip igmp watermark-warn {watermark_warning}', frrconfig)
+
+ for interface in interfaces:
+ frrconfig = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertIn(f'interface {interface}', frrconfig)
+ self.assertIn(f' ip igmp', frrconfig)
+ self.assertIn(f' ip igmp version {version}', frrconfig)
+ self.assertIn(f' ip igmp query-interval {query_interval}', frrconfig)
+ self.assertIn(f' ip igmp query-max-response-time {query_max_response_time}', frrconfig)
+
+ for join, join_config in igmp_join.items():
+ if 'source' in join_config:
+ for source in join_config['source']:
+ self.assertIn(f' ip igmp join {join} {source}', frrconfig)
+ else:
+ self.assertIn(f' ip igmp join {join}', frrconfig)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_pim6.py b/smoketest/scripts/cli/test_protocols_pim6.py
new file mode 100755
index 000000000..ba24edca2
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_pim6.py
@@ -0,0 +1,146 @@
+#!/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):
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(TestProtocolsPIMv6, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
+
+ 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):
+ interfaces = Section.interfaces('ethernet')
+ # Use an invalid multicast 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 multicast 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)
+
+ def test_pim6_03_basic(self):
+ interfaces = Section.interfaces('ethernet')
+ join_prune_interval = '123'
+ keep_alive_timer = '77'
+ packets = '5'
+ register_suppress_time = '99'
+ dr_priority = '100'
+ hello = '50'
+
+ self.cli_set(base_path + ['join-prune-interval', join_prune_interval])
+ self.cli_set(base_path + ['keep-alive-timer', keep_alive_timer])
+ self.cli_set(base_path + ['packets', packets])
+ self.cli_set(base_path + ['register-suppress-time', register_suppress_time])
+
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'dr-priority', dr_priority])
+ self.cli_set(base_path + ['interface', interface, 'hello', hello])
+ self.cli_set(base_path + ['interface', interface, 'no-bsm'])
+ self.cli_set(base_path + ['interface', interface, 'no-unicast-bsm'])
+ self.cli_set(base_path + ['interface', interface, 'passive'])
+
+ self.cli_commit()
+
+ # Verify FRR pim6d configuration
+ config = self.getFRRconfig(daemon=PROCESS_NAME)
+ self.assertIn(f'ipv6 pim join-prune-interval {join_prune_interval}', config)
+ self.assertIn(f'ipv6 pim keep-alive-timer {keep_alive_timer}', config)
+ self.assertIn(f'ipv6 pim packets {packets}', config)
+ self.assertIn(f'ipv6 pim register-suppress-time {register_suppress_time}', config)
+
+ for interface in interfaces:
+ config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
+ self.assertIn(f' ipv6 pim drpriority {dr_priority}', config)
+ self.assertIn(f' ipv6 pim hello {hello}', config)
+ self.assertIn(f' no ipv6 pim bsm', config)
+ self.assertIn(f' no ipv6 pim unicast-bsm', config)
+ self.assertIn(f' ipv6 pim passive', config)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_rip.py b/smoketest/scripts/cli/test_protocols_rip.py
index 925499fc8..bfc327fd4 100755
--- a/smoketest/scripts/cli/test_protocols_rip.py
+++ b/smoketest/scripts/cli/test_protocols_rip.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
@@ -34,7 +34,8 @@ class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestProtocolsRIP, cls).setUpClass()
-
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
@@ -65,8 +66,8 @@ class TestProtocolsRIP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def test_rip_01_parameters(self):
distance = '40'
diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py
index 0a8ce7eef..0cfb065c6 100755
--- a/smoketest/scripts/cli/test_protocols_ripng.py
+++ b/smoketest/scripts/cli/test_protocols_ripng.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
@@ -31,28 +31,43 @@ route_map = 'FooBar123'
base_path = ['protocols', 'ripng']
class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['policy', 'access-list6', acl_in, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'access-list6', acl_in, 'rule', '10', 'source', 'any'])
- self.cli_set(['policy', 'access-list6', acl_out, 'rule', '20', 'action', 'deny'])
- self.cli_set(['policy', 'access-list6', acl_out, 'rule', '20', 'source', 'any'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'prefix', '2001:db8::/32'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'action', 'deny'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'prefix', '2001:db8::/32'])
- self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(TestProtocolsRIPng, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+ # 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_set(cls, ['policy', 'access-list6', acl_in, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'access-list6', acl_in, 'rule', '10', 'source', 'any'])
+ cls.cli_set(cls, ['policy', 'access-list6', acl_out, 'rule', '20', 'action', 'deny'])
+ cls.cli_set(cls, ['policy', 'access-list6', acl_out, 'rule', '20', 'source', 'any'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in, 'rule', '100', 'prefix', '2001:db8::/32'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'action', 'deny'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out, 'rule', '200', 'prefix', '2001:db8::/32'])
+ cls.cli_set(cls, ['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
+
+ @classmethod
+ def tearDownClass(cls):
+ # call base-classes classmethod
+ super(TestProtocolsRIPng, cls).tearDownClass()
+
+ cls.cli_delete(cls, ['policy', 'access-list6', acl_in])
+ cls.cli_delete(cls, ['policy', 'access-list6', acl_out])
+ cls.cli_delete(cls, ['policy', 'prefix-list6', prefix_list_in])
+ cls.cli_delete(cls, ['policy', 'prefix-list6', prefix_list_out])
+ cls.cli_delete(cls, ['policy', 'route-map', route_map])
def tearDown(self):
self.cli_delete(base_path)
- self.cli_delete(['policy', 'access-list6', acl_in])
- self.cli_delete(['policy', 'access-list6', acl_out])
- self.cli_delete(['policy', 'prefix-list6', prefix_list_in])
- self.cli_delete(['policy', 'prefix-list6', prefix_list_out])
- self.cli_delete(['policy', 'route-map', route_map])
self.cli_commit()
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def test_ripng_01_parameters(self):
metric = '8'
diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py
index f4aedcbc3..ab3f076ac 100755
--- a/smoketest/scripts/cli/test_protocols_rpki.py
+++ b/smoketest/scripts/cli/test_protocols_rpki.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
@@ -31,6 +31,16 @@ rpki_ssh_key = '/config/auth/id_rsa_rpki'
rpki_ssh_pub = f'{rpki_ssh_key}.pub'
class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(TestProtocolsRPKI, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
@@ -39,8 +49,8 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
# frrconfig = self.getFRRconfig('rpki')
# self.assertNotIn('rpki', frrconfig)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
def test_rpki(self):
polling = '7200'
diff --git a/smoketest/scripts/cli/test_protocols_segment-routing.py b/smoketest/scripts/cli/test_protocols_segment-routing.py
new file mode 100755
index 000000000..403c05924
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_segment-routing.py
@@ -0,0 +1,112 @@
+#!/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 unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.utils.process import cmd
+from vyos.utils.process import process_named_running
+from vyos.utils.system import sysctl_read
+
+base_path = ['protocols', 'segment-routing']
+PROCESS_NAME = 'zebra'
+
+class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(TestProtocolsSegmentRouting, cls).setUpClass()
+ # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same
+ cls.daemon_pid = process_named_running(PROCESS_NAME)
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # check process health and continuity
+ self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME))
+
+ def test_srv6(self):
+ interfaces = Section.interfaces('ethernet', vlan=False)
+ locators = {
+ 'foo' : { 'prefix' : '2001:a::/64' },
+ 'foo' : { 'prefix' : '2001:b::/64', 'usid' : {} },
+ }
+
+ for locator, locator_config in locators.items():
+ self.cli_set(base_path + ['srv6', 'locator', locator, 'prefix', locator_config['prefix']])
+ if 'usid' in locator_config:
+ self.cli_set(base_path + ['srv6', 'locator', locator, 'behavior-usid'])
+
+ # verify() - SRv6 should be enabled on at least one interface!
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'srv6'])
+
+ self.cli_commit()
+
+ for interface in interfaces:
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1')
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '0') # default
+
+ frrconfig = self.getFRRconfig(f'segment-routing', daemon='zebra')
+ self.assertIn(f'segment-routing', frrconfig)
+ self.assertIn(f' srv6', frrconfig)
+ self.assertIn(f' locators', frrconfig)
+ for locator, locator_config in locators.items():
+ self.assertIn(f' locator {locator}', frrconfig)
+ self.assertIn(f' prefix {locator_config["prefix"]} block-len 40 node-len 24 func-bits 16', frrconfig)
+
+ def test_srv6_sysctl(self):
+ interfaces = Section.interfaces('ethernet', vlan=False)
+
+ # HMAC accept
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'srv6'])
+ self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'ignore'])
+ self.cli_commit()
+
+ for interface in interfaces:
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1')
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '-1') # ignore
+
+ # HMAC drop
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface, 'srv6'])
+ self.cli_set(base_path + ['interface', interface, 'srv6', 'hmac', 'drop'])
+ self.cli_commit()
+
+ for interface in interfaces:
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_enabled'), '1')
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{interface}.seg6_require_hmac'), '1') # drop
+
+ # Disable SRv6 on first interface
+ first_if = interfaces[-1]
+ self.cli_delete(base_path + ['interface', first_if])
+ self.cli_commit()
+
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{first_if}.seg6_enabled'), '0')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py
index 3743be788..81e7326f8 100755
--- a/smoketest/scripts/cli/test_qos.py
+++ b/smoketest/scripts/cli/test_qos.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -543,5 +543,60 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):
dport = int(match_config['dport'])
self.assertEqual(f'{dport:x}', filter['options']['match']['value'])
+
+ def test_11_shaper(self):
+ bandwidth = 250
+ default_bandwidth = 20
+ default_ceil = 30
+ class_bandwidth = 50
+ class_ceil = 80
+ dst_address = '192.0.2.8/32'
+
+ for interface in self._interfaces:
+ shaper_name = f'qos-shaper-{interface}'
+
+ self.cli_set(base_path + ['interface', interface, 'egress', shaper_name])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'bandwidth', f'{bandwidth}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'bandwidth', f'{default_bandwidth}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'ceiling', f'{default_ceil}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'default', 'queue-type', 'fair-queue'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '23', 'bandwidth', f'{class_bandwidth}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '23', 'ceiling', f'{class_ceil}mbit'])
+ self.cli_set(base_path + ['policy', 'shaper', shaper_name, 'class', '23', 'match', '10', 'ip', 'destination', 'address', dst_address])
+
+ bandwidth += 1
+ default_bandwidth += 1
+ default_ceil += 1
+ class_bandwidth += 1
+ class_ceil += 1
+
+ # commit changes
+ self.cli_commit()
+
+ bandwidth = 250
+ default_bandwidth = 20
+ default_ceil = 30
+ class_bandwidth = 50
+ class_ceil = 80
+
+ for interface in self._interfaces:
+ config_entries = (
+ f'root rate {bandwidth}Mbit ceil {bandwidth}Mbit',
+ f'prio 0 rate {class_bandwidth}Mbit ceil {class_ceil}Mbit',
+ f'prio 7 rate {default_bandwidth}Mbit ceil {default_ceil}Mbit'
+ )
+
+ output = cmd(f'tc class show dev {interface}')
+
+ for config_entry in config_entries:
+ self.assertIn(config_entry, output)
+
+ bandwidth += 1
+ default_bandwidth += 1
+ default_ceil += 1
+ class_bandwidth += 1
+ class_ceil += 1
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_bcast-relay.py b/smoketest/scripts/cli/test_service_broadcast-relay.py
index 87901869e..87901869e 100755
--- a/smoketest/scripts/cli/test_service_bcast-relay.py
+++ b/smoketest/scripts/cli/test_service_broadcast-relay.py
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index ee8a07b37..ae46b18ba 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.py
@@ -24,16 +24,26 @@ 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']
+name_path = base_path + ['name']
+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 +57,36 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# IPv4 standard DDNS service configuration
def test_01_dyndns_service_standard(self):
- ddns = ['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(name_path + [svc, 'address', interface])
+ self.cli_set(name_path + [svc, 'host-name', hostname])
+ self.cli_set(name_path + [svc, 'password', password])
for opt, value in details.items():
- self.cli_set(base_path + ddns + [svc, opt, value])
+ self.cli_set(name_path + [svc, opt, value])
- # commit changes
+ # 'zone' option is supported by 'cloudfare' and 'zoneedit1', but not 'freedns'
+ self.cli_set(name_path + [svc, 'zone', zone])
+ if details['protocol'] in ['cloudflare', 'zoneedit1']:
+ pass
+ else:
+ # exception is raised for unsupported ones
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(name_path + [svc, 'zone'])
+
+ # 'ttl' option is supported by 'cloudfare', but not 'freedns' and 'zoneedit'
+ self.cli_set(name_path + [svc, 'ttl', ttl])
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(name_path + [svc, 'ttl'])
# commit changes
self.cli_commit()
@@ -79,9 +95,9 @@ 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'password={password}', 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():
if opt == 'username':
@@ -93,63 +109,81 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# IPv6 only DDNS service configuration
def test_02_dyndns_service_ipv6(self):
- timeout = '60'
- ddns = ['address', interface, 'service', 'dynv6']
+ interval = '60'
+ svc_path = name_path + ['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 + ['interval', interval])
+ self.cli_set(svc_path + ['address', interface])
+ self.cli_set(svc_path + ['ip-version', ip_version])
+ self.cli_set(svc_path + ['protocol', proto])
+ self.cli_set(svc_path + ['server', server])
+ self.cli_set(svc_path + ['username', username])
+ self.cli_set(svc_path + ['password', password])
+ self.cli_set(svc_path + ['host-name', hostname])
+ self.cli_set(svc_path + ['wait-time', wait_time])
+
+ # expiry-time must be greater than wait-time, exception is raised otherwise
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(svc_path + ['expiry-time', expiry_time_bad])
+ self.cli_commit()
+ self.cli_set(svc_path + ['expiry-time', expiry_time_good])
# commit changes
self.cli_commit()
# Check the generating config parameters
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
- self.assertIn(f'daemon={timeout}', ddclient_conf)
+ self.assertIn(f'daemon={interval}', ddclient_conf)
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'password={password}', 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'
+ 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(name_path + [name, 'address', interface])
+ self.cli_set(name_path + [name, 'host-name', hostname])
+ self.cli_set(name_path + [name, 'password', password])
for opt, value in details.items():
- self.cli_set(base_path + ddns + [svc, opt, value])
+ self.cli_set(name_path + [name, opt, value])
+
+ # Dual stack is supported by 'cloudfare' and 'freedns' but not 'googledomains'
+ # exception is raised for unsupported ones
+ self.cli_set(name_path + [name, 'ip-version', ip_version])
+ if details['protocol'] not in ['cloudflare', 'freedns']:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(name_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)
- self.assertIn(f'password={password}', 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():
if opt == 'username':
@@ -161,19 +195,19 @@ 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 = name_path + ['vyos']
+ proto = 'nsupdate'
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(svc_path + ['address', interface])
+ self.cli_set(svc_path + ['protocol', proto])
+ self.cli_set(svc_path + ['server', server])
+ self.cli_set(svc_path + ['zone', zone])
+ self.cli_set(svc_path + ['key', key_file.name])
+ self.cli_set(svc_path + ['ttl', ttl])
+ self.cli_set(svc_path + ['host-name', hostname])
# commit changes
self.cli_commit()
@@ -182,11 +216,137 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
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'protocol={proto}', 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'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 = name_path + ['namecheap']
+ proto = 'namecheap'
+ hostnames = ['@', 'www', hostname, f'@.{hostname}']
+
+ for name in hostnames:
+ self.cli_set(svc_path + ['address', interface])
+ self.cli_set(svc_path + ['protocol', proto])
+ self.cli_set(svc_path + ['server', server])
+ self.cli_set(svc_path + ['username', username])
+ self.cli_set(svc_path + ['password', password])
+ self.cli_set(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_web_options(self):
+ # Check if DDNS service can be configured and runs
+ svc_path = name_path + ['cloudflare']
+ proto = 'cloudflare'
+ web_url_good = 'https://ifconfig.me/ip'
+ web_url_bad = 'http:/ifconfig.me/ip'
+
+ self.cli_set(svc_path + ['protocol', proto])
+ self.cli_set(svc_path + ['zone', zone])
+ self.cli_set(svc_path + ['password', password])
+ self.cli_set(svc_path + ['host-name', hostname])
+ self.cli_set(svc_path + ['web-options', 'url', web_url_good])
+
+ # web-options is supported only with web service based address lookup
+ # exception is raised for interface based address lookup
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(svc_path + ['address', interface])
+ self.cli_commit()
+ self.cli_set(svc_path + ['address', 'web'])
+
+ # commit changes
+ self.cli_commit()
+
+ # web-options must be a valid URL
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(svc_path + ['web-options', 'url', web_url_bad])
+ self.cli_commit()
+ self.cli_set(svc_path + ['web-options', 'url', web_url_good])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check the generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ self.assertIn(f'usev4=webv4', ddclient_conf)
+ self.assertIn(f'webv4={web_url_good}', ddclient_conf)
+ self.assertIn(f'protocol={proto}', ddclient_conf)
+ self.assertIn(f'zone={zone}', ddclient_conf)
+ self.assertIn(f'password=\'{password}\'', ddclient_conf)
+ self.assertIn(f'{hostname}', ddclient_conf)
+
+ def test_07_dyndns_dynamic_interface(self):
+ # Check if DDNS service can be configured and runs
+ svc_path = name_path + ['namecheap']
+ proto = 'namecheap'
+ dyn_interface = 'pppoe587'
+
+ self.cli_set(svc_path + ['address', dyn_interface])
+ self.cli_set(svc_path + ['protocol', proto])
+ self.cli_set(svc_path + ['server', server])
+ self.cli_set(svc_path + ['username', username])
+ self.cli_set(svc_path + ['password', password])
+ self.cli_set(svc_path + ['host-name', hostname])
+
+ # Dynamic interface will raise a warning but still go through
+ # XXX: We should have idiomatic class "ConfigSessionWarning" wrapping
+ # "Warning" similar to "ConfigSessionError".
+ # with self.assertWarns(Warning):
+ # self.cli_commit()
+ self.cli_commit()
+
+ # Check the generating config parameters
+ ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}')
+ self.assertIn(f'ifv4={dyn_interface}', 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'{hostname}', ddclient_conf)
+
+ def test_08_dyndns_vrf(self):
+ # Table number randomized, but should be within range 100-65535
+ vrf_table = '58710'
+ vrf_name = f'vyos-test-{vrf_table}'
+ svc_path = name_path + ['cloudflare']
+ proto = 'cloudflare'
+
+ self.cli_set(['vrf', 'name', vrf_name, 'table', vrf_table])
+ self.cli_set(base_path + ['vrf', vrf_name])
+
+ self.cli_set(svc_path + ['address', interface])
+ self.cli_set(svc_path + ['protocol', proto])
+ self.cli_set(svc_path + ['host-name', hostname])
+ self.cli_set(svc_path + ['zone', zone])
+ self.cli_set(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_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py
index bc50a4ffe..85a5f1448 100755
--- a/smoketest/scripts/cli/test_service_dns_forwarding.py
+++ b/smoketest/scripts/cli/test_service_dns_forwarding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2022 VyOS maintainers and contributors
+# Copyright (C) 2019-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
@@ -43,7 +43,6 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestServicePowerDNS, cls).setUpClass()
-
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
@@ -239,6 +238,45 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value('dns64-prefix')
self.assertEqual(tmp, dns_prefix)
+ def test_exclude_throttle_adress(self):
+ exclude_throttle_adress_examples = [
+ '192.168.128.255',
+ '10.0.0.0/25',
+ '2001:db8:85a3:8d3:1319:8a2e:370:7348',
+ '64:ff9b::/96'
+ ]
+
+ for network in allow_from:
+ self.cli_set(base_path + ['allow-from', network])
+ for address in listen_adress:
+ self.cli_set(base_path + ['listen-address', address])
+
+ for exclude_throttle_adress in exclude_throttle_adress_examples:
+ self.cli_set(base_path + ['exclude-throttle-address', exclude_throttle_adress])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify dont-throttle-netmasks configuration
+ tmp = get_config_value('dont-throttle-netmasks')
+ self.assertEqual(tmp, ','.join(exclude_throttle_adress_examples))
+
+ def test_serve_stale_extension(self):
+ server_stale = '20'
+ for network in allow_from:
+ self.cli_set(base_path + ['allow-from', network])
+ for address in listen_adress:
+ self.cli_set(base_path + ['listen-address', address])
+
+ self.cli_set(base_path + ['serve-stale-extension', server_stale])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ tmp = get_config_value('serve-stale-extensions')
+ self.assertEqual(tmp, server_stale)
+
def test_listening_port(self):
# We can listen on a different port compared to '53' but only one at a time
for port in ['1053', '5353']:
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 1ae5c104c..8d9b8459e 100755
--- a/smoketest/scripts/cli/test_service_https.py
+++ b/smoketest/scripts/cli/test_service_https.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
+# Copyright (C) 2019-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,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
+import json
from requests import request
from urllib3.exceptions import InsecureRequestWarning
@@ -22,7 +23,11 @@ from urllib3.exceptions import InsecureRequestWarning
from base_vyostest_shim import VyOSUnitTestSHIM
from base_vyostest_shim import ignore_warning
from vyos.utils.file import read_file
-from vyos.utils.process import run
+from vyos.utils.file import write_file
+from vyos.utils.process import call
+from vyos.utils.process import process_named_running
+
+from vyos.configsession import ConfigSessionError
base_path = ['service', 'https']
pki_base = ['pki']
@@ -48,78 +53,114 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
"""
+dh_1024 = """
+MIGHAoGBAM3nvMkHGi/xmRs8cYg4pcl5sAanxel9EM+1XobVhUViXw8JvlmSEVOj
+n2aXUifc4SEs3WDzVPRC8O8qQWjvErpTq/HOgt3aqBCabMgvflmt706XP0KiqnpW
+EyvNiI27J3wBUzEXLIS110MxPAX5Tcug974PecFcOxn1RWrbWcx/AgEC
+"""
+
+dh_2048 = """
+MIIBCAKCAQEA1mld/V7WnxxRinkOlhx/BoZkRELtIUQFYxyARBqYk4C5G3YnZNNu
+zjaGyPnfIKHu8SIUH85OecM+5/co9nYlcUJuph2tbR6qNgPw7LOKIhf27u7WhvJk
+iVsJhwZiWmvvMV4jTParNEI2svoooMyhHXzeweYsg6YtgLVmwiwKj3XP3gRH2i3B
+Mq8CDS7X6xaKvjfeMPZBFqOM5nb6HhsbaAUyiZxrfipLvXxtnbzd/eJUQVfVdxM3
+pn0i+QrO2tuNAzX7GoPc9pefrbb5xJmGS50G0uqsR59+7LhYmyZSBASA0lxTEW9t
+kv/0LPvaYTY57WL7hBeqqHy/WPZHPzDI3wIBAg==
+"""
+# to test load config via HTTP URL
+nginx_tmp_site = '/etc/nginx/sites-enabled/smoketest'
+nginx_conf_smoketest = """
+server {
+ listen 8000;
+ server_name localhost;
+
+ root /tmp;
+
+ index index.html;
+
+ location / {
+ try_files $uri $uri/ =404;
+ autoindex on;
+ }
+}
+"""
+
+PROCESS_NAME = 'nginx'
+
class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
+ @classmethod
+ def setUpClass(cls):
+ super(TestHTTPSService, cls).setUpClass()
+
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
- self.cli_delete(base_path)
- self.cli_delete(pki_base)
+ cls.cli_delete(cls, base_path)
+ cls.cli_delete(cls, pki_base)
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestHTTPSService, cls).tearDownClass()
+ call(f'sudo rm -f {nginx_tmp_site}')
def tearDown(self):
self.cli_delete(base_path)
self.cli_delete(pki_base)
self.cli_commit()
- def test_default(self):
- self.cli_set(base_path)
- self.cli_commit()
-
- ret = run('sudo /usr/sbin/nginx -t')
- self.assertEqual(ret, 0)
-
- def test_server_block(self):
- vhost_id = 'example'
- address = '0.0.0.0'
- port = '8443'
- name = 'example.org'
-
- test_path = base_path + ['virtual-host', vhost_id]
-
- self.cli_set(test_path + ['listen-address', address])
- self.cli_set(test_path + ['listen-port', port])
- self.cli_set(test_path + ['server-name', name])
-
- self.cli_commit()
-
- ret = run('sudo /usr/sbin/nginx -t')
- self.assertEqual(ret, 0)
-
- nginx_config = read_file('/etc/nginx/sites-enabled/default')
- self.assertIn(f'listen {address}:{port} ssl;', nginx_config)
- self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config)
+ # Check for stopped process
+ self.assertFalse(process_named_running(PROCESS_NAME))
def test_certificate(self):
- self.cli_set(pki_base + ['certificate', 'test_https', 'certificate', cert_data.replace('\n','')])
- self.cli_set(pki_base + ['certificate', 'test_https', 'private', 'key', key_data.replace('\n','')])
-
- self.cli_set(base_path + ['certificates', 'certificate', 'test_https'])
+ cert_name = 'test_https'
+ dh_name = 'dh-test'
+
+ self.cli_set(base_path + ['certificates', 'certificate', cert_name])
+ # verify() - certificates do not exist (yet)
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(pki_base + ['certificate', cert_name, 'certificate', cert_data.replace('\n','')])
+ self.cli_set(pki_base + ['certificate', cert_name, 'private', 'key', key_data.replace('\n','')])
+
+ self.cli_set(base_path + ['certificates', 'dh-params', dh_name])
+ # verify() - dh-params do not exist (yet)
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(pki_base + ['dh', dh_name, 'parameters', dh_1024.replace('\n','')])
+ # verify() - dh-param minimum length is 2048 bit
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(pki_base + ['dh', dh_name, 'parameters', dh_2048.replace('\n','')])
self.cli_commit()
+ self.assertTrue(process_named_running(PROCESS_NAME))
+ self.debug = False
+
+ def test_api_missing_keys(self):
+ self.cli_set(base_path + ['api'])
+ self.assertRaises(ConfigSessionError, self.cli_commit)
- ret = run('sudo /usr/sbin/nginx -t')
- self.assertEqual(ret, 0)
+ def test_api_incomplete_key(self):
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01'])
+ self.assertRaises(ConfigSessionError, self.cli_commit)
@ignore_warning(InsecureRequestWarning)
def test_api_auth(self):
vhost_id = 'example'
address = '127.0.0.1'
- port = '443'
+ port = '443' # default value
name = 'localhost'
- self.cli_set(base_path + ['api', 'socket'])
key = 'MySuperSecretVyOS'
self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
- test_path = base_path + ['virtual-host', vhost_id]
- self.cli_set(test_path + ['listen-address', address])
- self.cli_set(test_path + ['listen-port', port])
- self.cli_set(test_path + ['server-name', name])
+ self.cli_set(base_path + ['listen-address', address])
self.cli_commit()
nginx_config = read_file('/etc/nginx/sites-enabled/default')
self.assertIn(f'listen {address}:{port} ssl;', nginx_config)
- self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config)
+ self.assertIn(f'ssl_protocols TLSv1.2 TLSv1.3;', nginx_config) # default
url = f'https://{address}/retrieve'
payload = {'data': '{"op": "showConfig", "path": []}', 'key': f'{key}'}
@@ -138,6 +179,13 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
# Must get HTTP code 401 on missing key (Unauthorized)
self.assertEqual(r.status_code, 401)
+ # Check path config
+ payload = {'data': '{"op": "showConfig", "path": ["system", "login"]}', 'key': f'{key}'}
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ response = r.json()
+ vyos_user_exists = 'vyos' in response.get('data', {}).get('user', {})
+ self.assertTrue(vyos_user_exists, "The 'vyos' user does not exist in the response.")
+
# GraphQL auth test: a missing key will return status code 400, as
# 'key' is a non-nullable field in the schema; an incorrect key is
# caught by the resolver, and returns success 'False', so one must
@@ -240,5 +288,174 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
success = r.json()['data']['ShowVersion']['success']
self.assertTrue(success)
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_add_delete(self):
+ address = '127.0.0.1'
+ key = 'VyOS-key'
+ url = f'https://{address}/retrieve'
+ payload = {'data': '{"op": "showConfig", "path": []}', 'key': f'{key}'}
+ headers = {}
+
+ self.cli_set(base_path)
+ self.cli_commit()
+
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ # api not configured; expect 503
+ self.assertEqual(r.status_code, 503)
+
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ self.cli_commit()
+
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ # api configured; expect 200
+ self.assertEqual(r.status_code, 200)
+
+ self.cli_delete(base_path + ['api'])
+ self.cli_commit()
+
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ # api deleted; expect 503
+ self.assertEqual(r.status_code, 503)
+
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_show(self):
+ address = '127.0.0.1'
+ key = 'VyOS-key'
+ url = f'https://{address}/show'
+ headers = {}
+
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ self.cli_commit()
+
+ payload = {
+ 'data': '{"op": "show", "path": ["system", "image"]}',
+ 'key': f'{key}',
+ }
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_generate(self):
+ address = '127.0.0.1'
+ key = 'VyOS-key'
+ url = f'https://{address}/generate'
+ headers = {}
+
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ self.cli_commit()
+
+ payload = {
+ 'data': '{"op": "generate", "path": ["macsec", "mka", "cak", "gcm-aes-256"]}',
+ 'key': f'{key}',
+ }
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_configure(self):
+ address = '127.0.0.1'
+ key = 'VyOS-key'
+ url = f'https://{address}/configure'
+ headers = {}
+ conf_interface = 'dum0'
+ conf_address = '192.0.2.44/32'
+
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ self.cli_commit()
+
+ payload_path = [
+ "interfaces",
+ "dummy",
+ f"{conf_interface}",
+ "address",
+ f"{conf_address}",
+ ]
+
+ payload = {'data': json.dumps({"op": "set", "path": payload_path}), 'key': key}
+
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_config_file(self):
+ address = '127.0.0.1'
+ key = 'VyOS-key'
+ url = f'https://{address}/config-file'
+ headers = {}
+
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ self.cli_commit()
+
+ payload = {
+ 'data': '{"op": "save"}',
+ 'key': f'{key}',
+ }
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_reset(self):
+ address = '127.0.0.1'
+ key = 'VyOS-key'
+ url = f'https://{address}/reset'
+ headers = {}
+
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ self.cli_commit()
+
+ payload = {
+ 'data': '{"op": "reset", "path": ["ip", "arp", "table"]}',
+ 'key': f'{key}',
+ }
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_config_file_load_http(self):
+ # Test load config from HTTP URL
+ address = '127.0.0.1'
+ key = 'VyOS-key'
+ url = f'https://{address}/config-file'
+ url_config = f'https://{address}/configure'
+ headers = {}
+ tmp_file = 'tmp-config.boot'
+
+ self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key])
+ self.cli_commit()
+
+ # load config via HTTP requires nginx config
+ call(f'sudo touch {nginx_tmp_site}')
+ call(f'sudo chmod 666 {nginx_tmp_site}')
+ write_file(nginx_tmp_site, nginx_conf_smoketest)
+ call('sudo systemctl reload nginx')
+
+ # save config
+ payload = {
+ 'data': '{"op": "save", "file": "/tmp/tmp-config.boot"}',
+ 'key': f'{key}',
+ }
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ # change config
+ payload = {
+ 'data': '{"op": "set", "path": ["interfaces", "dummy", "dum1", "address", "192.0.2.31/32"]}',
+ 'key': f'{key}',
+ }
+ r = request('POST', url_config, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ # load config from URL
+ payload = {
+ 'data': '{"op": "load", "file": "http://localhost:8000/tmp-config.boot"}',
+ 'key': f'{key}',
+ }
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ self.assertEqual(r.status_code, 200)
+
+ # cleanup tmp nginx conf
+ call(f'sudo rm -f {nginx_tmp_site}')
+ call('sudo systemctl reload nginx')
+
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=5)
diff --git a/smoketest/scripts/cli/test_service_ids.py b/smoketest/scripts/cli/test_service_ids_ddos-protection.py
index 91b056eea..91b056eea 100755
--- a/smoketest/scripts/cli/test_service_ids.py
+++ b/smoketest/scripts/cli/test_service_ids_ddos-protection.py
diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py
index 4dd3e761c..6e95b3bd1 100755
--- a/smoketest/scripts/cli/test_service_ipoe-server.py
+++ b/smoketest/scripts/cli/test_service_ipoe-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -17,28 +17,35 @@
import re
import unittest
+from collections import OrderedDict
from base_accel_ppp_test import BasicAccelPPPTest
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
-
from configparser import ConfigParser
+from configparser import RawConfigParser
-ac_name = 'ACN'
-interface = 'eth0'
+ac_name = "ACN"
+interface = "eth0"
-def getConfig(string, end='cli'):
- command = f'cat /run/accel-pppd/ipoe.conf | sed -n "/^{string}/,/^{end}/p"'
- out = cmd(command)
- return out
+class MultiOrderedDict(OrderedDict):
+ # Accel-ppp has duplicate keys in config file (gw-ip-address)
+ # This class is used to define dictionary which can contain multiple values
+ # in one key.
+ def __setitem__(self, key, value):
+ if isinstance(value, list) and key in self:
+ self[key].extend(value)
+ else:
+ super(OrderedDict, self).__setitem__(key, value)
class TestServiceIPoEServer(BasicAccelPPPTest.TestCase):
@classmethod
def setUpClass(cls):
- cls._base_path = ['service', 'ipoe-server']
- cls._config_file = '/run/accel-pppd/ipoe.conf'
- cls._chap_secrets = '/run/accel-pppd/ipoe.chap-secrets'
+ cls._base_path = ["service", "ipoe-server"]
+ cls._config_file = "/run/accel-pppd/ipoe.conf"
+ cls._chap_secrets = "/run/accel-pppd/ipoe.chap-secrets"
+ cls._protocol_section = "ipoe"
# call base-classes classmethod
super(TestServiceIPoEServer, cls).setUpClass()
@@ -47,22 +54,29 @@ class TestServiceIPoEServer(BasicAccelPPPTest.TestCase):
super().verify(conf)
# Validate configuration values
- accel_modules = list(conf['modules'].keys())
- self.assertIn('log_syslog', accel_modules)
- self.assertIn('ipoe', accel_modules)
- self.assertIn('shaper', accel_modules)
- self.assertIn('ipv6pool', accel_modules)
- self.assertIn('ipv6_nd', accel_modules)
- self.assertIn('ipv6_dhcp', accel_modules)
- self.assertIn('ippool', accel_modules)
-
- def basic_config(self):
- self.set(['interface', interface, 'client-subnet', '192.168.0.0/24'])
+ accel_modules = list(conf["modules"].keys())
+ self.assertIn("log_syslog", accel_modules)
+ self.assertIn("ipoe", accel_modules)
+ self.assertIn("shaper", accel_modules)
+ self.assertIn("ipv6pool", accel_modules)
+ self.assertIn("ipv6_nd", accel_modules)
+ self.assertIn("ipv6_dhcp", accel_modules)
+ self.assertIn("ippool", accel_modules)
+
+ def initial_gateway_config(self):
+ self._gateway = "192.0.2.1/24"
+ super().initial_gateway_config()
+
+ def initial_auth_config(self):
+ self.set(["authentication", "mode", "noauth"])
+
+ def basic_protocol_specific_config(self):
+ self.set(["interface", interface, "client-subnet", "192.168.0.0/24"])
def test_accel_local_authentication(self):
- mac_address = '08:00:27:2f:d8:06'
- self.set(['authentication', 'interface', interface, 'mac', mac_address])
- self.set(['authentication', 'mode', 'local'])
+ mac_address = "08:00:27:2f:d8:06"
+ self.set(["authentication", "interface", interface, "mac", mac_address])
+ self.set(["authentication", "mode", "local"])
# No IPoE interface configured
with self.assertRaises(ConfigSessionError):
@@ -70,115 +84,110 @@ class TestServiceIPoEServer(BasicAccelPPPTest.TestCase):
# Test configuration of local authentication for PPPoE server
self.basic_config()
-
+ # Rewrite authentication from basic_config
+ self.set(["authentication", "interface", interface, "mac", mac_address])
+ self.set(["authentication", "mode", "local"])
# commit changes
self.cli_commit()
# Validate configuration values
- conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
conf.read(self._config_file)
# check proper path to chap-secrets file
- self.assertEqual(conf['chap-secrets']['chap-secrets'], self._chap_secrets)
+ self.assertEqual(conf["chap-secrets"]["chap-secrets"], self._chap_secrets)
- accel_modules = list(conf['modules'].keys())
- self.assertIn('chap-secrets', accel_modules)
+ accel_modules = list(conf["modules"].keys())
+ self.assertIn("chap-secrets", accel_modules)
# basic verification
self.verify(conf)
# check local users
- tmp = cmd(f'sudo cat {self._chap_secrets}')
- regex = f'{interface}\s+\*\s+{mac_address}\s+\*'
+ tmp = cmd(f"sudo cat {self._chap_secrets}")
+ regex = f"{interface}\s+\*\s+{mac_address}\s+\*"
tmp = re.findall(regex, tmp)
self.assertTrue(tmp)
- def test_accel_named_pool(self):
- first_pool = 'VyOS-pool1'
- first_subnet = '192.0.2.0/25'
- first_gateway = '192.0.2.1'
- second_pool = 'Vyos-pool2'
- second_subnet = '203.0.113.0/25'
- second_gateway = '203.0.113.1'
-
- self.set(['authentication', 'mode', 'noauth'])
- self.set(['client-ip-pool', 'name', first_pool, 'gateway-address', first_gateway])
- self.set(['client-ip-pool', 'name', first_pool, 'subnet', first_subnet])
- self.set(['client-ip-pool', 'name', second_pool, 'gateway-address', second_gateway])
- self.set(['client-ip-pool', 'name', second_pool, 'subnet', second_subnet])
- self.set(['interface', interface])
+ def test_accel_ipv4_pool(self):
+ self.basic_config(is_gateway=False, is_client_pool=False)
+ gateway = ["172.16.0.1/25", "192.0.2.1/24"]
+ subnet = "172.16.0.0/24"
+ first_pool = "POOL1"
+ second_pool = "POOL2"
+ range = "192.0.2.10-192.0.2.20"
+ range_config = "192.0.2.10-20"
+
+ for gw in gateway:
+ self.set(["gateway-address", gw])
+
+ self.set(["client-ip-pool", first_pool, "range", subnet])
+ self.set(["client-ip-pool", first_pool, "next-pool", second_pool])
+ self.set(["client-ip-pool", second_pool, "range", range])
+ self.set(["default-pool", first_pool])
# commit changes
- self.cli_commit()
+ self.cli_commit()
# Validate configuration values
- conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False)
+ conf = RawConfigParser(
+ allow_no_value=True,
+ delimiters="=",
+ strict=False,
+ dict_type=MultiOrderedDict,
+ )
conf.read(self._config_file)
- self.assertTrue(conf['ipoe']['interface'], f'{interface},shared=1,mode=L2,ifcfg=1,start=dhcpv4,ipv6=1')
- self.assertTrue(conf['ipoe']['noauth'], '1')
- self.assertTrue(conf['ipoe']['ip-pool'], first_pool)
- self.assertTrue(conf['ipoe']['ip-pool'], second_pool)
- self.assertTrue(conf['ipoe']['gw-ip-address'], f'{first_gateway}/25')
- self.assertTrue(conf['ipoe']['gw-ip-address'], f'{second_gateway}/25')
-
- config = getConfig('[ip-pool]')
- pool_config = f'''{second_subnet},name={second_pool}
-{first_subnet},name={first_pool}
-gw-ip-address={second_gateway}/25
-gw-ip-address={first_gateway}/25'''
- self.assertIn(pool_config, config)
+ self.assertIn(
+ f"{first_pool},next={second_pool}", conf["ip-pool"][f"{subnet},name"]
+ )
+ self.assertIn(second_pool, conf["ip-pool"][f"{range_config},name"])
+
+ gw_pool_config_list = conf.get("ip-pool", "gw-ip-address")
+ gw_ipoe_config_list = conf.get(self._protocol_section, "gw-ip-address")
+ for gw in gateway:
+ self.assertIn(gw.split("/")[0], gw_pool_config_list)
+ self.assertIn(gw, gw_ipoe_config_list)
+ self.assertIn(first_pool, conf[self._protocol_section]["ip-pool"])
def test_accel_next_pool(self):
- first_pool = 'VyOS-pool1'
- first_subnet = '192.0.2.0/25'
- first_gateway = '192.0.2.1'
- second_pool = 'Vyos-pool2'
- second_subnet = '203.0.113.0/25'
- second_gateway = '203.0.113.1'
- third_pool = 'Vyos-pool3'
- third_subnet = '198.51.100.0/24'
- third_gateway = '198.51.100.1'
-
- self.set(['authentication', 'mode', 'noauth'])
- self.set(['client-ip-pool', 'name', first_pool, 'gateway-address', first_gateway])
- self.set(['client-ip-pool', 'name', first_pool, 'subnet', first_subnet])
- self.set(['client-ip-pool', 'name', first_pool, 'next-pool', second_pool])
- self.set(['client-ip-pool', 'name', second_pool, 'gateway-address', second_gateway])
- self.set(['client-ip-pool', 'name', second_pool, 'subnet', second_subnet])
- self.set(['client-ip-pool', 'name', second_pool, 'next-pool', third_pool])
- self.set(['client-ip-pool', 'name', third_pool, 'gateway-address', third_gateway])
- self.set(['client-ip-pool', 'name', third_pool, 'subnet', third_subnet])
- self.set(['interface', interface])
+ self.basic_config(is_gateway=False, is_client_pool=False)
+
+ first_pool = "VyOS-pool1"
+ first_subnet = "192.0.2.0/25"
+ first_gateway = "192.0.2.1/24"
+ second_pool = "Vyos-pool2"
+ second_subnet = "203.0.113.0/25"
+ second_gateway = "203.0.113.1/24"
+ third_pool = "Vyos-pool3"
+ third_subnet = "198.51.100.0/24"
+ third_gateway = "198.51.100.1/24"
+
+ self.set(["gateway-address", f"{first_gateway}"])
+ self.set(["gateway-address", f"{second_gateway}"])
+ self.set(["gateway-address", f"{third_gateway}"])
+
+ self.set(["client-ip-pool", first_pool, "range", first_subnet])
+ self.set(["client-ip-pool", first_pool, "next-pool", second_pool])
+ self.set(["client-ip-pool", second_pool, "range", second_subnet])
+ self.set(["client-ip-pool", second_pool, "next-pool", third_pool])
+ self.set(["client-ip-pool", third_pool, "range", third_subnet])
# commit changes
self.cli_commit()
-
- # Validate configuration values
- conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False)
- conf.read(self._config_file)
-
- self.assertTrue(conf['ipoe']['interface'], f'{interface},shared=1,mode=L2,ifcfg=1,start=dhcpv4,ipv6=1')
- self.assertTrue(conf['ipoe']['noauth'], '1')
- self.assertTrue(conf['ipoe']['ip-pool'], first_pool)
- self.assertTrue(conf['ipoe']['gw-ip-address'], f'{first_gateway}/25')
- self.assertTrue(conf['ipoe']['gw-ip-address'], f'{second_gateway}/25')
- self.assertTrue(conf['ipoe']['gw-ip-address'], f'{third_gateway}/24')
-
- config = getConfig('[ip-pool]')
+ config = self.getConfig("ip-pool")
# T5099 required specific order
- pool_config = f'''{third_subnet},name={third_pool}
+ pool_config = f"""gw-ip-address={first_gateway.split('/')[0]}
+gw-ip-address={second_gateway.split('/')[0]}
+gw-ip-address={third_gateway.split('/')[0]}
+{third_subnet},name={third_pool}
{second_subnet},name={second_pool},next={third_pool}
-{first_subnet},name={first_pool},next={second_pool}
-gw-ip-address={third_gateway}/24
-gw-ip-address={second_gateway}/25
-gw-ip-address={first_gateway}/25'''
+{first_subnet},name={first_pool},next={second_pool}"""
self.assertIn(pool_config, config)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main(verbosity=2)
-
diff --git a/smoketest/scripts/cli/test_service_lldp.py b/smoketest/scripts/cli/test_service_lldp.py
index ee26844ab..7e30b43f5 100755
--- a/smoketest/scripts/cli/test_service_lldp.py
+++ b/smoketest/scripts/cli/test_service_lldp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -122,6 +122,20 @@ class TestServiceLLDP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'configure ports {interface} med location elin "{elin}"', config)
self.assertIn(f'configure system interface pattern "{interface}"', config)
+ def test_06_lldp_snmp(self):
+ self.cli_set(base_path + ['snmp'])
+
+ # verify - can not start lldp snmp without snmp beeing configured
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(['service', 'snmp'])
+ self.cli_commit()
+
+ # SNMP required process to be started with -x option
+ tmp = read_file('/etc/default/lldpd')
+ self.assertIn('-x', tmp)
+
+ self.cli_delete(['service', 'snmp'])
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
deleted file mode 100755
index 9a9839025..000000000
--- a/smoketest/scripts/cli/test_service_mdns-repeater.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env python3
-#
-# 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
-# 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 configparser import ConfigParser
-from vyos.utils.process import process_named_running
-
-base_path = ['service', 'mdns', 'repeater']
-intf_base = ['interfaces', 'dummy']
-config_file = '/run/avahi-daemon/avahi-daemon.conf'
-
-
-class TestServiceMDNSrepeater(VyOSUnitTestSHIM.TestCase):
- def tearDown(self):
- # Check for running process
- self.assertTrue(process_named_running('avahi-daemon'))
-
- self.cli_delete(base_path)
- self.cli_delete(intf_base + ['dum10'])
- self.cli_delete(intf_base + ['dum20'])
- self.cli_commit()
-
- # Check that there is no longer a running process
- self.assertFalse(process_named_running('avahi-daemon'))
-
- def test_service(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 + ['interface', 'dum20'])
-
- for domain in domains:
- self.cli_set(base_path + ['browse-domain', domain])
-
- for service in services:
- self.cli_set(base_path + ['allow-service', service])
-
- self.cli_commit()
-
- # Validate configuration values
- conf = ConfigParser(delimiters='=')
- conf.read(config_file)
-
- self.assertEqual(conf['server']['allow-interfaces'], 'dum10, dum20')
- self.assertEqual(conf['server']['browse-domains'], ', '.join(domains))
- self.assertEqual(conf['reflector']['enable-reflector'], 'yes')
- self.assertEqual(conf['reflector']['reflect-filters'], ', '.join(services))
-
-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
new file mode 100755
index 000000000..f2fb3b509
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_mdns_repeater.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+#
+# 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
+# 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 configparser import ConfigParser
+from vyos.configsession import ConfigSessionError
+from vyos.utils.process import process_named_running
+
+base_path = ['service', 'mdns', 'repeater']
+intf_base = ['interfaces', 'dummy']
+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'))
+
+ 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_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']
+
+ 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])
+
+ for service in services:
+ self.cli_set(base_path + ['allow-service', service])
+
+ 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'], '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_service_ndp-proxy.py b/smoketest/scripts/cli/test_service_ndp-proxy.py
new file mode 100755
index 000000000..a947ec478
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_ndp-proxy.py
@@ -0,0 +1,70 @@
+#!/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 cmd
+from vyos.utils.process import process_named_running
+
+PROCESS_NAME = 'ndppd'
+NDPPD_CONF = '/run/ndppd/ndppd.conf'
+base_path = ['service', 'ndp-proxy']
+
+def getConfigSection(string=None, end=' {', endsection='^}'):
+ tmp = f'cat {NDPPD_CONF} | sed -n "/^{string}{end}/,/{endsection}/p"'
+ out = cmd(tmp)
+ return out
+
+class TestServiceNDPProxy(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceNDPProxy, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # delete testing SSH config
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_basic(self):
+ interfaces = Section.interfaces('ethernet')
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(base_path + ['interface', interface, 'enable-router-bit'])
+
+ self.cli_commit()
+
+ for interface in interfaces:
+ config = getConfigSection(f'proxy {interface}')
+ self.assertIn(f'proxy {interface}', config)
+ self.assertIn(f'router yes', config)
+ self.assertIn(f'timeout 500', config) # default value
+ self.assertIn(f'ttl 30000', config) # default value
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py
index 963784f0a..fa3bb87db 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -32,7 +32,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
cls._base_path = ['service', 'pppoe-server']
cls._config_file = '/run/accel-pppd/pppoe.conf'
cls._chap_secrets = '/run/accel-pppd/pppoe.chap-secrets'
-
+ cls._protocol_section = 'pppoe'
# call base-classes classmethod
super(TestServicePPPoEServer, cls).setUpClass()
@@ -65,13 +65,11 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
super().verify(conf)
- def basic_config(self):
+ def basic_protocol_specific_config(self):
self.cli_set(local_if + ['address', '192.0.2.1/32'])
-
self.set(['access-concentrator', ac_name])
self.set(['interface', interface])
- super().basic_config()
def test_pppoe_server_ppp_options(self):
# Test configuration of local authentication for PPPoE server
@@ -120,7 +118,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
# check interface-cache
self.assertEqual(conf['ppp']['unit-cache'], interface_cache)
-
def test_pppoe_server_authentication_protocols(self):
# Test configuration of local authentication for PPPoE server
self.basic_config()
@@ -137,68 +134,25 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
self.assertEqual(conf['modules']['auth_mschap_v2'], None)
-
- def test_pppoe_server_client_ip_pool(self):
- # Test configuration of IPv6 client pools
- self.basic_config()
-
- subnet = '172.18.0.0/24'
+ def test_pppoe_server_shaper(self):
fwmark = '223'
- limiter = 'htb'
-
- self.set(['client-ip-pool', 'subnet', subnet])
+ limiter = 'tbf'
+ self.basic_config()
- start = '192.0.2.10'
- stop = '192.0.2.20'
- stop_octet = stop.split('.')[3]
- start_stop = f'{start}-{stop_octet}'
- self.set(['client-ip-pool', 'start', start])
- self.set(['client-ip-pool', 'stop', stop])
self.set(['shaper', 'fwmark', fwmark])
-
# commit changes
- self.cli_commit()
-
- # Validate configuration values
- conf = ConfigParser(allow_no_value=True)
- conf.read(self._config_file)
- # check configured subnet
- self.assertEqual(conf['ip-pool'][subnet], None)
- self.assertEqual(conf['ip-pool'][start_stop], None)
- self.assertEqual(conf['ip-pool']['gw-ip-address'], self._gateway)
- self.assertEqual(conf['shaper']['fwmark'], fwmark)
- self.assertEqual(conf['shaper']['down-limiter'], limiter)
-
-
- def test_pppoe_server_client_ip_pool_name(self):
- # Test configuration of named client pools
- self.basic_config()
-
- subnet = '192.0.2.0/24'
- gateway = '192.0.2.1'
- pool = 'VYOS'
-
- subnet_name = f'{subnet},name'
- gw_ip_prefix = f'{gateway}/24'
-
- self.set(['client-ip-pool', 'name', pool, 'subnet', subnet])
- self.set(['client-ip-pool', 'name', pool, 'gateway-address', gateway])
- self.cli_delete(self._base_path + ['gateway-address'])
-
- # commit changes
self.cli_commit()
# Validate configuration values
conf = ConfigParser(allow_no_value=True, delimiters='=')
conf.read(self._config_file)
- # Validate configuration
- self.assertEqual(conf['ip-pool'][subnet_name], pool)
- self.assertEqual(conf['ip-pool']['gw-ip-address'], gateway)
- self.assertEqual(conf['pppoe']['ip-pool'], pool)
- self.assertEqual(conf['pppoe']['gw-ip-address'], gw_ip_prefix)
+ # basic verification
+ self.verify(conf)
+ self.assertEqual(conf['shaper']['fwmark'], fwmark)
+ self.assertEqual(conf['shaper']['down-limiter'], limiter)
def test_pppoe_server_client_ipv6_pool(self):
# Test configuration of IPv6 client pools
@@ -239,15 +193,10 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
self.assertEqual(conf['ipv6-pool'][client_prefix], None)
self.assertEqual(conf['ipv6-pool']['delegate'], f'{delegate_prefix},{delegate_mask}')
-
def test_accel_radius_authentication(self):
radius_called_sid = 'ifname:mac'
- radius_acct_interim_jitter = '9'
- radius_acct_interim_interval = '60'
self.set(['authentication', 'radius', 'called-sid-format', radius_called_sid])
- self.set(['authentication', 'radius', 'acct-interim-jitter', radius_acct_interim_jitter])
- self.set(['authentication', 'radius', 'accounting-interim-interval', radius_acct_interim_interval])
# run common tests
super().test_accel_radius_authentication()
@@ -258,9 +207,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
# Validate configuration
self.assertEqual(conf['pppoe']['called-sid'], radius_called_sid)
- self.assertEqual(conf['radius']['acct-interim-jitter'], radius_acct_interim_jitter)
- self.assertEqual(conf['radius']['acct-interim-interval'], radius_acct_interim_interval)
-
def test_pppoe_server_vlan(self):
@@ -284,5 +230,6 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
tmp = ','.join(vlans)
self.assertIn(f'vlan-mon={interface},{tmp}', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_salt.py b/smoketest/scripts/cli/test_service_salt-minion.py
index 48a588b72..48a588b72 100755
--- a/smoketest/scripts/cli/test_service_salt.py
+++ b/smoketest/scripts/cli/test_service_salt-minion.py
diff --git a/smoketest/scripts/cli/test_system_frr.py b/smoketest/scripts/cli/test_system_frr.py
index 3eb0cd0ab..a2ce58bf6 100755
--- a/smoketest/scripts/cli/test_system_frr.py
+++ b/smoketest/scripts/cli/test_system_frr.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 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,13 +16,13 @@
import re
import unittest
+
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.utils.file import read_file
config_file = '/etc/frr/daemons'
base_path = ['system', 'frr']
-
def daemons_config_parse(daemons_config):
# create regex for parsing daemons options
regex_daemon_config = re.compile(
@@ -33,13 +33,20 @@ def daemons_config_parse(daemons_config):
for daemon in regex_daemon_config.finditer(daemons_config):
daemon_name = daemon.group('daemon_name')
daemon_options = daemon.group('daemon_options')
- daemons_config_dict[daemon_name] = daemon_options
+ daemons_config_dict[daemon_name] = daemon_options.lstrip()
# return daemons config
return (daemons_config_dict)
class TestSystemFRR(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestSystemFRR, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
def tearDown(self):
self.cli_delete(base_path)
@@ -64,7 +71,7 @@ class TestSystemFRR(VyOSUnitTestSHIM.TestCase):
else:
self.assertFalse(snmp_enabled)
- def test_frr_snmp_addandremove(self):
+ def test_frr_snmp_add_remove(self):
# test enabling and disabling of SNMP integration
test_daemon_names = ['ospfd', 'bgpd']
for test_daemon_name in test_daemon_names:
@@ -124,7 +131,7 @@ class TestSystemFRR(VyOSUnitTestSHIM.TestCase):
irdp_enabled = regex_irdp.match(daemons_config_dict['zebra'])
self.assertTrue(irdp_enabled)
- def test_frr_bmpandsnmp(self):
+ def test_frr_bmp_and_snmp(self):
# test empty config section
self.cli_set(base_path + ['bmp'])
self.cli_set(base_path + ['snmp', 'bgpd'])
@@ -141,6 +148,15 @@ class TestSystemFRR(VyOSUnitTestSHIM.TestCase):
self.assertTrue(bmp_enabled)
self.assertTrue(snmp_enabled)
+ def test_frr_file_descriptors(self):
+ file_descriptors = '4096'
+
+ self.cli_set(base_path + ['descriptors', file_descriptors])
+ self.cli_commit()
+
+ # read the config file and check content
+ daemons_config = read_file(config_file)
+ self.assertIn(f'MAX_FDS={file_descriptors}', daemons_config)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_nameserver.py b/smoketest/scripts/cli/test_system_nameserver.py
deleted file mode 100755
index 4979a7c72..000000000
--- a/smoketest/scripts/cli/test_system_nameserver.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import re
-import unittest
-
-from base_vyostest_shim import VyOSUnitTestSHIM
-
-from vyos.configsession import ConfigSessionError
-
-from vyos.utils.file import read_file
-
-RESOLV_CONF = '/etc/resolv.conf'
-
-test_servers = ['192.0.2.10', '2001:db8:1::100']
-base_path = ['system', 'name-server']
-
-def get_name_servers():
- resolv_conf = read_file(RESOLV_CONF)
- return re.findall(r'\n?nameserver\s+(.*)', resolv_conf)
-
-class TestSystemNameServer(VyOSUnitTestSHIM.TestCase):
- def tearDown(self):
- # Delete existing name servers
- self.cli_delete(base_path)
- self.cli_commit()
-
- def test_nameserver_add(self):
- # Check if server is added to resolv.conf
- for s in test_servers:
- self.cli_set(base_path + [s])
- self.cli_commit()
-
- servers = get_name_servers()
- for s in servers:
- self.assertTrue(s in servers)
-
- def test_nameserver_delete(self):
- # Test if a deleted server disappears from resolv.conf
- for s in test_servers:
- self.cli_delete(base_path + [s])
- self.cli_commit()
-
- servers = get_name_servers()
- for s in servers:
- self.assertTrue(test_server_1 not in servers)
-
-if __name__ == '__main__':
- unittest.main(verbosity=2)
-
diff --git a/smoketest/scripts/cli/test_system_resolvconf.py b/smoketest/scripts/cli/test_system_resolvconf.py
new file mode 100755
index 000000000..d8726a301
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_resolvconf.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019-2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.utils.file import read_file
+
+RESOLV_CONF = '/etc/resolv.conf'
+
+name_servers = ['192.0.2.10', '2001:db8:1::100']
+domain_name = 'vyos.net'
+domain_search = ['vyos.net', 'vyos.io']
+
+base_path_nameserver = ['system', 'name-server']
+base_path_domainname = ['system', 'domain-name']
+base_path_domainsearch = ['system', 'domain-search']
+
+def get_name_servers():
+ resolv_conf = read_file(RESOLV_CONF)
+ return re.findall(r'\n?nameserver\s+(.*)', resolv_conf)
+
+def get_domain_name():
+ resolv_conf = read_file(RESOLV_CONF)
+ res = re.findall(r'\n?domain\s+(.*)', resolv_conf)
+ return res[0] if res else None
+
+def get_domain_searches():
+ resolv_conf = read_file(RESOLV_CONF)
+ res = re.findall(r'\n?search\s+(.*)', resolv_conf)
+ return res[0].split() if res else []
+
+class TestSystemResolvConf(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestSystemResolvConf, cls).setUpClass()
+ # Clear out current configuration to allow running this test on a live system
+ cls.cli_delete(cls, base_path_nameserver)
+ cls.cli_delete(cls, base_path_domainname)
+ cls.cli_delete(cls, base_path_domainsearch)
+
+ def tearDown(self):
+ # Delete test entries servers
+ self.cli_delete(base_path_nameserver)
+ self.cli_delete(base_path_domainname)
+ self.cli_delete(base_path_domainsearch)
+ self.cli_commit()
+
+ def test_nameserver(self):
+ # Check if server is added to resolv.conf
+ for s in name_servers:
+ self.cli_set(base_path_nameserver + [s])
+ self.cli_commit()
+
+ for s in get_name_servers():
+ self.assertTrue(s in name_servers)
+
+ # Test if a deleted server disappears from resolv.conf
+ for s in name_servers:
+ self.cli_delete(base_path_nameserver + [s])
+ self.cli_commit()
+
+ for s in get_name_servers():
+ self.assertTrue(s not in name_servers)
+
+ def test_domainname(self):
+ # Check if domain-name is added to resolv.conf
+ self.cli_set(base_path_domainname + [domain_name])
+ self.cli_commit()
+
+ self.assertEqual(get_domain_name(), domain_name)
+
+ # Test if domain-name disappears from resolv.conf
+ self.cli_delete(base_path_domainname + [domain_name])
+ self.cli_commit()
+
+ self.assertTrue(get_domain_name() is None)
+
+ def test_domainsearch(self):
+ # Check if domain-search is added to resolv.conf
+ for s in domain_search:
+ self.cli_set(base_path_domainsearch + [s])
+ self.cli_commit()
+
+ for s in get_domain_searches():
+ self.assertTrue(s in domain_search)
+
+ # Test if domain-search disappears from resolv.conf
+ for s in domain_search:
+ self.cli_delete(base_path_domainsearch + [s])
+ self.cli_commit()
+
+ for s in get_domain_searches():
+ self.assertTrue(s not in domain_search)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_syslog.py b/smoketest/scripts/cli/test_system_syslog.py
new file mode 100755
index 000000000..933a5704c
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_syslog.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019-2023 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.template import is_ipv4
+from vyos.template import address_from_cidr
+from vyos.utils.process import call
+from vyos.utils.process import DEVNULL
+from vyos.utils.file import read_file
+from vyos.utils.process import process_named_running
+from vyos.version import get_version_data
+
+PROCESS_NAME = 'rsyslogd'
+RSYSLOG_CONF = '/etc/rsyslog.d/00-vyos.conf'
+
+base_path = ['system', 'syslog']
+
+def get_config_value(key):
+ tmp = read_file(RSYSLOG_CONF)
+ tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
+ return tmp[0]
+
+class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestRSYSLOGService, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, base_path)
+
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # delete testing SYSLOG config
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Check for running process
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_syslog_basic(self):
+ host1 = '198.51.100.1'
+ host2 = '192.0.2.1'
+
+ self.cli_set(base_path + ['host', host1, 'port', '999'])
+ self.cli_set(base_path + ['host', host1, 'facility', 'all', 'level', 'all'])
+ self.cli_set(base_path + ['host', host2, 'facility', 'kern', 'level', 'err'])
+ self.cli_set(base_path + ['console', 'facility', 'all', 'level', 'warning'])
+
+
+ self.cli_commit()
+ # verify log level and facilities in config file
+ # *.warning /dev/console
+ # *.* @198.51.100.1:999
+ # kern.err @192.0.2.1:514
+ config = [get_config_value('\*.\*'), get_config_value('kern.err'), get_config_value('\*.warning')]
+ expected = ['@198.51.100.1:999', '@192.0.2.1:514', '/dev/console']
+
+ for i in range(0,3):
+ self.assertIn(expected[i], config[i])
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_ipsec.py b/smoketest/scripts/cli/test_vpn_ipsec.py
index 01b0406bf..f5369ee7a 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -18,6 +18,8 @@ import os
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
from vyos.utils.process import call
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
@@ -44,78 +46,66 @@ secret = 'MYSECRETKEY'
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_name = 'MyVyOS-CA'
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_name = 'MyVyOS-IntCA'
+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_name = 'peer1'
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
@@ -125,6 +115,7 @@ class TestVPNIPsec(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, ['pki'])
cls.cli_set(cls, base_path + ['interface', f'{interface}.{vif}'])
@@ -155,6 +146,15 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# Check for no longer running process
self.assertFalse(process_named_running(PROCESS_NAME))
+ def setupPKI(self):
+ 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','')])
+
+ def tearDownPKI(self):
+ self.cli_delete(['pki'])
+
def test_01_dhcp_fail_handling(self):
# Skip process check - connection is not created for this test
self.skip_process_check = True
@@ -398,11 +398,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
def test_05_x509_site2site(self):
# Enable PKI
- peer_name = 'peer1'
- ca_name = 'MyVyOS-CA'
- self.cli_set(['pki', 'ca', ca_name, 'certificate', 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','')])
+ self.setupPKI()
vti = 'vti20'
self.cli_set(vti_path + [vti, 'address', '192.168.0.1/31'])
@@ -415,7 +411,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,9 +462,17 @@ 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)
+ # Disable PKI
+ self.tearDownPKI()
+
def test_06_flex_vpn_vips(self):
local_address = '192.0.2.5'
@@ -545,5 +549,351 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.assertIn(line, charon_conf)
+ def test_07_ikev2_road_warrior(self):
+ # This is a known to be good configuration for Microsoft Windows 10 and Apple iOS 17
+ self.setupPKI()
+
+ ike_group = 'IKE-RW'
+ esp_group = 'ESP-RW'
+
+ conn_name = 'vyos-rw'
+ local_address = '192.0.2.1'
+ ip_pool_name = 'ra-rw-ipv4'
+ username = 'vyos'
+ password = 'secret'
+ ike_lifetime = '7200'
+ eap_lifetime = '3600'
+ local_id = 'ipsec.vyos.net'
+
+ name_servers = ['172.16.254.100', '172.16.254.101']
+ prefix = '172.16.250.0/28'
+
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'dh-group', '2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'hash', 'sha256'])
+
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', eap_lifetime])
+ self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'disable'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'hash', 'sha384'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'hash', 'sha1'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'hash', 'sha256'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-id', local_id])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-users', 'username', username, 'password', password])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'server-mode', 'x509'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'certificate', peer_name])
+ # verify() - CA cert required for x509 auth
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', ca_name])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'esp-group', esp_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'ike-group', ike_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'local-address', local_address])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'pool', ip_pool_name])
+
+ for ns in name_servers:
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'name-server', ns])
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'prefix', prefix])
+
+ self.cli_commit()
+
+ # verify applied configuration
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_lines = [
+ f'{conn_name}',
+ f'remote_addrs = %any',
+ f'local_addrs = {local_address}',
+ f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048',
+ f'version = 2',
+ f'send_certreq = no',
+ f'rekey_time = {ike_lifetime}s',
+ f'keyingtries = 0',
+ f'pools = {ip_pool_name}',
+ f'id = "{local_id}"',
+ f'auth = pubkey',
+ f'certs = peer1.pem',
+ f'auth = eap-mschapv2',
+ f'eap_id = %any',
+ f'esp_proposals = aes256-sha512,aes256-sha384,aes256-sha256,aes256-sha1,aes128gcm128-sha256',
+ f'rekey_time = {eap_lifetime}s',
+ f'rand_time = 540s',
+ f'dpd_action = clear',
+ f'inactivity = 28800',
+ f'local_ts = 0.0.0.0/0,::/0',
+ ]
+ for line in swanctl_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_secrets_lines = [
+ f'eap-{conn_name}-{username}',
+ f'secret = "{password}"',
+ f'id-{conn_name}-{username} = "{username}"',
+ ]
+ for line in swanctl_secrets_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_pool_lines = [
+ f'{ip_pool_name}',
+ f'addrs = {prefix}',
+ f'dns = {",".join(name_servers)}',
+ ]
+ for line in swanctl_pool_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'{ca_name}_1.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
+
+ self.tearDownPKI()
+
+ def test_08_ikev2_road_warrior_client_auth_eap_tls(self):
+ # This is a known to be good configuration for Microsoft Windows 10 and Apple iOS 17
+ self.setupPKI()
+
+ ike_group = 'IKE-RW'
+ esp_group = 'ESP-RW'
+
+ conn_name = 'vyos-rw'
+ local_address = '192.0.2.1'
+ ip_pool_name = 'ra-rw-ipv4'
+ username = 'vyos'
+ password = 'secret'
+ ike_lifetime = '7200'
+ eap_lifetime = '3600'
+ local_id = 'ipsec.vyos.net'
+
+ name_servers = ['172.16.254.100', '172.16.254.101']
+ prefix = '172.16.250.0/28'
+
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'dh-group', '2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'hash', 'sha256'])
+
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', eap_lifetime])
+ self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'disable'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'hash', 'sha384'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'hash', 'sha1'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'hash', 'sha256'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-id', local_id])
+ # Use EAP-TLS auth instead of default EAP-MSCHAPv2
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'client-mode', 'eap-tls'])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'server-mode', 'x509'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'certificate', peer_name])
+ # verify() - CA cert required for x509 auth
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', ca_name])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'esp-group', esp_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'ike-group', ike_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'local-address', local_address])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'pool', ip_pool_name])
+
+ for ns in name_servers:
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'name-server', ns])
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'prefix', prefix])
+
+ self.cli_commit()
+
+ # verify applied configuration
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_lines = [
+ f'{conn_name}',
+ f'remote_addrs = %any',
+ f'local_addrs = {local_address}',
+ f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048',
+ f'version = 2',
+ f'send_certreq = no',
+ f'rekey_time = {ike_lifetime}s',
+ f'keyingtries = 0',
+ f'pools = {ip_pool_name}',
+ f'id = "{local_id}"',
+ f'auth = pubkey',
+ f'certs = peer1.pem',
+ f'auth = eap-tls',
+ f'eap_id = %any',
+ f'esp_proposals = aes256-sha512,aes256-sha384,aes256-sha256,aes256-sha1,aes128gcm128-sha256',
+ f'rekey_time = {eap_lifetime}s',
+ f'rand_time = 540s',
+ f'dpd_action = clear',
+ f'inactivity = 28800',
+ f'local_ts = 0.0.0.0/0,::/0',
+ ]
+ for line in swanctl_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_pool_lines = [
+ f'{ip_pool_name}',
+ f'addrs = {prefix}',
+ f'dns = {",".join(name_servers)}',
+ ]
+ for line in swanctl_pool_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'{ca_name}_1.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
+
+ self.tearDownPKI()
+
+ def test_09_ikev2_road_warrior_client_auth_x509(self):
+ # This is a known to be good configuration for Microsoft Windows 10 and Apple iOS 17
+ self.setupPKI()
+
+ ike_group = 'IKE-RW'
+ esp_group = 'ESP-RW'
+
+ conn_name = 'vyos-rw'
+ local_address = '192.0.2.1'
+ ip_pool_name = 'ra-rw-ipv4'
+ ike_lifetime = '7200'
+ eap_lifetime = '3600'
+ local_id = 'ipsec.vyos.net'
+
+ name_servers = ['172.16.254.100', '172.16.254.101']
+ prefix = '172.16.250.0/28'
+
+ # IKE
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'lifetime', ike_lifetime])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'dh-group', '2'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'dh-group', '14'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '10', 'hash', 'sha256'])
+
+ # ESP
+ self.cli_set(base_path + ['esp-group', esp_group, 'lifetime', eap_lifetime])
+ self.cli_set(base_path + ['esp-group', esp_group, 'pfs', 'disable'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha512'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '2', 'hash', 'sha384'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '3', 'hash', 'sha256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'encryption', 'aes256'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '4', 'hash', 'sha1'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'encryption', 'aes128gcm128'])
+ self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '10', 'hash', 'sha256'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'local-id', local_id])
+ # Use client-mode x509 instead of default EAP-MSCHAPv2
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'client-mode', 'x509'])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'server-mode', 'x509'])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'certificate', peer_name])
+ # verify() - CA cert required for x509 auth
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'authentication', 'x509', 'ca-certificate', ca_name])
+
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'esp-group', esp_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'ike-group', ike_group])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'local-address', local_address])
+ self.cli_set(base_path + ['remote-access', 'connection', conn_name, 'pool', ip_pool_name])
+
+ for ns in name_servers:
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'name-server', ns])
+ self.cli_set(base_path + ['remote-access', 'pool', ip_pool_name, 'prefix', prefix])
+
+ self.cli_commit()
+
+ # verify applied configuration
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_lines = [
+ f'{conn_name}',
+ f'remote_addrs = %any',
+ f'local_addrs = {local_address}',
+ f'proposals = aes256-sha512-modp2048,aes256-sha256-modp2048,aes256-sha256-modp1024,aes128gcm128-sha256-modp2048',
+ f'version = 2',
+ f'send_certreq = no',
+ f'rekey_time = {ike_lifetime}s',
+ f'keyingtries = 0',
+ f'pools = {ip_pool_name}',
+ f'id = "{local_id}"',
+ f'auth = pubkey',
+ f'certs = peer1.pem',
+ f'esp_proposals = aes256-sha512,aes256-sha384,aes256-sha256,aes256-sha1,aes128gcm128-sha256',
+ f'rekey_time = {eap_lifetime}s',
+ f'rand_time = 540s',
+ f'dpd_action = clear',
+ f'inactivity = 28800',
+ f'local_ts = 0.0.0.0/0,::/0',
+ ]
+ for line in swanctl_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_unexpected_lines = [
+ f'auth = eap-',
+ f'eap_id'
+ ]
+ for unexpected_line in swanctl_unexpected_lines:
+ self.assertNotIn(unexpected_line, swanctl_conf)
+
+ swanctl_pool_lines = [
+ f'{ip_pool_name}',
+ f'addrs = {prefix}',
+ f'dns = {",".join(name_servers)}',
+ ]
+ for line in swanctl_pool_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'{ca_name}_1.pem')))
+ self.assertTrue(os.path.exists(os.path.join(CERT_PATH, f'{peer_name}.pem')))
+
+ self.tearDownPKI()
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_l2tp.py b/smoketest/scripts/cli/test_vpn_l2tp.py
new file mode 100755
index 000000000..5b3e419bd
--- /dev/null
+++ b/smoketest/scripts/cli/test_vpn_l2tp.py
@@ -0,0 +1,135 @@
+#!/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 re
+import unittest
+
+from base_accel_ppp_test import BasicAccelPPPTest
+from configparser import ConfigParser
+from vyos.utils.process import cmd
+
+
+class TestVPNL2TPServer(BasicAccelPPPTest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls._base_path = ['vpn', 'l2tp', 'remote-access']
+ cls._config_file = '/run/accel-pppd/l2tp.conf'
+ cls._chap_secrets = '/run/accel-pppd/l2tp.chap-secrets'
+ cls._protocol_section = 'l2tp'
+ # call base-classes classmethod
+ super(TestVPNL2TPServer, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestVPNL2TPServer, cls).tearDownClass()
+
+ def basic_protocol_specific_config(self):
+ pass
+
+ def test_l2tp_server_ppp_options(self):
+ # Test configuration of local authentication for PPPoE server
+ self.basic_config()
+ mtu = '1425'
+ lcp_echo_failure = '5'
+ lcp_echo_interval = '40'
+ lcp_echo_timeout = '3000'
+ # other settings
+ mppe = 'require'
+ self.set(['ccp-disable'])
+ self.set(['ppp-options', 'mppe', mppe])
+ self.set(['authentication', 'radius', 'preallocate-vif'])
+ self.set(['mtu', mtu])
+ self.set(['ppp-options', 'lcp-echo-failure', lcp_echo_failure])
+ self.set(['ppp-options', 'lcp-echo-interval', lcp_echo_interval])
+ self.set(['ppp-options', 'lcp-echo-timeout', lcp_echo_timeout])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+
+ # basic verification
+ self.verify(conf)
+
+ # check ppp
+ self.assertEqual(conf['ppp']['mppe'], mppe)
+ self.assertFalse(conf['ppp'].getboolean('ccp'))
+ self.assertEqual(conf['ppp']['unit-preallocate'], '1')
+ self.assertTrue(conf['ppp'].getboolean('verbose'))
+ self.assertTrue(conf['ppp'].getboolean('check-ip'))
+ self.assertEqual(conf['ppp']['mtu'], mtu)
+ self.assertEqual(conf['ppp']['lcp-echo-interval'], lcp_echo_interval)
+ self.assertEqual(conf['ppp']['lcp-echo-timeout'], lcp_echo_timeout)
+ self.assertEqual(conf['ppp']['lcp-echo-failure'], lcp_echo_failure)
+
+ def test_l2tp_server_authentication_protocols(self):
+ # Test configuration of local authentication for PPPoE server
+ self.basic_config()
+
+ # explicitly test mschap-v2 - no special reason
+ self.set( ['authentication', 'protocols', 'mschap-v2'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True)
+ conf.read(self._config_file)
+
+ self.assertEqual(conf['modules']['auth_mschap_v2'], None)
+
+ def test_l2tp_server_client_ipv6_pool(self):
+ # Test configuration of IPv6 client pools
+ self.basic_config()
+
+ # Enable IPv6
+ allow_ipv6 = 'allow'
+ random = 'random'
+ self.set(['ppp-options', 'ipv6', allow_ipv6])
+ self.set(['ppp-options', 'ipv6-intf-id', random])
+ self.set(['ppp-options', 'ipv6-accept-peer-intf-id'])
+ self.set(['ppp-options', 'ipv6-peer-intf-id', random])
+
+ prefix = '2001:db8:ffff::/64'
+ prefix_mask = '128'
+ client_prefix = f'{prefix},{prefix_mask}'
+ self.set(['client-ipv6-pool', 'prefix', prefix, 'mask', prefix_mask])
+
+ delegate_prefix = '2001:db8::/40'
+ delegate_mask = '56'
+ self.set(['client-ipv6-pool', 'delegate', delegate_prefix, 'delegation-prefix', delegate_mask])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+
+ for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']:
+ self.assertEqual(conf['modules'][tmp], None)
+
+ self.assertEqual(conf['ppp']['ipv6'], allow_ipv6)
+ self.assertEqual(conf['ppp']['ipv6-intf-id'], random)
+ self.assertEqual(conf['ppp']['ipv6-peer-intf-id'], random)
+ self.assertTrue(conf['ppp'].getboolean('ipv6-accept-peer-intf-id'))
+
+ self.assertEqual(conf['ipv6-pool'][client_prefix], None)
+ self.assertEqual(conf['ipv6-pool']['delegate'], f'{delegate_prefix},{delegate_mask}')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py
index 04abeb1aa..c4502fada 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -141,5 +141,26 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase):
otp_config = read_file(otp_file)
self.assertIn(f'HOTP/T30/6 {user} - {otp}', otp_config)
+
+ # Verify HTTP security headers
+ self.cli_set(base_path + ['http-security-headers'])
+ self.cli_commit()
+
+ daemon_config = read_file(config_file)
+
+ self.assertIn('included-http-headers = Strict-Transport-Security: max-age=31536000 ; includeSubDomains', daemon_config)
+ self.assertIn('included-http-headers = X-Frame-Options: deny', daemon_config)
+ self.assertIn('included-http-headers = X-Content-Type-Options: nosniff', daemon_config)
+ self.assertIn('included-http-headers = Content-Security-Policy: default-src "none"', daemon_config)
+ self.assertIn('included-http-headers = X-Permitted-Cross-Domain-Policies: none', daemon_config)
+ self.assertIn('included-http-headers = Referrer-Policy: no-referrer', daemon_config)
+ self.assertIn('included-http-headers = Clear-Site-Data: "cache","cookies","storage"', daemon_config)
+ self.assertIn('included-http-headers = Cross-Origin-Embedder-Policy: require-corp', daemon_config)
+ self.assertIn('included-http-headers = Cross-Origin-Opener-Policy: same-origin', daemon_config)
+ self.assertIn('included-http-headers = Cross-Origin-Resource-Policy: same-origin', daemon_config)
+ self.assertIn('included-http-headers = X-XSS-Protection: 0', daemon_config)
+ self.assertIn('included-http-headers = Pragma: no-cache', daemon_config)
+ self.assertIn('included-http-headers = Cache-control: no-store, no-cache', daemon_config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_pptp.py b/smoketest/scripts/cli/test_vpn_pptp.py
new file mode 100755
index 000000000..0d9ea312e
--- /dev/null
+++ b/smoketest/scripts/cli/test_vpn_pptp.py
@@ -0,0 +1,223 @@
+#!/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 re
+import unittest
+
+from configparser import ConfigParser
+from vyos.utils.process import cmd
+from base_accel_ppp_test import BasicAccelPPPTest
+from vyos.template import is_ipv4
+
+
+class TestVPNPPTPServer(BasicAccelPPPTest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls._base_path = ['vpn', 'pptp', 'remote-access']
+ cls._config_file = '/run/accel-pppd/pptp.conf'
+ cls._chap_secrets = '/run/accel-pppd/pptp.chap-secrets'
+ cls._protocol_section = 'pptp'
+ # call base-classes classmethod
+ super(TestVPNPPTPServer, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestVPNPPTPServer, cls).tearDownClass()
+
+ def basic_protocol_specific_config(self):
+ pass
+
+ def test_accel_name_servers(self):
+ # Verify proper Name-Server configuration for IPv4
+ self.basic_config()
+
+ nameserver = ["192.0.2.1", "192.0.2.2"]
+ for ns in nameserver:
+ self.set(["name-server", ns])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
+ conf.read(self._config_file)
+
+ # IPv4 and IPv6 nameservers must be checked individually
+ for ns in nameserver:
+ self.assertIn(ns, [conf["dns"]["dns1"], conf["dns"]["dns2"]])
+
+ def test_accel_local_authentication(self):
+ # Test configuration of local authentication
+ self.basic_config()
+
+ # upload / download limit
+ user = "test"
+ password = "test2"
+ static_ip = "100.100.100.101"
+ upload = "5000"
+ download = "10000"
+
+ self.set(
+ [
+ "authentication",
+ "local-users",
+ "username",
+ user,
+ "password",
+ password,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "local-users",
+ "username",
+ user,
+ "static-ip",
+ static_ip,
+ ]
+ )
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
+ conf.read(self._config_file)
+
+ # check proper path to chap-secrets file
+ self.assertEqual(conf["chap-secrets"]["chap-secrets"], self._chap_secrets)
+
+ # basic verification
+ self.verify(conf)
+
+ # check local users
+ tmp = cmd(f"sudo cat {self._chap_secrets}")
+ regex = f"{user}\s+\*\s+{password}\s+{static_ip}\s"
+ tmp = re.findall(regex, tmp)
+ self.assertTrue(tmp)
+
+ # Check local-users default value(s)
+ self.delete(["authentication", "local-users", "username", user, "static-ip"])
+ # commit changes
+ self.cli_commit()
+
+ # check local users
+ tmp = cmd(f"sudo cat {self._chap_secrets}")
+ regex = f"{user}\s+\*\s+{password}\s+\*\s"
+ tmp = re.findall(regex, tmp)
+ self.assertTrue(tmp)
+
+ def test_accel_radius_authentication(self):
+ # Test configuration of RADIUS authentication for PPPoE server
+ self.basic_config()
+
+ radius_server = "192.0.2.22"
+ radius_key = "secretVyOS"
+ radius_port = "2000"
+ radius_port_acc = "3000"
+
+ self.set(["authentication", "mode", "radius"])
+ self.set(
+ ["authentication", "radius", "server", radius_server, "key", radius_key]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "server",
+ radius_server,
+ "port",
+ radius_port,
+ ]
+ )
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "server",
+ radius_server,
+ "acct-port",
+ radius_port_acc,
+ ]
+ )
+
+ nas_id = "VyOS-PPPoE"
+ nas_ip = "7.7.7.7"
+ self.set(["authentication", "radius", "nas-identifier", nas_id])
+ self.set(["authentication", "radius", "nas-ip-address", nas_ip])
+
+ source_address = "1.2.3.4"
+ self.set(["authentication", "radius", "source-address", source_address])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters="=", strict=False)
+ conf.read(self._config_file)
+
+ # basic verification
+ self.verify(conf)
+
+ # check auth
+ self.assertTrue(conf["radius"].getboolean("verbose"))
+ self.assertEqual(conf["radius"]["acct-timeout"], "30")
+ self.assertEqual(conf["radius"]["timeout"], "30")
+ self.assertEqual(conf["radius"]["max-try"], "3")
+
+ self.assertEqual(conf["radius"]["nas-identifier"], nas_id)
+ self.assertEqual(conf["radius"]["nas-ip-address"], nas_ip)
+ self.assertEqual(conf["radius"]["bind"], source_address)
+
+ server = conf["radius"]["server"].split(",")
+ self.assertEqual(radius_server, server[0])
+ self.assertEqual(radius_key, server[1])
+ self.assertEqual(f"auth-port={radius_port}", server[2])
+ self.assertEqual(f"acct-port={radius_port_acc}", server[3])
+ self.assertEqual(f"req-limit=0", server[4])
+ self.assertEqual(f"fail-time=0", server[5])
+
+ #
+ # Disable Radius Accounting
+ #
+ self.delete(["authentication", "radius", "server", radius_server, "acct-port"])
+ self.set(
+ [
+ "authentication",
+ "radius",
+ "server",
+ radius_server,
+ "disable-accounting",
+ ]
+ )
+
+ # commit changes
+ self.cli_commit()
+
+ conf.read(self._config_file)
+
+ server = conf["radius"]["server"].split(",")
+ self.assertEqual(radius_server, server[0])
+ self.assertEqual(radius_key, server[1])
+ self.assertEqual(f"auth-port={radius_port}", server[2])
+ self.assertEqual(f"acct-port=0", server[3])
+ self.assertEqual(f"req-limit=0", server[4])
+ self.assertEqual(f"fail-time=0", server[5])
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vpn_sstp.py b/smoketest/scripts/cli/test_vpn_sstp.py
index 232eafcf2..f0695d577 100755
--- a/smoketest/scripts/cli/test_vpn_sstp.py
+++ b/smoketest/scripts/cli/test_vpn_sstp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2022 VyOS maintainers and contributors
+# Copyright (C) 2020-2023 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -47,7 +47,7 @@ class TestVPNSSTPServer(BasicAccelPPPTest.TestCase):
cls._base_path = ['vpn', 'sstp']
cls._config_file = '/run/accel-pppd/sstp.conf'
cls._chap_secrets = '/run/accel-pppd/sstp.chap-secrets'
-
+ cls._protocol_section = 'sstp'
# call base-classes classmethod
super(TestVPNSSTPServer, cls).setUpClass()
@@ -58,26 +58,23 @@ class TestVPNSSTPServer(BasicAccelPPPTest.TestCase):
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, pki_path)
-
super(TestVPNSSTPServer, cls).tearDownClass()
- def basic_config(self):
- # SSL is mandatory
+ def basic_protocol_specific_config(self):
self.set(['ssl', 'ca-certificate', 'sstp'])
self.set(['ssl', 'certificate', 'sstp'])
- self.set(['client-ip-pool', 'subnet', '192.0.2.0/24'])
-
- super().basic_config()
def test_accel_local_authentication(self):
# Change default port
port = '8443'
self.set(['port', port])
+ self.basic_config()
super().test_accel_local_authentication()
config = read_file(self._config_file)
self.assertIn(f'port={port}', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 0f658f366..6207a1b41 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -30,6 +30,7 @@ from vyos.utils.process import cmd
from vyos.utils.file import read_file
from vyos.utils.network import get_interface_config
from vyos.utils.network import is_intf_addr_assigned
+from vyos.utils.system import sysctl_read
base_path = ['vrf']
vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo']
@@ -58,6 +59,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
for vrf in vrfs:
self.assertNotIn(vrf, interfaces())
+ # If there is no VRF defined, strict_mode should be off
+ self.assertEqual(sysctl_read('net.vrf.strict_mode'), '0')
def test_vrf_vni_and_table_id(self):
base_table = '1000'
@@ -130,8 +133,9 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Ensure VRF was created
self.assertIn(vrf, interfaces())
# Verify IP forwarding is 1 (enabled)
- self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '1')
- self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '1')
+ self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '1')
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '1')
+
# Test for proper loopback IP assignment
for addr in loopbacks:
self.assertTrue(is_intf_addr_assigned(vrf, addr))
@@ -149,10 +153,11 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify VRF configuration
- tmp = read_file('/proc/sys/net/ipv4/tcp_l3mdev_accept')
- self.assertIn(tmp, '1')
- tmp = read_file('/proc/sys/net/ipv4/udp_l3mdev_accept')
- self.assertIn(tmp, '1')
+ self.assertEqual(sysctl_read('net.ipv4.tcp_l3mdev_accept'), '1')
+ self.assertEqual(sysctl_read('net.ipv4.udp_l3mdev_accept'), '1')
+
+ # If there is any VRF defined, strict_mode should be on
+ self.assertEqual(sysctl_read('net.vrf.strict_mode'), '1')
def test_vrf_table_id_is_unalterable(self):
# Linux Kernel prohibits the change of a VRF table on the fly.
@@ -290,8 +295,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Ensure VRF was created
self.assertIn(vrf, interfaces())
# Verify IP forwarding is 0 (disabled)
- self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '0')
- self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '0')
+ self.assertEqual(sysctl_read(f'net.ipv4.conf.{vrf}.forwarding'), '0')
+ self.assertEqual(sysctl_read(f'net.ipv6.conf.{vrf}.forwarding'), '0')
def test_vrf_ip_protocol_route_map(self):
table = '6000'
@@ -489,4 +494,4 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)