summaryrefslogtreecommitdiff
path: root/smoketest/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'smoketest/scripts')
-rw-r--r--smoketest/scripts/cli/base_accel_ppp_test.py499
-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_container.py134
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py297
-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_ethernet.py13
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py35
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py4
-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.py142
-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)0
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_wan.py (renamed from smoketest/scripts/cli/test_load_balancing_wan.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py48
-rwxr-xr-xsmoketest/scripts/cli/test_nat64.py102
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py22
-rwxr-xr-xsmoketest/scripts/cli/test_pki.py78
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py77
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py19
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py28
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py198
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_igmp-proxy.py29
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py75
-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.py84
-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.py24
-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_dhcp-server.py649
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-server.py214
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_dynamic.py196
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_forwarding.py116
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py311
-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.py246
-rwxr-xr-xsmoketest/scripts/cli/test_service_lldp.py16
-rwxr-xr-xsmoketest/scripts/cli/test_service_mdns_repeater.py (renamed from smoketest/scripts/cli/test_service_mdns-repeater.py)0
-rwxr-xr-xsmoketest/scripts/cli/test_service_ndp-proxy.py70
-rwxr-xr-xsmoketest/scripts/cli/test_service_ntp.py33
-rwxr-xr-xsmoketest/scripts/cli/test_service_pppoe-server.py159
-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_conntrack.py52
-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_sflow.py28
-rwxr-xr-xsmoketest/scripts/cli/test_system_syslog.py85
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py389
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_l2tp.py100
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py21
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_pptp.py204
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_sstp.py13
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py26
-rwxr-xr-xsmoketest/scripts/system/test_module_load.py4
66 files changed, 4595 insertions, 1101 deletions
diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py
index 989028f64..0e6e522b9 100644
--- a/smoketest/scripts/cli/base_accel_ppp_test.py
+++ b/smoketest/scripts/cli/base_accel_ppp_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -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,313 @@ 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)
+
+ def test_accel_ipv6_pool(self):
+ # Test configuration of IPv6 client pools
+ self.basic_config(is_gateway=False, is_client_pool=False)
+
+ # Enable IPv6
+ allow_ipv6 = 'allow'
+ self.set(['ppp-options', 'ipv6', allow_ipv6])
+
+ pool_name = 'ipv6_test_pool'
+ prefix_1 = '2001:db8:fffe::/56'
+ prefix_mask = '64'
+ prefix_2 = '2001:db8:ffff::/56'
+ client_prefix_1 = f'{prefix_1},{prefix_mask}'
+ client_prefix_2 = f'{prefix_2},{prefix_mask}'
+ self.set(
+ ['client-ipv6-pool', pool_name, 'prefix', prefix_1, 'mask',
+ prefix_mask])
+ self.set(
+ ['client-ipv6-pool', pool_name, 'prefix', prefix_2, 'mask',
+ prefix_mask])
+
+ delegate_1_prefix = '2001:db8:fff1::/56'
+ delegate_2_prefix = '2001:db8:fff2::/56'
+ delegate_mask = '64'
+ self.set(
+ ['client-ipv6-pool', pool_name, 'delegate', delegate_1_prefix,
+ 'delegation-prefix', delegate_mask])
+ self.set(
+ ['client-ipv6-pool', pool_name, 'delegate', delegate_2_prefix,
+ 'delegation-prefix', delegate_mask])
+
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=',
+ strict=False)
+ 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)
+
+ config = self.getConfig("ipv6-pool")
+ pool_config = f"""{client_prefix_1},name={pool_name}
+{client_prefix_2},name={pool_name}
+delegate={delegate_1_prefix},{delegate_mask},name={pool_name}
+delegate={delegate_2_prefix},{delegate_mask},name={pool_name}"""
+ self.assertIn(pool_config, config)
+
+ def test_accel_ppp_options(self):
+ # Test configuration of local authentication for PPPoE server
+ self.basic_config()
+
+ # other settings
+ mppe = 'require'
+ self.set(['ppp-options', 'disable-ccp'])
+ self.set(['ppp-options', 'mppe', mppe])
+
+ # min-mtu
+ min_mtu = '1400'
+ self.set(['ppp-options', 'min-mtu', min_mtu])
+
+ # mru
+ mru = '9000'
+ self.set(['ppp-options', 'mru', mru])
+
+ # interface-cache
+ interface_cache = '128000'
+ self.set(['ppp-options', 'interface-cache', interface_cache])
+
+ # ipv6
+ allow_ipv6 = 'allow'
+ allow_ipv4 = 'require'
+ random = 'random'
+ lcp_failure = '4'
+ lcp_interval = '40'
+ lcp_timeout = '100'
+ self.set(['ppp-options', 'ipv4', allow_ipv4])
+ self.set(['ppp-options', 'ipv6', allow_ipv6])
+ self.set(['ppp-options', 'ipv6-interface-id', random])
+ self.set(['ppp-options', 'ipv6-accept-peer-interface-id'])
+ self.set(['ppp-options', 'ipv6-peer-interface-id', random])
+ self.set(['ppp-options', 'lcp-echo-failure', lcp_failure])
+ self.set(['ppp-options', 'lcp-echo-interval', lcp_interval])
+ self.set(['ppp-options', 'lcp-echo-timeout', lcp_timeout])
+ # commit changes
+ self.cli_commit()
+
+ # Validate configuration values
+ conf = ConfigParser(allow_no_value=True, delimiters='=')
+ conf.read(self._config_file)
+
+ self.assertEqual(conf['chap-secrets']['gw-ip-address'], self._gateway)
+
+ # check ppp
+ self.assertEqual(conf['ppp']['mppe'], mppe)
+ self.assertEqual(conf['ppp']['min-mtu'], min_mtu)
+ self.assertEqual(conf['ppp']['mru'], mru)
+
+ self.assertEqual(conf['ppp']['ccp'],'0')
+
+ # check interface-cache
+ self.assertEqual(conf['ppp']['unit-cache'], interface_cache)
+
+ #check ipv6
+ 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['ppp']['lcp-echo-failure'], lcp_failure)
+ self.assertEqual(conf['ppp']['lcp-echo-interval'], lcp_interval)
+ self.assertEqual(conf['ppp']['lcp-echo-timeout'], lcp_timeout) \ No newline at end of file
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_container.py b/smoketest/scripts/cli/test_container.py
index b43c05fae..9094e27dd 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,123 @@ 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}')
+ 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]['Gateway'] , str(ip_interface(prefix).ip + 1))
+ self.assertEqual(c['NetworkSettings']['Networks'][net_name]['IPAddress'], str(ip_interface(prefix).ip + ii))
+
+ 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}')
- json_subnet = n['subnets'][0]['subnet']
+ 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()
- c = cmd_to_json(f'sudo podman container inspect {cont_name}')
- json_ip = c['NetworkSettings']['Networks'][net_name]['IPAddress']
+ 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))
+
+ def test_uid_gid(self):
+ cont_name = 'uid-test'
+ gid = '100'
+ uid = '1001'
+
+ self.cli_set(base_path + ['name', cont_name, 'allow-host-networks'])
+ self.cli_set(base_path + ['name', cont_name, 'image', cont_image])
+ self.cli_set(base_path + ['name', cont_name, 'gid', gid])
+
+ # verify() - GID can only be set if UID is set
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['name', cont_name, 'uid', uid])
+
+ self.cli_commit()
- self.assertEqual(json_subnet, prefix)
- self.assertEqual(json_ip, cont_ip)
+ # verify
+ tmp = cmd(f'sudo podman exec -it {cont_name} id -u')
+ self.assertEqual(tmp, uid)
+ tmp = cmd(f'sudo podman exec -it {cont_name} id -g')
+ self.assertEqual(tmp, gid)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 7b4ba11d0..a7dd11145 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,24 @@ 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'],
+ ['log prefix "[ipv4-OUT-filter-default-D]"','OUT-filter default-action drop', 'drop'],
['chain NAME_smoketest'],
['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[ipv4-NAM-smoketest-1-A]" log level debug', 'ip ttl 15', 'accept'],
['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[ipv4-NAM-smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'],
- ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop']
+ ['log prefix "[ipv4-smoketest-default-D]"','smoketest default-action', 'drop']
]
self.verify_nftables(nftables_search, 'ip vyos_filter')
@@ -287,7 +292,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 +300,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'])
@@ -326,16 +331,18 @@ 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;'],
['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;'],
['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 "[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')
@@ -372,7 +379,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'])
@@ -396,35 +403,81 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_filter')
+ def test_ipv4_dynamic_groups(self):
+ group01 = 'knock01'
+ group02 = 'allowed'
+
+ self.cli_set(['firewall', 'group', 'dynamic-group', 'address-group', group01])
+ self.cli_set(['firewall', 'group', 'dynamic-group', 'address-group', group02])
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'destination', 'port', '5151'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'address-group', group01])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'timeout', '30s'])
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'destination', 'port', '7272'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'source', 'group', 'dynamic-address-group', group01])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'address-group', group02])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'timeout', '5m'])
+
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'destination', 'port', '22'])
+ self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '30', 'source', 'group', 'dynamic-address-group', group02])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'DA_{group01}'],
+ [f'DA_{group02}'],
+ ['type ipv4_addr'],
+ ['flags dynamic,timeout'],
+ ['chain VYOS_INPUT_filter {'],
+ ['type filter hook input priority filter', 'policy accept'],
+ ['tcp dport 5151', f'update @DA_{group01}', '{ ip saddr timeout 30s }', 'drop'],
+ ['tcp dport 7272', f'ip saddr @DA_{group01}', f'update @DA_{group02}', '{ ip saddr timeout 5m }', 'drop'],
+ ['tcp dport 22', f'ip saddr @DA_{group02}', 'accept']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
def test_ipv6_basic_rules(self):
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()
@@ -432,15 +485,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 "[ipv6-NAM-v6-smoketest-1-A]" log level crit', 'accept'],
- [f'"{name} default-action drop"', f'log prefix "[{name}-default-D]"', 'drop']
+ [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')
@@ -451,7 +512,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'])
@@ -483,7 +544,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['ip6 saddr 2001:db8::/64', 'meta mark != 0x000019ff-0x00001e56', f'jump NAME6_{name}'],
[f'chain NAME6_{name}'],
['ip6 length { 65, 513, 1025 }', 'ip6 dscp { af21, 0x35 }', 'accept'],
- [f'log prefix "[{name}-default-D]"', 'drop']
+ [f'log prefix "[ipv6-{name}-default-D]"', 'drop']
]
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
@@ -495,7 +556,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'])
@@ -519,25 +580,70 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+ def test_ipv6_dynamic_groups(self):
+ group01 = 'knock01'
+ group02 = 'allowed'
+
+ self.cli_set(['firewall', 'group', 'dynamic-group', 'ipv6-address-group', group01])
+ self.cli_set(['firewall', 'group', 'dynamic-group', 'ipv6-address-group', group02])
+
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'destination', 'port', '5151'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'address-group', group01])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '10', 'add-address-to-group', 'source-address', 'timeout', '30s'])
+
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'destination', 'port', '7272'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'source', 'group', 'dynamic-address-group', group01])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'address-group', group02])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '20', 'add-address-to-group', 'source-address', 'timeout', '5m'])
+
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'destination', 'port', '22'])
+ self.cli_set(['firewall', 'ipv6', 'input', 'filter', 'rule', '30', 'source', 'group', 'dynamic-address-group', group02])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'DA6_{group01}'],
+ [f'DA6_{group02}'],
+ ['type ipv6_addr'],
+ ['flags dynamic,timeout'],
+ ['chain VYOS_IPV6_INPUT_filter {'],
+ ['type filter hook input priority filter', 'policy accept'],
+ ['tcp dport 5151', f'update @DA6_{group01}', '{ ip6 saddr timeout 30s }', 'drop'],
+ ['tcp dport 7272', f'ip6 saddr @DA6_{group01}', f'update @DA6_{group02}', '{ ip6 saddr timeout 5m }', 'drop'],
+ ['tcp dport 22', f'ip6 saddr @DA6_{group02}', 'accept']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+
def test_ipv4_state_and_status_rules(self):
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', 'enable'])
+ 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'])
@@ -549,7 +655,12 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
['ct state new', 'ct status dnat', 'accept'],
['ct state { established, new }', 'ct status snat', 'accept'],
['ct state related', 'ct helper { "ftp", "pptp" }', 'accept'],
- ['drop', f'comment "{name} default-action drop"']
+ ['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')
@@ -566,14 +677,15 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
vlan_prior = '3'
self.cli_set(['firewall', 'bridge', 'name', name, 'default-action', 'accept'])
- self.cli_set(['firewall', 'bridge', 'name', name, 'enable-default-log'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'default-log'])
self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'action', 'accept'])
self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'source', 'mac-address', mac_address])
- self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'inbound-interface', 'interface-name', interface_in])
- self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', 'log', 'enable'])
+ self.cli_set(['firewall', 'bridge', 'name', name, 'rule', '1', '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'])
@@ -584,11 +696,13 @@ 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;'],
[f'vlan id {vlan_id}', 'accept'],
[f'vlan pcp {vlan_prior}', f'jump NAME_{name}'],
+ ['log prefix "[bri-FWD-filter-default-D]"', 'drop', 'FWD-filter default-action drop'],
[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']
+ [f'ether saddr {mac_address}', f'iifname "{interface_in}"', f'log prefix "[bri-NAM-{name}-1-A]" log level crit', 'accept'],
+ ['accept', f'{name} default-action accept']
]
self.verify_nftables(nftables_search, 'bridge vyos_filter')
@@ -635,6 +749,69 @@ 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', 'ipv6', 'name', 'smoketestv6', '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-eth0', 'intra-zone-filtering', 'firewall', 'ipv6-name', 'smoketestv6'])
+ 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_search_v6 = [
+ ['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 NAME6_smoketestv6'],
+ ['jump VYOS_STATE_POLICY6'],
+ ['chain VYOS_STATE_POLICY6'],
+ ['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')
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+ self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter')
+
def test_flow_offload(self):
self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0'])
self.cli_set(['firewall', 'flowtable', 'smoketest', 'offload', 'hardware'])
@@ -648,14 +825,14 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'action', 'offload'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'offload-target', 'smoketest'])
self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'protocol', 'tcp_udp'])
- self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'established', 'enable'])
- self.cli_set(['firewall', 'ipv4', 'forward', 'filter', 'rule', '1', 'state', 'related', 'enable'])
+ self.cli_set(['firewall', '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', 'enable'])
- self.cli_set(['firewall', 'ipv6', 'forward', 'filter', 'rule', '1', 'state', 'related', 'enable'])
+ 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()
@@ -673,5 +850,41 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
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_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index a39b81348..e414f18cb 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -141,15 +141,18 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
# Verify that no address remains on the system as this is an eternal
# interface.
- for intf in self._interfaces:
- self.assertNotIn(AF_INET, ifaddresses(intf))
+ for interface in self._interfaces:
+ self.assertNotIn(AF_INET, ifaddresses(interface))
# required for IPv6 link-local address
- self.assertIn(AF_INET6, ifaddresses(intf))
- for addr in ifaddresses(intf)[AF_INET6]:
+ self.assertIn(AF_INET6, ifaddresses(interface))
+ for addr in ifaddresses(interface)[AF_INET6]:
# checking link local addresses makes no sense
if is_ipv6_link_local(addr['addr']):
continue
- self.assertFalse(is_intf_addr_assigned(intf, addr['addr']))
+ self.assertFalse(is_intf_addr_assigned(interface, addr['addr']))
+ # Ensure no VLAN interfaces are left behind
+ tmp = [x for x in Section.interfaces('ethernet') if x.startswith(f'{interface}.')]
+ self.assertListEqual(tmp, [])
def test_offloading_rps(self):
# enable RPS on all available CPUs, RPS works with a CPU bitmask,
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 4a7e2418c..66c348976 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -506,11 +506,13 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
interface = 'vtun5001'
path = base_path + [interface]
+ encryption_cipher = 'aes256'
self.cli_set(path + ['mode', 'site-to-site'])
self.cli_set(path + ['local-address', '10.0.0.2'])
self.cli_set(path + ['remote-address', '192.168.0.3'])
self.cli_set(path + ['shared-secret-key', 'ovpn_test'])
+ self.cli_set(path + ['encryption', 'cipher', encryption_cipher])
self.cli_commit()
@@ -548,6 +550,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
port = ''
local_address = ''
remote_address = ''
+ encryption_cipher = 'aes256'
for ii in num_range:
interface = f'vtun{ii}'
@@ -571,6 +574,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
self.cli_set(path + ['remote-port', port])
self.cli_set(path + ['shared-secret-key', 'ovpn_test'])
self.cli_set(path + ['remote-address', remote_address])
+ self.cli_set(path + ['encryption', 'cipher', encryption_cipher])
self.cli_set(path + ['vrf', vrf_name])
self.cli_commit()
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 e9c9e68fd..18676491b 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -18,10 +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
@@ -31,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()
@@ -107,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.
@@ -138,7 +141,7 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
def test_vxlan_vlan_vni_mapping(self):
bridge = 'br0'
interface = 'vxlan0'
- source_interface = 'eth0'
+ source_address = '192.0.2.99'
vlan_to_vni = {
'10': '10010',
@@ -150,8 +153,8 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
'31': '10031',
}
- self.cli_set(self._base_path + [interface, 'external'])
- self.cli_set(self._base_path + [interface, 'source-interface', source_interface])
+ 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])
@@ -177,11 +180,140 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
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 274b97f22..274b97f22 100755
--- a/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
+++ b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
diff --git a/smoketest/scripts/cli/test_load_balancing_wan.py b/smoketest/scripts/cli/test_load-balancing_wan.py
index 47ca19b27..47ca19b27 100755
--- a/smoketest/scripts/cli/test_load_balancing_wan.py
+++ b/smoketest/scripts/cli/test_load-balancing_wan.py
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 703e5ab28..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)
@@ -167,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'])
@@ -186,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'])
@@ -236,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()
@@ -266,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'])
@@ -276,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])
@@ -290,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_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 4ac422d5f..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',
+ },
+ },
},
},
}
@@ -1541,6 +1568,56 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
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']
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
index 72192fb98..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,6 +192,7 @@ 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'])
@@ -200,7 +204,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
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])
@@ -209,13 +213,14 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
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'])
@@ -225,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'])
@@ -234,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'])
@@ -255,7 +260,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
['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]
+ ['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')
@@ -267,7 +273,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
['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],
['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]
+ ['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..716d0a806 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
@@ -32,6 +32,7 @@ peers = {
'multihop' : '',
'source_addr': '192.0.2.254',
'profile' : 'foo-bar-baz',
+ 'minimum_ttl': '20',
},
'192.0.2.20' : {
'echo_mode' : '',
@@ -63,6 +64,7 @@ profiles = {
'intv_rx' : '222',
'intv_tx' : '333',
'shutdown' : '',
+ 'minimum_ttl': '40',
},
'foo-bar-baz' : {
'intv_mult' : '4',
@@ -77,11 +79,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'])
@@ -97,6 +111,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer', peer, 'interval', 'receive', peer_config["intv_rx"]])
if 'intv_tx' in peer_config:
self.cli_set(base_path + ['peer', peer, 'interval', 'transmit', peer_config["intv_tx"]])
+ if 'minimum_ttl' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'minimum-ttl', peer_config["minimum_ttl"]])
if 'multihop' in peer_config:
self.cli_set(base_path + ['peer', peer, 'multihop'])
if 'passive' in peer_config:
@@ -140,6 +156,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'receive-interval {peer_config["intv_rx"]}', peerconfig)
if 'intv_tx' in peer_config:
self.assertIn(f'transmit-interval {peer_config["intv_tx"]}', peerconfig)
+ if 'minimum_ttl' in peer_config:
+ self.assertIn(f'minimum-ttl {peer_config["minimum_ttl"]}', peerconfig)
if 'passive' in peer_config:
self.assertIn(f'passive-mode', peerconfig)
if 'shutdown' in peer_config:
@@ -161,6 +179,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['profile', profile, 'interval', 'receive', profile_config["intv_rx"]])
if 'intv_tx' in profile_config:
self.cli_set(base_path + ['profile', profile, 'interval', 'transmit', profile_config["intv_tx"]])
+ if 'minimum_ttl' in profile_config:
+ self.cli_set(base_path + ['profile', profile, 'minimum-ttl', profile_config["minimum_ttl"]])
if 'passive' in profile_config:
self.cli_set(base_path + ['profile', profile, 'passive'])
if 'shutdown' in profile_config:
@@ -198,6 +218,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' receive-interval {profile_config["intv_rx"]}', config)
if 'intv_tx' in profile_config:
self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config)
+ if 'minimum_ttl' in profile_config:
+ self.assertIn(f' minimum-ttl {profile_config["minimum_ttl"]}', config)
if 'passive' in profile_config:
self.assertIn(f' passive-mode', config)
if 'shutdown' in profile_config:
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 967958cab..08a6e1696 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'
@@ -174,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'])
@@ -192,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'
@@ -304,8 +319,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
tcp_keepalive_interval = '77'
tcp_keepalive_probes = '22'
- self.cli_set(base_path + ['parameters', 'router-id', router_id])
+ self.cli_set(base_path + ['parameters', 'allow-martian-nexthop'])
+ self.cli_set(base_path + ['parameters', 'no-hard-administrative-reset'])
self.cli_set(base_path + ['parameters', 'log-neighbor-changes'])
+ self.cli_set(base_path + ['parameters', 'labeled-unicast', 'explicit-null'])
+ self.cli_set(base_path + ['parameters', 'router-id', router_id])
# System AS number MUST be defined - as this is set in setUp() we remove
# this once for testing of the proper error
@@ -340,6 +358,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])
@@ -350,12 +370,15 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
frrconfig = self.getFRRconfig(f'router bgp {ASN}')
self.assertIn(f'router bgp {ASN}', frrconfig)
self.assertIn(f' bgp router-id {router_id}', frrconfig)
+ self.assertIn(f' bgp allow-martian-nexthop', frrconfig)
self.assertIn(f' bgp log-neighbor-changes', frrconfig)
self.assertIn(f' bgp default local-preference {local_pref}', frrconfig)
self.assertIn(f' bgp conditional-advertisement timer {cond_adv_timer}', frrconfig)
self.assertIn(f' bgp fast-convergence', frrconfig)
self.assertIn(f' bgp graceful-restart stalepath-time {stalepath_time}', frrconfig)
self.assertIn(f' bgp graceful-shutdown', frrconfig)
+ self.assertIn(f' no bgp hard-administrative-reset', frrconfig)
+ self.assertIn(f' bgp labeled-unicast explicit-null', frrconfig)
self.assertIn(f' bgp bestpath as-path multipath-relax', frrconfig)
self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig)
self.assertIn(f' bgp bestpath compare-routerid', frrconfig)
@@ -373,6 +396,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)
@@ -722,15 +749,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()
@@ -741,12 +778,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'
@@ -1097,5 +1142,150 @@ 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'
+ nexthop_ipv4 = '192.0.0.1'
+ nexthop_ipv6 = '2001:db8:100:200::2'
+
+ self.cli_set(base_path + ['srv6', 'locator', locator_name])
+ self.cli_set(base_path + ['sid', 'vpn', 'per-vrf', 'export', sid])
+ self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'sid', 'vpn', 'export', sid])
+ # verify() - SID per VRF and SID per address-family are mutually exclusive!
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['address-family', 'ipv4-unicast', '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)
+
+ # Now test AFI SID
+ self.cli_delete(base_path + ['sid'])
+ self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'sid', 'vpn', 'export', sid])
+ self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'nexthop', 'vpn', 'export', nexthop_ipv4])
+ self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'sid', 'vpn', 'export', sid])
+ self.cli_set(base_path + ['address-family', 'ipv6-unicast', 'nexthop', 'vpn', 'export', nexthop_ipv6])
+
+ 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)
+
+ afiv4_config = self.getFRRconfig(' address-family ipv4 unicast')
+ self.assertIn(f' sid vpn export {sid}', afiv4_config)
+ self.assertIn(f' nexthop vpn export {nexthop_ipv4}', afiv4_config)
+ afiv6_config = self.getFRRconfig(' address-family ipv6 unicast')
+ self.assertIn(f' sid vpn export {sid}', afiv6_config)
+ self.assertIn(f' nexthop vpn export {nexthop_ipv6}', afiv4_config)
+
+ def test_bgp_25_ipv4_labeled_unicast_peer_group(self):
+ pg_ipv4 = 'foo4'
+ ipv4_max_prefix = '20'
+ ipv4_prefix = '192.0.2.0/24'
+
+ self.cli_set(base_path + ['listen', 'range', ipv4_prefix, 'peer-group', pg_ipv4])
+ self.cli_set(base_path + ['parameters', 'labeled-unicast', 'ipv4-explicit-null'])
+ 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_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' bgp listen range {ipv4_prefix} peer-group {pg_ipv4}', frrconfig)
+ self.assertIn(f' bgp labeled-unicast ipv4-explicit-null', 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)
+
+ def test_bgp_26_ipv6_labeled_unicast_peer_group(self):
+ pg_ipv6 = 'foo6'
+ ipv6_max_prefix = '200'
+ ipv6_prefix = '2001:db8:1000::/64'
+
+ self.cli_set(base_path + ['listen', 'range', ipv6_prefix, 'peer-group', pg_ipv6])
+ self.cli_set(base_path + ['parameters', 'labeled-unicast', 'ipv6-explicit-null'])
+
+ 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_ipv6} peer-group', frrconfig)
+ self.assertIn(f' neighbor {pg_ipv6} remote-as external', frrconfig)
+ self.assertIn(f' bgp listen range {ipv6_prefix} peer-group {pg_ipv6}', frrconfig)
+ self.assertIn(f' bgp labeled-unicast ipv6-explicit-null', frrconfig)
+
+ 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 747fb5e80..aa5f2f38c 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])
@@ -324,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)
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
index 1be12836d..ba24edca2 100755
--- a/smoketest/scripts/cli/test_protocols_pim6.py
+++ b/smoketest/scripts/cli/test_protocols_pim6.py
@@ -24,18 +24,27 @@ 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):
- # 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_pim6_01_mld_simple(self):
# commit changes
interfaces = Section.interfaces('ethernet')
-
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
@@ -43,68 +52,95 @@ class TestProtocolsPIMv6(VyOSUnitTestSHIM.TestCase):
# Verify FRR pim6d configuration
for interface in interfaces:
- config = self.getFRRconfig(
- f'interface {interface}', daemon=PROCESS_NAME)
+ 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_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)
+ config = self.getFRRconfig(f'interface {interface}', daemon=PROCESS_NAME)
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ipv6 mld', config)
self.assertIn(f' ipv6 mld version 1', config)
def test_pim6_02_mld_join(self):
- # commit changes
interfaces = Section.interfaces('ethernet')
-
- # Use an invalid multiple group address
+ # Use an invalid multicast group address
for interface in interfaces:
- self.cli_set(base_path + ['interface',
- interface, 'mld', 'join', 'fd00::1234'])
+ self.cli_set(base_path + ['interface', interface, 'mld', 'join', 'fd00::1234'])
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_delete(base_path + ['interface'])
- # Use a valid multiple group address
+ # Use a valid multicast group address
for interface in interfaces:
- self.cli_set(base_path + ['interface',
- interface, 'mld', 'join', 'ff18::1234'])
+ 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)
+ 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_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)
+ 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..b43c626c4 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-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
@@ -26,11 +26,20 @@ from vyos.utils.process import process_named_running
base_path = ['protocols', 'rpki']
PROCESS_NAME = 'bgpd'
-rpki_known_hosts = '/config/auth/known_hosts'
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 +48,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'
@@ -81,7 +90,6 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'rpki cache {peer} {port} preference {preference}', frrconfig)
def test_rpki_ssh(self):
- self.skipTest('Currently untested, see: https://github.com/FRRouting/frr/issues/7978')
polling = '7200'
cache = {
'192.0.2.3' : {
@@ -104,7 +112,6 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['cache', peer, 'ssh', 'username', peer_config['username']])
self.cli_set(base_path + ['cache', peer, 'ssh', 'public-key-file', rpki_ssh_pub])
self.cli_set(base_path + ['cache', peer, 'ssh', 'private-key-file', rpki_ssh_key])
- self.cli_set(base_path + ['cache', peer, 'ssh', 'known-hosts-file', rpki_known_hosts])
# commit changes
self.cli_commit()
@@ -117,7 +124,7 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
port = peer_config['port']
preference = peer_config['preference']
username = peer_config['username']
- self.assertIn(f'rpki cache {peer} {port} {username} {rpki_ssh_key} {rpki_known_hosts} preference {preference}', frrconfig)
+ self.assertIn(f'rpki cache {peer} {port} {username} {rpki_ssh_key} {rpki_ssh_pub} preference {preference}', frrconfig)
def test_rpki_verify_preference(self):
@@ -146,7 +153,4 @@ if __name__ == '__main__':
if not os.path.isfile(rpki_ssh_key):
cmd(f'ssh-keygen -t rsa -f {rpki_ssh_key} -N ""')
- if not os.path.isfile(rpki_known_hosts):
- cmd(f'touch {rpki_known_hosts}')
-
unittest.main(verbosity=2)
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_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index 093e43494..194289567 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -14,21 +14,25 @@
# 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 json import loads
+
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
-from vyos.template import address_from_cidr
from vyos.template import inc_ip
from vyos.template import dec_ip
-from vyos.template import netmask_from_cidr
-PROCESS_NAME = 'dhcpd'
-DHCPD_CONF = '/run/dhcp-server/dhcpd.conf'
+PROCESS_NAME = 'kea-dhcp4'
+CTRL_PROCESS_NAME = 'kea-ctrl-agent'
+KEA4_CONF = '/run/kea/kea-dhcp4.conf'
+KEA4_CTRL = '/run/kea/dhcp4-ctrl-socket'
base_path = ['service', 'dhcp-server']
+interface = 'dum8765'
subnet = '192.0.2.0/25'
router = inc_ip(subnet, 1)
dns_1 = inc_ip(subnet, 2)
@@ -39,19 +43,51 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestServiceDHCPServer, cls).setUpClass()
+ # Clear out current configuration to allow running this test on a live system
+ cls.cli_delete(cls, base_path)
cidr_mask = subnet.split('/')[-1]
- cls.cli_set(cls, ['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}'])
+ cls.cli_set(cls, ['interfaces', 'dummy', interface, 'address', f'{router}/{cidr_mask}'])
@classmethod
def tearDownClass(cls):
- cls.cli_delete(cls, ['interfaces', 'dummy', 'dum8765'])
+ cls.cli_delete(cls, ['interfaces', 'dummy', interface])
super(TestServiceDHCPServer, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
self.cli_commit()
+ def walk_path(self, obj, path):
+ current = obj
+
+ for i, key in enumerate(path):
+ if isinstance(key, str):
+ self.assertTrue(isinstance(current, dict), msg=f'Failed path: {path}')
+ self.assertTrue(key in current, msg=f'Failed path: {path}')
+ elif isinstance(key, int):
+ self.assertTrue(isinstance(current, list), msg=f'Failed path: {path}')
+ self.assertTrue(0 <= key < len(current), msg=f'Failed path: {path}')
+ else:
+ assert False, "Invalid type"
+
+ current = current[key]
+
+ return current
+
+ def verify_config_object(self, obj, path, value):
+ base_obj = self.walk_path(obj, path)
+ self.assertTrue(isinstance(base_obj, list))
+ self.assertTrue(any(True for v in base_obj if v == value))
+
+ def verify_config_value(self, obj, path, key, value):
+ base_obj = self.walk_path(obj, path)
+ if isinstance(base_obj, list):
+ self.assertTrue(any(True for v in base_obj if key in v and v[key] == value))
+ elif isinstance(base_obj, dict):
+ self.assertTrue(key in base_obj)
+ self.assertEqual(base_obj[key], value)
+
def test_dhcp_single_pool_range(self):
shared_net_name = 'SMOKE-1'
@@ -60,15 +96,15 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
range_1_start = inc_ip(subnet, 40)
range_1_stop = inc_ip(subnet, 50)
- self.cli_set(base_path + ['dynamic-dns-update'])
+ self.cli_set(base_path + ['listen-interface', interface])
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
# we use the first subnet IP address as default gateway
- self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['name-server', dns_1])
- self.cli_set(pool + ['name-server', dns_2])
- self.cli_set(pool + ['domain-name', domain_name])
- self.cli_set(pool + ['ping-check'])
+ self.cli_set(pool + ['option', 'default-router', router])
+ self.cli_set(pool + ['option', 'name-server', dns_1])
+ self.cli_set(pool + ['option', 'name-server', dns_2])
+ self.cli_set(pool + ['option', 'domain-name', domain_name])
# check validate() - No DHCP address range or active static-mapping set
with self.assertRaises(ConfigSessionError):
@@ -81,20 +117,39 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
- self.assertIn(f'ddns-update-style interim;', config)
- self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
- self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config)
- self.assertIn(f'option routers {router};', config)
- self.assertIn(f'option domain-name "{domain_name}";', config)
- self.assertIn(f'default-lease-time 86400;', config)
- self.assertIn(f'max-lease-time 86400;', config)
- self.assertIn(f'ping-check true;', config)
- self.assertIn(f'range {range_0_start} {range_0_stop};', config)
- self.assertIn(f'range {range_1_start} {range_1_stop};', config)
- self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp4', 'interfaces-config'], 'interfaces', [interface])
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'id', 1)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400)
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name', 'data': domain_name})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_1_start} - {range_1_stop}'})
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -115,67 +170,185 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
ipv6_only_preferred = '300'
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
# we use the first subnet IP address as default gateway
- self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['name-server', dns_1])
- self.cli_set(pool + ['name-server', dns_2])
- self.cli_set(pool + ['domain-name', domain_name])
- self.cli_set(pool + ['ip-forwarding'])
- self.cli_set(pool + ['smtp-server', smtp_server])
- self.cli_set(pool + ['pop-server', smtp_server])
- self.cli_set(pool + ['time-server', time_server])
- self.cli_set(pool + ['tftp-server-name', tftp_server])
+ self.cli_set(pool + ['option', 'default-router', router])
+ self.cli_set(pool + ['option', 'name-server', dns_1])
+ self.cli_set(pool + ['option', 'name-server', dns_2])
+ self.cli_set(pool + ['option', 'domain-name', domain_name])
+ self.cli_set(pool + ['option', 'ip-forwarding'])
+ self.cli_set(pool + ['option', 'smtp-server', smtp_server])
+ self.cli_set(pool + ['option', 'pop-server', smtp_server])
+ self.cli_set(pool + ['option', 'time-server', time_server])
+ self.cli_set(pool + ['option', 'tftp-server-name', tftp_server])
for search in search_domains:
- self.cli_set(pool + ['domain-search', search])
- self.cli_set(pool + ['bootfile-name', bootfile_name])
- self.cli_set(pool + ['bootfile-server', bootfile_server])
- self.cli_set(pool + ['wpad-url', wpad])
- self.cli_set(pool + ['server-identifier', server_identifier])
+ self.cli_set(pool + ['option', 'domain-search', search])
+ self.cli_set(pool + ['option', 'bootfile-name', bootfile_name])
+ self.cli_set(pool + ['option', 'bootfile-server', bootfile_server])
+ self.cli_set(pool + ['option', 'wpad-url', wpad])
+ self.cli_set(pool + ['option', 'server-identifier', server_identifier])
- self.cli_set(pool + ['static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1'])
- self.cli_set(pool + ['ipv6-only-preferred', ipv6_only_preferred])
+ self.cli_set(pool + ['option', 'static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1'])
+ self.cli_set(pool + ['option', 'ipv6-only-preferred', ipv6_only_preferred])
+ self.cli_set(pool + ['option', 'time-zone', 'Europe/London'])
- # check validate() - No DHCP address range or active static-mapping set
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
self.cli_set(pool + ['range', '0', 'start', range_0_start])
self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
-
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
- self.assertIn(f'ddns-update-style none;', config)
- self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
- self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config)
- self.assertIn(f'option routers {router};', config)
- self.assertIn(f'option domain-name "{domain_name}";', config)
-
- search = '"' + ('", "').join(search_domains) + '"'
- self.assertIn(f'option domain-search {search};', config)
-
- self.assertIn(f'option ip-forwarding true;', config)
- self.assertIn(f'option smtp-server {smtp_server};', config)
- self.assertIn(f'option pop-server {smtp_server};', config)
- self.assertIn(f'option time-servers {time_server};', config)
- self.assertIn(f'option wpad-url "{wpad}";', config)
- self.assertIn(f'option dhcp-server-identifier {server_identifier};', config)
- self.assertIn(f'option tftp-server-name "{tftp_server}";', config)
- self.assertIn(f'option bootfile-name "{bootfile_name}";', config)
- self.assertIn(f'filename "{bootfile_name}";', config)
- self.assertIn(f'next-server {bootfile_server};', config)
- self.assertIn(f'default-lease-time 86400;', config)
- self.assertIn(f'max-lease-time 86400;', config)
- self.assertIn(f'range {range_0_start} {range_0_stop};', config)
- self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
- self.assertIn(f'option rfc8925-ipv6-only-preferred {ipv6_only_preferred};', config)
-
- # weird syntax for those static routes
- self.assertIn(f'option rfc3442-static-route 24,10,0,0,192,0,2,1, 0,192,0,2,1;', config)
- self.assertIn(f'option windows-static-route 24,10,0,0,192,0,2,1;', config)
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'boot-file-name', bootfile_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'next-server', bootfile_server)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400)
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name', 'data': domain_name})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-search', 'data': ', '.join(search_domains)})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'pop-server', 'data': smtp_server})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'smtp-server', 'data': smtp_server})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'time-servers', 'data': time_server})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'dhcp-server-identifier', 'data': server_identifier})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'tftp-server-name', 'data': tftp_server})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'wpad-url', 'data': wpad})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'rfc3442-static-route', 'data': '24,10,0,0,192,0,2,1, 0,192,0,2,1'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'windows-static-route', 'data': '24,10,0,0,192,0,2,1'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'v6-only-preferred', 'data': ipv6_only_preferred})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'ip-forwarding', 'data': "true"})
+
+ # Time zone
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'pcode', 'data': 'GMT0BST,M3.5.0/1,M10.5.0'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'tcode', 'data': 'Europe/London'})
+
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'})
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def test_dhcp_single_pool_options_scoped(self):
+ shared_net_name = 'SMOKE-2'
+
+ range_0_start = inc_ip(subnet, 10)
+ range_0_stop = inc_ip(subnet, 20)
+
+ range_router = inc_ip(subnet, 5)
+ range_dns_1 = inc_ip(subnet, 6)
+ range_dns_2 = inc_ip(subnet, 7)
+
+ shared_network = base_path + ['shared-network-name', shared_net_name]
+ pool = shared_network + ['subnet', subnet]
+
+ self.cli_set(pool + ['subnet-id', '1'])
+
+ # we use the first subnet IP address as default gateway
+ self.cli_set(shared_network + ['option', 'default-router', router])
+ self.cli_set(shared_network + ['option', 'name-server', dns_1])
+ self.cli_set(shared_network + ['option', 'name-server', dns_2])
+ self.cli_set(shared_network + ['option', 'domain-name', domain_name])
+
+ self.cli_set(pool + ['range', '0', 'start', range_0_start])
+ self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
+ self.cli_set(pool + ['range', '0', 'option', 'default-router', range_router])
+ self.cli_set(pool + ['range', '0', 'option', 'name-server', range_dns_1])
+ self.cli_set(pool + ['range', '0', 'option', 'name-server', range_dns_2])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400)
+
+ # Verify shared-network options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'option-data'],
+ {'name': 'domain-name', 'data': domain_name})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+
+ # Verify range options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': f'{range_dns_1}, {range_dns_2}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools', 0, 'option-data'],
+ {'name': 'routers', 'data': range_router})
+
+ # Verify pool
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], 'pool', f'{range_0_start} - {range_0_stop}')
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -185,11 +358,12 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
domain_name = 'private'
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
# we use the first subnet IP address as default gateway
- self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['name-server', dns_1])
- self.cli_set(pool + ['name-server', dns_2])
- self.cli_set(pool + ['domain-name', domain_name])
+ self.cli_set(pool + ['option', 'default-router', router])
+ self.cli_set(pool + ['option', 'name-server', dns_1])
+ self.cli_set(pool + ['option', 'name-server', dns_2])
+ self.cli_set(pool + ['option', 'domain-name', domain_name])
# check validate() - No DHCP address range or active static-mapping set
with self.assertRaises(ConfigSessionError):
@@ -198,34 +372,81 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
client_base = 10
for client in ['client1', 'client2', 'client3']:
mac = '00:50:00:00:00:{}'.format(client_base)
- self.cli_set(pool + ['static-mapping', client, 'mac-address', mac])
+ self.cli_set(pool + ['static-mapping', client, 'mac', mac])
self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)])
client_base += 1
+ # cannot have both mac-address and duid set
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(pool + ['static-mapping', 'client1', 'duid', '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:11'])
+ self.cli_commit()
+ self.cli_delete(pool + ['static-mapping', 'client1', 'duid'])
+
+ # cannot have mappings with duplicate IP addresses
+ self.cli_set(pool + ['static-mapping', 'dupe1', 'mac', '00:50:00:00:fe:ff'])
+ self.cli_set(pool + ['static-mapping', 'dupe1', 'ip-address', inc_ip(subnet, 10)])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ # Should allow disabled duplicate
+ self.cli_set(pool + ['static-mapping', 'dupe1', 'disable'])
+ self.cli_commit()
+ self.cli_delete(pool + ['static-mapping', 'dupe1'])
+
+ # cannot have mappings with duplicate MAC addresses
+ self.cli_set(pool + ['static-mapping', 'dupe2', 'mac', '00:50:00:00:00:10'])
+ self.cli_set(pool + ['static-mapping', 'dupe2', 'ip-address', inc_ip(subnet, 120)])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(pool + ['static-mapping', 'dupe2'])
+
+
+ # cannot have mappings with duplicate MAC addresses
+ self.cli_set(pool + ['static-mapping', 'dupe3', 'duid', '00:01:02:03:04:05:06:07:aa:aa:aa:aa:aa:01'])
+ self.cli_set(pool + ['static-mapping', 'dupe3', 'ip-address', inc_ip(subnet, 121)])
+ self.cli_set(pool + ['static-mapping', 'dupe4', 'duid', '00:01:02:03:04:05:06:07:aa:aa:aa:aa:aa:01'])
+ self.cli_set(pool + ['static-mapping', 'dupe4', 'ip-address', inc_ip(subnet, 121)])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(pool + ['static-mapping', 'dupe3'])
+ self.cli_delete(pool + ['static-mapping', 'dupe4'])
+
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
- self.assertIn(f'ddns-update-style none;', config)
- self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
- self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config)
- self.assertIn(f'option routers {router};', config)
- self.assertIn(f'option domain-name "{domain_name}";', config)
- self.assertIn(f'default-lease-time 86400;', config)
- self.assertIn(f'max-lease-time 86400;', config)
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'id', 1)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400)
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name', 'data': domain_name})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
client_base = 10
for client in ['client1', 'client2', 'client3']:
mac = '00:50:00:00:00:{}'.format(client_base)
ip = inc_ip(subnet, client_base)
- self.assertIn(f'host {shared_net_name}_{client}' + ' {', config)
- self.assertIn(f'fixed-address {ip};', config)
- self.assertIn(f'hardware ethernet {mac};', config)
- client_base += 1
- self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'reservations'],
+ {'hostname': client, 'hw-address': mac, 'ip-address': ip})
+
+ client_base += 1
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -245,10 +466,11 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
range_1_stop = inc_ip(subnet, 40)
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', str(int(network) + 1)])
# we use the first subnet IP address as default gateway
- self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['name-server', dns_1])
- self.cli_set(pool + ['domain-name', domain_name])
+ self.cli_set(pool + ['option', 'default-router', router])
+ self.cli_set(pool + ['option', 'name-server', dns_1])
+ self.cli_set(pool + ['option', 'domain-name', domain_name])
self.cli_set(pool + ['lease', lease_time])
self.cli_set(pool + ['range', '0', 'start', range_0_start])
@@ -259,14 +481,16 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
client_base = 60
for client in ['client1', 'client2', 'client3', 'client4']:
mac = '02:50:00:00:00:{}'.format(client_base)
- self.cli_set(pool + ['static-mapping', client, 'mac-address', mac])
+ self.cli_set(pool + ['static-mapping', client, 'mac', mac])
self.cli_set(pool + ['static-mapping', client, 'ip-address', inc_ip(subnet, client_base)])
client_base += 1
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
for network in ['0', '1', '2', '3']:
shared_net_name = f'VyOS-SMOKETEST-{network}'
subnet = f'192.0.{network}.0/24'
@@ -278,27 +502,44 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
range_1_start = inc_ip(subnet, 30)
range_1_stop = inc_ip(subnet, 40)
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
-
- self.assertIn(f'ddns-update-style none;', config)
- self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
- self.assertIn(f'option domain-name-servers {dns_1};', config)
- self.assertIn(f'option routers {router};', config)
- self.assertIn(f'option domain-name "{domain_name}";', config)
- self.assertIn(f'default-lease-time {lease_time};', config)
- self.assertIn(f'max-lease-time {lease_time};', config)
- self.assertIn(f'range {range_0_start} {range_0_stop};', config)
- self.assertIn(f'range {range_1_start} {range_1_stop};', config)
- self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'subnet', subnet)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'id', int(network) + 1)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'valid-lifetime', int(lease_time))
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'max-valid-lifetime', int(lease_time))
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name', 'data': domain_name})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
+ {'name': 'domain-name-servers', 'data': dns_1})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'],
+ {'pool': f'{range_1_start} - {range_1_stop}'})
client_base = 60
for client in ['client1', 'client2', 'client3', 'client4']:
mac = '02:50:00:00:00:{}'.format(client_base)
ip = inc_ip(subnet, client_base)
- self.assertIn(f'host {shared_net_name}_{client}' + ' {', config)
- self.assertIn(f'fixed-address {ip};', config)
- self.assertIn(f'hardware ethernet {mac};', config)
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'reservations'],
+ {'hostname': client, 'hw-address': mac, 'ip-address': ip})
+
client_base += 1
# Check for running process
@@ -311,7 +552,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
range_0_stop = inc_ip(subnet, 20)
pool = base_path + ['shared-network-name', 'EXCLUDE-TEST', 'subnet', subnet]
- self.cli_set(pool + ['default-router', router])
+ self.cli_set(pool + ['subnet-id', '1'])
+ self.cli_set(pool + ['option', 'default-router', router])
self.cli_set(pool + ['exclude', router])
self.cli_set(pool + ['range', '0', 'start', range_0_start])
self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
@@ -319,14 +561,23 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- # VErify
- config = read_file(DHCPD_CONF)
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
- self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
- self.assertIn(f'option routers {router};', config)
- self.assertIn(f'range {range_0_start} {range_0_stop};', config)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST')
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'})
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -344,7 +595,8 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
range_0_start_excl = inc_ip(exclude_addr, 1)
pool = base_path + ['shared-network-name', 'EXCLUDE-TEST-2', 'subnet', subnet]
- self.cli_set(pool + ['default-router', router])
+ self.cli_set(pool + ['subnet-id', '1'])
+ self.cli_set(pool + ['option', 'default-router', router])
self.cli_set(pool + ['exclude', exclude_addr])
self.cli_set(pool + ['range', '0', 'start', range_0_start])
self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
@@ -352,15 +604,27 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- # Verify
- config = read_file(DHCPD_CONF)
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST-2')
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop_excl}'})
- self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
- self.assertIn(f'option routers {router};', config)
- self.assertIn(f'range {range_0_start} {range_0_stop_excl};', config)
- self.assertIn(f'range {range_0_start_excl} {range_0_stop};', config)
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start_excl} - {range_0_stop}'})
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -377,48 +641,32 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
range_0_stop = '10.0.250.255'
pool = base_path + ['shared-network-name', 'RELAY', 'subnet', relay_subnet]
- self.cli_set(pool + ['default-router', relay_router])
+ self.cli_set(pool + ['subnet-id', '1'])
+ self.cli_set(pool + ['option', 'default-router', relay_router])
self.cli_set(pool + ['range', '0', 'start', range_0_start])
self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
- # Check the relay network
- self.assertIn(f'subnet {network} netmask {netmask}' + r' { }', config)
-
- relay_network = address_from_cidr(relay_subnet)
- relay_netmask = netmask_from_cidr(relay_subnet)
- self.assertIn(f'subnet {relay_network} netmask {relay_netmask}' + r' {', config)
- self.assertIn(f'option routers {relay_router};', config)
- self.assertIn(f'range {range_0_start} {range_0_stop};', config)
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
- def test_dhcp_invalid_raw_options(self):
- shared_net_name = 'SMOKE-5'
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
- range_0_start = inc_ip(subnet, 10)
- range_0_stop = inc_ip(subnet, 20)
-
- pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
- # we use the first subnet IP address as default gateway
- self.cli_set(pool + ['default-router', router])
- self.cli_set(pool + ['range', '0', 'start', range_0_start])
- self.cli_set(pool + ['range', '0', 'stop', range_0_stop])
+ self.verify_config_value(obj, ['Dhcp4', 'interfaces-config'], 'interfaces', [f'{interface}/{router}'])
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'RELAY')
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', relay_subnet)
- self.cli_set(base_path + ['global-parameters', 'this-is-crap'])
- # check generate() - dhcpd should not acceot this garbage config
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- self.cli_delete(base_path + ['global-parameters'])
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': relay_router})
- # commit changes
- self.cli_commit()
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'})
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -431,8 +679,9 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
range_0_stop = inc_ip(subnet, 20)
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
+ self.cli_set(pool + ['subnet-id', '1'])
# we use the first subnet IP address as default gateway
- self.cli_set(pool + ['default-router', router])
+ self.cli_set(pool + ['option', 'default-router', router])
# check validate() - No DHCP address range or active static-mapping set
with self.assertRaises(ConfigSessionError):
@@ -449,41 +698,43 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['failover', 'remote', failover_remote])
self.cli_set(base_path + ['failover', 'status', 'primary'])
- # check validate() - failover needs to be enabled for at least one subnet
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
- self.cli_set(pool + ['enable-failover'])
-
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
-
- self.assertIn(f'failover peer "{failover_name}"' + r' {', config)
- self.assertIn(f'primary;', config)
- self.assertIn(f'mclt 1800;', config)
- self.assertIn(f'mclt 1800;', config)
- self.assertIn(f'split 128;', config)
- self.assertIn(f'port 647;', config)
- self.assertIn(f'peer port 647;', config)
- self.assertIn(f'max-response-delay 30;', config)
- self.assertIn(f'max-unacked-updates 10;', config)
- self.assertIn(f'load balance max seconds 3;', config)
- self.assertIn(f'address {failover_local};', config)
- self.assertIn(f'peer address {failover_remote};', config)
-
- network = address_from_cidr(subnet)
- netmask = netmask_from_cidr(subnet)
- self.assertIn(f'ddns-update-style none;', config)
- self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config)
- self.assertIn(f'option routers {router};', config)
- self.assertIn(f'range {range_0_start} {range_0_stop};', config)
- self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
- self.assertIn(f'failover peer "{failover_name}";', config)
- self.assertIn(f'deny dynamic bootp clients;', config)
+ config = read_file(KEA4_CONF)
+ obj = loads(config)
+
+ # Verify failover
+ self.verify_config_value(obj, ['Dhcp4', 'control-socket'], 'socket-name', KEA4_CTRL)
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
+ {'name': os.uname()[1], 'url': f'http://{failover_local}:647/', 'role': 'primary', 'auto-failover': True})
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'],
+ {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'standby', 'auto-failover': True})
+
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet)
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'],
+ {'name': 'routers', 'data': router})
+
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'],
+ {'pool': f'{range_0_start} - {range_0_stop}'})
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertTrue(process_named_running(CTRL_PROCESS_NAME))
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py
index 4d9dabc3f..5a831b8a0 100755
--- a/smoketest/scripts/cli/test_service_dhcpv6-server.py
+++ b/smoketest/scripts/cli/test_service_dhcpv6-server.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
@@ -16,6 +16,8 @@
import unittest
+from json import loads
+
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
@@ -23,8 +25,8 @@ from vyos.template import inc_ip
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
-PROCESS_NAME = 'dhcpd'
-DHCPD_CONF = '/run/dhcp-server/dhcpdv6.conf'
+PROCESS_NAME = 'kea-dhcp6'
+KEA6_CONF = '/run/kea/kea-dhcp6.conf'
base_path = ['service', 'dhcpv6-server']
subnet = '2001:db8:f00::/64'
@@ -39,6 +41,9 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestServiceDHCPv6Server, cls).setUpClass()
+ # Clear out current configuration to allow running this test on a live system
+ cls.cli_delete(cls, base_path)
+
cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_addr])
@classmethod
@@ -52,6 +57,36 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
+ def walk_path(self, obj, path):
+ current = obj
+
+ for i, key in enumerate(path):
+ if isinstance(key, str):
+ self.assertTrue(isinstance(current, dict), msg=f'Failed path: {path}')
+ self.assertTrue(key in current, msg=f'Failed path: {path}')
+ elif isinstance(key, int):
+ self.assertTrue(isinstance(current, list), msg=f'Failed path: {path}')
+ self.assertTrue(0 <= key < len(current), msg=f'Failed path: {path}')
+ else:
+ assert False, "Invalid type"
+
+ current = current[key]
+
+ return current
+
+ def verify_config_object(self, obj, path, value):
+ base_obj = self.walk_path(obj, path)
+ self.assertTrue(isinstance(base_obj, list))
+ self.assertTrue(any(True for v in base_obj if v == value))
+
+ def verify_config_value(self, obj, path, key, value):
+ base_obj = self.walk_path(obj, path)
+ if isinstance(base_obj, list):
+ self.assertTrue(any(True for v in base_obj if key in v and v[key] == value))
+ elif isinstance(base_obj, dict):
+ self.assertTrue(key in base_obj)
+ self.assertEqual(base_obj[key], value)
+
def test_single_pool(self):
shared_net_name = 'SMOKE-1'
search_domains = ['foo.vyos.net', 'bar.vyos.net']
@@ -67,66 +102,106 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
self.cli_set(base_path + ['preference', preference])
-
+ self.cli_set(pool + ['subnet-id', '1'])
# we use the first subnet IP address as default gateway
- self.cli_set(pool + ['name-server', dns_1])
- self.cli_set(pool + ['name-server', dns_2])
- self.cli_set(pool + ['name-server', dns_2])
self.cli_set(pool + ['lease-time', 'default', lease_time])
self.cli_set(pool + ['lease-time', 'maximum', max_lease_time])
self.cli_set(pool + ['lease-time', 'minimum', min_lease_time])
- self.cli_set(pool + ['nis-domain', domain])
- self.cli_set(pool + ['nisplus-domain', domain])
- self.cli_set(pool + ['sip-server', sip_server])
- self.cli_set(pool + ['sntp-server', sntp_server])
- self.cli_set(pool + ['address-range', 'start', range_start, 'stop', range_stop])
+ self.cli_set(pool + ['option', 'name-server', dns_1])
+ self.cli_set(pool + ['option', 'name-server', dns_2])
+ self.cli_set(pool + ['option', 'name-server', dns_2])
+ self.cli_set(pool + ['option', 'nis-domain', domain])
+ self.cli_set(pool + ['option', 'nisplus-domain', domain])
+ self.cli_set(pool + ['option', 'sip-server', sip_server])
+ self.cli_set(pool + ['option', 'sntp-server', sntp_server])
+ self.cli_set(pool + ['range', '1', 'start', range_start])
+ self.cli_set(pool + ['range', '1', 'stop', range_stop])
for server in nis_servers:
- self.cli_set(pool + ['nis-server', server])
- self.cli_set(pool + ['nisplus-server', server])
+ self.cli_set(pool + ['option', 'nis-server', server])
+ self.cli_set(pool + ['option', 'nisplus-server', server])
for search in search_domains:
- self.cli_set(pool + ['domain-search', search])
+ self.cli_set(pool + ['option', 'domain-search', search])
client_base = 1
for client in ['client1', 'client2', 'client3']:
- cid = '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{}'.format(client_base)
- self.cli_set(pool + ['static-mapping', client, 'identifier', cid])
+ duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}'
+ self.cli_set(pool + ['static-mapping', client, 'duid', duid])
self.cli_set(pool + ['static-mapping', client, 'ipv6-address', inc_ip(subnet, client_base)])
self.cli_set(pool + ['static-mapping', client, 'ipv6-prefix', inc_ip(subnet, client_base << 64) + '/64'])
client_base += 1
+ # cannot have both mac-address and duid set
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(pool + ['static-mapping', 'client1', 'mac', '00:50:00:00:00:11'])
+ self.cli_commit()
+ self.cli_delete(pool + ['static-mapping', 'client1', 'mac'])
+
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
- self.assertIn(f'option dhcp6.preference {preference};', config)
-
- self.assertIn(f'subnet6 {subnet}' + r' {', config)
- search = '"' + '", "'.join(search_domains) + '"'
- nissrv = ', '.join(nis_servers)
- self.assertIn(f'range6 {range_start} {range_stop};', config)
- self.assertIn(f'default-lease-time {lease_time};', config)
- self.assertIn(f'default-lease-time {lease_time};', config)
- self.assertIn(f'max-lease-time {max_lease_time};', config)
- self.assertIn(f'min-lease-time {min_lease_time};', config)
- self.assertIn(f'option dhcp6.domain-search {search};', config)
- self.assertIn(f'option dhcp6.name-servers {dns_1}, {dns_2};', config)
- self.assertIn(f'option dhcp6.nis-domain-name "{domain}";', config)
- self.assertIn(f'option dhcp6.nis-servers {nissrv};', config)
- self.assertIn(f'option dhcp6.nisp-domain-name "{domain}";', config)
- self.assertIn(f'option dhcp6.nisp-servers {nissrv};', config)
- self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+ config = read_file(KEA6_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet)
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'id', 1)
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'valid-lifetime', int(lease_time))
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'min-valid-lifetime', int(min_lease_time))
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'max-valid-lifetime', int(max_lease_time))
+
+ # Verify options
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'dns-servers', 'data': f'{dns_1}, {dns_2}'})
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'domain-search', 'data': ", ".join(search_domains)})
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'nis-domain-name', 'data': domain})
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'nis-servers', 'data': ", ".join(nis_servers)})
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'nisp-domain-name', 'data': domain})
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'nisp-servers', 'data': ", ".join(nis_servers)})
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'sntp-servers', 'data': sntp_server})
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'],
+ {'name': 'sip-server-dns', 'data': sip_server})
+
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pools'],
+ {'pool': f'{range_start} - {range_stop}'})
client_base = 1
for client in ['client1', 'client2', 'client3']:
- cid = '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{}'.format(client_base)
+ duid = f'00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{client_base:02}'
ip = inc_ip(subnet, client_base)
prefix = inc_ip(subnet, client_base << 64) + '/64'
- self.assertIn(f'host {shared_net_name}_{client}' + ' {', config)
- self.assertIn(f'fixed-address6 {ip};', config)
- self.assertIn(f'fixed-prefix6 {prefix};', config)
- self.assertIn(f'host-identifier option dhcp6.client-id {cid};', config)
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'reservations'],
+ {'hostname': client, 'duid': duid, 'ip-addresses': [ip], 'prefixes': [prefix]})
+
client_base += 1
# Check for running process
@@ -138,22 +213,44 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
range_start = inc_ip(subnet, 256) # ::100
range_stop = inc_ip(subnet, 65535) # ::ffff
delegate_start = '2001:db8:ee::'
- delegate_stop = '2001:db8:ee:ff00::'
- delegate_len = '56'
+ delegate_len = '64'
+ prefix_len = '56'
+ exclude_len = '66'
pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet]
-
- self.cli_set(pool + ['address-range', 'start', range_start, 'stop', range_stop])
- self.cli_set(pool + ['prefix-delegation', 'start', delegate_start, 'stop', delegate_stop])
- self.cli_set(pool + ['prefix-delegation', 'start', delegate_start, 'prefix-length', delegate_len])
+ self.cli_set(pool + ['subnet-id', '1'])
+ self.cli_set(pool + ['range', '1', 'start', range_start])
+ self.cli_set(pool + ['range', '1', 'stop', range_stop])
+ self.cli_set(pool + ['prefix-delegation', 'prefix', delegate_start, 'delegated-length', delegate_len])
+ self.cli_set(pool + ['prefix-delegation', 'prefix', delegate_start, 'prefix-length', prefix_len])
+ self.cli_set(pool + ['prefix-delegation', 'prefix', delegate_start, 'excluded-prefix', delegate_start])
+ self.cli_set(pool + ['prefix-delegation', 'prefix', delegate_start, 'excluded-prefix-length', exclude_len])
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
- self.assertIn(f'subnet6 {subnet}' + r' {', config)
- self.assertIn(f'range6 {range_start} {range_stop};', config)
- self.assertIn(f'prefix6 {delegate_start} {delegate_stop} /{delegate_len};', config)
+ config = read_file(KEA6_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet)
+
+ # Verify pools
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pools'],
+ {'pool': f'{range_start} - {range_stop}'})
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pd-pools'],
+ {
+ 'prefix': delegate_start,
+ 'prefix-len': int(prefix_len),
+ 'delegated-len': int(delegate_len),
+ 'excluded-prefix': delegate_start,
+ 'excluded-prefix-len': int(exclude_len)
+ })
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -165,15 +262,22 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['global-parameters', 'name-server', ns_global_1])
self.cli_set(base_path + ['global-parameters', 'name-server', ns_global_2])
- self.cli_set(base_path + ['shared-network-name', shared_net_name, 'subnet', subnet])
+ self.cli_set(base_path + ['shared-network-name', shared_net_name, 'subnet', subnet, 'subnet-id', '1'])
# commit changes
self.cli_commit()
- config = read_file(DHCPD_CONF)
- self.assertIn(f'option dhcp6.name-servers {ns_global_1}, {ns_global_2};', config)
- self.assertIn(f'subnet6 {subnet}' + r' {', config)
- self.assertIn(f'set shared-networkname = "{shared_net_name}";', config)
+ config = read_file(KEA6_CONF)
+ obj = loads(config)
+
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name)
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet)
+ self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'id', 1)
+
+ self.verify_config_object(
+ obj,
+ ['Dhcp6', 'option-data'],
+ {'name': 'dns-servers', "code": 23, "space": "dhcp6", "csv-format": True, 'data': f'{ns_global_1}, {ns_global_2}'})
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py
index acabc0070..c39d4467a 100755
--- a/smoketest/scripts/cli/test_service_dns_dynamic.py
+++ b/smoketest/scripts/cli/test_service_dns_dynamic.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
@@ -17,8 +17,6 @@
import os
import unittest
import tempfile
-import random
-import string
from base_vyostest_shim import VyOSUnitTestSHIM
@@ -32,6 +30,7 @@ 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'
@@ -58,38 +57,36 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# IPv4 standard DDNS service configuration
def test_01_dyndns_service_standard(self):
- svc_path = ['address', interface, 'service']
services = {'cloudflare': {'protocol': 'cloudflare'},
'freedns': {'protocol': 'freedns', 'username': username},
'zoneedit': {'protocol': 'zoneedit1', 'username': username}}
for svc, details in services.items():
- self.cli_set(base_path + svc_path + [svc, 'host-name', hostname])
- self.cli_set(base_path + svc_path + [svc, 'password', password])
- self.cli_set(base_path + svc_path + [svc, 'zone', zone])
- self.cli_set(base_path + svc_path + [svc, 'ttl', ttl])
+ self.cli_set(name_path + [svc, 'address', 'interface', 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 + svc_path + [svc, opt, value])
+ self.cli_set(name_path + [svc, opt, value])
- # 'zone' option is supported and required by 'cloudfare', but not 'freedns' and 'zoneedit'
- self.cli_set(base_path + svc_path + [svc, 'zone', zone])
- if details['protocol'] == 'cloudflare':
+ # '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(base_path + svc_path + [svc, 'zone'])
+ self.cli_delete(name_path + [svc, 'zone'])
# 'ttl' option is supported by 'cloudfare', but not 'freedns' and 'zoneedit'
- self.cli_set(base_path + svc_path + [svc, 'ttl', ttl])
+ self.cli_set(name_path + [svc, 'ttl', ttl])
if details['protocol'] == 'cloudflare':
pass
else:
# exception is raised for unsupported ones
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_delete(base_path + svc_path + [svc, 'ttl'])
+ self.cli_delete(name_path + [svc, 'ttl'])
# commit changes
self.cli_commit()
@@ -100,7 +97,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'daemon=300', ddclient_conf)
self.assertIn(f'usev4=ifv4', ddclient_conf)
self.assertIn(f'ifv4={interface}', ddclient_conf)
- self.assertIn(f'password={password}', ddclient_conf)
+ self.assertIn(f'password=\'{password}\'', ddclient_conf)
for opt in details.keys():
if opt == 'username':
@@ -112,65 +109,66 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
# IPv6 only DDNS service configuration
def test_02_dyndns_service_ipv6(self):
- timeout = '60'
- svc_path = ['address', interface, 'service', 'dynv6']
+ interval = '60'
+ svc_path = name_path + ['dynv6']
proto = 'dyndns2'
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 + svc_path + ['ip-version', ip_version])
- self.cli_set(base_path + svc_path + ['protocol', proto])
- self.cli_set(base_path + svc_path + ['server', server])
- self.cli_set(base_path + svc_path + ['username', username])
- self.cli_set(base_path + svc_path + ['password', password])
- self.cli_set(base_path + svc_path + ['host-name', hostname])
- self.cli_set(base_path + svc_path + ['wait-time', wait_time])
+ self.cli_set(base_path + ['interval', interval])
+ self.cli_set(svc_path + ['address', 'interface', 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
- self.cli_set(base_path + svc_path + ['expiry-time', expiry_time_bad])
with self.assertRaises(ConfigSessionError):
+ self.cli_set(svc_path + ['expiry-time', expiry_time_bad])
self.cli_commit()
- self.cli_set(base_path + svc_path + ['expiry-time', expiry_time_good])
+ 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={server}', ddclient_conf)
self.assertIn(f'login={username}', ddclient_conf)
- self.assertIn(f'password={password}', 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):
- svc_path = ['address', interface, 'service']
services = {'cloudflare': {'protocol': 'cloudflare', 'zone': zone},
'freedns': {'protocol': 'freedns', 'username': username},
'google': {'protocol': 'googledomains', 'username': username}}
ip_version = 'both'
for name, details in services.items():
- self.cli_set(base_path + svc_path + [name, 'host-name', hostname])
- self.cli_set(base_path + svc_path + [name, 'password', password])
+ self.cli_set(name_path + [name, 'address', 'interface', 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 + svc_path + [name, 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(base_path + svc_path + [name, 'ip-version', ip_version])
+ 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(base_path + svc_path + [name, 'ip-version'])
+ self.cli_delete(name_path + [name, 'ip-version'])
# commit changes
self.cli_commit()
@@ -185,7 +183,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
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)
+ self.assertIn(f'password=\'{password}\'', ddclient_conf)
for opt in details.keys():
if opt == 'username':
@@ -197,16 +195,19 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
def test_04_dyndns_rfc2136(self):
# Check if DDNS service can be configured and runs
- svc_path = ['address', interface, 'rfc2136', 'vyos']
+ 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 + svc_path + ['server', server])
- self.cli_set(base_path + svc_path + ['zone', zone])
- self.cli_set(base_path + svc_path + ['key', key_file.name])
- self.cli_set(base_path + svc_path + ['ttl', ttl])
- self.cli_set(base_path + svc_path + ['host-name', hostname])
+ self.cli_set(svc_path + ['address', 'interface', 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()
@@ -215,24 +216,25 @@ 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'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 = ['address', interface, 'service', 'namecheap']
+ svc_path = name_path + ['namecheap']
proto = 'namecheap'
hostnames = ['@', 'www', hostname, f'@.{hostname}']
for name in hostnames:
- self.cli_set(base_path + svc_path + ['protocol', proto])
- self.cli_set(base_path + svc_path + ['server', server])
- self.cli_set(base_path + svc_path + ['username', username])
- self.cli_set(base_path + svc_path + ['password', password])
- self.cli_set(base_path + svc_path + ['host-name', name])
+ self.cli_set(svc_path + ['address', 'interface', 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()
@@ -242,20 +244,94 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase):
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'password=\'{password}\'', ddclient_conf)
self.assertIn(f'{name}', ddclient_conf)
- def test_06_dyndns_vrf(self):
- vrf_name = f'vyos-test-{"".join(random.choices(string.ascii_letters + string.digits, k=5))}'
- svc_path = ['address', interface, 'service', 'cloudflare']
+ 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 = 'https://ifconfig.me/ip'
+ web_skip = 'Current IP Address:'
+
+ 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])
+
+ # not specifying either 'interface' or 'web' will raise an exception
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(svc_path + ['address', 'web'])
+
+ # specifying both 'interface' and 'web' will raise an exception as well
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(svc_path + ['address', 'interface', interface])
+ self.cli_commit()
+ self.cli_delete(svc_path + ['address', 'interface'])
+ self.cli_commit()
+
+ # web option 'skip' is useless without the option 'url'
+ with self.assertRaises(ConfigSessionError):
+ self.cli_set(svc_path + ['address', 'web', 'skip', web_skip])
+ self.cli_commit()
+ self.cli_set(svc_path + ['address', 'web', 'url', web_url])
+ 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}', ddclient_conf)
+ self.assertIn(f'webv4-skip=\'{web_skip}\'', 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', 'interface', 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', '12345'])
+ self.cli_set(['vrf', 'name', vrf_name, 'table', vrf_table])
self.cli_set(base_path + ['vrf', vrf_name])
- self.cli_set(base_path + svc_path + ['protocol', 'cloudflare'])
- self.cli_set(base_path + svc_path + ['host-name', hostname])
- self.cli_set(base_path + svc_path + ['zone', zone])
- self.cli_set(base_path + svc_path + ['password', password])
+ self.cli_set(svc_path + ['address', 'interface', 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()
diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py
index bc50a4ffe..079c584ba 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
@@ -24,9 +24,10 @@ from vyos.template import bracketize_ipv6
from vyos.utils.file import read_file
from vyos.utils.process import process_named_running
-CONFIG_FILE = '/run/powerdns/recursor.conf'
-FORWARD_FILE = '/run/powerdns/recursor.forward-zones.conf'
-HOSTSD_FILE = '/run/powerdns/recursor.vyos-hostsd.conf.lua'
+PDNS_REC_RUN_DIR = '/run/pdns-recursor'
+CONFIG_FILE = f'{PDNS_REC_RUN_DIR}/recursor.conf'
+FORWARD_FILE = f'{PDNS_REC_RUN_DIR}/recursor.forward-zones.conf'
+HOSTSD_FILE = f'{PDNS_REC_RUN_DIR}/recursor.vyos-hostsd.conf.lua'
PROCESS_NAME= 'pdns_recursor'
base_path = ['service', 'dns', 'forwarding']
@@ -43,7 +44,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)
@@ -59,11 +59,23 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertFalse(process_named_running(PROCESS_NAME))
+ def setUp(self):
+ # forward to base class
+ super().setUp()
+ 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])
+
def test_basic_forwarding(self):
# Check basic DNS forwarding settings
cache_size = '20'
negative_ttl = '120'
+ # remove code from setUp() as in this test-case we validate the proper
+ # handling of assertions when specific CLI nodes are missing
+ self.cli_delete(base_path)
+
self.cli_set(base_path + ['cache-size', cache_size])
self.cli_set(base_path + ['negative-ttl', negative_ttl])
@@ -118,12 +130,6 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
def test_dnssec(self):
# DNSSEC option testing
-
- 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])
-
options = ['off', 'process-no-validate', 'process', 'log-fail', 'validate']
for option in options:
self.cli_set(base_path + ['dnssec', option])
@@ -136,12 +142,6 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
def test_external_nameserver(self):
# Externe Domain Name Servers (DNS) addresses
-
- 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])
-
nameservers = {'192.0.2.1': {}, '192.0.2.2': {'port': '53'}, '2001:db8::1': {'port': '853'}}
for h,p in nameservers.items():
if 'port' in p:
@@ -163,11 +163,6 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
self.assertEqual(tmp, 'yes')
def test_domain_forwarding(self):
- 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])
-
domains = ['vyos.io', 'vyos.net', 'vyos.com']
nameservers = {'192.0.2.1': {}, '192.0.2.2': {'port': '53'}, '2001:db8::1': {'port': '853'}}
for domain in domains:
@@ -204,11 +199,6 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'addNTA("{domain}", "static")', hosts_conf)
def test_no_rfc1918_forwarding(self):
- 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 + ['no-serve-rfc1918'])
# commit changes
@@ -220,12 +210,6 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
def test_dns64(self):
dns_prefix = '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])
-
# Check dns64-prefix - must be prefix /96
self.cli_set(base_path + ['dns64-prefix', '2001:db8:aabb::/64'])
with self.assertRaises(ConfigSessionError):
@@ -239,21 +223,73 @@ 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 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'
+ 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']:
+ for port in ['10053', '10054']:
self.cli_set(base_path + ['port', port])
- 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])
-
# commit changes
self.cli_commit()
-
# verify local-port configuration
tmp = get_config_value('local-port')
self.assertEqual(tmp, port)
+ def test_ecs_add_for(self):
+ options = ['0.0.0.0/0', '!10.0.0.0/8', 'fc00::/7', '!fe80::/10']
+ for param in options:
+ self.cli_set(base_path + ['options', 'ecs-add-for', param])
+
+ # commit changes
+ self.cli_commit()
+ # verify ecs_add_for configuration
+ tmp = get_config_value('ecs-add-for')
+ self.assertEqual(tmp, ','.join(options))
+
+ def test_ecs_ipv4_bits(self):
+ option_value = '24'
+ self.cli_set(base_path + ['options', 'ecs-ipv4-bits', option_value])
+ # commit changes
+ self.cli_commit()
+ # verify ecs_ipv4_bits configuration
+ tmp = get_config_value('ecs-ipv4-bits')
+ self.assertEqual(tmp, option_value)
+
+ def test_edns_subnet_allow_list(self):
+ options = ['192.0.2.1/32', 'example.com', 'fe80::/10']
+ for param in options:
+ self.cli_set(base_path + ['options', 'edns-subnet-allow-list', param])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify edns_subnet_allow_list configuration
+ tmp = get_config_value('edns-subnet-allow-list')
+ self.assertEqual(tmp, ','.join(options))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 1ae5c104c..94eade2d7 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,14 +15,20 @@
# 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
+from time import sleep
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 +54,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 +180,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 +289,175 @@ 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()
+ sleep(2)
+
+ 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..20a168b58 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-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
@@ -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,153 @@ 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()
+ config = self.getConfig("ip-pool")
+ # T5099 required specific order
+ 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}"""
+ self.assertIn(pool_config, config)
+
+ def test_accel_ipv6_pool(self):
+ # Test configuration of IPv6 client pools
+ self.basic_config(is_gateway=False, is_client_pool=False)
+
+ pool_name = 'ipv6_test_pool'
+ prefix_1 = '2001:db8:fffe::/56'
+ prefix_mask = '64'
+ prefix_2 = '2001:db8:ffff::/56'
+ client_prefix_1 = f'{prefix_1},{prefix_mask}'
+ client_prefix_2 = f'{prefix_2},{prefix_mask}'
+ self.set(['client-ipv6-pool', pool_name, 'prefix', prefix_1, 'mask',
+ prefix_mask])
+ self.set(['client-ipv6-pool', pool_name, 'prefix', prefix_2, 'mask',
+ prefix_mask])
+
+ delegate_1_prefix = '2001:db8:fff1::/56'
+ delegate_2_prefix = '2001:db8:fff2::/56'
+ delegate_mask = '64'
+ self.set(['client-ipv6-pool', pool_name, 'delegate', delegate_1_prefix,
+ 'delegation-prefix', delegate_mask])
+ self.set(['client-ipv6-pool', pool_name, 'delegate', delegate_2_prefix,
+ 'delegation-prefix', delegate_mask])
+
+ # 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')
+ for tmp in ['ipv6pool', 'ipv6_nd', 'ipv6_dhcp']:
+ self.assertEqual(conf['modules'][tmp], None)
- config = getConfig('[ip-pool]')
- # T5099 required specific order
- pool_config = f'''{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'''
+ config = self.getConfig("ipv6-pool")
+ pool_config = f"""{client_prefix_1},name={pool_name}
+{client_prefix_2},name={pool_name}
+delegate={delegate_1_prefix},{delegate_mask},name={pool_name}
+delegate={delegate_2_prefix},{delegate_mask},name={pool_name}"""
self.assertIn(pool_config, config)
+ @unittest.skip("PPP is not a part of IPoE")
+ def test_accel_ppp_options(self):
+ pass
-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
index f2fb3b509..f2fb3b509 100755
--- a/smoketest/scripts/cli/test_service_mdns-repeater.py
+++ b/smoketest/scripts/cli/test_service_mdns_repeater.py
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_ntp.py b/smoketest/scripts/cli/test_service_ntp.py
index 5e385d5ad..ae45fe2f4 100755
--- a/smoketest/scripts/cli/test_service_ntp.py
+++ b/smoketest/scripts/cli/test_service_ntp.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
@@ -43,7 +43,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.assertFalse(process_named_running(PROCESS_NAME))
- def test_01_ntp_options(self):
+ def test_base_options(self):
# Test basic NTP support with multiple servers and their options
servers = ['192.0.2.1', '192.0.2.2']
options = ['nts', 'noselect', 'prefer']
@@ -77,7 +77,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
for pool in pools:
self.assertIn(f'pool {pool} iburst', config)
- def test_02_ntp_clients(self):
+ def test_clients(self):
# Test the allowed-networks statement
listen_address = ['127.0.0.1', '::1']
for listen in listen_address:
@@ -107,7 +107,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
for listen in listen_address:
self.assertIn(f'bindaddress {listen}', config)
- def test_03_ntp_interface(self):
+ def test_interface(self):
interfaces = ['eth0']
for interface in interfaces:
self.cli_set(base_path + ['interface', interface])
@@ -124,7 +124,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
for interface in interfaces:
self.assertIn(f'binddevice {interface}', config)
- def test_04_ntp_vrf(self):
+ def test_vrf(self):
vrf_name = 'vyos-mgmt'
self.cli_set(['vrf', 'name', vrf_name, 'table', '12345'])
@@ -142,5 +142,28 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['vrf', 'name', vrf_name])
+ def test_leap_seconds(self):
+ servers = ['time1.vyos.net', 'time2.vyos.net']
+ for server in servers:
+ self.cli_set(base_path + ['server', server])
+
+ self.cli_commit()
+
+ # Check generated client address configuration
+ # this file must be read with higher permissions
+ config = cmd(f'sudo cat {NTP_CONF}')
+ self.assertIn('leapsectz right/UTC', config) # CLI default
+
+ for mode in ['ignore', 'system', 'smear']:
+ self.cli_set(base_path + ['leap-second', mode])
+ self.cli_commit()
+ config = cmd(f'sudo cat {NTP_CONF}')
+ if mode != 'smear':
+ self.assertIn(f'leapsecmode {mode}', config)
+ else:
+ self.assertIn(f'leapsecmode slew', config)
+ self.assertIn(f'maxslewrate 1000', config)
+ self.assertIn(f'smoothtime 400 0.001024 leaponly', config)
+
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..d7c7aa164 100755
--- a/smoketest/scripts/cli/test_service_pppoe-server.py
+++ b/smoketest/scripts/cli/test_service_pppoe-server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022-2023 VyOS maintainers and contributors
+# Copyright (C) 2022-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -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()
@@ -59,68 +59,22 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):
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'], '30')
- self.assertEqual(conf['ppp']['lcp-echo-timeout'], '0')
- self.assertEqual(conf['ppp']['lcp-echo-failure'], '3')
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
+ def test_pppoe_limits(self):
self.basic_config()
-
- # other settings
- mppe = 'require'
- self.set(['ppp-options', 'ccp'])
- self.set(['ppp-options', 'mppe', mppe])
self.set(['limits', 'connection-limit', '20/min'])
-
- # min-mtu
- min_mtu = '1400'
- self.set(['ppp-options', 'min-mtu', min_mtu])
-
- # mru
- mru = '9000'
- self.set(['ppp-options', 'mru', mru])
-
- # interface-cache
- interface_cache = '128000'
- self.set(['ppp-options', 'interface-cache', interface_cache])
-
- # 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)
-
- self.assertEqual(conf['chap-secrets']['gw-ip-address'], self._gateway)
-
- # check ppp
- self.assertEqual(conf['ppp']['mppe'], mppe)
- self.assertEqual(conf['ppp']['min-mtu'], min_mtu)
- self.assertEqual(conf['ppp']['mru'], mru)
-
- self.assertTrue(conf['ppp'].getboolean('ccp'))
-
- # check other settings
self.assertEqual(conf['connlimit']['limit'], '20/min')
- # 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,117 +91,30 @@ 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])
-
- 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
+ limiter = 'tbf'
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'])
-
+ self.set(['shaper', 'fwmark', fwmark])
# 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)
-
- def test_pppoe_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}')
+ # basic verification
+ self.verify(conf)
+ self.assertEqual(conf['shaper']['fwmark'], fwmark)
+ self.assertEqual(conf['shaper']['down-limiter'], limiter)
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 +125,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 +148,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_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py
index 7657ab724..cea34138e 100755
--- a/smoketest/scripts/cli/test_system_conntrack.py
+++ b/smoketest/scripts/cli/test_system_conntrack.py
@@ -31,6 +31,14 @@ def get_sysctl(parameter):
return read_file(f'/proc/sys/{tmp}')
class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestSystemConntrack, 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()
@@ -297,5 +305,49 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
self.cli_delete(['firewall'])
+ def test_conntrack_timeout_custom(self):
+
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'source', 'address', '192.0.2.1'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'destination', 'address', '192.0.2.2'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'destination', 'port', '22'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'protocol', 'tcp', 'syn-sent', '77'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'protocol', 'tcp', 'close', '88'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '1', 'protocol', 'tcp', 'established', '99'])
+
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '2', 'inbound-interface', 'eth1'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '2', 'source', 'address', '198.51.100.1'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv4', 'rule', '2', 'protocol', 'udp', 'unreplied', '55'])
+
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'source', 'address', '2001:db8::1'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'inbound-interface', 'eth2'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'protocol', 'tcp', 'time-wait', '22'])
+ self.cli_set(base_path + ['timeout', 'custom', 'ipv6', 'rule', '1', 'protocol', 'tcp', 'last-ack', '33'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['ct timeout ct-timeout-1 {'],
+ ['protocol tcp'],
+ ['policy = { syn_sent : 77, established : 99, close : 88 }'],
+ ['ct timeout ct-timeout-2 {'],
+ ['protocol udp'],
+ ['policy = { unreplied : 55 }'],
+ ['chain VYOS_CT_TIMEOUT {'],
+ ['ip saddr 192.0.2.1', 'ip daddr 192.0.2.2', 'tcp dport 22', 'ct timeout set "ct-timeout-1"'],
+ ['iifname "eth1"', 'meta l4proto udp', 'ip saddr 198.51.100.1', 'ct timeout set "ct-timeout-2"']
+ ]
+
+ nftables6_search = [
+ ['ct timeout ct-timeout-1 {'],
+ ['protocol tcp'],
+ ['policy = { last_ack : 33, time_wait : 22 }'],
+ ['chain VYOS_CT_TIMEOUT {'],
+ ['iifname "eth2"', 'meta l4proto tcp', 'ip6 saddr 2001:db8::1', 'ct timeout set "ct-timeout-1"']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_conntrack')
+ self.verify_nftables(nftables6_search, 'ip6 vyos_conntrack')
+
+ self.cli_delete(['firewall'])
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_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_sflow.py b/smoketest/scripts/cli/test_system_sflow.py
index 63262db69..c0424d915 100755
--- a/smoketest/scripts/cli/test_system_sflow.py
+++ b/smoketest/scripts/cli/test_system_sflow.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-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
@@ -17,6 +17,7 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from time import sleep
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
@@ -26,12 +27,11 @@ from vyos.utils.file import read_file
PROCESS_NAME = 'hsflowd'
base_path = ['system', 'sflow']
+vrf = 'mgmt'
hsflowd_conf = '/run/sflow/hsflowd.conf'
-
class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
-
@classmethod
def setUpClass(cls):
super(TestSystemFlowAccounting, cls).setUpClass()
@@ -45,6 +45,7 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
self.assertTrue(process_named_running(PROCESS_NAME))
self.cli_delete(base_path)
+ self.cli_delete(['vrf', 'name', vrf])
self.cli_commit()
# after service removal process must no longer run
@@ -96,6 +97,27 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
for interface in Section.interfaces('ethernet'):
self.assertIn(f'pcap {{ dev={interface} }}', hsflowd)
+ def test_vrf(self):
+ interface = 'eth0'
+ server = '192.0.2.1'
+
+ # Check if sFlow service can be bound to given VRF
+ self.cli_set(['vrf', 'name', vrf, 'table', '10100'])
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(base_path + ['server', server])
+ self.cli_set(base_path + ['vrf', vrf])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ hsflowd = read_file(hsflowd_conf)
+ self.assertIn(f'collector {{ ip = {server} udpport = 6343 }}', hsflowd) # default port
+ self.assertIn(f'pcap {{ dev=eth0 }}', hsflowd)
+
+ # Check for process in VRF
+ tmp = cmd(f'ip vrf pids {vrf}')
+ self.assertIn(PROCESS_NAME, tmp)
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 17b1b395c..09e10a2c4 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2023 VyOS maintainers and contributors
+# Copyright (C) 2021-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,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,6 +46,7 @@ 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 = """
MIICMDCCAdegAwIBAgIUBCzIjYvD7SPbx5oU18IYg7NVxQ0wCgYIKoZIzj0EAwIw
ZzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
@@ -59,6 +62,7 @@ CgYIKoZIzj0EAwIDRwAwRAIgX1spXjrUc10r3g/Zm4O31LU5O08J2vVqFo94zHE5
0VgCIG4JK9Zg5O/yn4mYksZux7efiHRUzL2y2TXQ9IqrqM8W
"""
+int_ca_name = 'MyVyOS-IntCA'
int_ca_pem = """
MIICYDCCAgWgAwIBAgIUcFx2BVYErHI+SneyPYHijxXt1cgwCgYIKoZIzj0EAwIw
ZzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
@@ -75,6 +79,7 @@ CCqGSM49BAMCA0kAMEYCIQCnqWbElgOL9dGO3iLxasFNq/hM7vM/DzaiHi4BowxW
0gIhAMohefNj+QgLfPhvyODHIPE9LMyfp7lJEaCC2K8PCSFD
"""
+peer_name = 'peer1'
peer_cert = """
MIICSTCCAfCgAwIBAgIUPxYleUgCo/glVVePze3QmAFgi6MwCgYIKoZIzj0EAwIw
bzELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
@@ -110,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}'])
@@ -140,7 +146,16 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# Check for no longer running process
self.assertFalse(process_named_running(PROCESS_NAME))
- def test_01_dhcp_fail_handling(self):
+ 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_dhcp_fail_handling(self):
# Skip process check - connection is not created for this test
self.skip_process_check = True
@@ -170,7 +185,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_delete(ethernet_path + [interface, 'vif', vif, 'address'])
- def test_02_site_to_site(self):
+ def test_site_to_site(self):
self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
local_address = '192.0.2.10'
@@ -233,6 +248,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'remote_ts = 10.2.0.0/16',
f'priority = {priority}',
f'mode = tunnel',
+ f'replay_window = 32',
]
for line in swanctl_conf_lines:
self.assertIn(line, swanctl_conf)
@@ -248,7 +264,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.assertRegex(swanctl_conf, fr'{line}')
- def test_03_site_to_site_vti(self):
+ def test_site_to_site_vti(self):
local_address = '192.0.2.10'
vti = 'vti10'
# IKE
@@ -302,6 +318,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'remote_ts = 172.17.10.0/24,172.17.11.0/24',
f'ipcomp = yes',
f'start_action = none',
+ f'replay_window = 32',
f'if_id_in = {if_id}', # will be 11 for vti10 - shifted by one
f'if_id_out = {if_id}',
f'updown = "/etc/ipsec.d/vti-up-down {vti}"'
@@ -318,7 +335,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.assertRegex(swanctl_conf, fr'{line}')
- def test_04_dmvpn(self):
+ def test_dmvpn(self):
tunnel_if = 'tun100'
nhrp_secret = 'secret'
ike_lifetime = '3600'
@@ -381,15 +398,9 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# There is only one NHRP test so no need to delete this globally in tearDown()
self.cli_delete(nhrp_path)
- def test_05_x509_site2site(self):
+ def test_site_to_site_x509(self):
# Enable PKI
- peer_name = 'peer1'
- ca_name = 'MyVyOS-CA'
- int_ca_name = 'MyVyOS-IntCA'
- self.cli_set(['pki', 'ca', ca_name, 'certificate', ca_pem.replace('\n','')])
- self.cli_set(['pki', 'ca', int_ca_name, 'certificate', int_ca_pem.replace('\n','')])
- self.cli_set(['pki', 'certificate', peer_name, 'certificate', peer_cert.replace('\n','')])
- self.cli_set(['pki', 'certificate', peer_name, 'private', 'key', peer_key.replace('\n','')])
+ self.setupPKI()
vti = 'vti20'
self.cli_set(vti_path + [vti, 'address', '192.168.0.1/31'])
@@ -461,8 +472,11 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# 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):
+
+ def test_flex_vpn_vips(self):
local_address = '192.0.2.5'
local_id = 'vyos-r1'
remote_id = 'vyos-r2'
@@ -537,5 +551,352 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.assertIn(line, charon_conf)
+ def test_remote_access(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'replay_window = 32',
+ 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_remote_access_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_remote_access_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..e253f0e49
--- /dev/null
+++ b/smoketest/scripts/cli/test_vpn_l2tp.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023-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
+# 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_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_vpn_l2tp_dependence_ipsec_swanctl(self):
+ # Test config vpn for tasks T3843 and T5926
+
+ base_path = ['vpn', 'l2tp', 'remote-access']
+ # make precondition
+ self.cli_set(['interfaces', 'dummy', 'dum0', 'address', '203.0.113.1/32'])
+ self.cli_set(['vpn', 'ipsec', 'interface', 'dum0'])
+
+ self.cli_commit()
+ # check ipsec apply to swanctl
+ self.assertEqual('', cmd('echo vyos | sudo -S swanctl -L '))
+
+ self.cli_set(base_path + ['authentication', 'local-users', 'username', 'foo', 'password', 'bar'])
+ self.cli_set(base_path + ['authentication', 'mode', 'local'])
+ self.cli_set(base_path + ['authentication', 'protocols', 'chap'])
+ self.cli_set(base_path + ['client-ip-pool', 'first', 'range', '10.200.100.100-10.200.100.110'])
+ self.cli_set(base_path + ['description', 'VPN - REMOTE'])
+ self.cli_set(base_path + ['name-server', '1.1.1.1'])
+ self.cli_set(base_path + ['ipsec-settings', 'authentication', 'mode', 'pre-shared-secret'])
+ self.cli_set(base_path + ['ipsec-settings', 'authentication', 'pre-shared-secret', 'SeCret'])
+ self.cli_set(base_path + ['ipsec-settings', 'ike-lifetime', '8600'])
+ self.cli_set(base_path + ['ipsec-settings', 'lifetime', '3600'])
+ self.cli_set(base_path + ['outside-address', '203.0.113.1'])
+ self.cli_set(base_path + ['gateway-address', '203.0.113.1'])
+
+ self.cli_commit()
+
+ # check l2tp apply to swanctl
+ self.assertTrue('l2tp_remote_access:' in cmd('echo vyos | sudo -S swanctl -L '))
+
+ self.cli_delete(['vpn', 'l2tp'])
+ self.cli_commit()
+
+ # check l2tp apply to swanctl after delete config
+ self.assertEqual('', cmd('echo vyos | sudo -S swanctl -L '))
+
+ # need to correct tearDown test
+ self.basic_config()
+ self.cli_set(base_path + ['authentication', 'protocols', 'chap'])
+ self.cli_commit()
+
+
+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..40dcb7f80
--- /dev/null
+++ b/smoketest/scripts/cli/test_vpn_pptp.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023-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
+# 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_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..a3090ee41 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']
@@ -52,6 +53,11 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# call base-classes classmethod
super(VRFTest, cls).setUpClass()
+ def setUp(self):
+ # VRF strict_most ist always enabled
+ tmp = read_file('/proc/sys/net/vrf/strict_mode')
+ self.assertEqual(tmp, '1')
+
def tearDown(self):
# delete all VRFs
self.cli_delete(base_path)
@@ -130,8 +136,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 +156,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 +298,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 +497,4 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/system/test_module_load.py b/smoketest/scripts/system/test_module_load.py
index 9d94f01e6..fc60c7220 100755
--- a/smoketest/scripts/system/test_module_load.py
+++ b/smoketest/scripts/system/test_module_load.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
@@ -18,7 +18,7 @@ import unittest
from vyos.utils.process import cmd
modules = {
- "intel": ["e1000", "e1000e", "igb", "ixgb", "ixgbe", "ixgbevf", "i40e",
+ "intel": ["e1000", "e1000e", "igb", "ixgbe", "ixgbevf", "i40e",
"i40evf", "iavf"],
"intel_qat": ["qat_200xx", "qat_200xxvf", "qat_c3xxx", "qat_c3xxxvf",
"qat_c62x", "qat_c62xvf", "qat_d15xx", "qat_d15xxvf",