summaryrefslogtreecommitdiff
path: root/smoketest/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'smoketest/scripts')
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py103
-rw-r--r--smoketest/scripts/cli/base_vyostest_shim.py17
-rwxr-xr-xsmoketest/scripts/cli/test_component_version.py36
-rwxr-xr-xsmoketest/scripts/cli/test_configd_init.py38
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py233
-rwxr-xr-xsmoketest/scripts/cli/test_ha_virtual_server.py146
-rwxr-xr-xsmoketest/scripts/cli/test_ha_vrrp.py46
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bonding.py43
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_bridge.py5
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_dummy.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_ethernet.py152
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_geneve.py6
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_l2tpv3.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_loopback.py2
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_macsec.py5
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_netns.py83
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_openvpn.py114
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pppoe.py14
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_pseudo_ethernet.py5
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_tunnel.py108
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py75
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireguard.py9
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_wireless.py2
-rwxr-xr-xsmoketest/scripts/cli/test_nat.py119
-rwxr-xr-xsmoketest/scripts/cli/test_nat66.py4
-rwxr-xr-xsmoketest/scripts/cli/test_pki.py9
-rwxr-xr-xsmoketest/scripts/cli/test_policy.py433
-rwxr-xr-xsmoketest/scripts/cli/test_policy_route.py145
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bfd.py85
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py335
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_isis.py51
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_mpls.py116
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_nhrp.py9
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospf.py171
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ospfv3.py135
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_ripng.py22
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_rpki.py2
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static.py57
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_static_arp.py88
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcp-server.py9
-rwxr-xr-xsmoketest/scripts/cli/test_service_dhcpv6-server.py20
-rwxr-xr-xsmoketest/scripts/cli/test_service_https.py78
-rwxr-xr-xsmoketest/scripts/cli/test_service_ids.py94
-rwxr-xr-xsmoketest/scripts/cli/test_service_lldp.py127
-rwxr-xr-xsmoketest/scripts/cli/test_service_monitoring_telegraf.py65
-rwxr-xr-xsmoketest/scripts/cli/test_service_salt.py105
-rwxr-xr-xsmoketest/scripts/cli/test_service_snmp.py104
-rwxr-xr-xsmoketest/scripts/cli/test_service_ssh.py123
-rwxr-xr-xsmoketest/scripts/cli/test_service_tftp-server.py39
-rwxr-xr-xsmoketest/scripts/cli/test_service_upnp.py105
-rwxr-xr-xsmoketest/scripts/cli/test_service_webproxy.py16
-rwxr-xr-xsmoketest/scripts/cli/test_system_conntrack.py24
-rwxr-xr-xsmoketest/scripts/cli/test_system_flow-accounting.py230
-rwxr-xr-xsmoketest/scripts/cli/test_system_ipv6.py17
-rwxr-xr-xsmoketest/scripts/cli/test_system_login.py10
-rwxr-xr-xsmoketest/scripts/cli/test_system_logs.py117
-rwxr-xr-xsmoketest/scripts/cli/test_system_ntp.py60
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_ipsec.py111
-rwxr-xr-xsmoketest/scripts/cli/test_vpn_openconnect.py42
-rwxr-xr-xsmoketest/scripts/cli/test_vrf.py93
-rwxr-xr-xsmoketest/scripts/cli/test_zone_policy.py69
61 files changed, 4075 insertions, 610 deletions
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index 340ec4edd..816ba6dcd 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -56,6 +56,7 @@ def is_mirrored_to(interface, mirror_if, qdisc):
class BasicInterfaceTest:
class TestCase(VyOSUnitTestSHIM.TestCase):
+ _test_dhcp = False
_test_ip = False
_test_mtu = False
_test_vlan = False
@@ -77,18 +78,25 @@ class BasicInterfaceTest:
# choose IPv6 minimum MTU value for tests - this must always work
_mtu = '1280'
- def setUp(self):
+ @classmethod
+ def setUpClass(cls):
+ super(BasicInterfaceTest.TestCase, cls).setUpClass()
+
# Setup mirror interfaces for SPAN (Switch Port Analyzer)
- for span in self._mirror_interfaces:
+ for span in cls._mirror_interfaces:
section = Section.section(span)
- self.cli_set(['interfaces', section, span])
+ cls.cli_set(cls, ['interfaces', section, span])
- def tearDown(self):
+ @classmethod
+ def tearDownClass(cls):
# Tear down mirror interfaces for SPAN (Switch Port Analyzer)
- for span in self._mirror_interfaces:
+ for span in cls._mirror_interfaces:
section = Section.section(span)
- self.cli_delete(['interfaces', section, span])
+ cls.cli_delete(cls, ['interfaces', section, span])
+
+ super(BasicInterfaceTest.TestCase, cls).tearDownClass()
+ def tearDown(self):
self.cli_delete(self._base_path)
self.cli_commit()
@@ -96,6 +104,35 @@ class BasicInterfaceTest:
for intf in self._interfaces:
self.assertNotIn(intf, interfaces())
+ # No daemon that was started during a test should remain running
+ for daemon in ['dhcp6c', 'dhclient']:
+ self.assertFalse(process_named_running(daemon))
+
+ def test_dhcp_disable_interface(self):
+ if not self._test_dhcp:
+ self.skipTest('not supported')
+
+ # When interface is configured as admin down, it must be admin down
+ # even when dhcpc starts on the given interface
+ for interface in self._interfaces:
+ self.cli_set(self._base_path + [interface, 'disable'])
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_set(self._base_path + [interface, 'disable'])
+
+ # Also enable DHCP (ISC DHCP always places interface in admin up
+ # state so we check that we do not start DHCP client.
+ # https://phabricator.vyos.net/T2767
+ self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
+
+ self.cli_commit()
+
+ # Validate interface state
+ for interface in self._interfaces:
+ flags = read_file(f'/sys/class/net/{interface}/flags')
+ self.assertEqual(int(flags, 16) & 1, 0)
+
def test_span_mirror(self):
if not self._mirror_interfaces:
self.skipTest('not supported')
@@ -171,10 +208,10 @@ class BasicInterfaceTest:
def test_add_multiple_ip_addresses(self):
# Add address
for intf in self._interfaces:
+ for option in self._options.get(intf, []):
+ self.cli_set(self._base_path + [intf] + option.split())
for addr in self._test_addr:
self.cli_set(self._base_path + [intf, 'address', addr])
- for option in self._options.get(intf, []):
- self.cli_set(self._base_path + [intf] + option.split())
self.cli_commit()
@@ -202,6 +239,7 @@ class BasicInterfaceTest:
self.cli_commit()
for interface in self._interfaces:
+ self.assertIn(AF_INET6, ifaddresses(interface))
for addr in ifaddresses(interface)[AF_INET6]:
self.assertTrue(is_ipv6_link_local(addr['addr']))
@@ -297,6 +335,23 @@ class BasicInterfaceTest:
self.assertEqual(Interface(vif).get_admin_state(), 'up')
+ # T4064: Delete interface addresses, keep VLAN interface
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ for vlan in self._vlan_range:
+ base = self._base_path + [interface, 'vif', vlan]
+ self.cli_delete(base + ['address'])
+
+ self.cli_commit()
+
+ # Verify no IP address is assigned
+ for interface in self._interfaces:
+ for vlan in self._vlan_range:
+ vif = f'{intf}.{vlan}'
+ for address in self._test_addr:
+ self.assertFalse(is_intf_addr_assigned(vif, address))
+
+
def test_vif_8021q_mtu_limits(self):
# XXX: This testcase is not allowed to run as first testcase, reason
# is the Wireless test will first load the wifi kernel hwsim module
@@ -493,6 +548,24 @@ class BasicInterfaceTest:
tmp = get_interface_config(vif)
self.assertEqual(tmp['mtu'], int(self._mtu))
+
+ # T4064: Delete interface addresses, keep VLAN interface
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ for vif_s in self._qinq_range:
+ for vif_c in self._vlan_range:
+ self.cli_delete(self._base_path + [interface, 'vif-s', vif_s, 'vif-c', vif_c, 'address'])
+
+ self.cli_commit()
+ # Verify no IP address is assigned
+ for interface in self._interfaces:
+ base = self._base_path + [interface]
+ for vif_s in self._qinq_range:
+ for vif_c in self._vlan_range:
+ vif = f'{interface}.{vif_s}.{vif_c}'
+ for address in self._test_addr:
+ self.assertFalse(is_intf_addr_assigned(vif, address))
+
# T3972: remove vif-c interfaces from vif-s
for interface in self._interfaces:
base = self._base_path + [interface]
@@ -572,11 +645,11 @@ class BasicInterfaceTest:
self.cli_commit()
for interface in self._interfaces:
- base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN'
- out = cmd('sudo iptables-save -t mangle')
+ base_options = f'oifname "{interface}"'
+ out = cmd('sudo nft list chain raw VYOS_TCP_MSS')
for line in out.splitlines():
if line.startswith(base_options):
- self.assertIn(f'--set-mss {mss}', line)
+ self.assertIn(f'tcp option maxseg size set {mss}', line)
tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms')
self.assertEqual(tmp, str((int(arp_tmo) * 1000))) # tmo value is in milli seconds
@@ -627,11 +700,11 @@ class BasicInterfaceTest:
self.cli_commit()
for interface in self._interfaces:
- base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN'
- out = cmd('sudo ip6tables-save -t mangle')
+ base_options = f'oifname "{interface}"'
+ out = cmd('sudo nft list chain ip6 raw VYOS_TCP_MSS')
for line in out.splitlines():
if line.startswith(base_options):
- self.assertIn(f'--set-mss {mss}', line)
+ self.assertIn(f'tcp option maxseg size set {mss}', line)
proc_base = f'/proc/sys/net/ipv6/conf/{interface}'
diff --git a/smoketest/scripts/cli/base_vyostest_shim.py b/smoketest/scripts/cli/base_vyostest_shim.py
index 50f80e7d1..7cfb53045 100644
--- a/smoketest/scripts/cli/base_vyostest_shim.py
+++ b/smoketest/scripts/cli/base_vyostest_shim.py
@@ -16,6 +16,7 @@ import os
import unittest
from time import sleep
+from typing import Type
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
@@ -73,7 +74,7 @@ class VyOSUnitTestSHIM:
def cli_commit(self):
self._session.commit()
# during a commit there is a process opening commit_lock, and run() returns 0
- while run(f'sudo lsof | grep -q {commit_lock}') == 0:
+ while run(f'sudo lsof -nP {commit_lock}') == 0:
sleep(0.250)
def getFRRconfig(self, string, end='$', endsection='^!', daemon=''):
@@ -85,3 +86,17 @@ class VyOSUnitTestSHIM:
print(f'\n\ncommand "{command}" returned:\n')
pprint.pprint(out)
return out
+
+# standard construction; typing suggestion: https://stackoverflow.com/a/70292317
+def ignore_warning(warning: Type[Warning]):
+ import warnings
+ from functools import wraps
+
+ def inner(f):
+ @wraps(f)
+ def wrapped(*args, **kwargs):
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", category=warning)
+ return f(*args, **kwargs)
+ return wrapped
+ return inner
diff --git a/smoketest/scripts/cli/test_component_version.py b/smoketest/scripts/cli/test_component_version.py
new file mode 100755
index 000000000..777379bdd
--- /dev/null
+++ b/smoketest/scripts/cli/test_component_version.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from vyos.systemversions import get_system_versions, get_system_component_version
+
+# After T3474, component versions should be updated in the files in
+# vyos-1x/interface-definitions/include/version/
+# This test verifies that the legacy version in curver_DATA does not exceed
+# that in the xml cache.
+class TestComponentVersion(unittest.TestCase):
+ def setUp(self):
+ self.legacy_d = get_system_versions()
+ self.xml_d = get_system_component_version()
+
+ def test_component_version(self):
+ self.assertTrue(set(self.legacy_d).issubset(set(self.xml_d)))
+ for k, v in self.legacy_d.items():
+ self.assertTrue(v <= self.xml_d[k])
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_configd_init.py b/smoketest/scripts/cli/test_configd_init.py
new file mode 100755
index 000000000..5dec89963
--- /dev/null
+++ b/smoketest/scripts/cli/test_configd_init.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+from time import sleep
+
+from vyos.util import cmd, is_systemd_service_running
+
+class TestConfigdInit(unittest.TestCase):
+ def setUp(self):
+ self.running_state = is_systemd_service_running('vyos-configd.service')
+
+ def test_configd_init(self):
+ if not self.running_state:
+ cmd('sudo systemctl start vyos-configd.service')
+ # allow time for init to succeed/fail
+ sleep(2)
+ self.assertTrue(is_systemd_service_running('vyos-configd.service'))
+
+ def tearDown(self):
+ if not self.running_state:
+ cmd('sudo systemctl stop vyos-configd.service')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
new file mode 100755
index 000000000..b8f944575
--- /dev/null
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from glob import glob
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.util import cmd
+
+sysfs_config = {
+ 'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'default': '0', 'test_value': 'disable'},
+ 'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'default': '1', 'test_value': 'enable'},
+ 'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route', 'default': '0', 'test_value': 'enable'},
+ 'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
+ 'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'default': '-1', 'test_value': 'enable'},
+ 'log_martians': {'sysfs': '/proc/sys/net/ipv4/conf/all/log_martians', 'default': '1', 'test_value': 'disable'},
+ 'receive_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_redirects', 'default': '0', 'test_value': 'enable'},
+ 'send_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/send_redirects', 'default': '1', 'test_value': 'disable'},
+ 'syn_cookies': {'sysfs': '/proc/sys/net/ipv4/tcp_syncookies', 'default': '1', 'test_value': 'disable'},
+ 'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337', 'default': '0', 'test_value': 'enable'}
+}
+
+class TestFirewall(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestFirewall, cls).setUpClass()
+
+ # ensure we can also run this test on a live system - so lets clean
+ # out the current configuration :)
+ cls.cli_delete(cls, ['firewall'])
+
+ cls.cli_set(cls, ['interfaces', 'ethernet', 'eth0', 'address', '172.16.10.1/24'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'ethernet', 'eth0', 'address', '172.16.10.1/24'])
+ super(TestFirewall, cls).tearDownClass()
+
+ def tearDown(self):
+ self.cli_delete(['interfaces', 'ethernet', 'eth0', 'firewall'])
+ self.cli_delete(['firewall'])
+ self.cli_commit()
+
+ def test_groups(self):
+ self.cli_set(['firewall', 'group', 'mac-group', 'smoketest_mac', 'mac-address', '00:01:02:03:04:05'])
+ self.cli_set(['firewall', 'group', 'network-group', 'smoketest_network', 'network', '172.16.99.0/24'])
+ self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '53'])
+ self.cli_set(['firewall', 'group', 'port-group', 'smoketest_port', 'port', '123'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'group', 'network-group', 'smoketest_network'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'group', 'port-group', 'smoketest_port'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'source', 'group', 'mac-group', 'smoketest_mac'])
+
+ self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['iifname "eth0"', 'jump NAME_smoketest'],
+ ['ip saddr { 172.16.99.0/24 }', 'ip daddr 172.16.10.10', 'th dport { 53, 123 }', 'return'],
+ ['ether saddr { 00:01:02:03:04:05 }', 'return']
+ ]
+
+ nftables_output = cmd('sudo nft list table ip filter')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched, msg=search)
+
+ def test_basic_rules(self):
+ self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'source', 'address', '172.16.20.10'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'reject'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'destination', 'port', '8888'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'tcp', 'flags', 'syn'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'tcp', 'flags', 'not', 'ack'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'protocol', 'tcp'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'destination', 'port', '22'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'limit', 'rate', '5/minute'])
+
+ self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['iifname "eth0"', 'jump NAME_smoketest'],
+ ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'return'],
+ ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'reject'],
+ ['tcp dport { 22 }', 'limit rate 5/minute', 'return'],
+ ['smoketest default-action', 'drop']
+ ]
+
+ nftables_output = cmd('sudo nft list table ip filter')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched, msg=search)
+
+ def test_basic_rules_ipv6(self):
+ self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '1', 'source', 'address', '2002::1'])
+ self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '1', 'destination', 'address', '2002::1:1'])
+ self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'action', 'reject'])
+ self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'protocol', 'tcp_udp'])
+ self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'destination', 'port', '8888'])
+
+ self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'ipv6-name', 'v6-smoketest'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['iifname "eth0"', 'jump NAME6_v6-smoketest'],
+ ['saddr 2002::1', 'daddr 2002::1:1', 'return'],
+ ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'reject'],
+ ['smoketest default-action', 'drop']
+ ]
+
+ nftables_output = cmd('sudo nft list table ip6 filter')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched, msg=search)
+
+ def test_state_policy(self):
+ self.cli_set(['firewall', 'state-policy', 'established', 'action', 'accept'])
+ self.cli_set(['firewall', 'state-policy', 'related', 'action', 'accept'])
+ self.cli_set(['firewall', 'state-policy', 'invalid', 'action', 'drop'])
+
+ self.cli_commit()
+
+ chains = {
+ 'ip filter': ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
+ 'ip6 filter': ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
+ }
+
+ for table in ['ip filter', 'ip6 filter']:
+ for chain in chains[table]:
+ nftables_output = cmd(f'sudo nft list chain {table} {chain}')
+ self.assertTrue('jump VYOS_STATE_POLICY' in nftables_output)
+
+ def test_state_and_status_rules(self):
+ self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'state', 'established', 'enable'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'state', 'related', 'enable'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'reject'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'state', 'invalid', 'enable'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'state', 'new', 'enable'])
+
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'connection-status', 'nat', 'destination'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'state', 'new', 'enable'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'state', 'established', 'enable'])
+ self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'connection-status', 'nat', 'source'])
+
+ self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['iifname "eth0"', 'jump NAME_smoketest'],
+ ['ct state { established, related }', 'return'],
+ ['ct state { invalid }', 'reject'],
+ ['ct state { new }', 'ct status { dnat }', 'return'],
+ ['ct state { established, new }', 'ct status { snat }', 'return'],
+ ['smoketest default-action', 'drop']
+ ]
+
+ nftables_output = cmd('sudo nft list table ip filter')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched, msg=search)
+
+ def test_sysfs(self):
+ for name, conf in sysfs_config.items():
+ paths = glob(conf['sysfs'])
+ for path in paths:
+ with open(path, 'r') as f:
+ self.assertEqual(f.read().strip(), conf['default'], msg=path)
+
+ self.cli_set(['firewall', name.replace("_", "-"), conf['test_value']])
+
+ self.cli_commit()
+
+ for name, conf in sysfs_config.items():
+ paths = glob(conf['sysfs'])
+ for path in paths:
+ with open(path, 'r') as f:
+ self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_ha_virtual_server.py b/smoketest/scripts/cli/test_ha_virtual_server.py
new file mode 100755
index 000000000..e3a91283e
--- /dev/null
+++ b/smoketest/scripts/cli/test_ha_virtual_server.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig.vrrp import VRRP
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.template import inc_ip
+
+PROCESS_NAME = 'keepalived'
+KEEPALIVED_CONF = VRRP.location['config']
+base_path = ['high-availability']
+vrrp_interface = 'eth1'
+
+class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ self.cli_delete(['interfaces', 'ethernet', vrrp_interface, 'address'])
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Process must be terminated after deleting the config
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_ha_virtual_server(self):
+ algo = 'least-connection'
+ delay = '10'
+ method = 'nat'
+ persistence_timeout = '600'
+ vip = '203.0.113.111'
+ vport = '2222'
+ rservers = ['192.0.2.21', '192.0.2.22', '192.0.2.23']
+ rport = '22'
+ proto = 'tcp'
+ connection_timeout = '30'
+
+ vserver_base = base_path + ['virtual-server']
+
+ self.cli_set(vserver_base + [vip, 'algorithm', algo])
+ self.cli_set(vserver_base + [vip, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vip, 'forward-method', method])
+ self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vip, 'port', vport])
+ self.cli_set(vserver_base + [vip, 'protocol', proto])
+ for rs in rservers:
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEEPALIVED_CONF)
+
+ self.assertIn(f'delay_loop {delay}', config)
+ self.assertIn(f'lb_algo lc', config)
+ self.assertIn(f'lb_kind {method.upper()}', config)
+ self.assertIn(f'persistence_timeout {persistence_timeout}', config)
+ self.assertIn(f'protocol {proto.upper()}', config)
+ for rs in rservers:
+ self.assertIn(f'real_server {rs} {rport}', config)
+ self.assertIn(f'{proto.upper()}_CHECK', config)
+ self.assertIn(f'connect_timeout {connection_timeout}', config)
+
+ def test_02_ha_virtual_server_and_vrrp(self):
+ algo = 'least-connection'
+ delay = '15'
+ method = 'nat'
+ persistence_timeout = '300'
+ vip = '203.0.113.222'
+ vport = '22322'
+ rservers = ['192.0.2.11', '192.0.2.12']
+ rport = '222'
+ proto = 'tcp'
+ connection_timeout = '23'
+ group = 'VyOS'
+ vrid = '99'
+
+ vrrp_base = base_path + ['vrrp', 'group']
+ vserver_base = base_path + ['virtual-server']
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'address', '203.0.113.10/24'])
+
+ # VRRP config
+ self.cli_set(vrrp_base + [group, 'description', group])
+ self.cli_set(vrrp_base + [group, 'interface', vrrp_interface])
+ self.cli_set(vrrp_base + [group, 'address', vip + '/24'])
+ self.cli_set(vrrp_base + [group, 'vrid', vrid])
+
+ # Virtual-server config
+ self.cli_set(vserver_base + [vip, 'algorithm', algo])
+ self.cli_set(vserver_base + [vip, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vip, 'forward-method', method])
+ self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vip, 'port', vport])
+ self.cli_set(vserver_base + [vip, 'protocol', proto])
+ for rs in rservers:
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEEPALIVED_CONF)
+
+ # Keepalived vrrp
+ self.assertIn(f'# {group}', config)
+ self.assertIn(f'interface {vrrp_interface}', config)
+ self.assertIn(f'virtual_router_id {vrid}', config)
+ self.assertIn(f'priority 100', config) # default value
+ self.assertIn(f'advert_int 1', config) # default value
+ self.assertIn(f'preempt_delay 0', config) # default value
+
+ # Keepalived virtual-server
+ self.assertIn(f'delay_loop {delay}', config)
+ self.assertIn(f'lb_algo lc', config)
+ self.assertIn(f'lb_kind {method.upper()}', config)
+ self.assertIn(f'persistence_timeout {persistence_timeout}', config)
+ self.assertIn(f'protocol {proto.upper()}', config)
+ for rs in rservers:
+ self.assertIn(f'real_server {rs} {rport}', config)
+ self.assertIn(f'{proto.upper()}_CHECK', config)
+ self.assertIn(f'connect_timeout {connection_timeout}', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py
index 2524bf2b1..68905e447 100755
--- a/smoketest/scripts/cli/test_ha_vrrp.py
+++ b/smoketest/scripts/cli/test_ha_vrrp.py
@@ -27,7 +27,7 @@ from vyos.template import inc_ip
PROCESS_NAME = 'keepalived'
KEEPALIVED_CONF = VRRP.location['config']
-base_path = ['high-availability', 'vrrp']
+base_path = ['high-availability']
vrrp_interface = 'eth1'
groups = ['VLAN77', 'VLAN78', 'VLAN201']
@@ -44,7 +44,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
vlan_id = group.lstrip('VLAN')
- self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id])
+ self.cli_delete(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id])
self.cli_delete(base_path)
self.cli_commit()
@@ -56,7 +56,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
vlan_id = group.lstrip('VLAN')
vip = f'100.64.{vlan_id}.1/24'
- group_base = base_path + ['group', group]
+ group_base = base_path + ['vrrp', 'group', group]
self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
@@ -91,7 +91,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
vlan_id = group.lstrip('VLAN')
vip = f'100.64.{vlan_id}.1/24'
- group_base = base_path + ['group', group]
+ group_base = base_path + ['vrrp', 'group', group]
self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
@@ -108,7 +108,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
# Authentication
self.cli_set(group_base + ['authentication', 'type', 'plaintext-password'])
- self.cli_set(group_base + ['authentication', 'password', f'vyos-{group}'])
+ self.cli_set(group_base + ['authentication', 'password', f'{group}'])
# commit changes
self.cli_commit()
@@ -129,7 +129,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' {vip}', config)
# Authentication
- self.assertIn(f'auth_pass "vyos-{group}"', config)
+ self.assertIn(f'auth_pass "{group}"', config)
self.assertIn(f'auth_type PASS', config)
def test_03_sync_group(self):
@@ -138,7 +138,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
vlan_id = group.lstrip('VLAN')
vip = f'100.64.{vlan_id}.1/24'
- group_base = base_path + ['group', group]
+ group_base = base_path + ['vrrp', 'group', group]
self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
@@ -146,7 +146,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
self.cli_set(group_base + ['address', vip])
self.cli_set(group_base + ['vrid', vlan_id])
- self.cli_set(base_path + ['sync-group', sync_group, 'member', group])
+ self.cli_set(base_path + ['vrrp', 'sync-group', sync_group, 'member', group])
# commit changes
self.cli_commit()
@@ -166,5 +166,35 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
self.assertIn(f'{group}', config)
+ def test_04_exclude_vrrp_interface(self):
+ group = 'VyOS-WAN'
+ none_vrrp_interface = 'eth2'
+ vlan_id = '24'
+ vip = '100.64.24.1/24'
+ vip_dev = '192.0.2.2/24'
+ vrid = '150'
+ group_base = base_path + ['vrrp', 'group', group]
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', '100.64.24.11/24'])
+ self.cli_set(group_base + ['interface', f'{vrrp_interface}.{vlan_id}'])
+ self.cli_set(group_base + ['address', vip])
+ self.cli_set(group_base + ['address', vip_dev, 'interface', none_vrrp_interface])
+ self.cli_set(group_base + ['track', 'exclude-vrrp-interface'])
+ self.cli_set(group_base + ['track', 'interface', none_vrrp_interface])
+ self.cli_set(group_base + ['vrid', vrid])
+
+ # commit changes
+ self.cli_commit()
+
+ config = getConfig(f'vrrp_instance {group}')
+
+ self.assertIn(f'interface {vrrp_interface}.{vlan_id}', config)
+ self.assertIn(f'virtual_router_id {vrid}', config)
+ self.assertIn(f'dont_track_primary', config)
+ self.assertIn(f' {vip}', config)
+ self.assertIn(f' {vip_dev} dev {none_vrrp_interface}', config)
+ self.assertIn(f'track_interface', config)
+ self.assertIn(f' {none_vrrp_interface}', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_bonding.py b/smoketest/scripts/cli/test_interfaces_bonding.py
index 86000553e..237abb487 100755
--- a/smoketest/scripts/cli/test_interfaces_bonding.py
+++ b/smoketest/scripts/cli/test_interfaces_bonding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -28,6 +28,7 @@ from vyos.util import read_file
class BondingInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
@@ -36,7 +37,6 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
cls._test_vlan = True
cls._test_qinq = True
cls._base_path = ['interfaces', 'bonding']
- cls._interfaces = ['bond0']
cls._mirror_interfaces = ['dum21354']
cls._members = []
@@ -52,9 +52,10 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
cls._options['bond0'] = []
for member in cls._members:
cls._options['bond0'].append(f'member interface {member}')
+ cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(BondingInterfaceTest, cls).setUpClass()
def test_add_single_ip_address(self):
super().test_add_single_ip_address()
@@ -150,5 +151,41 @@ class BondingInterfaceTest(BasicInterfaceTest.TestCase):
defined_policy = read_file(f'/sys/class/net/{interface}/bonding/xmit_hash_policy').split()
self.assertEqual(defined_policy[0], hash_policy)
+ def test_bonding_multi_use_member(self):
+ # Define available bonding hash policies
+ for interface in ['bond10', 'bond20']:
+ for member in self._members:
+ self.cli_set(self._base_path + [interface, 'member', 'interface', member])
+
+ # check validate() - can not use the same member interfaces multiple times
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_delete(self._base_path + ['bond20'])
+
+ self.cli_commit()
+
+ def test_bonding_uniq_member_description(self):
+ ethernet_path = ['interfaces', 'ethernet']
+ for interface in self._interfaces:
+ for option in self._options.get(interface, []):
+ self.cli_set(self._base_path + [interface] + option.split())
+
+ self.cli_commit()
+
+ # Add any changes on bonding members
+ # For example add description on separate ethX interfaces
+ for interface in self._interfaces:
+ for member in self._members:
+ self.cli_set(ethernet_path + [member, 'description', member + '_interface'])
+
+ self.cli_commit()
+
+ # verify config
+ for interface in self._interfaces:
+ slaves = read_file(f'/sys/class/net/{interface}/bonding/slaves').split()
+ for member in self._members:
+ self.assertIn(member, slaves)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py
index 4f7e03298..ca0ead9e8 100755
--- a/smoketest/scripts/cli/test_interfaces_bridge.py
+++ b/smoketest/scripts/cli/test_interfaces_bridge.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -31,6 +31,7 @@ from vyos.validate import is_intf_addr_assigned
class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
@@ -55,7 +56,7 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):
cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(BridgeInterfaceTest, cls).setUpClass()
def tearDown(self):
for intf in self._interfaces:
diff --git a/smoketest/scripts/cli/test_interfaces_dummy.py b/smoketest/scripts/cli/test_interfaces_dummy.py
index dedc6fe05..d96ec2c5d 100755
--- a/smoketest/scripts/cli/test_interfaces_dummy.py
+++ b/smoketest/scripts/cli/test_interfaces_dummy.py
@@ -24,7 +24,7 @@ class DummyInterfaceTest(BasicInterfaceTest.TestCase):
cls._base_path = ['interfaces', 'dummy']
cls._interfaces = ['dum435', 'dum8677', 'dum0931', 'dum089']
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(DummyInterfaceTest, cls).setUpClass()
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 6d80e4c96..05d2ae5f5 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,32 +18,82 @@ import os
import re
import unittest
+from netifaces import AF_INET
+from netifaces import AF_INET6
+from netifaces import ifaddresses
+
from base_interfaces_test import BasicInterfaceTest
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
+from vyos.pki import CERT_BEGIN
+from vyos.template import is_ipv6
from vyos.util import cmd
from vyos.util import process_named_running
from vyos.util import read_file
+from vyos.validate import is_ipv6_link_local
+
+server_ca_root_cert_data = """
+MIIBcTCCARagAwIBAgIUDcAf1oIQV+6WRaW7NPcSnECQ/lUwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjBa
+Fw0zMjAyMTUxOTQxMjBaMB4xHDAaBgNVBAMME1Z5T1Mgc2VydmVyIHJvb3QgQ0Ew
+WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ0y24GzKQf4aM2Ir12tI9yITOIzAUj
+ZXyJeCmYI6uAnyAMqc4Q4NKyfq3nBi4XP87cs1jlC1P2BZ8MsjL5MdGWozIwMDAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRwC/YaieMEnjhYa7K3Flw/o0SFuzAK
+BggqhkjOPQQDAgNJADBGAiEAh3qEj8vScsjAdBy5shXzXDVVOKWCPTdGrPKnu8UW
+a2cCIQDlDgkzWmn5ujc5ATKz1fj+Se/aeqwh4QyoWCVTFLIxhQ==
+"""
-cert_data = """
-MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
-WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
-bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
-MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
-BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
-UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
-01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
-QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
-BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
-+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
-ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
-+dm/LDnp7C0=
+server_ca_intermediate_cert_data = """
+MIIBmTCCAT+gAwIBAgIUNzrtHzLmi3QpPK57tUgCnJZhXXQwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
+Fw0zMjAyMTUxOTQxMjFaMCYxJDAiBgNVBAMMG1Z5T1Mgc2VydmVyIGludGVybWVk
+aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEl2nJ1CzoqPV6hWII2m
+eGN/uieU6wDMECTk/LgG8CCCSYb488dibUiFN/1UFsmoLIdIhkx/6MUCYh62m8U2
+WNujUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMV3YwH88I5gFsFUibbQ
+kMR0ECPsMB8GA1UdIwQYMBaAFHAL9hqJ4wSeOFhrsrcWXD+jRIW7MAoGCCqGSM49
+BAMCA0gAMEUCIQC/ahujD9dp5pMMCd3SZddqGC9cXtOwMN0JR3e5CxP13AIgIMQm
+jMYrinFoInxmX64HfshYqnUY8608nK9D2BNPOHo=
"""
-key_data = """
-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
-2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
-u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+client_ca_root_cert_data = """
+MIIBcDCCARagAwIBAgIUZmoW2xVdwkZSvglnkCq0AHKa6zIwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
+Fw0zMjAyMTUxOTQxMjFaMB4xHDAaBgNVBAMME1Z5T1MgY2xpZW50IHJvb3QgQ0Ew
+WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATUpKXzQk2NOVKDN4VULk2yw4mOKPvn
+mg947+VY7lbpfOfAUD0QRg95qZWCw899eKnXp/U4TkAVrmEKhUb6OJTFozIwMDAP
+BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTXu6xGWUl25X3sBtrhm3BJSICIATAK
+BggqhkjOPQQDAgNIADBFAiEAnTzEwuTI9bz2Oae3LZbjP6f/f50KFJtjLZFDbQz7
+DpYCIDNRHV8zBUibC+zg5PqMpQBKd/oPfNU76nEv6xkp/ijO
+"""
+
+client_ca_intermediate_cert_data = """
+MIIBmDCCAT+gAwIBAgIUJEMdotgqA7wU4XXJvEzDulUAGqgwCgYIKoZIzj0EAwIw
+HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjJa
+Fw0zMjAyMTUxOTQxMjJaMCYxJDAiBgNVBAMMG1Z5T1MgY2xpZW50IGludGVybWVk
+aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGyIVIi217s9j3O+WQ2b
+6R65/Z0ZjQpELxPjBRc0CA0GFCo+pI5EvwI+jNFArvTAJ5+ZdEWUJ1DQhBKDDQdI
+avCjUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOUS8oNJjChB1Rb9Blcl
+ETvziHJ9MB8GA1UdIwQYMBaAFNe7rEZZSXblfewG2uGbcElIgIgBMAoGCCqGSM49
+BAMCA0cAMEQCIArhaxWgRsAUbEeNHD/ULtstLHxw/P97qPUSROLQld53AiBjgiiz
+9pDfISmpekZYz6bIDWRIR0cXUToZEMFNzNMrQg==
+"""
+
+client_cert_data = """
+MIIBmTCCAUCgAwIBAgIUV5T77XdE/tV82Tk4Vzhp5BIFFm0wCgYIKoZIzj0EAwIw
+JjEkMCIGA1UEAwwbVnlPUyBjbGllbnQgaW50ZXJtZWRpYXRlIENBMB4XDTIyMDIx
+NzE5NDEyMloXDTMyMDIxNTE5NDEyMlowIjEgMB4GA1UEAwwXVnlPUyBjbGllbnQg
+Y2VydGlmaWNhdGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARuyynqfc/qJj5e
+KJ03oOH8X4Z8spDeAPO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAh
+CIhytmJao1AwTjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTIFKrxZ+PqOhYSUqnl
+TGCUmM7wTjAfBgNVHSMEGDAWgBTlEvKDSYwoQdUW/QZXJRE784hyfTAKBggqhkjO
+PQQDAgNHADBEAiAvO8/jvz05xqmP3OXD53XhfxDLMIxzN4KPoCkFqvjlhQIgIHq2
+/geVx3rAOtSps56q/jiDouN/aw01TdpmGKVAa9U=
+"""
+
+client_key_data = """
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxaxAQsJwjoOCByQE
++qSYKtKtJzbdbOnTsKNSrfgkFH6hRANCAARuyynqfc/qJj5eKJ03oOH8X4Z8spDe
+APO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAhCIhytmJa
"""
def get_wpa_supplicant_value(interface, key):
@@ -51,9 +101,14 @@ def get_wpa_supplicant_value(interface, key):
tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp)
return tmp[0]
+def get_certificate_count(interface, cert_type):
+ tmp = read_file(f'/run/wpa_supplicant/{interface}_{cert_type}.pem')
+ return tmp.count(CERT_BEGIN)
+
class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
@@ -79,7 +134,7 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
cls._macs[interface] = read_file(f'/sys/class/net/{interface}/address')
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(EthernetInterfaceTest, cls).setUpClass()
def tearDown(self):
for interface in self._interfaces:
@@ -91,30 +146,19 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_set(self._base_path + [interface, 'speed', 'auto'])
self.cli_set(self._base_path + [interface, 'hw-id', self._macs[interface]])
- # Tear down mirror interfaces for SPAN (Switch Port Analyzer)
- for span in self._mirror_interfaces:
- section = Section.section(span)
- self.cli_delete(['interfaces', section, span])
-
self.cli_commit()
- def test_dhcp_disable_interface(self):
- # When interface is configured as admin down, it must be admin down
- # even when dhcpc starts on the given interface
- for interface in self._interfaces:
- self.cli_set(self._base_path + [interface, 'disable'])
-
- # Also enable DHCP (ISC DHCP always places interface in admin up
- # state so we check that we do not start DHCP client.
- # https://phabricator.vyos.net/T2767
- self.cli_set(self._base_path + [interface, 'address', 'dhcp'])
-
- self.cli_commit()
-
- # Validate interface state
- for interface in self._interfaces:
- flags = read_file(f'/sys/class/net/{interface}/flags')
- self.assertEqual(int(flags, 16) & 1, 0)
+ # 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))
+ # required for IPv6 link-local address
+ self.assertIn(AF_INET6, ifaddresses(intf))
+ for addr in ifaddresses(intf)[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']))
def test_offloading_rps(self):
# enable RPS on all available CPUs, RPS works woth a CPU bitmask,
@@ -165,16 +209,23 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
self.cli_commit()
def test_eapol_support(self):
- ca_name = 'eapol'
- cert_name = 'eapol'
+ ca_certs = {
+ 'eapol-server-ca-root': server_ca_root_cert_data,
+ 'eapol-server-ca-intermediate': server_ca_intermediate_cert_data,
+ 'eapol-client-ca-root': client_ca_root_cert_data,
+ 'eapol-client-ca-intermediate': client_ca_intermediate_cert_data,
+ }
+ cert_name = 'eapol-client'
- self.cli_set(['pki', 'ca', ca_name, 'certificate', cert_data.replace('\n','')])
- self.cli_set(['pki', 'certificate', cert_name, 'certificate', cert_data.replace('\n','')])
- self.cli_set(['pki', 'certificate', cert_name, 'private', 'key', key_data.replace('\n','')])
+ for name, data in ca_certs.items():
+ self.cli_set(['pki', 'ca', name, 'certificate', data.replace('\n','')])
+
+ self.cli_set(['pki', 'certificate', cert_name, 'certificate', client_cert_data.replace('\n','')])
+ self.cli_set(['pki', 'certificate', cert_name, 'private', 'key', client_key_data.replace('\n','')])
for interface in self._interfaces:
# Enable EAPoL
- self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', ca_name])
+ self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-server-ca-intermediate'])
self.cli_set(self._base_path + [interface, 'eapol', 'certificate', cert_name])
self.cli_commit()
@@ -206,7 +257,12 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
tmp = get_wpa_supplicant_value(interface, 'identity')
self.assertEqual(f'"{mac}"', tmp)
- self.cli_delete(['pki', 'ca', ca_name])
+ # Check certificate files have the full chain
+ self.assertEqual(get_certificate_count(interface, 'ca'), 2)
+ self.assertEqual(get_certificate_count(interface, 'cert'), 3)
+
+ for name in ca_certs:
+ self.cli_delete(['pki', 'ca', name])
self.cli_delete(['pki', 'certificate', cert_name])
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_interfaces_geneve.py b/smoketest/scripts/cli/test_interfaces_geneve.py
index 6233ade6e..0e5098aa7 100755
--- a/smoketest/scripts/cli/test_interfaces_geneve.py
+++ b/smoketest/scripts/cli/test_interfaces_geneve.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -34,7 +34,7 @@ class GeneveInterfaceTest(BasicInterfaceTest.TestCase):
}
cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(GeneveInterfaceTest, cls).setUpClass()
def test_geneve_parameters(self):
tos = '40'
@@ -43,7 +43,7 @@ class GeneveInterfaceTest(BasicInterfaceTest.TestCase):
for option in self._options.get(intf, []):
self.cli_set(self._base_path + [intf] + option.split())
- self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'dont-fragment'])
+ self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'df', 'set'])
self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'tos', tos])
self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'ttl', str(ttl)])
ttl += 10
diff --git a/smoketest/scripts/cli/test_interfaces_l2tpv3.py b/smoketest/scripts/cli/test_interfaces_l2tpv3.py
index 06ced5c40..aed8e6f15 100755
--- a/smoketest/scripts/cli/test_interfaces_l2tpv3.py
+++ b/smoketest/scripts/cli/test_interfaces_l2tpv3.py
@@ -39,7 +39,7 @@ class L2TPv3InterfaceTest(BasicInterfaceTest.TestCase):
}
cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(L2TPv3InterfaceTest, cls).setUpClass()
def test_add_single_ip_address(self):
super().test_add_single_ip_address()
diff --git a/smoketest/scripts/cli/test_interfaces_loopback.py b/smoketest/scripts/cli/test_interfaces_loopback.py
index 85b5ca6d6..5ff9c250e 100755
--- a/smoketest/scripts/cli/test_interfaces_loopback.py
+++ b/smoketest/scripts/cli/test_interfaces_loopback.py
@@ -29,7 +29,7 @@ class LoopbackInterfaceTest(BasicInterfaceTest.TestCase):
cls._base_path = ['interfaces', 'loopback']
cls._interfaces = ['lo']
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(LoopbackInterfaceTest, cls).setUpClass()
def tearDown(self):
self.cli_delete(self._base_path)
diff --git a/smoketest/scripts/cli/test_interfaces_macsec.py b/smoketest/scripts/cli/test_interfaces_macsec.py
index e4280a5b7..e5e5a558e 100755
--- a/smoketest/scripts/cli/test_interfaces_macsec.py
+++ b/smoketest/scripts/cli/test_interfaces_macsec.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -40,6 +40,7 @@ def get_cipher(interface):
class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._base_path = ['interfaces', 'macsec']
@@ -52,7 +53,7 @@ class MACsecInterfaceTest(BasicInterfaceTest.TestCase):
cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(MACsecInterfaceTest, cls).setUpClass()
def test_macsec_encryption(self):
# MACsec can be operating in authentication and encryption mode - both
diff --git a/smoketest/scripts/cli/test_interfaces_netns.py b/smoketest/scripts/cli/test_interfaces_netns.py
new file mode 100755
index 000000000..9975a6b09
--- /dev/null
+++ b/smoketest/scripts/cli/test_interfaces_netns.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import json
+import unittest
+
+from netifaces import interfaces
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Interface
+from vyos.ifconfig import Section
+from vyos.util import cmd
+
+base_path = ['netns']
+namespaces = ['mgmt', 'front', 'back', 'ams-ix']
+
+class NETNSTest(VyOSUnitTestSHIM.TestCase):
+
+ def setUp(self):
+ self._interfaces = ['dum10', 'dum12', 'dum50']
+
+ def test_create_netns(self):
+ for netns in namespaces:
+ base = base_path + ['name', netns]
+ self.cli_set(base)
+
+ # commit changes
+ self.cli_commit()
+
+ netns_list = cmd('ip netns ls')
+
+ # Verify NETNS configuration
+ for netns in namespaces:
+ self.assertTrue(netns in netns_list)
+
+
+ def test_netns_assign_interface(self):
+ netns = 'foo'
+ self.cli_set(['netns', 'name', netns])
+
+ # Set
+ for iface in self._interfaces:
+ self.cli_set(['interfaces', 'dummy', iface, 'netns', netns])
+
+ # commit changes
+ self.cli_commit()
+
+ netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show')
+
+ for iface in self._interfaces:
+ self.assertTrue(iface in netns_iface_list)
+
+ # Delete
+ for iface in self._interfaces:
+ self.cli_delete(['interfaces', 'dummy', iface, 'netns', netns])
+
+ # commit changes
+ self.cli_commit()
+
+ netns_iface_list = cmd(f'sudo ip netns exec {netns} ip link show')
+
+ for iface in self._interfaces:
+ self.assertNotIn(iface, netns_iface_list)
+
+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 f8a6ae986..b2143d16e 100755
--- a/smoketest/scripts/cli/test_interfaces_openvpn.py
+++ b/smoketest/scripts/cli/test_interfaces_openvpn.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -37,10 +37,46 @@ PROCESS_NAME = 'openvpn'
base_path = ['interfaces', 'openvpn']
-cert_data = 'MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIwWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIxMDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3LftzngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93+dm/LDnp7C0='
-key_data = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww'
-dh_data = 'MIIBCAKCAQEApzGAPcQlLJiOyfGZgl1qxNgufXkdpjG7lMaOrO4TGr1giFe3jIFOFxJNC/G9Dn+KSukaWssVVR+Jwr/JesZFPawihS03wC7cZsccykNRIjiteqJDwYJZUHieOxyCuCeY4pqOUCl1uswRGjLvIFtwynpnXKKuz2YtjNifma90PEgv/vVWKix+Q0TAbdbzJzO5xp8UVn9DuYfSr10k3LbDqDM7w5ezHZxFk24S5pN/yoOpdbxB8TS67q3IYXxR3F+RseKu4J3AvkxXSP1j7COXddPpLnvbJT/SW8NrjuC/n0eKGvmeyqNv108Y89jnT79MxMMRQk66iwlsd1m4pa/OYwIBAg=='
-ovpn_key_data = '443f2a710ac411c36894b2531e62c4550b079b8f3f08997f4be57c64abfdaaa431d2396b01ecec3a2c0618959e8186d99f489742d25673ffb3268841ebb2e7042a2daabe584e79d51d2b1d7409bf8840f7e42efa3e660a521719b04ee88b9043e6315ae12da7c9abd55f67eeed71a9ee8c6e163b5d2661fc332cf90cb45658b4adf892f79537d37d3a3d90da283ce885adf325ffd2b5be92067cdf0345c7712c9d36b642c170351b6d9ce9f6230c7a2617b0c181121bce7d5373404fb68e65210b36e6d40ef2769cf8990503859f6f2db3c85ba74420430a6250d6a74ca51ece4b85124bfdfec0c8a530cefa7350378d81a4539f74bed832a902ae4798142e4a'
+cert_data = """
+MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
+WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
+MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
+BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
+UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
+01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
+QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
+BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
++JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
+ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
++dm/LDnp7C0=
+"""
+
+key_data = """
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
+2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
+u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+"""
+
+dh_data = """
+MIIBCAKCAQEApzGAPcQlLJiOyfGZgl1qxNgufXkdpjG7lMaOrO4TGr1giFe3jIFO
+FxJNC/G9Dn+KSukaWssVVR+Jwr/JesZFPawihS03wC7cZsccykNRIjiteqJDwYJZ
+UHieOxyCuCeY4pqOUCl1uswRGjLvIFtwynpnXKKuz2YtjNifma90PEgv/vVWKix+
+Q0TAbdbzJzO5xp8UVn9DuYfSr10k3LbDqDM7w5ezHZxFk24S5pN/yoOpdbxB8TS6
+7q3IYXxR3F+RseKu4J3AvkxXSP1j7COXddPpLnvbJT/SW8NrjuC/n0eKGvmeyqNv
+108Y89jnT79MxMMRQk66iwlsd1m4pa/OYwIBAg==
+"""
+
+ovpn_key_data = """
+443f2a710ac411c36894b2531e62c4550b079b8f3f08997f4be57c64abfdaaa4
+31d2396b01ecec3a2c0618959e8186d99f489742d25673ffb3268841ebb2e704
+2a2daabe584e79d51d2b1d7409bf8840f7e42efa3e660a521719b04ee88b9043
+e6315ae12da7c9abd55f67eeed71a9ee8c6e163b5d2661fc332cf90cb45658b4
+adf892f79537d37d3a3d90da283ce885adf325ffd2b5be92067cdf0345c7712c
+9d36b642c170351b6d9ce9f6230c7a2617b0c181121bce7d5373404fb68e6521
+0b36e6d40ef2769cf8990503859f6f2db3c85ba74420430a6250d6a74ca51ece
+4b85124bfdfec0c8a530cefa7350378d81a4539f74bed832a902ae4798142e4a
+"""
remote_port = '1194'
protocol = 'udp'
@@ -59,20 +95,28 @@ def get_vrf(interface):
return tmp
class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['interfaces', 'dummy', dummy_if, 'address', '192.0.2.1/32'])
- self.cli_set(['vrf', 'name', vrf_name, 'table', '12345'])
+ @classmethod
+ def setUpClass(cls):
+ super(TestInterfacesOpenVPN, cls).setUpClass()
- self.cli_set(['pki', 'ca', 'ovpn_test', 'certificate', cert_data])
- self.cli_set(['pki', 'certificate', 'ovpn_test', 'certificate', cert_data])
- self.cli_set(['pki', 'certificate', 'ovpn_test', 'private', 'key', key_data])
- self.cli_set(['pki', 'dh', 'ovpn_test', 'parameters', dh_data])
- self.cli_set(['pki', 'openvpn', 'shared-secret', 'ovpn_test', 'key', ovpn_key_data])
+ cls.cli_set(cls, ['interfaces', 'dummy', dummy_if, 'address', '192.0.2.1/32'])
+ cls.cli_set(cls, ['vrf', 'name', vrf_name, 'table', '12345'])
+
+ cls.cli_set(cls, ['pki', 'ca', 'ovpn_test', 'certificate', cert_data.replace('\n','')])
+ cls.cli_set(cls, ['pki', 'certificate', 'ovpn_test', 'certificate', cert_data.replace('\n','')])
+ cls.cli_set(cls, ['pki', 'certificate', 'ovpn_test', 'private', 'key', key_data.replace('\n','')])
+ cls.cli_set(cls, ['pki', 'dh', 'ovpn_test', 'parameters', dh_data.replace('\n','')])
+ cls.cli_set(cls, ['pki', 'openvpn', 'shared-secret', 'ovpn_test', 'key', ovpn_key_data.replace('\n','')])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', dummy_if])
+ cls.cli_delete(cls, ['vrf'])
+
+ super(TestInterfacesOpenVPN, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
- self.cli_delete(['interfaces', 'dummy', dummy_if])
- self.cli_delete(['vrf'])
self.cli_commit()
def test_openvpn_client_verify(self):
@@ -532,6 +576,46 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
+ def test_openvpn_options(self):
+ # Ensure OpenVPN process restart on openvpn-option CLI node change
+
+ interface = 'vtun5001'
+ path = base_path + [interface]
+
+ 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_commit()
+
+ # Now verify the OpenVPN "raw" option passing. Once an openvpn-option is
+ # added, modified or deleted from the CLI, OpenVPN daemon must be restarted
+ cur_pid = process_named_running('openvpn')
+ self.cli_set(path + ['openvpn-option', '--persist-tun'])
+ self.cli_commit()
+
+ # PID must be different as OpenVPN Must be restarted
+ new_pid = process_named_running('openvpn')
+ self.assertNotEqual(cur_pid, new_pid)
+ cur_pid = new_pid
+
+ self.cli_set(path + ['openvpn-option', '--persist-key'])
+ self.cli_commit()
+
+ # PID must be different as OpenVPN Must be restarted
+ new_pid = process_named_running('openvpn')
+ self.assertNotEqual(cur_pid, new_pid)
+ cur_pid = new_pid
+
+ self.cli_delete(path + ['openvpn-option'])
+ self.cli_commit()
+
+ # PID must be different as OpenVPN Must be restarted
+ new_pid = process_named_running('openvpn')
+ self.assertNotEqual(cur_pid, new_pid)
+ cur_pid = new_pid
+
def test_openvpn_site2site_interfaces_tun(self):
# Create two OpenVPN site-to-site interfaces
diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py
index 4f1e1ee99..8927121a8 100755
--- a/smoketest/scripts/cli/test_interfaces_pppoe.py
+++ b/smoketest/scripts/cli/test_interfaces_pppoe.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -34,9 +34,12 @@ def get_config_value(interface, key):
# add a classmethod to setup a temporaray PPPoE server for "proper" validation
class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self._interfaces = ['pppoe10', 'pppoe20', 'pppoe30']
- self._source_interface = 'eth0'
+ @classmethod
+ def setUpClass(cls):
+ super(PPPoEInterfaceTest, cls).setUpClass()
+
+ cls._interfaces = ['pppoe10', 'pppoe20', 'pppoe30']
+ cls._source_interface = 'eth0'
def tearDown(self):
# Validate PPPoE client process
@@ -60,7 +63,6 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + [interface, 'authentication', 'user', user])
self.cli_set(base_path + [interface, 'authentication', 'password', passwd])
- self.cli_set(base_path + [interface, 'default-route', 'auto'])
self.cli_set(base_path + [interface, 'mtu', mtu])
self.cli_set(base_path + [interface, 'no-peer-dns'])
@@ -136,7 +138,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase):
for interface in self._interfaces:
self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos'])
self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos'])
- self.cli_set(base_path + [interface, 'default-route', 'none'])
+ self.cli_set(base_path + [interface, 'no-default-route'])
self.cli_set(base_path + [interface, 'no-peer-dns'])
self.cli_set(base_path + [interface, 'source-interface', self._source_interface])
self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf'])
diff --git a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py
index ae899cddd..a51b8d52c 100755
--- a/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_pseudo_ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -23,6 +23,7 @@ from base_interfaces_test import BasicInterfaceTest
class PEthInterfaceTest(BasicInterfaceTest.TestCase):
@classmethod
def setUpClass(cls):
+ cls._test_dhcp = True
cls._test_ip = True
cls._test_ipv6 = True
cls._test_ipv6_pd = True
@@ -47,7 +48,7 @@ class PEthInterfaceTest(BasicInterfaceTest.TestCase):
cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(PEthInterfaceTest, cls).setUpClass()
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index fc2e254d6..44bfbb5f0 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -42,16 +42,16 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
}
cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(TunnelInterfaceTest, cls).setUpClass()
- def setUp(self):
- super().setUp()
- self.cli_set(['interfaces', 'dummy', source_if, 'address', self.local_v4 + '/32'])
- self.cli_set(['interfaces', 'dummy', source_if, 'address', self.local_v6 + '/128'])
+ # create some test interfaces
+ cls.cli_set(cls, ['interfaces', 'dummy', source_if, 'address', cls.local_v4 + '/32'])
+ cls.cli_set(cls, ['interfaces', 'dummy', source_if, 'address', cls.local_v6 + '/128'])
- def tearDown(self):
- self.cli_delete(['interfaces', 'dummy', source_if])
- super().tearDown()
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', source_if])
+ super().tearDownClass()
def test_ipv4_encapsulations(self):
# When running tests ensure that for certain encapsulation types the
@@ -202,7 +202,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(encapsulation, conf['linkinfo']['info_kind'])
self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
- self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
# Change remote ip address (inc host by 2
new_remote = inc_ip(remote_ip4, 2)
@@ -239,7 +239,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(encapsulation, conf['linkinfo']['info_kind'])
self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
- self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
self.assertEqual(int(idx), conf['linkinfo']['info_data']['erspan_index'])
@@ -295,7 +295,7 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(encapsulation, conf['linkinfo']['info_kind'])
self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
- self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
+ self.assertEqual(64, conf['linkinfo']['info_data']['ttl'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
self.assertEqual(erspan_ver, conf['linkinfo']['info_data']['erspan_ver'])
@@ -312,5 +312,89 @@ class TunnelInterfaceTest(BasicInterfaceTest.TestCase):
conf = get_interface_config(interface)
self.assertEqual(new_remote, conf['linkinfo']['info_data']['remote'])
+ def test_tunnel_src_any_gre_key(self):
+ interface = f'tun1280'
+ encapsulation = 'gre'
+ src_addr = '0.0.0.0'
+ key = '127'
+
+ self.cli_set(self._base_path + [interface, 'encapsulation', encapsulation])
+ self.cli_set(self._base_path + [interface, 'source-address', src_addr])
+ # GRE key must be supplied with a 0.0.0.0 source address
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(self._base_path + [interface, 'parameters', 'ip', 'key', key])
+
+ self.cli_commit()
+
+ def test_multiple_gre_tunnel_same_remote(self):
+ tunnels = {
+ 'tun10' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.4',
+ },
+ 'tun20' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.4',
+ },
+ }
+
+ for tunnel, tunnel_config in tunnels.items():
+ self.cli_set(self._base_path + [tunnel, 'encapsulation', tunnel_config['encapsulation']])
+ if 'source_interface' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'source-interface', tunnel_config['source_interface']])
+ if 'remote' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'remote', tunnel_config['remote']])
+
+ # GRE key must be supplied when two or more tunnels are formed to the same desitnation
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for tunnel, tunnel_config in tunnels.items():
+ self.cli_set(self._base_path + [tunnel, 'parameters', 'ip', 'key', tunnel.lstrip('tun')])
+
+ self.cli_commit()
+
+ for tunnel, tunnel_config in tunnels.items():
+ conf = get_interface_config(tunnel)
+ ip_key = tunnel.lstrip('tun')
+
+ self.assertEqual(tunnel_config['source_interface'], conf['link'])
+ self.assertEqual(tunnel_config['encapsulation'], conf['linkinfo']['info_kind'])
+ self.assertEqual(tunnel_config['remote'], conf['linkinfo']['info_data']['remote'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['ikey'])
+ self.assertEqual(f'0.0.0.{ip_key}', conf['linkinfo']['info_data']['okey'])
+
+ def test_multiple_gre_tunnel_different_remote(self):
+ tunnels = {
+ 'tun10' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.4',
+ },
+ 'tun20' : {
+ 'encapsulation' : 'gre',
+ 'source_interface' : source_if,
+ 'remote' : '1.2.3.5',
+ },
+ }
+
+ for tunnel, tunnel_config in tunnels.items():
+ self.cli_set(self._base_path + [tunnel, 'encapsulation', tunnel_config['encapsulation']])
+ if 'source_interface' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'source-interface', tunnel_config['source_interface']])
+ if 'remote' in tunnel_config:
+ self.cli_set(self._base_path + [tunnel, 'remote', tunnel_config['remote']])
+
+ self.cli_commit()
+
+ for tunnel, tunnel_config in tunnels.items():
+ conf = get_interface_config(tunnel)
+
+ self.assertEqual(tunnel_config['source_interface'], conf['link'])
+ self.assertEqual(tunnel_config['encapsulation'], conf['linkinfo']['info_kind'])
+ self.assertEqual(tunnel_config['remote'], conf['linkinfo']['info_data']['remote'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index f63c850d8..058f13721 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -16,9 +16,11 @@
import unittest
+from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
+from vyos.util import get_bridge_fdb
from vyos.util import get_interface_config
-
+from vyos.template import is_ipv6
from base_interfaces_test import BasicInterfaceTest
class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
@@ -32,10 +34,12 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
'vxlan10': ['vni 10', 'remote 127.0.0.2'],
'vxlan20': ['vni 20', 'group 239.1.1.1', 'source-interface eth0'],
'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)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(VXLANInterfaceTest, cls).setUpClass()
def test_vxlan_parameters(self):
tos = '40'
@@ -44,7 +48,7 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
for option in self._options.get(intf, []):
self.cli_set(self._base_path + [intf] + option.split())
- self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'dont-fragment'])
+ self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'df', 'set'])
self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'tos', tos])
self.cli_set(self._base_path + [intf, 'parameters', 'ip', 'ttl', str(ttl)])
ttl += 10
@@ -54,21 +58,34 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
ttl = 20
for interface in self._interfaces:
options = get_interface_config(interface)
+ bridge = get_bridge_fdb(interface)
vni = options['linkinfo']['info_data']['id']
self.assertIn(f'vni {vni}', self._options[interface])
- if any('link' in s for s in self._options[interface]):
+ if any('source-interface' in s for s in self._options[interface]):
link = options['linkinfo']['info_data']['link']
self.assertIn(f'source-interface {link}', self._options[interface])
- if any('local6' in s for s in self._options[interface]):
- remote = options['linkinfo']['info_data']['local6']
- self.assertIn(f'source-address {local6}', self._options[interface])
-
- if any('remote6' in s for s in self._options[interface]):
- remote = options['linkinfo']['info_data']['remote6']
- self.assertIn(f'remote {remote}', self._options[interface])
+ # Verify source-address setting was properly configured on the Kernel
+ if any('source-address' in s for s in self._options[interface]):
+ for s in self._options[interface]:
+ if 'source-address' in s:
+ address = s.split()[-1]
+ if is_ipv6(address):
+ tmp = options['linkinfo']['info_data']['local6']
+ else:
+ tmp = options['linkinfo']['info_data']['local']
+ self.assertIn(f'source-address {tmp}', self._options[interface])
+
+ # Verify remote setting was properly configured on the Kernel
+ if any('remote' in s for s in self._options[interface]):
+ for s in self._options[interface]:
+ if 'remote' in s:
+ for fdb in bridge:
+ if 'mac' in fdb and fdb['mac'] == '00:00:00:00:00:00':
+ remote = fdb['dst']
+ self.assertIn(f'remote {remote}', self._options[interface])
if any('group' in s for s in self._options[interface]):
group = options['linkinfo']['info_data']['group']
@@ -78,6 +95,9 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
label = options['linkinfo']['info_data']['label']
self.assertIn(f'parameters ipv6 flowlabel {label}', self._options[interface])
+ if any('external' in s for s in self._options[interface]):
+ self.assertTrue(options['linkinfo']['info_data']['external'])
+
self.assertEqual('vxlan', options['linkinfo']['info_kind'])
self.assertEqual('set', options['linkinfo']['info_data']['df'])
self.assertEqual(f'0x{tos}', options['linkinfo']['info_data']['tos'])
@@ -85,5 +105,36 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
self.assertEqual(Interface(interface).get_admin_state(), 'up')
ttl += 10
+ 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, 'source-address', source_address])
+
+ # Both 'VNI' and 'external' can not be specified at the same time.
+ self.cli_set(self._base_path + [interface, 'vni', '111'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(self._base_path + [interface, 'vni'])
+
+ # Now add some more interfaces - this must fail and a CLI error needs
+ # to be generated as Linux can only handle one VXLAN tunnel when using
+ # external mode.
+ for intf in self._interfaces:
+ for option in self._options.get(intf, []):
+ self.cli_set(self._base_path + [intf] + option.split())
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ # Remove those test interfaces again
+ for intf in self._interfaces:
+ self.cli_delete(self._base_path + [intf])
+
+ self.cli_commit()
+
+ options = get_interface_config(interface)
+ self.assertTrue(options['linkinfo']['info_data']['external'])
+ self.assertEqual('vxlan', options['linkinfo']['info_kind'])
+
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 aaf27a2c4..f3e9670f7 100755
--- a/smoketest/scripts/cli/test_interfaces_wireguard.py
+++ b/smoketest/scripts/cli/test_interfaces_wireguard.py
@@ -23,10 +23,13 @@ from vyos.configsession import ConfigSessionError
base_path = ['interfaces', 'wireguard']
class WireGuardInterfaceTest(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self._test_addr = ['192.0.2.1/26', '192.0.2.255/31', '192.0.2.64/32',
+ @classmethod
+ def setUpClass(cls):
+ super(WireGuardInterfaceTest, cls).setUpClass()
+
+ cls._test_addr = ['192.0.2.1/26', '192.0.2.255/31', '192.0.2.64/32',
'2001:db8:1::ffff/64', '2001:db8:101::1/112']
- self._interfaces = ['wg0', 'wg1']
+ cls._interfaces = ['wg0', 'wg1']
def tearDown(self):
self.cli_delete(base_path)
diff --git a/smoketest/scripts/cli/test_interfaces_wireless.py b/smoketest/scripts/cli/test_interfaces_wireless.py
index 4f539a23c..a24f37d8d 100755
--- a/smoketest/scripts/cli/test_interfaces_wireless.py
+++ b/smoketest/scripts/cli/test_interfaces_wireless.py
@@ -48,7 +48,7 @@ class WirelessInterfaceTest(BasicInterfaceTest.TestCase):
}
cls._interfaces = list(cls._options)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(WirelessInterfaceTest, cls).setUpClass()
def test_wireless_add_single_ip_address(self):
# derived method to check if member interfaces are enslaved properly
diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py
index 75c628244..408facfb3 100755
--- a/smoketest/scripts/cli/test_nat.py
+++ b/smoketest/scripts/cli/test_nat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -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 jmespath
import json
import unittest
@@ -29,10 +28,13 @@ src_path = base_path + ['source']
dst_path = base_path + ['destination']
class TestNAT(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
+ @classmethod
+ def setUpClass(cls):
+ super(TestNAT, 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)
+ cls.cli_delete(cls, base_path)
def tearDown(self):
self.cli_delete(base_path)
@@ -57,36 +59,44 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- tmp = cmd('sudo nft -j list table nat')
+ tmp = cmd('sudo nft -j list chain ip nat POSTROUTING')
data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
for idx in range(0, len(data_json)):
- rule = str(rules[idx])
data = data_json[idx]
- network = f'192.168.{rule}.0/24'
-
- self.assertEqual(data['chain'], 'POSTROUTING')
- self.assertEqual(data['comment'], f'SRC-NAT-{rule}')
- self.assertEqual(data['family'], 'ip')
- self.assertEqual(data['table'], 'nat')
-
- iface = dict_search('match.right', data['expr'][0])
- direction = dict_search('match.left.payload.field', data['expr'][1])
- address = dict_search('match.right.prefix.addr', data['expr'][1])
- mask = dict_search('match.right.prefix.len', data['expr'][1])
+ if idx == 0:
+ self.assertEqual(data['chain'], 'POSTROUTING')
+ self.assertEqual(data['family'], 'ip')
+ self.assertEqual(data['table'], 'nat')
- if int(rule) < 200:
- self.assertEqual(direction, 'saddr')
- self.assertEqual(iface, outbound_iface_100)
- # check for masquerade keyword
- self.assertIn('masquerade', data['expr'][3])
+ jump_target = dict_search('jump.target', data['expr'][1])
+ self.assertEqual(jump_target,'VYOS_PRE_SNAT_HOOK')
else:
- self.assertEqual(direction, 'daddr')
- self.assertEqual(iface, outbound_iface_200)
- # check for return keyword due to 'exclude'
- self.assertIn('return', data['expr'][3])
-
- self.assertEqual(f'{address}/{mask}', network)
+ rule = str(rules[idx - 1])
+ network = f'192.168.{rule}.0/24'
+
+ self.assertEqual(data['chain'], 'POSTROUTING')
+ self.assertEqual(data['comment'], f'SRC-NAT-{rule}')
+ self.assertEqual(data['family'], 'ip')
+ self.assertEqual(data['table'], 'nat')
+
+ iface = dict_search('match.right', data['expr'][0])
+ direction = dict_search('match.left.payload.field', data['expr'][1])
+ address = dict_search('match.right.prefix.addr', data['expr'][1])
+ mask = dict_search('match.right.prefix.len', data['expr'][1])
+
+ if int(rule) < 200:
+ self.assertEqual(direction, 'saddr')
+ self.assertEqual(iface, outbound_iface_100)
+ # check for masquerade keyword
+ self.assertIn('masquerade', data['expr'][3])
+ else:
+ self.assertEqual(direction, 'daddr')
+ self.assertEqual(iface, outbound_iface_200)
+ # check for return keyword due to 'exclude'
+ self.assertIn('return', data['expr'][3])
+
+ self.assertEqual(f'{address}/{mask}', network)
def test_dnat(self):
rules = ['100', '110', '120', '130', '200', '210', '220', '230']
@@ -109,33 +119,42 @@ class TestNAT(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- tmp = cmd('sudo nft -j list table nat')
+ tmp = cmd('sudo nft -j list chain ip nat PREROUTING')
data_json = jmespath.search('nftables[?rule].rule[?chain]', json.loads(tmp))
for idx in range(0, len(data_json)):
- rule = str(rules[idx])
data = data_json[idx]
- port = int(f'10{rule}')
-
- self.assertEqual(data['chain'], 'PREROUTING')
- self.assertEqual(data['comment'].split()[0], f'DST-NAT-{rule}')
- self.assertEqual(data['family'], 'ip')
- self.assertEqual(data['table'], 'nat')
-
- iface = dict_search('match.right', data['expr'][0])
- direction = dict_search('match.left.payload.field', data['expr'][1])
- protocol = dict_search('match.left.payload.protocol', data['expr'][1])
- dnat_addr = dict_search('dnat.addr', data['expr'][3])
- dnat_port = dict_search('dnat.port', data['expr'][3])
-
- self.assertEqual(direction, 'sport')
- self.assertEqual(dnat_addr, '192.0.2.1')
- self.assertEqual(dnat_port, port)
- if int(rule) < 200:
- self.assertEqual(iface, inbound_iface_100)
- self.assertEqual(protocol, inbound_proto_100)
+ if idx == 0:
+ self.assertEqual(data['chain'], 'PREROUTING')
+ self.assertEqual(data['family'], 'ip')
+ self.assertEqual(data['table'], 'nat')
+
+ jump_target = dict_search('jump.target', data['expr'][1])
+ self.assertEqual(jump_target,'VYOS_PRE_DNAT_HOOK')
else:
- self.assertEqual(iface, inbound_iface_200)
+
+ rule = str(rules[idx - 1])
+ port = int(f'10{rule}')
+
+ self.assertEqual(data['chain'], 'PREROUTING')
+ self.assertEqual(data['comment'].split()[0], f'DST-NAT-{rule}')
+ self.assertEqual(data['family'], 'ip')
+ self.assertEqual(data['table'], 'nat')
+
+ iface = dict_search('match.right', data['expr'][0])
+ direction = dict_search('match.left.payload.field', data['expr'][1])
+ protocol = dict_search('match.left.payload.protocol', data['expr'][1])
+ dnat_addr = dict_search('dnat.addr', data['expr'][3])
+ dnat_port = dict_search('dnat.port', data['expr'][3])
+
+ self.assertEqual(direction, 'sport')
+ self.assertEqual(dnat_addr, '192.0.2.1')
+ self.assertEqual(dnat_port, port)
+ if int(rule) < 200:
+ self.assertEqual(iface, inbound_iface_100)
+ self.assertEqual(protocol, inbound_proto_100)
+ else:
+ self.assertEqual(iface, inbound_iface_200)
def test_snat_required_translation_address(self):
# T2813: Ensure translation address is specified
diff --git a/smoketest/scripts/cli/test_nat66.py b/smoketest/scripts/cli/test_nat66.py
index 8afe0da26..aac6a30f9 100755
--- a/smoketest/scripts/cli/test_nat66.py
+++ b/smoketest/scripts/cli/test_nat66.py
@@ -32,7 +32,7 @@ dst_path = base_path + ['destination']
class TestNAT66(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
- super(cls, cls).setUpClass()
+ super(TestNAT66, cls).setUpClass()
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
@@ -185,4 +185,4 @@ class TestNAT66(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_pki.py b/smoketest/scripts/cli/test_pki.py
index 45a4bd61e..e92123dbc 100755
--- a/smoketest/scripts/cli/test_pki.py
+++ b/smoketest/scripts/cli/test_pki.py
@@ -129,8 +129,13 @@ xGsJxVHfSKeooUQn6q76sg==
"""
class TestPKI(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_delete(base_path)
+ @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)
def tearDown(self):
self.cli_delete(base_path)
diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py
index 1286a768d..e8c6ff19b 100755
--- a/smoketest/scripts/cli/test_policy.py
+++ b/smoketest/scripts/cli/test_policy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -307,7 +307,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
continue
for rule, rule_config in as_path_config['rule'].items():
- tmp = f'bgp as-path access-list {as_path}'
+ tmp = f'bgp as-path access-list {as_path} seq {rule}'
if rule_config['action'] == 'permit':
tmp += ' permit'
else:
@@ -665,6 +665,40 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.assertIn(tmp, config)
+ def test_prefix_list_duplicates(self):
+ # FRR does not allow to specify the same profix list rule multiple times
+ #
+ # vyos(config)# ip prefix-list foo seq 10 permit 192.0.2.0/24
+ # vyos(config)# ip prefix-list foo seq 20 permit 192.0.2.0/24
+ # % Configuration failed.
+ # Error type: validation
+ # Error description: duplicated prefix list value: 192.0.2.0/24
+
+ # There is also a VyOS verify() function to test this
+
+ prefix = '100.64.0.0/10'
+ prefix_list = 'duplicates'
+ test_range = range(20, 25)
+ path = base_path + ['prefix-list', prefix_list]
+
+ for rule in test_range:
+ self.cli_set(path + ['rule', str(rule), 'action', 'permit'])
+ self.cli_set(path + ['rule', str(rule), 'prefix', prefix])
+
+ # Duplicate prefixes
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ for rule in test_range:
+ self.cli_set(path + ['rule', str(rule), 'le', str(rule)])
+
+ self.cli_commit()
+
+ config = self.getFRRconfig('ip prefix-list', end='')
+ for rule in test_range:
+ tmp = f'ip prefix-list {prefix_list} seq {rule} permit {prefix} le {rule}'
+ self.assertIn(tmp, config)
+
def test_route_map(self):
access_list = '50'
as_path_list = '100'
@@ -766,27 +800,28 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
'10' : {
'action' : 'deny',
'set' : {
- 'aggregator-as' : '1234567890',
- 'aggregator-ip' : '10.255.255.0',
- 'as-path-exclude' : '1234',
- 'as-path-prepend' : '1234567890 987654321',
- 'atomic-aggregate' : '',
- 'distance' : '110',
- 'extcommunity-bw' : '20000',
- 'extcommunity-rt' : '123:456',
- 'extcommunity-soo' : '456:789',
- 'ipv6-next-hop-global': '2001::1',
- 'ipv6-next-hop-local' : 'fe80::1',
- 'ip-next-hop' : '192.168.1.1',
- 'large-community' : '100:200:300',
- 'local-preference' : '500',
- 'metric' : '150',
- 'metric-type' : 'type-1',
- 'origin' : 'incomplete',
- 'originator-id' : '172.16.10.1',
- 'src' : '100.0.0.1',
- 'tag' : '65530',
- 'weight' : '2',
+ 'aggregator-as' : '1234567890',
+ 'aggregator-ip' : '10.255.255.0',
+ 'as-path-exclude' : '1234',
+ 'as-path-prepend' : '1234567890 987654321',
+ 'as-path-prepend-last-as' : '5',
+ 'atomic-aggregate' : '',
+ 'distance' : '110',
+ 'extcommunity-bw' : '20000',
+ 'extcommunity-rt' : '123:456',
+ 'extcommunity-soo' : '456:789',
+ 'ipv6-next-hop-global' : '2001::1',
+ 'ipv6-next-hop-local' : 'fe80::1',
+ 'ip-next-hop' : '192.168.1.1',
+ 'large-community' : '100:200:300',
+ 'local-preference' : '500',
+ 'metric' : '150',
+ 'metric-type' : 'type-1',
+ 'origin' : 'incomplete',
+ 'originator-id' : '172.16.10.1',
+ 'src' : '100.0.0.1',
+ 'tag' : '65530',
+ 'weight' : '2',
},
},
},
@@ -814,6 +849,13 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
'evpn-vni' : '1234',
},
},
+ '20' : {
+ 'action' : 'permit',
+ 'set' : {
+ 'evpn-gateway-ipv4' : '192.0.2.99',
+ 'evpn-gateway-ipv6' : '2001:db8:f00::1',
+ },
+ },
},
},
}
@@ -924,9 +966,9 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
if 'aggregator-ip' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'aggregator', 'ip', rule_config['set']['aggregator-ip']])
if 'as-path-exclude' in rule_config['set']:
- self.cli_set(path + ['rule', rule, 'set', 'as-path-exclude', rule_config['set']['as-path-exclude']])
+ self.cli_set(path + ['rule', rule, 'set', 'as-path', 'exclude', rule_config['set']['as-path-exclude']])
if 'as-path-prepend' in rule_config['set']:
- self.cli_set(path + ['rule', rule, 'set', 'as-path-prepend', rule_config['set']['as-path-prepend']])
+ self.cli_set(path + ['rule', rule, 'set', 'as-path', 'prepend', rule_config['set']['as-path-prepend']])
if 'atomic-aggregate' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'atomic-aggregate'])
if 'distance' in rule_config['set']:
@@ -961,6 +1003,10 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_set(path + ['rule', rule, 'set', 'tag', rule_config['set']['tag']])
if 'weight' in rule_config['set']:
self.cli_set(path + ['rule', rule, 'set', 'weight', rule_config['set']['weight']])
+ if 'evpn-gateway-ipv4' in rule_config['set']:
+ self.cli_set(path + ['rule', rule, 'set', 'evpn', 'gateway', 'ipv4', rule_config['set']['evpn-gateway-ipv4']])
+ if 'evpn-gateway-ipv6' in rule_config['set']:
+ self.cli_set(path + ['rule', rule, 'set', 'evpn', 'gateway', 'ipv6', rule_config['set']['evpn-gateway-ipv6']])
self.cli_commit()
@@ -1030,7 +1076,7 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
tmp = f'match ipv6 address prefix-list {rule_config["match"]["ipv6-address-pfx"]}'
self.assertIn(tmp, config)
if 'ipv6-nexthop' in rule_config['match']:
- tmp = f'match ipv6 next-hop {rule_config["match"]["ipv6-nexthop"]}'
+ tmp = f'match ipv6 next-hop address {rule_config["match"]["ipv6-nexthop"]}'
self.assertIn(tmp, config)
if 'large-community' in rule_config['match']:
tmp = f'match large-community {rule_config["match"]["large-community"]}'
@@ -1084,6 +1130,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
tmp += 'as-path exclude ' + rule_config['set']['as-path-exclude']
elif 'as-path-prepend' in rule_config['set']:
tmp += 'as-path prepend ' + rule_config['set']['as-path-prepend']
+ elif 'as-path-prepend-last-as' in rule_config['set']:
+ tmp += 'as-path prepend last-as' + rule_config['set']['as-path-prepend-last-as']
elif 'atomic-aggregate' in rule_config['set']:
tmp += 'atomic-aggregate'
elif 'distance' in rule_config['set']:
@@ -1118,6 +1166,10 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
tmp += 'tag ' + rule_config['set']['tag']
elif 'weight' in rule_config['set']:
tmp += 'weight ' + rule_config['set']['weight']
+ elif 'vpn-gateway-ipv4' in rule_config['set']:
+ tmp += 'evpn gateway ipv4 ' + rule_config['set']['vpn-gateway-ipv4']
+ elif 'vpn-gateway-ipv6' in rule_config['set']:
+ tmp += 'evpn gateway ipv6 ' + rule_config['set']['vpn-gateway-ipv6']
self.assertIn(tmp, config)
@@ -1135,18 +1187,13 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- # Check generated configuration
-
- # Expected values
original = """
50: from 203.0.113.1 lookup 23
50: from 203.0.113.2 lookup 23
"""
tmp = cmd('ip rule show prio 50')
- original = original.split()
- tmp = tmp.split()
- self.assertEqual(tmp, original)
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for fwmark
def test_fwmark_table_id(self):
@@ -1161,17 +1208,32 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- # Check generated configuration
-
- # Expected values
original = """
101: from all fwmark 0x18 lookup 154
"""
tmp = cmd('ip rule show prio 101')
- original = original.split()
- tmp = tmp.split()
- self.assertEqual(tmp, original)
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for destination
+ def test_destination_table_id(self):
+ path = base_path + ['local-route']
+
+ dst = '203.0.113.1'
+ rule = '102'
+ table = '154'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+
+ self.cli_commit()
+
+ original = """
+ 102: from all to 203.0.113.1 lookup 154
+ """
+ tmp = cmd('ip rule show prio 102')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
# Test set table for sources with fwmark
def test_fwmark_sources_table_id(self):
@@ -1188,18 +1250,301 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
- # Check generated configuration
-
- # Expected values
original = """
100: from 203.0.113.11 fwmark 0x17 lookup 150
100: from 203.0.113.12 fwmark 0x17 lookup 150
"""
tmp = cmd('ip rule show prio 100')
- original = original.split()
- tmp = tmp.split()
- self.assertEqual(tmp, original)
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources with iif
+ def test_iif_sources_table_id(self):
+ path = base_path + ['local-route']
+
+ sources = ['203.0.113.11', '203.0.113.12']
+ iif = 'lo'
+ rule = '100'
+ table = '150'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'source', src])
+
+ self.cli_commit()
+
+ # Check generated configuration
+ # Expected values
+ original = """
+ 100: from 203.0.113.11 iif lo lookup 150
+ 100: from 203.0.113.12 iif lo lookup 150
+ """
+ tmp = cmd('ip rule show prio 100')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources and destinations with fwmark
+ def test_fwmark_sources_destination_table_id(self):
+ path = base_path + ['local-route']
+
+ sources = ['203.0.113.11', '203.0.113.12']
+ destinations = ['203.0.113.13', '203.0.113.15']
+ fwmk = '23'
+ rule = '103'
+ table = '150'
+ for src in sources:
+ for dst in destinations:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 103: from 203.0.113.11 to 203.0.113.13 fwmark 0x17 lookup 150
+ 103: from 203.0.113.11 to 203.0.113.15 fwmark 0x17 lookup 150
+ 103: from 203.0.113.12 to 203.0.113.13 fwmark 0x17 lookup 150
+ 103: from 203.0.113.12 to 203.0.113.15 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table ipv6 for some sources ipv6
+ def test_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:123::/48', '2001:db8:126::/48']
+ rule = '50'
+ table = '23'
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+
+ self.cli_commit()
+
+ original = """
+ 50: from 2001:db8:123::/48 lookup 23
+ 50: from 2001:db8:126::/48 lookup 23
+ """
+ tmp = cmd('ip -6 rule show prio 50')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for fwmark ipv6
+ def test_fwmark_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ fwmk = '24'
+ rule = '100'
+ table = '154'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 100: from all fwmark 0x18 lookup 154
+ """
+ tmp = cmd('ip -6 rule show prio 100')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for destination ipv6
+ def test_destination_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ dst = '2001:db8:1337::/126'
+ rule = '101'
+ table = '154'
+
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+
+ self.cli_commit()
+
+ original = """
+ 101: from all to 2001:db8:1337::/126 lookup 154
+ """
+ tmp = cmd('ip -6 rule show prio 101')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources with fwmark ipv6
+ def test_fwmark_sources_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:1338::/126', '2001:db8:1339::/126']
+ fwmk = '23'
+ rule = '102'
+ table = '150'
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 102: from 2001:db8:1338::/126 fwmark 0x17 lookup 150
+ 102: from 2001:db8:1339::/126 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip -6 rule show prio 102')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources with iif ipv6
+ def test_iif_sources_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:1338::/126', '2001:db8:1339::/126']
+ iif = 'lo'
+ rule = '102'
+ table = '150'
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'inbound-interface', iif])
+
+ self.cli_commit()
+
+ # Check generated configuration
+ # Expected values
+ original = """
+ 102: from 2001:db8:1338::/126 iif lo lookup 150
+ 102: from 2001:db8:1339::/126 iif lo lookup 150
+ """
+ tmp = cmd('ip -6 rule show prio 102')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test set table for sources and destinations with fwmark ipv6
+ def test_fwmark_sources_destination_ipv6_table_id(self):
+ path = base_path + ['local-route6']
+
+ sources = ['2001:db8:1338::/126', '2001:db8:1339::/56']
+ destinations = ['2001:db8:13::/48', '2001:db8:16::/48']
+ fwmk = '23'
+ rule = '103'
+ table = '150'
+ for src in sources:
+ for dst in destinations:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1338::/126 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip -6 rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+
+ # Test delete table for sources and destination with fwmark ipv4/ipv6
+ def test_delete_ipv4_ipv6_table_id(self):
+ path = base_path + ['local-route']
+ path_v6 = base_path + ['local-route6']
+
+ sources = ['203.0.113.0/24', '203.0.114.5']
+ destinations = ['203.0.112.0/24', '203.0.116.5']
+ sources_v6 = ['2001:db8:1338::/126', '2001:db8:1339::/56']
+ destinations_v6 = ['2001:db8:13::/48', '2001:db8:16::/48']
+ fwmk = '23'
+ rule = '103'
+ table = '150'
+ for src in sources:
+ for dst in destinations:
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path + ['rule', rule, 'source', src])
+ self.cli_set(path + ['rule', rule, 'destination', dst])
+ self.cli_set(path + ['rule', rule, 'fwmark', fwmk])
+
+ for src in sources_v6:
+ for dst in destinations_v6:
+ self.cli_set(path_v6 + ['rule', rule, 'set', 'table', table])
+ self.cli_set(path_v6 + ['rule', rule, 'source', src])
+ self.cli_set(path_v6 + ['rule', rule, 'destination', dst])
+ self.cli_set(path_v6 + ['rule', rule, 'fwmark', fwmk])
+
+ self.cli_commit()
+
+ original = """
+ 103: from 203.0.113.0/24 to 203.0.116.5 fwmark 0x17 lookup 150
+ 103: from 203.0.114.5 to 203.0.112.0/24 fwmark 0x17 lookup 150
+ 103: from 203.0.114.5 to 203.0.116.5 fwmark 0x17 lookup 150
+ 103: from 203.0.113.0/24 to 203.0.112.0/24 fwmark 0x17 lookup 150
+ """
+ original_v6 = """
+ 103: from 2001:db8:1338::/126 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1339::/56 to 2001:db8:16::/48 fwmark 0x17 lookup 150
+ 103: from 2001:db8:1338::/126 to 2001:db8:13::/48 fwmark 0x17 lookup 150
+ """
+ tmp = cmd('ip rule show prio 103')
+ tmp_v6 = cmd('ip -6 rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original))
+ self.assertEqual(sort_ip(tmp_v6), sort_ip(original_v6))
+
+ self.cli_delete(path)
+ self.cli_delete(path_v6)
+ self.cli_commit()
+
+ tmp = cmd('ip rule show prio 103')
+ tmp_v6 = cmd('ip -6 rule show prio 103')
+
+ self.assertEqual(sort_ip(tmp), [])
+ self.assertEqual(sort_ip(tmp_v6), [])
+
+ # Test multiple commits ipv4
+ def test_multiple_commit_ipv4_table_id(self):
+ path = base_path + ['local-route']
+
+ sources = ['192.0.2.1', '192.0.2.2']
+ destination = '203.0.113.25'
+ rule = '105'
+ table = '151'
+ self.cli_set(path + ['rule', rule, 'set', 'table', table])
+ for src in sources:
+ self.cli_set(path + ['rule', rule, 'source', src])
+
+ self.cli_commit()
+
+ original_first = """
+ 105: from 192.0.2.1 lookup 151
+ 105: from 192.0.2.2 lookup 151
+ """
+ tmp = cmd('ip rule show prio 105')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original_first))
+
+ # Create second commit with added destination
+ self.cli_set(path + ['rule', rule, 'destination', destination])
+ self.cli_commit()
+
+ original_second = """
+ 105: from 192.0.2.1 to 203.0.113.25 lookup 151
+ 105: from 192.0.2.2 to 203.0.113.25 lookup 151
+ """
+ tmp = cmd('ip rule show prio 105')
+
+ self.assertEqual(sort_ip(tmp), sort_ip(original_second))
+
+
+def sort_ip(output):
+ o = '\n'.join([' '.join(line.strip().split()) for line in output.strip().splitlines()])
+ o = o.splitlines()
+ o.sort()
+ return o
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py
new file mode 100755
index 000000000..e2d70f289
--- /dev/null
+++ b/smoketest/scripts/cli/test_policy_route.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.util import cmd
+
+mark = '100'
+table_mark_offset = 0x7fffffff
+table_id = '101'
+interface = 'eth0'
+interface_ip = '172.16.10.1/24'
+
+class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestPolicyRoute, cls).setUpClass()
+
+ 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])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip])
+ cls.cli_delete(cls, ['protocols', 'static', 'table', table_id])
+
+ super(TestPolicyRoute, cls).tearDownClass()
+
+ def tearDown(self):
+ self.cli_delete(['interfaces', 'ethernet', interface, 'policy'])
+ self.cli_delete(['policy', 'route'])
+ self.cli_delete(['policy', 'route6'])
+ self.cli_commit()
+
+ def test_pbr_mark(self):
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'source', 'address', '172.16.20.10'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'mark', mark])
+
+ self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])
+
+ self.cli_commit()
+
+ mark_hex = "{0:#010x}".format(int(mark))
+
+ nftables_search = [
+ [f'iifname "{interface}"','jump VYOS_PBR_smoketest'],
+ ['ip daddr 172.16.10.10', 'ip saddr 172.16.20.10', 'meta mark set ' + mark_hex],
+ ]
+
+ nftables_output = cmd('sudo nft list table ip mangle')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
+ def test_pbr_table(self):
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'protocol', 'tcp'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'port', '8888'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'syn'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'tcp', 'flags', 'not', 'ack'])
+ self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'table', table_id])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'protocol', 'tcp_udp'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'destination', 'port', '8888'])
+ self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'set', 'table', table_id])
+
+ self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])
+ self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route6', 'smoketest6'])
+
+ self.cli_commit()
+
+ mark_hex = "{0:#010x}".format(table_mark_offset - int(table_id))
+
+ # IPv4
+
+ nftables_search = [
+ [f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'],
+ ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'meta mark set ' + mark_hex]
+ ]
+
+ nftables_output = cmd('sudo nft list table ip mangle')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
+ # IPv6
+
+ nftables6_search = [
+ [f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'],
+ ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'meta mark set ' + mark_hex]
+ ]
+
+ nftables6_output = cmd('sudo nft list table ip6 mangle')
+
+ for search in nftables6_search:
+ matched = False
+ for line in nftables6_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
+ # IP rule fwmark -> table
+
+ ip_rule_search = [
+ ['fwmark ' + hex(table_mark_offset - int(table_id)), 'lookup ' + table_id]
+ ]
+
+ ip_rule_output = cmd('ip rule show')
+
+ for search in ip_rule_search:
+ matched = False
+ for line in ip_rule_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_bfd.py b/smoketest/scripts/cli/test_protocols_bfd.py
index 297398d3c..fdc254a05 100755
--- a/smoketest/scripts/cli/test_protocols_bfd.py
+++ b/smoketest/scripts/cli/test_protocols_bfd.py
@@ -24,30 +24,35 @@ PROCESS_NAME = 'bfdd'
base_path = ['protocols', 'bfd']
dum_if = 'dum1001'
+vrf_name = 'red'
peers = {
'192.0.2.10' : {
'intv_rx' : '500',
'intv_tx' : '600',
'multihop' : '',
'source_addr': '192.0.2.254',
- },
+ 'profile' : 'foo-bar-baz',
+ },
'192.0.2.20' : {
'echo_mode' : '',
'intv_echo' : '100',
'intv_mult' : '100',
'intv_rx' : '222',
'intv_tx' : '333',
+ 'passive' : '',
'shutdown' : '',
+ 'profile' : 'foo',
'source_intf': dum_if,
- },
- '2001:db8::a' : {
+ },
+ '2001:db8::1000:1' : {
'source_addr': '2001:db8::1',
- 'source_intf': dum_if,
- },
- '2001:db8::b' : {
+ 'vrf' : vrf_name,
+ },
+ '2001:db8::2000:1' : {
'source_addr': '2001:db8::1',
'multihop' : '',
- },
+ 'profile' : 'baz_foo',
+ },
}
profiles = {
@@ -59,9 +64,15 @@ profiles = {
'intv_tx' : '333',
'shutdown' : '',
},
- 'bar' : {
+ 'foo-bar-baz' : {
+ 'intv_mult' : '4',
+ 'intv_rx' : '400',
+ 'intv_tx' : '400',
+ },
+ 'baz_foo' : {
'intv_mult' : '102',
'intv_rx' : '444',
+ 'passive' : '',
},
}
@@ -73,6 +84,8 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.assertTrue(process_named_running(PROCESS_NAME))
def test_bfd_peer(self):
+ self.cli_set(['vrf', 'name', vrf_name, 'table', '1000'])
+
for peer, peer_config in peers.items():
if 'echo_mode' in peer_config:
self.cli_set(base_path + ['peer', peer, 'echo-mode'])
@@ -86,18 +99,22 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer', peer, 'interval', 'transmit', peer_config["intv_tx"]])
if 'multihop' in peer_config:
self.cli_set(base_path + ['peer', peer, 'multihop'])
+ if 'passive' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'passive'])
if 'shutdown' in peer_config:
self.cli_set(base_path + ['peer', peer, 'shutdown'])
if 'source_addr' in peer_config:
self.cli_set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]])
if 'source_intf' in peer_config:
self.cli_set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]])
+ if 'vrf' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'vrf', peer_config["vrf"]])
# commit changes
self.cli_commit()
# Verify FRR bgpd configuration
- frrconfig = self.getFRRconfig('bfd')
+ frrconfig = self.getFRRconfig('bfd', daemon=PROCESS_NAME)
for peer, peer_config in peers.items():
tmp = f'peer {peer}'
if 'multihop' in peer_config:
@@ -106,28 +123,33 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
tmp += f' local-address {peer_config["source_addr"]}'
if 'source_intf' in peer_config:
tmp += f' interface {peer_config["source_intf"]}'
+ if 'vrf' in peer_config:
+ tmp += f' vrf {peer_config["vrf"]}'
self.assertIn(tmp, frrconfig)
- peerconfig = self.getFRRconfig(f' peer {peer}', end='')
+ peerconfig = self.getFRRconfig(f' peer {peer}', end='', daemon=PROCESS_NAME)
if 'echo_mode' in peer_config:
self.assertIn(f'echo-mode', peerconfig)
if 'intv_echo' in peer_config:
- self.assertIn(f'echo-interval {peer_config["intv_echo"]}', peerconfig)
+ self.assertIn(f'echo receive-interval {peer_config["intv_echo"]}', peerconfig)
+ self.assertIn(f'echo transmit-interval {peer_config["intv_echo"]}', peerconfig)
if 'intv_mult' in peer_config:
self.assertIn(f'detect-multiplier {peer_config["intv_mult"]}', peerconfig)
if 'intv_rx' in peer_config:
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 'passive' in peer_config:
+ self.assertIn(f'passive-mode', peerconfig)
if 'shutdown' in peer_config:
self.assertIn(f'shutdown', peerconfig)
else:
self.assertNotIn(f'shutdown', peerconfig)
- def test_bfd_profile(self):
- peer = '192.0.2.10'
+ self.cli_delete(['vrf', 'name', vrf_name])
+ def test_bfd_profile(self):
for profile, profile_config in profiles.items():
if 'echo_mode' in profile_config:
self.cli_set(base_path + ['profile', profile, 'echo-mode'])
@@ -139,10 +161,25 @@ 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 'passive' in profile_config:
+ self.cli_set(base_path + ['profile', profile, 'passive'])
if 'shutdown' in profile_config:
self.cli_set(base_path + ['profile', profile, 'shutdown'])
- self.cli_set(base_path + ['peer', peer, 'profile', list(profiles)[0]])
+ for peer, peer_config in peers.items():
+ if 'profile' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'profile', peer_config["profile"] + 'wrong'])
+ if 'source_addr' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'source', 'address', peer_config["source_addr"]])
+ if 'source_intf' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'source', 'interface', peer_config["source_intf"]])
+
+ # BFD profile does not exist!
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for peer, peer_config in peers.items():
+ if 'profile' in peer_config:
+ self.cli_set(base_path + ['peer', peer, 'profile', peer_config["profile"]])
# commit changes
self.cli_commit()
@@ -151,19 +188,27 @@ class TestProtocolsBFD(VyOSUnitTestSHIM.TestCase):
for profile, profile_config in profiles.items():
config = self.getFRRconfig(f' profile {profile}', endsection='^ !')
if 'echo_mode' in profile_config:
- self.assertIn(f'echo-mode', config)
+ self.assertIn(f' echo-mode', config)
if 'intv_echo' in profile_config:
- self.assertIn(f'echo-interval {profile_config["intv_echo"]}', config)
+ self.assertIn(f' echo receive-interval {profile_config["intv_echo"]}', config)
+ self.assertIn(f' echo transmit-interval {profile_config["intv_echo"]}', config)
if 'intv_mult' in profile_config:
- self.assertIn(f'detect-multiplier {profile_config["intv_mult"]}', config)
+ self.assertIn(f' detect-multiplier {profile_config["intv_mult"]}', config)
if 'intv_rx' in profile_config:
- self.assertIn(f'receive-interval {profile_config["intv_rx"]}', config)
+ 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)
+ self.assertIn(f' transmit-interval {profile_config["intv_tx"]}', config)
+ if 'passive' in profile_config:
+ self.assertIn(f' passive-mode', config)
if 'shutdown' in profile_config:
- self.assertIn(f'shutdown', config)
+ self.assertIn(f' shutdown', config)
else:
self.assertNotIn(f'shutdown', config)
+ for peer, peer_config in peers.items():
+ peerconfig = self.getFRRconfig(f' peer {peer}', end='', daemon=PROCESS_NAME)
+ if 'profile' in peer_config:
+ self.assertIn(f' profile {peer_config["profile"]}', peerconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index 16284ed01..9c0c93779 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -32,119 +32,154 @@ prefix_list_in = 'pfx-foo-in'
prefix_list_out = 'pfx-foo-out'
prefix_list_in6 = 'pfx-foo-in6'
prefix_list_out6 = 'pfx-foo-out6'
+bfd_profile = 'foo-bar-baz'
neighbor_config = {
'192.0.2.1' : {
- 'cap_dynamic' : '',
- 'cap_ext_next' : '',
- 'remote_as' : '100',
- 'adv_interv' : '400',
- 'passive' : '',
- 'password' : 'VyOS-Secure123',
- 'shutdown' : '',
- 'cap_over' : '',
- 'ttl_security' : '5',
- 'local_as' : '300',
- 'route_map_in' : route_map_in,
- 'route_map_out': route_map_out,
+ 'bfd' : '',
+ 'cap_dynamic' : '',
+ 'cap_ext_next' : '',
+ 'remote_as' : '100',
+ 'adv_interv' : '400',
+ 'passive' : '',
+ 'password' : 'VyOS-Secure123',
+ 'shutdown' : '',
+ 'cap_over' : '',
+ 'ttl_security' : '5',
+ 'local_as' : '300',
+ 'route_map_in' : route_map_in,
+ 'route_map_out' : route_map_out,
'no_send_comm_ext' : '',
- 'addpath_all' : '',
+ 'addpath_all' : '',
},
'192.0.2.2' : {
- 'remote_as' : '200',
- 'shutdown' : '',
- 'no_cap_nego' : '',
- 'port' : '667',
- 'cap_strict' : '',
- 'pfx_list_in' : prefix_list_in,
- 'pfx_list_out' : prefix_list_out,
+ 'bfd_profile' : bfd_profile,
+ 'remote_as' : '200',
+ 'shutdown' : '',
+ 'no_cap_nego' : '',
+ 'port' : '667',
+ 'cap_strict' : '',
+ 'advertise_map' : route_map_in,
+ 'non_exist_map' : route_map_out,
+ 'pfx_list_in' : prefix_list_in,
+ 'pfx_list_out' : prefix_list_out,
'no_send_comm_std' : '',
},
'192.0.2.3' : {
- 'description' : 'foo bar baz',
- 'remote_as' : '200',
- 'passive' : '',
- 'multi_hop' : '5',
- 'update_src' : 'lo',
+ 'advertise_map' : route_map_in,
+ 'description' : 'foo bar baz',
+ 'remote_as' : '200',
+ 'passive' : '',
+ 'multi_hop' : '5',
+ 'update_src' : 'lo',
+ 'peer_group' : 'foo',
+ 'graceful_rst' : '',
},
'2001:db8::1' : {
- 'cap_dynamic' : '',
- 'cap_ext_next' : '',
- 'remote_as' : '123',
- 'adv_interv' : '400',
- 'passive' : '',
- 'password' : 'VyOS-Secure123',
- 'shutdown' : '',
- 'cap_over' : '',
- 'ttl_security' : '5',
- 'local_as' : '300',
- 'solo' : '',
- 'route_map_in' : route_map_in,
- 'route_map_out': route_map_out,
+ 'advertise_map' : route_map_in,
+ 'exist_map' : route_map_out,
+ 'cap_dynamic' : '',
+ 'cap_ext_next' : '',
+ 'remote_as' : '123',
+ 'adv_interv' : '400',
+ 'passive' : '',
+ 'password' : 'VyOS-Secure123',
+ 'shutdown' : '',
+ 'cap_over' : '',
+ 'ttl_security' : '5',
+ 'local_as' : '300',
+ 'solo' : '',
+ 'route_map_in' : route_map_in,
+ 'route_map_out' : route_map_out,
'no_send_comm_std' : '',
'addpath_per_as' : '',
+ 'peer_group' : 'foo-bar',
},
'2001:db8::2' : {
- 'remote_as' : '456',
- 'shutdown' : '',
- 'no_cap_nego' : '',
- 'port' : '667',
- 'cap_strict' : '',
- 'pfx_list_in' : prefix_list_in6,
- 'pfx_list_out' : prefix_list_out6,
+ 'remote_as' : '456',
+ 'shutdown' : '',
+ 'no_cap_nego' : '',
+ 'port' : '667',
+ 'cap_strict' : '',
+ 'pfx_list_in' : prefix_list_in6,
+ 'pfx_list_out' : prefix_list_out6,
'no_send_comm_ext' : '',
+ 'peer_group' : 'foo-bar_baz',
+ 'graceful_rst_hlp' : ''
},
}
peer_group_config = {
'foo' : {
- 'remote_as' : '100',
- 'passive' : '',
- 'password' : 'VyOS-Secure123',
- 'shutdown' : '',
- 'cap_over' : '',
- 'ttl_security': '5',
+ 'advertise_map' : route_map_in,
+ 'exist_map' : route_map_out,
+ 'bfd' : '',
+ 'remote_as' : '100',
+ 'passive' : '',
+ 'password' : 'VyOS-Secure123',
+ 'shutdown' : '',
+ 'cap_over' : '',
+ 'ttl_security' : '5',
},
'bar' : {
- 'description' : 'foo peer bar group',
- 'remote_as' : '200',
- 'shutdown' : '',
- 'no_cap_nego' : '',
- 'local_as' : '300',
- 'pfx_list_in' : prefix_list_in,
- 'pfx_list_out' : prefix_list_out,
+ 'remote_as' : '111',
+ 'graceful_rst_no' : ''
+ },
+ 'foo-bar' : {
+ 'advertise_map' : route_map_in,
+ 'description' : 'foo peer bar group',
+ 'remote_as' : '200',
+ 'shutdown' : '',
+ 'no_cap_nego' : '',
+ 'local_as' : '300',
+ 'pfx_list_in' : prefix_list_in,
+ 'pfx_list_out' : prefix_list_out,
'no_send_comm_ext' : '',
},
- 'baz' : {
- 'cap_dynamic' : '',
- 'cap_ext_next' : '',
- 'remote_as' : '200',
- 'passive' : '',
- 'multi_hop' : '5',
- 'update_src' : 'lo',
- 'route_map_in' : route_map_in,
- 'route_map_out': route_map_out,
+ 'foo-bar_baz' : {
+ 'advertise_map' : route_map_in,
+ 'non_exist_map' : route_map_out,
+ 'bfd_profile' : bfd_profile,
+ 'cap_dynamic' : '',
+ 'cap_ext_next' : '',
+ 'remote_as' : '200',
+ 'passive' : '',
+ 'multi_hop' : '5',
+ 'update_src' : 'lo',
+ 'route_map_in' : route_map_in,
+ 'route_map_out' : route_map_out,
},
}
class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25'])
- self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25'])
-
- self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny'])
- self.cli_set(['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64'])
+ @classmethod
+ def setUpClass(cls):
+ super(TestProtocolsBGP, 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, ['policy', 'route-map', route_map_in, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'route-map', route_map_out, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_in, 'rule', '10', 'prefix', '192.0.2.0/25'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list', prefix_list_out, 'rule', '10', 'prefix', '192.0.2.128/25'])
+
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'action', 'permit'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_in6, 'rule', '10', 'prefix', '2001:db8:1000::/64'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'action', 'deny'])
+ cls.cli_set(cls, ['policy', 'prefix-list6', prefix_list_out6, 'rule', '10', 'prefix', '2001:db8:2000::/64'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['policy'])
+ def setUp(self):
self.cli_set(base_path + ['local-as', ASN])
def tearDown(self):
- self.cli_delete(['policy'])
self.cli_delete(['vrf'])
self.cli_delete(base_path)
self.cli_commit()
@@ -154,6 +189,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def verify_frr_config(self, peer, peer_config, frrconfig):
# recurring patterns to verify for both a simple neighbor and a peer-group
+ if 'bfd' in peer_config:
+ self.assertIn(f' neighbor {peer} bfd', frrconfig)
+ if 'bfd_profile' in peer_config:
+ self.assertIn(f' neighbor {peer} bfd profile {peer_config["bfd_profile"]}', frrconfig)
+ self.assertIn(f' neighbor {peer} bfd check-control-plane-failure', frrconfig)
if 'cap_dynamic' in peer_config:
self.assertIn(f' neighbor {peer} capability dynamic', frrconfig)
if 'cap_ext_next' in peer_config:
@@ -198,7 +238,19 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig)
if 'addpath_per_as' in peer_config:
self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig)
-
+ if 'advertise_map' in peer_config:
+ base = f' neighbor {peer} advertise-map {peer_config["advertise_map"]}'
+ if 'exist_map' in peer_config:
+ base = f'{base} exist-map {peer_config["exist_map"]}'
+ if 'non_exist_map' in peer_config:
+ base = f'{base} non-exist-map {peer_config["non_exist_map"]}'
+ self.assertIn(base, frrconfig)
+ if 'graceful_rst' in peer_config:
+ self.assertIn(f' neighbor {peer} graceful-restart', frrconfig)
+ if 'graceful_rst_no' in peer_config:
+ self.assertIn(f' neighbor {peer} graceful-restart-disable', frrconfig)
+ if 'graceful_rst_hlp' in peer_config:
+ self.assertIn(f' neighbor {peer} graceful-restart-helper', frrconfig)
def test_bgp_01_simple(self):
router_id = '127.0.0.1'
@@ -208,6 +260,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
max_path_v4ibgp = '4'
max_path_v6 = '8'
max_path_v6ibgp = '16'
+ cond_adv_timer = '30'
+ min_hold_time = '2'
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['parameters', 'log-neighbor-changes'])
@@ -229,6 +283,14 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['parameters', 'bestpath', 'bandwidth', 'default-weight-for-missing'])
self.cli_set(base_path + ['parameters', 'bestpath', 'compare-routerid'])
+ self.cli_set(base_path + ['parameters', 'conditional-advertisement', 'timer', cond_adv_timer])
+ self.cli_set(base_path + ['parameters', 'fast-convergence'])
+ self.cli_set(base_path + ['parameters', 'minimum-holdtime', min_hold_time])
+ self.cli_set(base_path + ['parameters', 'no-suppress-duplicates'])
+ self.cli_set(base_path + ['parameters', 'reject-as-sets'])
+ self.cli_set(base_path + ['parameters', 'shutdown'])
+ self.cli_set(base_path + ['parameters', 'suppress-fib-pending'])
+
# 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])
@@ -244,12 +306,19 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' bgp router-id {router_id}', 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' bgp bestpath as-path multipath-relax', frrconfig)
self.assertIn(f' bgp bestpath bandwidth default-weight-for-missing', frrconfig)
self.assertIn(f' bgp bestpath compare-routerid', frrconfig)
+ self.assertIn(f' bgp minimum-holdtime {min_hold_time}', frrconfig)
+ self.assertIn(f' bgp reject-as-sets', frrconfig)
+ self.assertIn(f' bgp shutdown', frrconfig)
+ self.assertIn(f' bgp suppress-fib-pending', frrconfig)
self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig)
+ self.assertIn(f' no bgp suppress-duplicates', frrconfig)
afiv4_config = self.getFRRconfig(' address-family ipv4 unicast')
self.assertIn(f' maximum-paths {max_path_v4}', afiv4_config)
@@ -263,6 +332,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def test_bgp_02_neighbors(self):
# Test out individual neighbor configuration items, not all of them are
# also available to a peer-group!
+ self.cli_set(base_path + ['parameters', 'deterministic-med'])
+
for peer, peer_config in neighbor_config.items():
afi = 'ipv4-unicast'
if is_ipv6(peer):
@@ -270,6 +341,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
if 'adv_interv' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'advertisement-interval', peer_config["adv_interv"]])
+ if 'bfd' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'bfd'])
+ if 'bfd_profile' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'bfd', 'profile', peer_config["bfd_profile"]])
+ self.cli_set(base_path + ['neighbor', peer, 'bfd', 'check-control-plane-failure'])
if 'cap_dynamic' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'capability', 'dynamic'])
if 'cap_ext_next' in peer_config:
@@ -318,6 +394,26 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-all'])
if 'addpath_per_as' in peer_config:
self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'addpath-tx-per-as'])
+ if 'graceful_rst' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'enable'])
+ if 'graceful_rst_no' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'disable'])
+ if 'graceful_rst_hlp' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'graceful-restart', 'restart-helper'])
+
+ # Conditional advertisement
+ if 'advertise_map' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'advertise-map', peer_config["advertise_map"]])
+ # Either exist-map or non-exist-map needs to be specified
+ if 'exist_map' not in peer_config and 'non_exist_map' not in peer_config:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', route_map_in])
+
+ if 'exist_map' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'exist-map', peer_config["exist_map"]])
+ if 'non_exist_map' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'conditionally-advertise', 'non-exist-map', peer_config["non_exist_map"]])
# commit changes
self.cli_commit()
@@ -339,6 +435,11 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
def test_bgp_03_peer_groups(self):
# Test out individual peer-group configuration items
for peer_group, config in peer_group_config.items():
+ if 'bfd' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'bfd'])
+ if 'bfd_profile' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'profile', config["bfd_profile"]])
+ self.cli_set(base_path + ['peer-group', peer_group, 'bfd', 'check-control-plane-failure'])
if 'cap_dynamic' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'capability', 'dynamic'])
if 'cap_ext_next' in config:
@@ -381,6 +482,31 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-all'])
if 'addpath_per_as' in config:
self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'addpath-tx-per-as'])
+ if 'graceful_rst' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'enable'])
+ if 'graceful_rst_no' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'disable'])
+ if 'graceful_rst_hlp' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'restart-helper'])
+
+ # Conditional advertisement
+ if 'advertise_map' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'advertise-map', config["advertise_map"]])
+ # Either exist-map or non-exist-map needs to be specified
+ if 'exist_map' not in config and 'non_exist_map' not in config:
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', route_map_in])
+
+ if 'exist_map' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'exist-map', config["exist_map"]])
+ if 'non_exist_map' in config:
+ self.cli_set(base_path + ['peer-group', peer_group, 'address-family', 'ipv4-unicast', 'conditionally-advertise', 'non-exist-map', config["non_exist_map"]])
+
+ for peer, peer_config in neighbor_config.items():
+ if 'peer_group' in peer_config:
+ self.cli_set(base_path + ['neighbor', peer, 'peer-group', peer_config['peer_group']])
+
# commit changes
self.cli_commit()
@@ -393,6 +519,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
self.verify_frr_config(peer, peer_config, frrconfig)
+ for peer, peer_config in neighbor_config.items():
+ if 'peer_group' in peer_config:
+ self.assertIn(f' neighbor {peer} peer-group {peer_config["peer_group"]}', frrconfig)
+
def test_bgp_04_afi_ipv4(self):
networks = {
@@ -752,5 +882,44 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' rt vpn import {rt_import}', afi_config)
self.assertIn(f' exit-address-family', afi_config)
+ def test_bgp_14_remote_as_peer_group_override(self):
+ # Peer-group member cannot override remote-as of peer-group
+ remote_asn = str(int(ASN) + 150)
+ neighbor = '192.0.2.1'
+ peer_group = 'bar'
+ interface = 'eth0'
+
+ self.cli_set(base_path + ['local-as', ASN])
+ self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn])
+ self.cli_set(base_path + ['neighbor', neighbor, 'peer-group', peer_group])
+ self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', remote_asn])
+
+ # Peer-group member cannot override remote-as of peer-group
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['neighbor', neighbor, 'remote-as'])
+
+ # re-test with interface based peer-group
+ self.cli_set(base_path + ['neighbor', interface, 'interface', 'peer-group', peer_group])
+ self.cli_set(base_path + ['neighbor', interface, 'interface', 'remote-as', 'external'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['neighbor', interface, 'interface', 'remote-as'])
+
+ # re-test with interface based v6only peer-group
+ self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'peer-group', peer_group])
+ self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as', 'external'])
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as'])
+
+ self.cli_commit()
+
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ self.assertIn(f'router bgp {ASN}', frrconfig)
+ self.assertIn(f' neighbor {neighbor} peer-group {peer_group}', frrconfig)
+ self.assertIn(f' neighbor {peer_group} peer-group', frrconfig)
+ self.assertIn(f' neighbor {peer_group} remote-as {remote_asn}', frrconfig)
+
if __name__ == '__main__':
- unittest.main(verbosity=2) \ No newline at end of file
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py
index f4b0a690d..ee4be0b37 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 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -33,7 +33,11 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
cls._interfaces = Section.interfaces('ethernet')
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(TestProtocolsISIS, 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)
@@ -71,13 +75,13 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' log-adjacency-changes', tmp)
self.assertIn(f' redistribute ipv4 connected level-2 route-map {route_map}', tmp)
for interface in self._interfaces:
- tmp = self.getFRRconfig(f'interface {interface}')
+ tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
@@ -104,11 +108,11 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR isisd configuration
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f'router isis {domain}', tmp)
self.assertIn(f' net {net}', tmp)
- tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}')
+ tmp = self.getFRRconfig(f'router isis {domain} vrf {vrf}', daemon='isisd')
self.assertIn(f'router isis {domain} vrf {vrf}', tmp)
self.assertIn(f' net {net}', tmp)
@@ -124,22 +128,26 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.isis_base_config()
self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map])
self.cli_set(base_path + ['route-map', route_map])
+ self.cli_set(base_path + ['level', 'level-2'])
# commit changes
self.cli_commit()
# Verify FRR configuration
zebra_route_map = f'ip protocol isis route-map {route_map}'
- frrconfig = self.getFRRconfig(zebra_route_map)
+ frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra')
self.assertIn(zebra_route_map, frrconfig)
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
+ self.assertIn(' is-type level-2-only', tmp)
+
# Remove the route-map again
self.cli_delete(base_path + ['route-map'])
# commit changes
self.cli_commit()
# Verify FRR configuration
- frrconfig = self.getFRRconfig(zebra_route_map)
+ frrconfig = self.getFRRconfig(zebra_route_map, daemon='zebra')
self.assertNotIn(zebra_route_map, frrconfig)
self.cli_delete(['policy', 'route-map', route_map])
@@ -159,7 +167,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f' net {net}', tmp)
for afi in ['ipv4', 'ipv6']:
@@ -172,6 +180,8 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
password = 'foo'
self.isis_base_config()
+ for interface in self._interfaces:
+ self.cli_set(base_path + ['interface', interface, 'password', 'plaintext-password', f'{password}-{interface}'])
self.cli_set(base_path + ['area-password', 'plaintext-password', password])
self.cli_set(base_path + ['area-password', 'md5', password])
@@ -192,24 +202,28 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' domain-password clear {password}', tmp)
self.assertIn(f' area-password clear {password}', tmp)
+ for interface in self._interfaces:
+ tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
+ self.assertIn(f' isis password clear {password}-{interface}', tmp)
- def test_isis_06_spf_delay(self):
- self.isis_base_config()
-
+ def test_isis_06_spf_delay_bfd(self):
network = 'point-to-point'
holddown = '10'
init_delay = '50'
long_delay = '200'
short_delay = '100'
time_to_learn = '75'
+ bfd_profile = 'isis-bfd'
+ self.cli_set(base_path + ['net', net])
for interface in self._interfaces:
self.cli_set(base_path + ['interface', interface, 'network', network])
+ self.cli_set(base_path + ['interface', interface, 'bfd', 'profile', bfd_profile])
self.cli_set(base_path + ['spf-delay-ietf', 'holddown', holddown])
# verify() - All types of spf-delay must be configured
@@ -226,11 +240,6 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError):
self.cli_commit()
- self.cli_set(base_path + ['spf-delay-ietf', 'long-delay', long_delay])
- # verify() - All types of spf-delay must be configured
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
self.cli_set(base_path + ['spf-delay-ietf', 'short-delay', short_delay])
# verify() - All types of spf-delay must be configured
with self.assertRaises(ConfigSessionError):
@@ -241,15 +250,17 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify all changes
- tmp = self.getFRRconfig(f'router isis {domain}')
+ tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd')
self.assertIn(f' net {net}', tmp)
self.assertIn(f' spf-delay-ietf init-delay {init_delay} short-delay {short_delay} long-delay {long_delay} holddown {holddown} time-to-learn {time_to_learn}', tmp)
for interface in self._interfaces:
- tmp = self.getFRRconfig(f'interface {interface}')
+ tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd')
self.assertIn(f' ip router isis {domain}', tmp)
self.assertIn(f' ipv6 router isis {domain}', tmp)
self.assertIn(f' isis network {network}', tmp)
+ self.assertIn(f' isis bfd', tmp)
+ self.assertIn(f' isis bfd profile {bfd_profile}', tmp)
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
new file mode 100755
index 000000000..76e6ca35a
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_mpls.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.util import process_named_running
+
+PROCESS_NAME = 'ldpd'
+base_path = ['protocols', 'mpls', 'ldp']
+
+peers = {
+ '192.0.2.10' : {
+ 'intv_rx' : '500',
+ 'intv_tx' : '600',
+ 'multihop' : '',
+ 'source_addr': '192.0.2.254',
+ },
+ '192.0.2.20' : {
+ 'echo_mode' : '',
+ 'intv_echo' : '100',
+ 'intv_mult' : '100',
+ 'intv_rx' : '222',
+ 'intv_tx' : '333',
+ 'passive' : '',
+ 'shutdown' : '',
+ },
+ '2001:db8::a' : {
+ 'source_addr': '2001:db8::1',
+ },
+ '2001:db8::b' : {
+ 'source_addr': '2001:db8::1',
+ 'multihop' : '',
+ },
+}
+
+profiles = {
+ 'foo' : {
+ 'echo_mode' : '',
+ 'intv_echo' : '100',
+ 'intv_mult' : '101',
+ 'intv_rx' : '222',
+ 'intv_tx' : '333',
+ 'shutdown' : '',
+ },
+ 'bar' : {
+ 'intv_mult' : '102',
+ 'intv_rx' : '444',
+ 'passive' : '',
+ },
+}
+
+class TestProtocolsMPLS(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestProtocolsMPLS, 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()
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ def test_mpls_basic(self):
+ router_id = '1.2.3.4'
+ transport_ipv4_addr = '5.6.7.8'
+ interfaces = Section.interfaces('ethernet')
+
+ self.cli_set(base_path + ['router-id', router_id])
+
+ # At least one LDP interface must be configured
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in interfaces:
+ self.cli_set(base_path + ['interface', interface])
+
+ # LDP transport address missing
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['discovery', 'transport-ipv4-address', transport_ipv4_addr])
+
+ # Commit changes
+ self.cli_commit()
+
+ # Validate configuration
+ frrconfig = self.getFRRconfig('mpls ldp', daemon=PROCESS_NAME)
+ self.assertIn(f'mpls ldp', frrconfig)
+ self.assertIn(f' router-id {router_id}', frrconfig)
+
+ # Validate AFI IPv4
+ afiv4_config = self.getFRRconfig(' address-family ipv4', daemon=PROCESS_NAME)
+ self.assertIn(f' discovery transport-address {transport_ipv4_addr}', afiv4_config)
+ for interface in interfaces:
+ self.assertIn(f' interface {interface}', afiv4_config)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_nhrp.py b/smoketest/scripts/cli/test_protocols_nhrp.py
index aa0ac268d..40b19fec7 100755
--- a/smoketest/scripts/cli/test_protocols_nhrp.py
+++ b/smoketest/scripts/cli/test_protocols_nhrp.py
@@ -18,6 +18,7 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.firewall import find_nftables_rule
from vyos.util import call, process_named_running, read_file
tunnel_path = ['interfaces', 'tunnel']
@@ -91,6 +92,14 @@ class TestProtocolsNHRP(VyOSUnitTestSHIM.TestCase):
for line in opennhrp_lines:
self.assertIn(line, tmp_opennhrp_conf)
+ firewall_matches = [
+ 'ip protocol gre',
+ 'ip saddr 192.0.2.1',
+ 'ip daddr 224.0.0.0/4',
+ 'comment "VYOS_NHRP_tun100"'
+ ]
+
+ self.assertTrue(find_nftables_rule('ip filter', 'VYOS_FW_OUTPUT', firewall_matches) is not None)
self.assertTrue(process_named_running('opennhrp'))
if __name__ == '__main__':
diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py
index 0529eefbd..e15ea478b 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 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -33,14 +33,25 @@ route_map = 'foo-bar-baz10'
log = logging.getLogger('TestProtocolsOSPF')
class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
- self.cli_set(['policy', 'route-map', route_map, 'rule', '20', 'action', 'permit'])
+ @classmethod
+ def setUpClass(cls):
+ super(TestProtocolsOSPF, cls).setUpClass()
+
+ 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'])
+
+ # 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):
+ cls.cli_delete(cls, ['policy', 'route-map', route_map])
+ super(TestProtocolsOSPF, cls).tearDownClass()
def tearDown(self):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
- self.cli_delete(['policy', 'route-map', route_map])
self.cli_delete(base_path)
self.cli_commit()
@@ -189,31 +200,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
for neighbor in neighbors:
self.assertIn(f' neighbor {neighbor} priority {priority} poll-interval {poll_interval}', frrconfig) # default
-
- def test_ospf_07_passive_interface(self):
- self.cli_set(base_path + ['passive-interface', 'default'])
- interfaces = Section.interfaces('ethernet')
- for interface in interfaces:
- self.cli_set(base_path + ['passive-interface-exclude', interface])
-
- # commit changes
- self.cli_commit()
-
- # Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
- try:
- self.assertIn(f'router ospf', frrconfig)
- self.assertIn(f' passive-interface default', frrconfig) # default
- for interface in interfaces:
- self.assertIn(f' no passive-interface {interface}', frrconfig) # default
- except:
- log.debug(frrconfig)
- log.debug(cmd('sudo dmesg'))
- log.debug(cmd('sudo cat /var/log/messages'))
- log.debug(cmd('vtysh -c "show run"'))
- self.fail('Now we can hopefully see why OSPF fails!')
-
- def test_ospf_08_redistribute(self):
+ def test_ospf_07_redistribute(self):
metric = '15'
metric_type = '1'
redistribute = ['bgp', 'connected', 'isis', 'kernel', 'rip', 'static']
@@ -223,9 +210,15 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['redistribute', protocol, 'route-map', route_map])
self.cli_set(base_path + ['redistribute', protocol, 'metric-type', metric_type])
+ # enable FRR debugging to find the root cause of failing testcases
+ cmd('touch /tmp/vyos.frr.debug')
+
# commit changes
self.cli_commit()
+ # disable FRR debugging
+ cmd('rm -f /tmp/vyos.frr.debug')
+
# Verify FRR ospfd configuration
frrconfig = self.getFRRconfig('router ospf')
try:
@@ -234,11 +227,10 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' redistribute {protocol} metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
except:
log.debug(frrconfig)
- log.debug(cmd('sudo cat /var/log/messages'))
- log.debug(cmd('vtysh -c "show run"'))
+ log.debug(cmd('sudo cat /tmp/vyos-configd-script-stdout'))
self.fail('Now we can hopefully see why OSPF fails!')
- def test_ospf_09_virtual_link(self):
+ def test_ospf_08_virtual_link(self):
networks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
area = '10'
shortcut = 'enable'
@@ -268,22 +260,26 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' network {network} area {area}', frrconfig)
- def test_ospf_10_interface_configuration(self):
+ def test_ospf_09_interface_configuration(self):
interfaces = Section.interfaces('ethernet')
password = 'vyos1234'
bandwidth = '10000'
cost = '150'
network = 'point-to-point'
priority = '200'
+ bfd_profile = 'vyos-test'
+ self.cli_set(base_path + ['passive-interface', 'default'])
for interface in interfaces:
- self.cli_set(base_path + ['interface', interface, 'authentication', 'plaintext-password', password])
- self.cli_set(base_path + ['interface', interface, 'bandwidth', bandwidth])
- self.cli_set(base_path + ['interface', interface, 'bfd'])
- self.cli_set(base_path + ['interface', interface, 'cost', cost])
- self.cli_set(base_path + ['interface', interface, 'mtu-ignore'])
- self.cli_set(base_path + ['interface', interface, 'network', network])
- self.cli_set(base_path + ['interface', interface, 'priority', priority])
+ base_interface = base_path + ['interface', interface]
+ self.cli_set(base_interface + ['authentication', 'plaintext-password', password])
+ self.cli_set(base_interface + ['bandwidth', bandwidth])
+ self.cli_set(base_interface + ['bfd', 'profile', bfd_profile])
+ self.cli_set(base_interface + ['cost', cost])
+ self.cli_set(base_interface + ['mtu-ignore'])
+ self.cli_set(base_interface + ['network', network])
+ self.cli_set(base_interface + ['priority', priority])
+ self.cli_set(base_interface + ['passive', 'disable'])
# commit changes
self.cli_commit()
@@ -293,45 +289,15 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf authentication-key {password}', config)
self.assertIn(f' ip ospf bfd', config)
+ self.assertIn(f' ip ospf bfd profile {bfd_profile}', config)
self.assertIn(f' ip ospf cost {cost}', config)
self.assertIn(f' ip ospf mtu-ignore', config)
self.assertIn(f' ip ospf network {network}', config)
self.assertIn(f' ip ospf priority {priority}', config)
+ self.assertIn(f' no ip ospf passive', config)
self.assertIn(f' bandwidth {bandwidth}', config)
-
- def test_ospf_11_vrfs(self):
- # It is safe to assume that when the basic VRF test works, all
- # other OSPF related features work, as we entirely inherit the CLI
- # templates and Jinja2 FRR template.
- table = '1000'
- vrf = 'blue'
- vrf_base = ['vrf', 'name', vrf]
- vrf_iface = 'eth1'
- self.cli_set(vrf_base + ['table', table])
- self.cli_set(vrf_base + ['protocols', 'ospf', 'interface', vrf_iface])
- self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
-
- # Also set a default VRF OSPF config
- self.cli_set(base_path)
- self.cli_commit()
-
- # Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf')
- self.assertIn(f'router ospf', frrconfig)
- self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
- self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
-
- frrconfig = self.getFRRconfig(f'router ospf vrf {vrf}')
- self.assertIn(f'router ospf vrf {vrf}', frrconfig)
- self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
- self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
-
- self.cli_delete(['vrf', 'name', vrf])
- self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
-
-
- def test_ospf_12_zebra_route_map(self):
+ def test_ospf_10_zebra_route_map(self):
# Implemented because of T3328
self.cli_set(base_path + ['route-map', route_map])
# commit changes
@@ -351,7 +317,7 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
frrconfig = self.getFRRconfig(zebra_route_map)
self.assertNotIn(zebra_route_map, frrconfig)
- def test_ospf_13_interface_area(self):
+ def test_ospf_11_interface_area(self):
area = '0'
interfaces = Section.interfaces('ethernet')
@@ -375,6 +341,61 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'interface {interface}', config)
self.assertIn(f' ip ospf area {area}', config)
+ def test_ospf_12_vrfs(self):
+ # It is safe to assume that when the basic VRF test works, all
+ # other OSPF related features work, as we entirely inherit the CLI
+ # templates and Jinja2 FRR template.
+ table = '1000'
+ vrf = 'blue'
+ vrf_base = ['vrf', 'name', vrf]
+ vrf_iface = 'eth1'
+ self.cli_set(vrf_base + ['table', table])
+ self.cli_set(vrf_base + ['protocols', 'ospf', 'interface', vrf_iface])
+ self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
+
+ # Also set a default VRF OSPF config
+ self.cli_set(base_path)
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf')
+ self.assertIn(f'router ospf', frrconfig)
+ self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
+ self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
+
+ frrconfig = self.getFRRconfig(f'router ospf vrf {vrf}')
+ self.assertIn(f'router ospf vrf {vrf}', frrconfig)
+ self.assertIn(f' auto-cost reference-bandwidth 100', frrconfig)
+ self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults
+
+ # cleanup
+ self.cli_delete(['vrf', 'name', vrf])
+ self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
+
+ def test_ospf_13_export_list(self):
+ # Verify explort-list works on ospf-area
+ acl = '100'
+ seq = '10'
+ area = '0.0.0.10'
+ network = '10.0.0.0/8'
+
+
+ self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'action', 'permit'])
+ self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'source', 'any'])
+ self.cli_set(['policy', 'access-list', acl, 'rule', seq, 'destination', 'any'])
+ self.cli_set(base_path + ['area', area, 'network', network])
+ self.cli_set(base_path + ['area', area, 'export-list', acl])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf')
+ self.assertIn(f'router ospf', frrconfig)
+ self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # default
+ self.assertIn(f' network {network} area {area}', frrconfig)
+ self.assertIn(f' area {area} export-list {acl}', frrconfig)
+
if __name__ == '__main__':
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_ospfv3.py b/smoketest/scripts/cli/test_protocols_ospfv3.py
index c0673629e..fa80ad555 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 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -18,16 +18,35 @@ import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.util import process_named_running
PROCESS_NAME = 'ospf6d'
base_path = ['protocols', 'ospfv3']
+route_map = 'foo-bar-baz-0815'
+
router_id = '192.0.2.1'
default_area = '0'
class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestProtocolsOSPFv3, cls).setUpClass()
+
+ 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'])
+
+ # 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):
+ cls.cli_delete(cls, ['policy', 'route-map', route_map])
+ super(TestProtocolsOSPFv3, cls).tearDownClass()
+
def tearDown(self):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
@@ -49,13 +68,13 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
- self.cli_set(base_path + ['area', default_area, 'interface', interface])
+ self.cli_set(base_path + ['interface', interface, 'area', default_area])
# commit changes
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {default_area} range {prefix}', frrconfig)
self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
@@ -63,7 +82,8 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' area {default_area} export-list {acl_name}', frrconfig)
for interface in interfaces:
- self.assertIn(f' interface {interface} area {default_area}', frrconfig)
+ if_config = self.getFRRconfig(f'interface {interface}', daemon='ospf6d')
+ self.assertIn(f'ipv6 ospf6 area {default_area}', if_config)
self.cli_delete(['policy', 'access-list6', acl_name])
@@ -83,7 +103,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' distance {dist_global}', frrconfig)
self.assertIn(f' distance ospf6 intra-area {dist_intra_area} inter-area {dist_inter_area} external {dist_external}', frrconfig)
@@ -103,12 +123,14 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
for protocol in redistribute:
self.assertIn(f' redistribute {protocol} route-map {route_map}', frrconfig)
+
def test_ospfv3_04_interfaces(self):
+ bfd_profile = 'vyos-ipv6'
self.cli_set(base_path + ['parameters', 'router-id', router_id])
self.cli_set(base_path + ['area', default_area])
@@ -118,7 +140,7 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
interfaces = Section.interfaces('ethernet')
for interface in interfaces:
if_base = base_path + ['interface', interface]
- self.cli_set(if_base + ['bfd'])
+ self.cli_set(if_base + ['bfd', 'profile', bfd_profile])
self.cli_set(if_base + ['cost', cost])
self.cli_set(if_base + ['instance-id', '0'])
self.cli_set(if_base + ['mtu-ignore'])
@@ -132,15 +154,16 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
cost = '100'
priority = '10'
for interface in interfaces:
- if_config = self.getFRRconfig(f'interface {interface}')
+ if_config = self.getFRRconfig(f'interface {interface}', daemon='ospf6d')
self.assertIn(f'interface {interface}', if_config)
self.assertIn(f' ipv6 ospf6 bfd', if_config)
+ self.assertIn(f' ipv6 ospf6 bfd profile {bfd_profile}', if_config)
self.assertIn(f' ipv6 ospf6 cost {cost}', if_config)
self.assertIn(f' ipv6 ospf6 mtu-ignore', if_config)
self.assertIn(f' ipv6 ospf6 network point-to-point', if_config)
@@ -161,10 +184,102 @@ class TestProtocolsOSPFv3(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify FRR ospfd configuration
- frrconfig = self.getFRRconfig('router ospf6')
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
self.assertIn(f'router ospf6', frrconfig)
self.assertIn(f' area {area_stub} stub', frrconfig)
self.assertIn(f' area {area_stub_nosum} stub no-summary', frrconfig)
+
+ def test_ospfv3_06_area_nssa(self):
+ area_nssa = '1.1.1.1'
+ area_nssa_nosum = '2.2.2.2'
+ area_nssa_default = '3.3.3.3'
+
+ self.cli_set(base_path + ['area', area_nssa, 'area-type', 'nssa'])
+ self.cli_set(base_path + ['area', area_nssa, 'area-type', 'stub'])
+ # can only set one area-type per OSPFv3 area
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base_path + ['area', area_nssa, 'area-type', 'stub'])
+
+ self.cli_set(base_path + ['area', area_nssa_nosum, 'area-type', 'nssa', 'no-summary'])
+ self.cli_set(base_path + ['area', area_nssa_nosum, 'area-type', 'nssa', 'default-information-originate'])
+ self.cli_set(base_path + ['area', area_nssa_default, 'area-type', 'nssa', 'default-information-originate'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ self.assertIn(f'router ospf6', frrconfig)
+ self.assertIn(f' area {area_nssa} nssa', frrconfig)
+ self.assertIn(f' area {area_nssa_nosum} nssa default-information-originate no-summary', frrconfig)
+ self.assertIn(f' area {area_nssa_default} nssa default-information-originate', frrconfig)
+
+
+ def test_ospfv3_07_default_originate(self):
+ seq = '100'
+ metric = '50'
+ metric_type = '1'
+
+ self.cli_set(base_path + ['default-information', 'originate', 'metric', metric])
+ self.cli_set(base_path + ['default-information', 'originate', 'metric-type', metric_type])
+ self.cli_set(base_path + ['default-information', 'originate', 'route-map', route_map])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ self.assertIn(f'router ospf6', frrconfig)
+ self.assertIn(f' default-information originate metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
+
+ # Now set 'always'
+ self.cli_set(base_path + ['default-information', 'originate', 'always'])
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ self.assertIn(f' default-information originate always metric {metric} metric-type {metric_type} route-map {route_map}', frrconfig)
+
+
+ def test_ospfv3_08_vrfs(self):
+ # It is safe to assume that when the basic VRF test works, all
+ # other OSPF related features work, as we entirely inherit the CLI
+ # templates and Jinja2 FRR template.
+ table = '1000'
+ vrf = 'blue'
+ vrf_base = ['vrf', 'name', vrf]
+ vrf_iface = 'eth1'
+ router_id = '1.2.3.4'
+ router_id_vrf = '1.2.3.5'
+
+ self.cli_set(vrf_base + ['table', table])
+ self.cli_set(vrf_base + ['protocols', 'ospfv3', 'interface', vrf_iface, 'bfd'])
+ self.cli_set(vrf_base + ['protocols', 'ospfv3', 'parameters', 'router-id', router_id_vrf])
+
+ self.cli_set(['interfaces', 'ethernet', vrf_iface, 'vrf', vrf])
+
+ # Also set a default VRF OSPF config
+ self.cli_set(base_path + ['parameters', 'router-id', router_id])
+ self.cli_commit()
+
+ # Verify FRR ospfd configuration
+ frrconfig = self.getFRRconfig('router ospf6', daemon='ospf6d')
+ self.assertIn(f'router ospf6', frrconfig)
+ self.assertIn(f' ospf6 router-id {router_id}', frrconfig)
+
+ frrconfig = self.getFRRconfig(f'interface {vrf_iface}', daemon='ospf6d')
+ self.assertIn(f'interface {vrf_iface}', frrconfig)
+ self.assertIn(f' ipv6 ospf6 bfd', frrconfig)
+
+ frrconfig = self.getFRRconfig(f'router ospf6 vrf {vrf}', daemon='ospf6d')
+ self.assertIn(f'router ospf6 vrf {vrf}', frrconfig)
+ self.assertIn(f' ospf6 router-id {router_id_vrf}', frrconfig)
+
+ # cleanup
+ self.cli_delete(['vrf', 'name', vrf])
+ self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf'])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_ripng.py b/smoketest/scripts/cli/test_protocols_ripng.py
index 40585e778..53336a533 100755
--- a/smoketest/scripts/cli/test_protocols_ripng.py
+++ b/smoketest/scripts/cli/test_protocols_ripng.py
@@ -54,7 +54,7 @@ class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
- def test_ripng(self):
+ def test_ripng_01_parameters(self):
metric = '8'
interfaces = Section.interfaces('ethernet')
aggregates = ['2001:db8:1000::/48', '2001:db8:2000::/48', '2001:db8:3000::/48']
@@ -121,5 +121,25 @@ class TestProtocolsRIPng(VyOSUnitTestSHIM.TestCase):
proto = 'ospf6'
self.assertIn(f' redistribute {proto} metric {metric} route-map {route_map}', frrconfig)
+ def test_ripng_02_zebra_route_map(self):
+ # Implemented because of T3328
+ self.cli_set(base_path + ['route-map', route_map])
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR configuration
+ zebra_route_map = f'ipv6 protocol ripng route-map {route_map}'
+ frrconfig = self.getFRRconfig(zebra_route_map)
+ self.assertIn(zebra_route_map, frrconfig)
+
+ # Remove the route-map again
+ self.cli_delete(base_path + ['route-map'])
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR configuration
+ frrconfig = self.getFRRconfig(zebra_route_map)
+ self.assertNotIn(zebra_route_map, frrconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_protocols_rpki.py b/smoketest/scripts/cli/test_protocols_rpki.py
index d9792ce8d..e5e45565b 100755
--- a/smoketest/scripts/cli/test_protocols_rpki.py
+++ b/smoketest/scripts/cli/test_protocols_rpki.py
@@ -36,8 +36,6 @@ class TestProtocolsRPKI(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Nothing RPKI specific should be left over in the config
- #
- # Disabled until T3266 is resolved
# frrconfig = self.getFRRconfig('rpki')
# self.assertNotIn('rpki', frrconfig)
diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py
index 4c4eb5a7c..19efe7786 100755
--- a/smoketest/scripts/cli/test_protocols_static.py
+++ b/smoketest/scripts/cli/test_protocols_static.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -52,9 +52,16 @@ routes = {
},
'blackhole' : { 'distance' : '90' },
},
- '100.64.0.0/10' : {
+ '100.64.0.0/16' : {
'blackhole' : { },
},
+ '100.65.0.0/16' : {
+ 'reject' : { 'distance' : '10', 'tag' : '200' },
+ },
+ '100.66.0.0/16' : {
+ 'blackhole' : { },
+ 'reject' : { 'distance' : '10', 'tag' : '200' },
+ },
'2001:db8:100::/40' : {
'next_hop' : {
'2001:db8::1' : { 'distance' : '10' },
@@ -74,6 +81,9 @@ routes = {
},
'blackhole' : { 'distance' : '250', 'tag' : '500' },
},
+ '2001:db8:300::/40' : {
+ 'reject' : { 'distance' : '250', 'tag' : '500' },
+ },
'2001:db8::/32' : {
'blackhole' : { 'distance' : '200', 'tag' : '600' },
},
@@ -82,9 +92,15 @@ routes = {
tables = ['80', '81', '82']
class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- # This is our "target" VRF when leaking routes:
- self.cli_set(['vrf', 'name', 'black', 'table', '43210'])
+ @classmethod
+ def setUpClass(cls):
+ super(TestProtocolsStatic, cls).setUpClass()
+ cls.cli_set(cls, ['vrf', 'name', 'black', 'table', '43210'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['vrf'])
+ super(TestProtocolsStatic, cls).tearDownClass()
def tearDown(self):
for route, route_config in routes.items():
@@ -135,6 +151,20 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
if 'tag' in route_config['blackhole']:
self.cli_set(base + ['blackhole', 'tag', route_config['blackhole']['tag']])
+ if 'reject' in route_config:
+ self.cli_set(base + ['reject'])
+ if 'distance' in route_config['reject']:
+ self.cli_set(base + ['reject', 'distance', route_config['reject']['distance']])
+ if 'tag' in route_config['reject']:
+ self.cli_set(base + ['reject', 'tag', route_config['reject']['tag']])
+
+ if {'blackhole', 'reject'} <= set(route_config):
+ # Can not use blackhole and reject at the same time
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_delete(base + ['blackhole'])
+ self.cli_delete(base + ['reject'])
+
# commit changes
self.cli_commit()
@@ -177,6 +207,11 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
else:
self.assertIn(tmp, frrconfig)
+ if {'blackhole', 'reject'} <= set(route_config):
+ # Can not use blackhole and reject at the same time
+ # Config error validated above - skip this route
+ continue
+
if 'blackhole' in route_config:
tmp = f'{ip_ipv6} route {route} blackhole'
if 'tag' in route_config['blackhole']:
@@ -186,6 +221,15 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
self.assertIn(tmp, frrconfig)
+ if 'reject' in route_config:
+ tmp = f'{ip_ipv6} route {route} reject'
+ if 'tag' in route_config['reject']:
+ tmp += ' tag ' + route_config['reject']['tag']
+ if 'distance' in route_config['reject']:
+ tmp += ' ' + route_config['reject']['distance']
+
+ self.assertIn(tmp, frrconfig)
+
def test_02_static_table(self):
for table in tables:
for route, route_config in routes.items():
@@ -389,11 +433,8 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase):
self.assertIn(tmp, frrconfig)
- self.cli_delete(['vrf'])
-
def test_04_static_zebra_route_map(self):
# Implemented because of T3328
- self.debug = True
route_map = 'foo-static-in'
self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit'])
diff --git a/smoketest/scripts/cli/test_protocols_static_arp.py b/smoketest/scripts/cli/test_protocols_static_arp.py
new file mode 100755
index 000000000..b61d8f854
--- /dev/null
+++ b/smoketest/scripts/cli/test_protocols_static_arp.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import json
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.util import cmd
+
+base_path = ['protocols', 'static', 'arp']
+interface = 'eth0'
+address = '192.0.2.1/24'
+
+class TestARP(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestARP, 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)
+
+ # we need a L2 interface with a L3 address to properly configure ARP entries
+ cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', address])
+
+ @classmethod
+ def tearDownClass(cls):
+ # cleanuop L2 interface
+ cls.cli_delete(cls, ['interfaces', 'ethernet', interface, 'address', address])
+ cls.cli_commit(cls)
+
+ super(TestARP, cls).tearDownClass()
+
+ def tearDown(self):
+ # delete test config
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ def test_static_arp(self):
+ test_data = {
+ '192.0.2.10' : { 'mac' : '00:01:02:03:04:0a' },
+ '192.0.2.11' : { 'mac' : '00:01:02:03:04:0b' },
+ '192.0.2.12' : { 'mac' : '00:01:02:03:04:0c' },
+ '192.0.2.13' : { 'mac' : '00:01:02:03:04:0d' },
+ '192.0.2.14' : { 'mac' : '00:01:02:03:04:0e' },
+ '192.0.2.15' : { 'mac' : '00:01:02:03:04:0f' },
+ }
+
+ for host, host_config in test_data.items():
+ self.cli_set(base_path + ['interface', interface, 'address', host, 'mac', host_config['mac']])
+
+ self.cli_commit()
+
+ arp_table = json.loads(cmd('ip -j -4 neigh show'))
+ for host, host_config in test_data.items():
+ # As we search within a list of hosts we need to mark if it was
+ # found or not. This ensures all hosts from test_data are processed
+ found = False
+ for entry in arp_table:
+ # Other ARP entry - not related to this testcase
+ if entry['dst'] not in list(test_data):
+ continue
+
+ if entry['dst'] == host:
+ self.assertEqual(entry['lladdr'], host_config['mac'])
+ self.assertEqual(entry['dev'], interface)
+ found = True
+
+ if found == False:
+ print(entry)
+ self.assertTrue(found)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py
index 14666db15..9c9d6d9f1 100755
--- a/smoketest/scripts/cli/test_service_dhcp-server.py
+++ b/smoketest/scripts/cli/test_service_dhcp-server.py
@@ -38,7 +38,7 @@ domain_name = 'vyos.net'
class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
- super(cls, cls).setUpClass()
+ super(TestServiceDHCPServer, cls).setUpClass()
cidr_mask = subnet.split('/')[-1]
cls.cli_set(cls, ['interfaces', 'dummy', 'dum8765', 'address', f'{router}/{cidr_mask}'])
@@ -46,7 +46,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
@classmethod
def tearDownClass(cls):
cls.cli_delete(cls, ['interfaces', 'dummy', 'dum8765'])
- super(cls, cls).tearDownClass()
+ super(TestServiceDHCPServer, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
@@ -461,12 +461,11 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'mclt 1800;', config)
self.assertIn(f'mclt 1800;', config)
self.assertIn(f'split 128;', config)
- self.assertIn(f'port 520;', config)
- self.assertIn(f'peer port 520;', 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'peer port 520;', config)
self.assertIn(f'address {failover_local};', config)
self.assertIn(f'peer address {failover_remote};', config)
diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py
index 7177f1505..f83453323 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 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -32,16 +32,24 @@ dns_1 = '2001:db8::1'
dns_2 = '2001:db8::2'
domain = 'vyos.net'
nis_servers = ['2001:db8:ffff::1', '2001:db8:ffff::2']
-interface = 'eth1'
+interface = 'eth0'
interface_addr = inc_ip(subnet, 1) + '/64'
-class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['interfaces', 'ethernet', interface, 'address', interface_addr])
+class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceDHCPv6Server, cls).setUpClass()
+ cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_addr])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'ethernet', interface, 'address', interface_addr])
+ cls.cli_commit(cls)
+
+ super(TestServiceDHCPv6Server, cls).tearDownClass()
def tearDown(self):
self.cli_delete(base_path)
- self.cli_delete(['interfaces', 'ethernet', interface, 'address', interface_addr])
self.cli_commit()
def test_single_pool(self):
diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py
index 8e69efd9c..71fb3e177 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-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -16,14 +16,37 @@
import unittest
+from requests import request
+from urllib3.exceptions import InsecureRequestWarning
+
from base_vyostest_shim import VyOSUnitTestSHIM
+from base_vyostest_shim import ignore_warning
+from vyos.util import read_file
from vyos.util import run
base_path = ['service', 'https']
-
pki_base = ['pki']
-cert_data = 'MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIwWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIxMDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3LftzngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93+dm/LDnp7C0='
-key_data = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww'
+
+cert_data = """
+MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
+WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
+MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
+BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
+UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
+01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
+QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
+BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
++JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
+ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
++dm/LDnp7C0=
+"""
+
+key_data = """
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
+2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
+u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+"""
class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
def setUp(self):
@@ -61,9 +84,13 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
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)
+
def test_certificate(self):
- self.cli_set(pki_base + ['certificate', 'test_https', 'certificate', cert_data])
- self.cli_set(pki_base + ['certificate', 'test_https', 'private', 'key', key_data])
+ 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'])
@@ -72,5 +99,44 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase):
ret = run('sudo /usr/sbin/nginx -t')
self.assertEqual(ret, 0)
+ @ignore_warning(InsecureRequestWarning)
+ def test_api_auth(self):
+ vhost_id = 'example'
+ address = '127.0.0.1'
+ port = '443'
+ 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_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)
+
+ url = f'https://{address}/retrieve'
+ payload = {'data': '{"op": "showConfig", "path": []}', 'key': f'{key}'}
+ headers = {}
+ r = request('POST', url, verify=False, headers=headers, data=payload)
+ # Must get HTTP code 200 on success
+ self.assertEqual(r.status_code, 200)
+
+ payload_invalid = {'data': '{"op": "showConfig", "path": []}', 'key': 'invalid'}
+ r = request('POST', url, verify=False, headers=headers, data=payload_invalid)
+ # Must get HTTP code 401 on invalid key (Unauthorized)
+ self.assertEqual(r.status_code, 401)
+
+ payload_no_key = {'data': '{"op": "showConfig", "path": []}'}
+ r = request('POST', url, verify=False, headers=headers, data=payload_no_key)
+ # Must get HTTP code 401 on missing key (Unauthorized)
+ self.assertEqual(r.status_code, 401)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ids.py b/smoketest/scripts/cli/test_service_ids.py
new file mode 100755
index 000000000..18f1b8ec5
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_ids.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.util import process_named_running
+from vyos.util import read_file
+
+PROCESS_NAME = 'fastnetmon'
+FASTNETMON_CONF = '/etc/fastnetmon.conf'
+base_path = ['service', 'ids', 'ddos-protection']
+
+class TestServiceIDS(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceIDS, 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 test config
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ self.assertFalse(os.path.exists(FASTNETMON_CONF))
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_fastnetmon(self):
+ networks = ['10.0.0.0/24', '10.5.5.0/24']
+ interfaces = ['eth0', 'eth1']
+ fps = '3500'
+ mbps = '300'
+ pps = '60000'
+
+ self.cli_set(base_path + ['mode', 'mirror'])
+ # Required network!
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for tmp in networks:
+ self.cli_set(base_path + ['network', tmp])
+
+ # Required interface(s)!
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for tmp in interfaces:
+ self.cli_set(base_path + ['listen-interface', tmp])
+
+ self.cli_set(base_path + ['direction', 'in'])
+ self.cli_set(base_path + ['threshold', 'fps', fps])
+ self.cli_set(base_path + ['threshold', 'pps', pps])
+ self.cli_set(base_path + ['threshold', 'mbps', mbps])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check configured port
+ config = read_file(FASTNETMON_CONF)
+ self.assertIn(f'mirror_afpacket = on', config)
+ self.assertIn(f'process_incoming_traffic = on', config)
+ self.assertIn(f'process_outgoing_traffic = off', config)
+ self.assertIn(f'ban_for_flows = on', config)
+ self.assertIn(f'threshold_flows = {fps}', config)
+ self.assertIn(f'ban_for_bandwidth = on', config)
+ self.assertIn(f'threshold_mbps = {mbps}', config)
+ self.assertIn(f'ban_for_pps = on', config)
+ self.assertIn(f'threshold_pps = {pps}', config)
+
+ tmp = ','.join(interfaces)
+ self.assertIn(f'interfaces = {tmp}', config)
+
+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
new file mode 100755
index 000000000..439c96c33
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_lldp.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import os
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig import Section
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.version import get_version_data
+
+PROCESS_NAME = 'lldpd'
+LLDPD_CONF = '/etc/lldpd.d/01-vyos.conf'
+base_path = ['service', 'lldp']
+mgmt_if = 'dum83513'
+mgmt_addr = ['1.2.3.4', '1.2.3.5']
+
+class TestServiceLLDP(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(TestServiceLLDP, cls).setUpClass()
+
+ # create a test interfaces
+ for addr in mgmt_addr:
+ cls.cli_set(cls, ['interfaces', 'dummy', mgmt_if, 'address', addr + '/32'])
+
+ # 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):
+ cls.cli_delete(cls, ['interfaces', 'dummy', mgmt_if])
+ super(TestServiceLLDP, cls).tearDownClass()
+
+ def tearDown(self):
+ # service must be running after it was configured
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # delete/stop LLDP service
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # service is no longer allowed to run after it was removed
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_lldp_basic(self):
+ self.cli_set(base_path)
+ self.cli_commit()
+
+ config = read_file(LLDPD_CONF)
+ version_data = get_version_data()
+ version = version_data['version']
+ self.assertIn(f'configure system platform VyOS', config)
+ self.assertIn(f'configure system description "VyOS {version}"', config)
+
+ def test_02_lldp_mgmt_address(self):
+ for addr in mgmt_addr:
+ self.cli_set(base_path + ['management-address', addr])
+ self.cli_commit()
+
+ config = read_file(LLDPD_CONF)
+ self.assertIn(f'configure system ip management pattern {",".join(mgmt_addr)}', config)
+
+ def test_03_lldp_interfaces(self):
+ for interface in Section.interfaces('ethernet'):
+ if not '.' in interface:
+ self.cli_set(base_path + ['interface', interface])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+
+ interface_list = []
+ for interface in Section.interfaces('ethernet'):
+ if not '.' in interface:
+ interface_list.append(interface)
+ tmp = ','.join(interface_list)
+ self.assertIn(f'configure system interface pattern "{tmp}"', config)
+
+ def test_04_lldp_all_interfaces(self):
+ self.cli_set(base_path + ['interface', 'all'])
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+ self.assertIn(f'configure system interface pattern "*"', config)
+
+ def test_05_lldp_location(self):
+ interface = 'eth0'
+ elin = '1234567890'
+ self.cli_set(base_path + ['interface', interface, 'location', 'elin', elin])
+
+ # commit changes
+ self.cli_commit()
+
+ # verify configuration
+ config = read_file(LLDPD_CONF)
+
+ self.assertIn(f'configure ports {interface} med location elin "{elin}"', config)
+ self.assertIn(f'configure system interface pattern "{interface}"', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_monitoring_telegraf.py b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
new file mode 100755
index 000000000..09937513e
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_monitoring_telegraf.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.util import process_named_running
+from vyos.util import read_file
+
+PROCESS_NAME = 'telegraf'
+TELEGRAF_CONF = '/run/telegraf/vyos-telegraf.conf'
+base_path = ['service', 'monitoring', 'telegraf']
+org = 'log@in.local'
+token = 'GuRJc12tIzfjnYdKRAIYbxdWd2aTpOT9PVYNddzDnFV4HkAcD7u7-kndTFXjGuXzJN6TTxmrvPODB4mnFcseDV=='
+port = '8888'
+url = 'https://foo.local'
+bucket = 'main'
+inputs = ['cpu', 'disk', 'mem', 'net', 'system', 'kernel', 'interrupts', 'syslog']
+
+class TestMonitoringTelegraf(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ def test_01_basic_config(self):
+ self.cli_set(base_path + ['authentication', 'organization', org])
+ self.cli_set(base_path + ['authentication', 'token', token])
+ self.cli_set(base_path + ['port', port])
+ self.cli_set(base_path + ['url', url])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ config = read_file(TELEGRAF_CONF)
+
+ # Check telegraf config
+ self.assertIn(f'organization = "{org}"', config)
+ self.assertIn(f' token = "$INFLUX_TOKEN"', config)
+ self.assertIn(f'urls = ["{url}:{port}"]', config)
+ self.assertIn(f'bucket = "{bucket}"', config)
+
+ for input in inputs:
+ self.assertIn(input, 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.py
new file mode 100755
index 000000000..00a4f2020
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_salt.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from socket import gethostname
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.util import cmd
+
+PROCESS_NAME = 'salt-minion'
+SALT_CONF = '/etc/salt/minion'
+base_path = ['service', 'salt-minion']
+
+interface = 'dum4456'
+
+class TestServiceSALT(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceSALT, 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', 'dummy', interface, 'address', '100.64.0.1/16'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', interface])
+ super(TestServiceSALT, cls).tearDownClass()
+
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # delete testing SALT config
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # For an unknown reason on QEMU systems (e.g. where smoketests are executed
+ # from the CI) salt-minion process is not killed by systemd. Apparently
+ # no issue on VMWare.
+ if cmd('systemd-detect-virt') != 'kvm':
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_default(self):
+ servers = ['192.0.2.1', '192.0.2.2']
+
+ for server in servers:
+ self.cli_set(base_path + ['master', server])
+
+ self.cli_commit()
+
+ # commiconf = read_file() Check configured port
+ conf = read_file(SALT_CONF)
+ self.assertIn(f' - {server}', conf)
+
+ # defaults
+ hostname = gethostname()
+ self.assertIn(f'hash_type: sha256', conf)
+ self.assertIn(f'id: {hostname}', conf)
+ self.assertIn(f'mine_interval: 60', conf)
+
+ def test_options(self):
+ server = '192.0.2.3'
+ hash = 'sha1'
+ id = 'foo'
+ interval = '120'
+
+ self.cli_set(base_path + ['master', server])
+ self.cli_set(base_path + ['hash', hash])
+ self.cli_set(base_path + ['id', id])
+ self.cli_set(base_path + ['interval', interval])
+ self.cli_set(base_path + ['source-interface', interface])
+
+ self.cli_commit()
+
+ # commiconf = read_file() Check configured port
+ conf = read_file(SALT_CONF)
+ self.assertIn(f'- {server}', conf)
+
+ # defaults
+ self.assertIn(f'hash_type: {hash}', conf)
+ self.assertIn(f'id: {id}', conf)
+ self.assertIn(f'mine_interval: {interval}', conf)
+ self.assertIn(f'source_interface_name: {interface}', conf)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_snmp.py b/smoketest/scripts/cli/test_service_snmp.py
index 058835c72..e80c689cc 100755
--- a/smoketest/scripts/cli/test_service_snmp.py
+++ b/smoketest/scripts/cli/test_service_snmp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2021 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -22,14 +22,25 @@ 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.util import call
+from vyos.util import DEVNULL
from vyos.util import read_file
from vyos.util import process_named_running
+from vyos.version import get_version_data
PROCESS_NAME = 'snmpd'
SNMPD_CONF = '/etc/snmp/snmpd.conf'
base_path = ['service', 'snmp']
+snmpv3_group = 'default_group'
+snmpv3_view = 'default_view'
+snmpv3_view_oid = '1'
+snmpv3_user = 'vyos'
+snmpv3_auth_pw = 'vyos12345678'
+snmpv3_priv_pw = 'vyos87654321'
+snmpv3_engine_id = '000000000000000000000002'
+
def get_config_value(key):
tmp = read_file(SNMPD_CONF)
tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
@@ -38,20 +49,29 @@ def get_config_value(key):
class TestSNMPService(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
- super(cls, cls).setUpClass()
+ super(TestSNMPService, 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 SNMP config
self.cli_delete(base_path)
self.cli_commit()
+ # Check for running process
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
def test_snmp_basic(self):
dummy_if = 'dum7312'
dummy_addr = '100.64.0.1/32'
+ contact = 'maintainers@vyos.io'
+ location = 'QEMU'
+
self.cli_set(['interfaces', 'dummy', dummy_if, 'address', dummy_addr])
# Check if SNMP can be configured and service runs
@@ -71,8 +91,8 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
for addr in listen:
self.cli_set(base_path + ['listen-address', addr, 'port', port])
- self.cli_set(base_path + ['contact', 'maintainers@vyos.io'])
- self.cli_set(base_path + ['location', 'qemu'])
+ self.cli_set(base_path + ['contact', contact])
+ self.cli_set(base_path + ['location', location])
self.cli_commit()
@@ -82,7 +102,6 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
config = get_config_value('agentaddress')
expected = 'unix:/run/snmpd.socket'
self.assertIn(expected, config)
-
for addr in listen:
if is_ipv4(addr):
expected = f'udp:{addr}:{port}'
@@ -90,6 +109,16 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
expected = f'udp6:[{addr}]:{port}'
self.assertIn(expected, config)
+ config = get_config_value('sysDescr')
+ version_data = get_version_data()
+ self.assertEqual('VyOS ' + version_data['version'], config)
+
+ config = get_config_value('SysContact')
+ self.assertEqual(contact, config)
+
+ config = get_config_value('SysLocation')
+ self.assertEqual(location, config)
+
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
self.cli_delete(['interfaces', 'dummy', dummy_if])
@@ -98,8 +127,7 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
def test_snmpv3_sha(self):
# Check if SNMPv3 can be configured with SHA authentication
# and service runs
-
- self.cli_set(base_path + ['v3', 'engineid', '000000000000000000000002'])
+ self.cli_set(base_path + ['v3', 'engineid', snmpv3_engine_id])
self.cli_set(base_path + ['v3', 'group', 'default', 'mode', 'ro'])
# check validate() - a view must be created before this can be committed
with self.assertRaises(ConfigSessionError):
@@ -109,46 +137,52 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['v3', 'group', 'default', 'view', 'default'])
# create user
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'sha'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'aes'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', 'default'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'plaintext-password', snmpv3_auth_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'type', 'sha'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'plaintext-password', snmpv3_priv_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'type', 'aes'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'group', 'default'])
self.cli_commit()
# commit will alter the CLI values - check if they have been updated:
hashed_password = '4e52fe55fd011c9c51ae2c65f4b78ca93dcafdfe'
- tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1]
+ tmp = self._session.show_config(base_path + ['v3', 'user', snmpv3_user, 'auth', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
- tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1]
+ hashed_password = '54705c8de9e81fdf61ad7ac044fa8fe611ddff6b'
+ tmp = self._session.show_config(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
# TODO: read in config file and check values
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # Try SNMPv3 connection
+ tmp = call(f'snmpwalk -v 3 -u {snmpv3_user} -a SHA -A {snmpv3_auth_pw} -x AES -X {snmpv3_priv_pw} -l authPriv 127.0.0.1', stdout=DEVNULL)
+ self.assertEqual(tmp, 0)
def test_snmpv3_md5(self):
# Check if SNMPv3 can be configured with MD5 authentication
# and service runs
+ self.cli_set(base_path + ['v3', 'engineid', snmpv3_engine_id])
- self.cli_set(base_path + ['v3', 'engineid', '000000000000000000000002'])
- self.cli_set(base_path + ['v3', 'group', 'default', 'mode', 'ro'])
- # check validate() - a view must be created before this can be comitted
+ # create user
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'plaintext-password', snmpv3_auth_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'auth', 'type', 'md5'])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'plaintext-password', snmpv3_priv_pw])
+ self.cli_set(base_path + ['v3', 'user', snmpv3_user, 'privacy', 'type', 'des'])
+
+ # check validate() - user requires a group to be created
with self.assertRaises(ConfigSessionError):
self.cli_commit()
+ self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', snmpv3_group])
- self.cli_set(base_path + ['v3', 'view', 'default', 'oid', '1'])
- self.cli_set(base_path + ['v3', 'group', 'default', 'view', 'default'])
+ self.cli_set(base_path + ['v3', 'group', snmpv3_group, 'mode', 'ro'])
+ # check validate() - a view must be created before this can be comitted
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
- # create user
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'auth', 'type', 'md5'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'plaintext-password', 'vyos12345678'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'privacy', 'type', 'des'])
- self.cli_set(base_path + ['v3', 'user', 'vyos', 'group', 'default'])
+ self.cli_set(base_path + ['v3', 'view', snmpv3_view, 'oid', snmpv3_view_oid])
+ self.cli_set(base_path + ['v3', 'group', snmpv3_group, 'view', snmpv3_view])
self.cli_commit()
@@ -157,13 +191,21 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase):
tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'auth', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
+ hashed_password = 'e11c83f2c510540a3c4de84ee66de440'
tmp = self._session.show_config(base_path + ['v3', 'user', 'vyos', 'privacy', 'encrypted-password']).split()[1]
self.assertEqual(tmp, hashed_password)
- # TODO: read in config file and check values
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ tmp = read_file(SNMPD_CONF)
+ # views
+ self.assertIn(f'view {snmpv3_view} included .{snmpv3_view_oid}', tmp)
+ # group
+ self.assertIn(f'group {snmpv3_group} usm {snmpv3_user}', tmp)
+ # access
+ self.assertIn(f'access {snmpv3_group} "" usm auth exact {snmpv3_view} none none', tmp)
+
+ # Try SNMPv3 connection
+ tmp = call(f'snmpwalk -v 3 -u {snmpv3_user} -a MD5 -A {snmpv3_auth_pw} -x DES -X {snmpv3_priv_pw} -l authPriv 127.0.0.1', stdout=DEVNULL)
+ self.assertEqual(tmp, 0)
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index a54c03919..0b029dd00 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -15,13 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import paramiko
import re
import unittest
+from pwd import getpwall
+
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
+from vyos.util import is_systemd_service_running
from vyos.util import process_named_running
from vyos.util import read_file
@@ -42,13 +46,16 @@ def get_config_value(key):
class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
- super(cls, cls).setUpClass()
+ super(TestServiceSSH, 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()
@@ -57,6 +64,11 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
self.assertTrue(os.path.isfile(key_dsa))
self.assertTrue(os.path.isfile(key_ed25519))
+ # Established SSH connections remains running after service is stopped.
+ # We can not use process_named_running here - we rather need to check
+ # that the systemd service is no longer running
+ self.assertFalse(is_systemd_service_running(PROCESS_NAME))
+
def test_ssh_default(self):
# Check if SSH service runs with default settings - used for checking
# behavior of <defaultValue> in XML definition
@@ -69,9 +81,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
port = get_config_value('Port')[0]
self.assertEqual('22', port)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
def test_ssh_single_listen_address(self):
# Check if SSH service can be configured and runs
self.cli_set(base_path + ['port', '1234'])
@@ -108,9 +117,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
keepalive = get_config_value('ClientAliveInterval')[0]
self.assertTrue("100" in keepalive)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
def test_ssh_multiple_listen_addresses(self):
# Check if SSH service can be configured and runs with multiple
# listen ports and listen-addresses
@@ -135,9 +141,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
for address in addresses:
self.assertIn(address, tmp)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
def test_ssh_vrf(self):
# Check if SSH service can be bound to given VRF
port = '22'
@@ -157,9 +160,6 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value('Port')
self.assertIn(port, tmp)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
-
# Check for process in VRF
tmp = cmd(f'ip vrf pids {vrf}')
self.assertIn(PROCESS_NAME, tmp)
@@ -167,5 +167,100 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
# delete VRF
self.cli_delete(['vrf', 'name', vrf])
+ def test_ssh_login(self):
+ # Perform SSH login and command execution with a predefined user. The
+ # result (output of uname -a) must match the output if the command is
+ # run natively.
+ #
+ # We also try to login as an invalid user - this is not allowed to work.
+
+ def ssh_send_cmd(command, username, password, host='localhost'):
+ """ SSH command execution helper """
+ # Try to login via SSH
+ ssh_client = paramiko.SSHClient()
+ ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+ ssh_client.connect(hostname='localhost', username=username, password=password)
+ _, stdout, stderr = ssh_client.exec_command(command)
+ output = stdout.read().decode().strip()
+ error = stderr.read().decode().strip()
+ ssh_client.close()
+ return output, error
+
+ test_user = 'ssh_test'
+ test_pass = 'v2i57DZs8idUwMN3VC92'
+ test_command = 'uname -a'
+
+ self.cli_set(base_path)
+ self.cli_set(['system', 'login', 'user', test_user, 'authentication', 'plaintext-password', test_pass])
+
+ # commit changes
+ self.cli_commit()
+
+ # Login with proper credentials
+ output, error = ssh_send_cmd(test_command, test_user, test_pass)
+ # verify login
+ self.assertFalse(error)
+ self.assertEqual(output, cmd(test_command))
+
+ # Login with invalid credentials
+ with self.assertRaises(paramiko.ssh_exception.AuthenticationException):
+ output, error = ssh_send_cmd(test_command, 'invalid_user', 'invalid_password')
+
+ self.cli_delete(['system', 'login', 'user', test_user])
+ self.cli_commit()
+
+ # After deletion the test user is not allowed to remain in /etc/passwd
+ usernames = [x[0] for x in getpwall()]
+ self.assertNotIn(test_user, usernames)
+
+ def test_ssh_dynamic_protection(self):
+ # check sshguard service
+
+ SSHGUARD_CONFIG = '/etc/sshguard/sshguard.conf'
+ SSHGUARD_WHITELIST = '/etc/sshguard/whitelist'
+ SSHGUARD_PROCESS = 'sshguard'
+ block_time = '123'
+ detect_time = '1804'
+ port = '22'
+ threshold = '10'
+ allow_list = ['192.0.2.0/24', '2001:db8::/48']
+
+ self.cli_set(base_path + ['dynamic-protection', 'block-time', block_time])
+ self.cli_set(base_path + ['dynamic-protection', 'detect-time', detect_time])
+ self.cli_set(base_path + ['dynamic-protection', 'threshold', threshold])
+ for allow in allow_list:
+ self.cli_set(base_path + ['dynamic-protection', 'allow-from', allow])
+
+ # commit changes
+ self.cli_commit()
+
+ # Check configured port
+ tmp = get_config_value('Port')
+ self.assertIn(port, tmp)
+
+ # Check sshgurad service
+ self.assertTrue(process_named_running(SSHGUARD_PROCESS))
+
+ sshguard_lines = [
+ f'THRESHOLD={threshold}',
+ f'BLOCK_TIME={block_time}',
+ f'DETECTION_TIME={detect_time}'
+ ]
+
+ tmp_sshguard_conf = read_file(SSHGUARD_CONFIG)
+ for line in sshguard_lines:
+ self.assertIn(line, tmp_sshguard_conf)
+
+ tmp_whitelist_conf = read_file(SSHGUARD_WHITELIST)
+ for allow in allow_list:
+ self.assertIn(allow, tmp_whitelist_conf)
+
+ # Delete service ssh dynamic-protection
+ # but not service ssh itself
+ self.cli_delete(base_path + ['dynamic-protection'])
+ self.cli_commit()
+
+ self.assertFalse(process_named_running(SSHGUARD_PROCESS))
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_tftp-server.py b/smoketest/scripts/cli/test_service_tftp-server.py
index 1a1bf0cdf..b57c33f26 100755
--- a/smoketest/scripts/cli/test_service_tftp-server.py
+++ b/smoketest/scripts/cli/test_service_tftp-server.py
@@ -20,6 +20,7 @@ from psutil import process_iter
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
+from vyos.util import cmd
from vyos.util import read_file
from vyos.util import process_named_running
from vyos.template import is_ipv6
@@ -29,6 +30,7 @@ base_path = ['service', 'tftp-server']
dummy_if_path = ['interfaces', 'dummy', 'dum69']
address_ipv4 = '192.0.2.1'
address_ipv6 = '2001:db8::1'
+vrf = 'mgmt'
class TestServiceTFTPD(VyOSUnitTestSHIM.TestCase):
def setUp(self):
@@ -97,5 +99,42 @@ class TestServiceTFTPD(VyOSUnitTestSHIM.TestCase):
count += 1
self.assertEqual(count, len(address))
+ def test_03_tftpd_vrf(self):
+ directory = '/tmp'
+ port = '69' # default port
+
+ self.cli_set(base_path + ['allow-upload'])
+ self.cli_set(base_path + ['directory', directory])
+ self.cli_set(base_path + ['listen-address', address_ipv4, 'vrf', vrf])
+
+ # VRF does yet not exist - an error must be thrown
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(['vrf', 'name', vrf, 'table', '1338'])
+ self.cli_set(dummy_if_path + ['vrf', vrf])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file('/etc/default/tftpd0')
+ # verify listen IP address
+ self.assertIn(f'{address_ipv4}:{port} -4', config)
+ # verify directory
+ self.assertIn(directory, config)
+ # verify upload
+ self.assertIn('--create --umask 000', config)
+
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ # Check for process in VRF
+ tmp = cmd(f'ip vrf pids {vrf}')
+ self.assertIn(PROCESS_NAME, tmp)
+
+ # delete VRF
+ self.cli_delete(dummy_if_path + ['vrf'])
+ self.cli_delete(['vrf', 'name', vrf])
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_upnp.py b/smoketest/scripts/cli/test_service_upnp.py
new file mode 100755
index 000000000..e4df88c1e
--- /dev/null
+++ b/smoketest/scripts/cli/test_service_upnp.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSession
+from vyos.configsession import ConfigSessionError
+from vyos.template import ip_from_cidr
+from vyos.util import read_file
+from vyos.util import process_named_running
+
+UPNP_CONF = '/run/upnp/miniupnp.conf'
+DAEMON = 'miniupnpd'
+interface = 'eth0'
+base_path = ['service', 'upnp']
+address_base = ['interfaces', 'ethernet', interface, 'address']
+
+ipv4_addr = '100.64.0.1/24'
+ipv6_addr = '2001:db8::1/64'
+
+class TestServiceUPnP(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestServiceUPnP, 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, address_base + [ipv4_addr])
+ cls.cli_set(cls, address_base + [ipv6_addr])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, address_base)
+ cls._session.commit()
+
+ super(TestServiceUPnP, cls).tearDownClass()
+
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(DAEMON))
+
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Check for running process
+ self.assertFalse(process_named_running(DAEMON))
+
+ def test_ipv4_base(self):
+ self.cli_set(base_path + ['nat-pmp'])
+ self.cli_set(base_path + ['listen', interface])
+
+ # check validate() - WAN interface is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['wan-interface', interface])
+
+ self.cli_commit()
+
+ config = read_file(UPNP_CONF)
+ self.assertIn(f'ext_ifname={interface}', config)
+ self.assertIn(f'listening_ip={interface}', config)
+ self.assertIn(f'enable_natpmp=yes', config)
+ self.assertIn(f'enable_upnp=yes', config)
+
+ def test_ipv6_base(self):
+ v6_addr = ip_from_cidr(ipv6_addr)
+
+ self.cli_set(base_path + ['nat-pmp'])
+ self.cli_set(base_path + ['listen', interface])
+ self.cli_set(base_path + ['listen', v6_addr])
+
+ # check validate() - WAN interface is mandatory
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ self.cli_set(base_path + ['wan-interface', interface])
+
+ self.cli_commit()
+
+ config = read_file(UPNP_CONF)
+ self.assertIn(f'ext_ifname={interface}', config)
+ self.assertIn(f'listening_ip={interface}', config)
+ self.assertIn(f'ipv6_listening_ip={v6_addr}', config)
+ self.assertIn(f'enable_natpmp=yes', config)
+ self.assertIn(f'enable_upnp=yes', config)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_webproxy.py b/smoketest/scripts/cli/test_service_webproxy.py
index 8a1a03ce7..772d6ab16 100755
--- a/smoketest/scripts/cli/test_service_webproxy.py
+++ b/smoketest/scripts/cli/test_service_webproxy.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -30,11 +30,19 @@ listen_if = 'dum3632'
listen_ip = '192.0.2.1'
class TestServiceWebProxy(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32'])
+ @classmethod
+ def setUpClass(cls):
+ # call base-classes classmethod
+ super(TestServiceWebProxy, cls).setUpClass()
+ # create a test interfaces
+ cls.cli_set(cls, ['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['interfaces', 'dummy', listen_if])
+ super(TestServiceWebProxy, cls).tearDownClass()
def tearDown(self):
- self.cli_delete(['interfaces', 'dummy', listen_if])
self.cli_delete(base_path)
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_system_conntrack.py b/smoketest/scripts/cli/test_system_conntrack.py
index b2934cf04..95c2a6c55 100755
--- a/smoketest/scripts/cli/test_system_conntrack.py
+++ b/smoketest/scripts/cli/test_system_conntrack.py
@@ -15,10 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
+import re
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.firewall import find_nftables_rule
from vyos.util import cmd
from vyos.util import read_file
@@ -156,8 +158,8 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
'driver' : ['nf_nat_h323', 'nf_conntrack_h323'],
},
'nfs' : {
- 'iptables' : ['-A VYATTA_CT_HELPER -p udp -m udp --dport 111 -j CT --helper rpc',
- '-A VYATTA_CT_HELPER -p tcp -m tcp --dport 111 -j CT --helper rpc'],
+ 'nftables' : ['ct helper set "rpc_tcp"',
+ 'ct helper set "rpc_udp"']
},
'pptp' : {
'driver' : ['nf_nat_pptp', 'nf_conntrack_pptp'],
@@ -166,9 +168,7 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
'driver' : ['nf_nat_sip', 'nf_conntrack_sip'],
},
'sqlnet' : {
- 'iptables' : ['-A VYATTA_CT_HELPER -p tcp -m tcp --dport 1536 -j CT --helper tns',
- '-A VYATTA_CT_HELPER -p tcp -m tcp --dport 1525 -j CT --helper tns',
- '-A VYATTA_CT_HELPER -p tcp -m tcp --dport 1521 -j CT --helper tns'],
+ 'nftables' : ['ct helper set "tns_tcp"']
},
'tftp' : {
'driver' : ['nf_nat_tftp', 'nf_conntrack_tftp'],
@@ -187,10 +187,9 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
if 'driver' in module_options:
for driver in module_options['driver']:
self.assertTrue(os.path.isdir(f'/sys/module/{driver}'))
- if 'iptables' in module_options:
- rules = cmd('sudo iptables-save -t raw')
- for ruleset in module_options['iptables']:
- self.assertIn(ruleset, rules)
+ if 'nftables' in module_options:
+ for rule in module_options['nftables']:
+ self.assertTrue(find_nftables_rule('raw', 'VYOS_CT_HELPER', [rule]) != None)
# unload modules
for module in modules:
@@ -204,10 +203,9 @@ class TestSystemConntrack(VyOSUnitTestSHIM.TestCase):
if 'driver' in module_options:
for driver in module_options['driver']:
self.assertFalse(os.path.isdir(f'/sys/module/{driver}'))
- if 'iptables' in module_options:
- rules = cmd('sudo iptables-save -t raw')
- for ruleset in module_options['iptables']:
- self.assertNotIn(ruleset, rules)
+ if 'nftables' in module_options:
+ for rule in module_options['nftables']:
+ self.assertTrue(find_nftables_rule('raw', 'VYOS_CT_HELPER', [rule]) == None)
def test_conntrack_hash_size(self):
hash_size = '65536'
diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py
index a2b5b1481..a6eef3fb6 100755
--- a/smoketest/scripts/cli/test_system_flow-accounting.py
+++ b/smoketest/scripts/cli/test_system_flow-accounting.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -20,6 +20,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
+from vyos.template import bracketize_ipv6
+from vyos.template import is_ipv6
from vyos.util import cmd
from vyos.util import process_named_running
from vyos.util import read_file
@@ -27,18 +29,21 @@ from vyos.util import read_file
PROCESS_NAME = 'uacctd'
base_path = ['system', 'flow-accounting']
-uacctd_conf = '/etc/pmacct/uacctd.conf'
+uacctd_conf = '/run/pmacct/uacctd.conf'
class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
- super(cls, cls).setUpClass()
+ super(TestSystemFlowAccounting, 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):
+ # after service removal process must no longer run
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
self.cli_delete(base_path)
self.cli_commit()
@@ -47,7 +52,10 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
def test_basic(self):
buffer_size = '5' # MiB
+ syslog = 'all'
+
self.cli_set(base_path + ['buffer-size', buffer_size])
+ self.cli_set(base_path + ['syslog-facility', syslog])
# You need to configure at least one interface for flow-accounting
with self.assertRaises(ConfigSessionError):
@@ -59,9 +67,20 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# verify configuration
- tmp = cmd('sudo iptables-save -t raw')
+ nftables_output = cmd('sudo nft list chain raw VYOS_CT_PREROUTING_HOOK').splitlines()
for interface in Section.interfaces('ethernet'):
- self.assertIn(f'-A VYATTA_CT_PREROUTING_HOOK -i {interface} -m comment --comment FLOW_ACCOUNTING_RULE -j NFLOG --nflog-group 2 --nflog-size 128 --nflog-threshold 100', tmp)
+ rule_found = False
+ ifname_search = f'iifname "{interface}"'
+
+ for nftables_line in nftables_output:
+ if 'FLOW_ACCOUNTING_RULE' in nftables_line and ifname_search in nftables_line:
+ self.assertIn('group 2', nftables_line)
+ self.assertIn('snaplen 128', nftables_line)
+ self.assertIn('queue-threshold 100', nftables_line)
+ rule_found = True
+ break
+
+ self.assertTrue(rule_found)
uacctd = read_file(uacctd_conf)
# circular queue size - buffer_size
@@ -73,8 +92,205 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):
tmp //= 1000
self.assertIn(f'plugin_buffer_size: {tmp}', uacctd)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ # when 'disable-imt' is not configured on the CLI it must be present
+ self.assertIn(f'imt_path: /tmp/uacctd.pipe', uacctd)
+ self.assertIn(f'imt_mem_pools_number: 169', uacctd)
+ self.assertIn(f'syslog: {syslog}', uacctd)
+ self.assertIn(f'plugins: memory', uacctd)
+
+ def test_sflow(self):
+ sampling_rate = '4000'
+ source_address = '192.0.2.1'
+ dummy_if = 'dum3841'
+ agent_address = '192.0.2.2'
+
+ sflow_server = {
+ '1.2.3.4' : { },
+ '5.6.7.8' : { 'port' : '6000' },
+ }
+
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', agent_address + '/32'])
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', source_address + '/32'])
+ self.cli_set(base_path + ['disable-imt'])
+
+ # You need to configure at least one interface for flow-accounting
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in Section.interfaces('ethernet'):
+ self.cli_set(base_path + ['interface', interface])
+
+
+ # You need to configure at least one sFlow or NetFlow protocol, or not
+ # set "disable-imt" for flow-accounting
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['sflow', 'agent-address', agent_address])
+ self.cli_set(base_path + ['sflow', 'sampling-rate', sampling_rate])
+ self.cli_set(base_path + ['sflow', 'source-address', source_address])
+ for server, server_config in sflow_server.items():
+ self.cli_set(base_path + ['sflow', 'server', server])
+ if 'port' in server_config:
+ self.cli_set(base_path + ['sflow', 'server', server, 'port', server_config['port']])
+
+ # commit changes
+ self.cli_commit()
+
+ uacctd = read_file(uacctd_conf)
+
+ # when 'disable-imt' is not configured on the CLI it must be present
+ self.assertNotIn(f'imt_path: /tmp/uacctd.pipe', uacctd)
+ self.assertNotIn(f'imt_mem_pools_number: 169', uacctd)
+ self.assertNotIn(f'plugins: memory', uacctd)
+
+ for server, server_config in sflow_server.items():
+ if 'port' in server_config:
+ self.assertIn(f'sfprobe_receiver[sf_{server}]: {server}', uacctd)
+ else:
+ self.assertIn(f'sfprobe_receiver[sf_{server}]: {server}:6343', uacctd)
+
+ self.assertIn(f'sfprobe_agentip[sf_{server}]: {agent_address}', uacctd)
+ self.assertIn(f'sampling_rate[sf_{server}]: {sampling_rate}', uacctd)
+ self.assertIn(f'sfprobe_source_ip[sf_{server}]: {source_address}', uacctd)
+
+ self.cli_delete(['interfaces', 'dummy', dummy_if])
+
+ def test_sflow_ipv6(self):
+ sampling_rate = '100'
+ sflow_server = {
+ '2001:db8::1' : { },
+ '2001:db8::2' : { 'port' : '6000' },
+ }
+
+ self.cli_set(base_path + ['disable-imt'])
+
+ # You need to configure at least one interface for flow-accounting
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+ for interface in Section.interfaces('ethernet'):
+ self.cli_set(base_path + ['interface', interface])
+
+
+ # You need to configure at least one sFlow or NetFlow protocol, or not
+ # set "disable-imt" for flow-accounting
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ self.cli_set(base_path + ['sflow', 'sampling-rate', sampling_rate])
+ for server, server_config in sflow_server.items():
+ self.cli_set(base_path + ['sflow', 'server', server])
+ if 'port' in server_config:
+ self.cli_set(base_path + ['sflow', 'server', server, 'port', server_config['port']])
+
+ # commit changes
+ self.cli_commit()
+
+ uacctd = read_file(uacctd_conf)
+
+ # when 'disable-imt' is not configured on the CLI it must be present
+ self.assertNotIn(f'imt_path: /tmp/uacctd.pipe', uacctd)
+ self.assertNotIn(f'imt_mem_pools_number: 169', uacctd)
+ self.assertNotIn(f'plugins: memory', uacctd)
+
+ for server, server_config in sflow_server.items():
+ tmp_srv = server
+ if is_ipv6(tmp_srv):
+ tmp_srv = tmp_srv.replace(':', '.')
+
+ if 'port' in server_config:
+ self.assertIn(f'sfprobe_receiver[sf_{tmp_srv}]: {bracketize_ipv6(server)}', uacctd)
+ else:
+ self.assertIn(f'sfprobe_receiver[sf_{tmp_srv}]: {bracketize_ipv6(server)}:6343', uacctd)
+ self.assertIn(f'sampling_rate[sf_{tmp_srv}]: {sampling_rate}', uacctd)
+
+ def test_netflow(self):
+ engine_id = '33'
+ max_flows = '667'
+ sampling_rate = '100'
+ source_address = '192.0.2.1'
+ dummy_if = 'dum3842'
+ agent_address = '192.0.2.10'
+ version = '10'
+ tmo_expiry = '120'
+ tmo_flow = '1200'
+ tmo_icmp = '60'
+ tmo_max = '50000'
+ tmo_tcp_fin = '100'
+ tmo_tcp_generic = '120'
+ tmo_tcp_rst = '99'
+ tmo_udp = '10'
+
+ netflow_server = {
+ '11.22.33.44' : { },
+ '55.66.77.88' : { 'port' : '6000' },
+ '2001:db8::1' : { },
+ }
+
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', agent_address + '/32'])
+ self.cli_set(['interfaces', 'dummy', dummy_if, 'address', source_address + '/32'])
+
+ for interface in Section.interfaces('ethernet'):
+ self.cli_set(base_path + ['interface', interface])
+
+ self.cli_set(base_path + ['netflow', 'engine-id', engine_id])
+ self.cli_set(base_path + ['netflow', 'max-flows', max_flows])
+ self.cli_set(base_path + ['netflow', 'sampling-rate', sampling_rate])
+ self.cli_set(base_path + ['netflow', 'source-address', source_address])
+ self.cli_set(base_path + ['netflow', 'version', version])
+
+ # timeouts
+ self.cli_set(base_path + ['netflow', 'timeout', 'expiry-interval', tmo_expiry])
+ self.cli_set(base_path + ['netflow', 'timeout', 'flow-generic', tmo_flow])
+ self.cli_set(base_path + ['netflow', 'timeout', 'icmp', tmo_icmp])
+ self.cli_set(base_path + ['netflow', 'timeout', 'max-active-life', tmo_max])
+ self.cli_set(base_path + ['netflow', 'timeout', 'tcp-fin', tmo_tcp_fin])
+ self.cli_set(base_path + ['netflow', 'timeout', 'tcp-generic', tmo_tcp_generic])
+ self.cli_set(base_path + ['netflow', 'timeout', 'tcp-rst', tmo_tcp_rst])
+ self.cli_set(base_path + ['netflow', 'timeout', 'udp', tmo_udp])
+
+ # You need to configure at least one netflow server
+ with self.assertRaises(ConfigSessionError):
+ self.cli_commit()
+
+ for server, server_config in netflow_server.items():
+ self.cli_set(base_path + ['netflow', 'server', server])
+ if 'port' in server_config:
+ self.cli_set(base_path + ['netflow', 'server', server, 'port', server_config['port']])
+
+ # commit changes
+ self.cli_commit()
+
+ uacctd = read_file(uacctd_conf)
+
+ tmp = []
+ for server, server_config in netflow_server.items():
+ tmp_srv = server
+ if is_ipv6(tmp_srv):
+ tmp_srv = tmp_srv.replace(':', '.')
+ tmp.append(f'nfprobe[nf_{tmp_srv}]')
+ tmp.append('memory')
+ self.assertIn('plugins: ' + ','.join(tmp), uacctd)
+
+ for server, server_config in netflow_server.items():
+ tmp_srv = server
+ if is_ipv6(tmp_srv):
+ tmp_srv = tmp_srv.replace(':', '.')
+
+ self.assertIn(f'nfprobe_engine[nf_{tmp_srv}]: {engine_id}', uacctd)
+ self.assertIn(f'nfprobe_maxflows[nf_{tmp_srv}]: {max_flows}', uacctd)
+ self.assertIn(f'sampling_rate[nf_{tmp_srv}]: {sampling_rate}', uacctd)
+ self.assertIn(f'nfprobe_source_ip[nf_{tmp_srv}]: {source_address}', uacctd)
+ self.assertIn(f'nfprobe_version[nf_{tmp_srv}]: {version}', uacctd)
+
+ if 'port' in server_config:
+ self.assertIn(f'nfprobe_receiver[nf_{tmp_srv}]: {bracketize_ipv6(server)}', uacctd)
+ else:
+ self.assertIn(f'nfprobe_receiver[nf_{tmp_srv}]: {bracketize_ipv6(server)}:2055', uacctd)
+
+ self.assertIn(f'nfprobe_timeouts[nf_{tmp_srv}]: expint={tmo_expiry}:general={tmo_flow}:icmp={tmo_icmp}:maxlife={tmo_max}:tcp.fin={tmo_tcp_fin}:tcp={tmo_tcp_generic}:tcp.rst={tmo_tcp_rst}:udp={tmo_udp}', uacctd)
+
+
+ self.cli_delete(['interfaces', 'dummy', dummy_if])
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ipv6.py b/smoketest/scripts/cli/test_system_ipv6.py
index 1325d4b39..c8aea9100 100755
--- a/smoketest/scripts/cli/test_system_ipv6.py
+++ b/smoketest/scripts/cli/test_system_ipv6.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021 VyOS maintainers and contributors
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -17,12 +17,16 @@
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.template import is_ipv4
from vyos.util import read_file
+from vyos.util import get_interface_config
+from vyos.validate import is_intf_addr_assigned
base_path = ['system', 'ipv6']
file_forwarding = '/proc/sys/net/ipv6/conf/all/forwarding'
-file_disable = '/etc/modprobe.d/vyos_disable_ipv6.conf'
+file_disable = '/proc/sys/net/ipv6/conf/all/disable_ipv6'
file_dad = '/proc/sys/net/ipv6/conf/all/accept_dad'
file_multipath = '/proc/sys/net/ipv6/fib_multipath_hash_policy'
@@ -41,15 +45,6 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase):
self.assertEqual(read_file(file_forwarding), '0')
- def test_system_ipv6_disable(self):
- # Do not assign any IPv6 address on interfaces, this requires a reboot
- # which can not be tested, but we can read the config file :)
- self.cli_set(base_path + ['disable'])
- self.cli_commit()
-
- # Verify configuration file
- self.assertEqual(read_file(file_disable), 'options ipv6 disable_ipv6=1')
-
def test_system_ipv6_strict_dad(self):
# This defaults to 1
self.assertEqual(read_file(file_dad), '1')
diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py
index 69a06eeac..1131b6f93 100755
--- a/smoketest/scripts/cli/test_system_login.py
+++ b/smoketest/scripts/cli/test_system_login.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -23,6 +23,7 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from distutils.version import LooseVersion
from platform import release as kernel_version
from subprocess import Popen, PIPE
+from pwd import getpwall
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
@@ -52,6 +53,11 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
+ # After deletion, a user is not allowed to remain in /etc/passwd
+ usernames = [x[0] for x in getpwall()]
+ for user in users:
+ self.assertNotIn(user, usernames)
+
def test_add_linux_system_user(self):
# We are not allowed to re-use a username already taken by the Linux
# base system
@@ -235,4 +241,4 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase):
self.assertTrue(tmp)
if __name__ == '__main__':
- unittest.main(verbosity=2, failfast=True)
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_logs.py b/smoketest/scripts/cli/test_system_logs.py
new file mode 100755
index 000000000..92fa9c3d9
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_logs.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import unittest
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.util import read_file
+
+# path to logrotate configs
+logrotate_atop_file = '/etc/logrotate.d/vyos-atop'
+logrotate_rsyslog_file = '/etc/logrotate.d/vyos-rsyslog'
+# default values
+default_atop_maxsize = '10M'
+default_atop_rotate = '10'
+default_rsyslog_size = '1M'
+default_rsyslog_rotate = '10'
+
+base_path = ['system', 'logs']
+
+
+def logrotate_config_parse(file_path):
+ # read the file
+ logrotate_config = read_file(file_path)
+ # create regex for parsing options
+ regex_options = re.compile(
+ r'(^\s+(?P<option_name_script>postrotate|prerotate|firstaction|lastaction|preremove)\n(?P<option_value_script>((?!endscript).)*)\n\s+endscript\n)|(^\s+(?P<option_name>[\S]+)([ \t]+(?P<option_value>\S+))*$)',
+ re.M | re.S)
+ # create empty dict for config
+ logrotate_config_dict = {}
+ # fill dictionary with actual config
+ for option in regex_options.finditer(logrotate_config):
+ option_name = option.group('option_name')
+ option_value = option.group('option_value')
+ option_name_script = option.group('option_name_script')
+ option_value_script = option.group('option_value_script')
+ if option_name:
+ logrotate_config_dict[option_name] = option_value
+ if option_name_script:
+ logrotate_config_dict[option_name_script] = option_value_script
+
+ # return config dictionary
+ return (logrotate_config_dict)
+
+
+class TestSystemLogs(VyOSUnitTestSHIM.TestCase):
+
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ def test_logs_defaults(self):
+ # test with empty section for default values
+ self.cli_set(base_path)
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config_atop = logrotate_config_parse(logrotate_atop_file)
+ logrotate_config_rsyslog = logrotate_config_parse(
+ logrotate_rsyslog_file)
+ self.assertEqual(logrotate_config_atop['maxsize'], default_atop_maxsize)
+ self.assertEqual(logrotate_config_atop['rotate'], default_atop_rotate)
+ self.assertEqual(logrotate_config_rsyslog['size'], default_rsyslog_size)
+ self.assertEqual(logrotate_config_rsyslog['rotate'],
+ default_rsyslog_rotate)
+
+ def test_logs_atop_maxsize(self):
+ # test for maxsize option
+ self.cli_set(base_path + ['logrotate', 'atop', 'max-size', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_atop_file)
+ self.assertEqual(logrotate_config['maxsize'], '50M')
+
+ def test_logs_atop_rotate(self):
+ # test for rotate option
+ self.cli_set(base_path + ['logrotate', 'atop', 'rotate', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_atop_file)
+ self.assertEqual(logrotate_config['rotate'], '50')
+
+ def test_logs_rsyslog_size(self):
+ # test for size option
+ self.cli_set(base_path + ['logrotate', 'messages', 'max-size', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_rsyslog_file)
+ self.assertEqual(logrotate_config['size'], '50M')
+
+ def test_logs_rsyslog_rotate(self):
+ # test for rotate option
+ self.cli_set(base_path + ['logrotate', 'messages', 'rotate', '50'])
+ self.cli_commit()
+
+ # read the config file and check content
+ logrotate_config = logrotate_config_parse(logrotate_rsyslog_file)
+ self.assertEqual(logrotate_config['rotate'], '50')
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_system_ntp.py
index e8cc64463..e2821687c 100755
--- a/smoketest/scripts/cli/test_system_ntp.py
+++ b/smoketest/scripts/cli/test_system_ntp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -14,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 re
import unittest
from base_vyostest_shim import VyOSUnitTestSHIM
@@ -29,17 +28,14 @@ PROCESS_NAME = 'ntpd'
NTP_CONF = '/run/ntpd/ntpd.conf'
base_path = ['system', 'ntp']
-def get_config_value(key):
- tmp = read_file(NTP_CONF)
- tmp = re.findall(r'\n?{}\s+(.*)'.format(key), tmp)
- # remove possible trailing whitespaces
- return [item.strip() for item in tmp]
-
class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
+ @classmethod
+ def setUpClass(cls):
+ super(TestSystemNTP, 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)
+ cls.cli_delete(cls, base_path)
def tearDown(self):
self.cli_delete(base_path)
@@ -47,35 +43,38 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.assertFalse(process_named_running(PROCESS_NAME))
- def test_ntp_options(self):
+ def test_01_ntp_options(self):
# Test basic NTP support with multiple servers and their options
servers = ['192.0.2.1', '192.0.2.2']
options = ['noselect', 'preempt', 'prefer']
- ntp_pool = 'pool.vyos.io'
+ pools = ['pool.vyos.io']
for server in servers:
for option in options:
self.cli_set(base_path + ['server', server, option])
# Test NTP pool
- self.cli_set(base_path + ['server', ntp_pool, 'pool'])
+ for pool in pools:
+ self.cli_set(base_path + ['server', pool, 'pool'])
# commit changes
self.cli_commit()
# Check generated configuration
- tmp = get_config_value('server')
- for server in servers:
- test = f'{server} iburst ' + ' '.join(options)
- self.assertTrue(test in tmp)
+ config = read_file(NTP_CONF)
+ self.assertIn('driftfile /var/lib/ntp/ntp.drift', config)
+ self.assertIn('restrict default noquery nopeer notrap nomodify', config)
+ self.assertIn('restrict source nomodify notrap noquery', config)
+ self.assertIn('restrict 127.0.0.1', config)
+ self.assertIn('restrict -6 ::1', config)
- tmp = get_config_value('pool')
- self.assertTrue(f'{ntp_pool} iburst' in tmp)
+ for server in servers:
+ self.assertIn(f'server {server} iburst ' + ' '.join(options), config)
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ for pool in pools:
+ self.assertIn(f'pool {pool} iburst', config)
- def test_ntp_clients(self):
+ def test_02_ntp_clients(self):
# Test the allowed-networks statement
listen_address = ['127.0.0.1', '::1']
for listen in listen_address:
@@ -96,23 +95,18 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Check generated client address configuration
+ config = read_file(NTP_CONF)
+ self.assertIn('restrict default ignore', config)
+
for network in networks:
network_address = address_from_cidr(network)
network_netmask = netmask_from_cidr(network)
-
- tmp = get_config_value(f'restrict {network_address}')[0]
- test = f'mask {network_netmask} nomodify notrap nopeer'
- self.assertTrue(tmp in test)
+ self.assertIn(f'restrict {network_address} mask {network_netmask} nomodify notrap nopeer', config)
# Check listen address
- tmp = get_config_value('interface')
- test = ['ignore wildcard']
+ self.assertIn('interface ignore wildcard', config)
for listen in listen_address:
- test.append(f'listen {listen}')
- self.assertEqual(tmp, test)
-
- # Check for running process
- self.assertTrue(process_named_running(PROCESS_NAME))
+ self.assertIn(f'interface listen {listen}', config)
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 c710aec6e..8a6514d57 100755
--- a/smoketest/scripts/cli/test_vpn_ipsec.py
+++ b/smoketest/scripts/cli/test_vpn_ipsec.py
@@ -28,6 +28,7 @@ vti_path = ['interfaces', 'vti']
nhrp_path = ['protocols', 'nhrp']
base_path = ['vpn', 'ipsec']
+charon_file = '/etc/strongswan.d/charon.conf'
dhcp_waiting_file = '/tmp/ipsec_dhcp_waiting'
swanctl_file = '/etc/swanctl/swanctl.conf'
@@ -111,9 +112,21 @@ rgiyCHemtMepq57Pl1Nmj49eEA==
"""
class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
- def setUp(self):
- self.cli_set(base_path + ['interface', f'{interface}.{vif}'])
+ @classmethod
+ def setUpClass(cls):
+ super(TestVPNIPsec, 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, base_path + ['interface', f'{interface}.{vif}'])
+ @classmethod
+ def tearDownClass(cls):
+ super(TestVPNIPsec, cls).tearDownClass()
+ cls.cli_delete(cls, base_path + ['interface', f'{interface}.{vif}'])
+
+ def setUp(self):
# Set IKE/ESP Groups
self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'encryption', 'aes128'])
self.cli_set(base_path + ['esp-group', esp_group, 'proposal', '1', 'hash', 'sha1'])
@@ -127,7 +140,6 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_delete(tunnel_path)
- self.cli_delete(ethernet_path)
self.cli_commit()
# Check for no longer running process
@@ -158,8 +170,14 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# Site to site
local_address = '192.0.2.10'
+ priority = '20'
+ life_bytes = '100000'
+ life_packets = '2000000'
peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+ self.cli_set(base_path + ['esp-group', esp_group, 'life-bytes', life_bytes])
+ self.cli_set(base_path + ['esp-group', esp_group, 'life-packets', life_packets])
+
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
self.cli_set(peer_base_path + ['ike-group', ike_group])
@@ -173,6 +191,10 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
self.cli_set(peer_base_path + ['tunnel', '1', 'remote', 'prefix', '172.17.11.0/24'])
self.cli_set(peer_base_path + ['tunnel', '1', 'remote', 'port', '443'])
+ self.cli_set(peer_base_path + ['tunnel', '2', 'local', 'prefix', '10.1.0.0/16'])
+ self.cli_set(peer_base_path + ['tunnel', '2', 'remote', 'prefix', '10.2.0.0/16'])
+ self.cli_set(peer_base_path + ['tunnel', '2', 'priority', priority])
+
self.cli_commit()
# Verify strongSwan configuration
@@ -180,6 +202,8 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
swanctl_conf_lines = [
f'version = 2',
f'auth = psk',
+ f'life_bytes = {life_bytes}',
+ f'life_packets = {life_packets}',
f'rekey_time = 28800s', # default value
f'proposals = aes128-sha1-modp1024',
f'esp_proposals = aes128-sha1-modp1024',
@@ -187,8 +211,15 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'local_addrs = {local_address} # dhcp:no',
f'remote_addrs = {peer_ip}',
f'mode = tunnel',
+ f'peer_{peer_ip.replace(".","-")}_tunnel_1',
f'local_ts = 172.16.10.0/24[tcp/443],172.16.11.0/24[tcp/443]',
- f'remote_ts = 172.17.10.0/24[tcp/443],172.17.11.0/24[tcp/443]'
+ f'remote_ts = 172.17.10.0/24[tcp/443],172.17.11.0/24[tcp/443]',
+ f'mode = tunnel',
+ f'peer_{peer_ip.replace(".","-")}_tunnel_2',
+ f'local_ts = 10.1.0.0/16',
+ f'remote_ts = 10.2.0.0/16',
+ f'priority = {priority}',
+ f'mode = tunnel',
]
for line in swanctl_conf_lines:
self.assertIn(line, swanctl_conf)
@@ -213,6 +244,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
+ self.cli_set(peer_base_path + ['connection-type', 'none'])
self.cli_set(peer_base_path + ['ike-group', ike_group])
self.cli_set(peer_base_path + ['default-esp-group', esp_group])
self.cli_set(peer_base_path + ['local-address', local_address])
@@ -241,6 +273,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
f'mode = tunnel',
f'local_ts = 172.16.10.0/24,172.16.11.0/24',
f'remote_ts = 172.17.10.0/24,172.17.11.0/24',
+ f'start_action = none',
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}"'
@@ -392,5 +425,75 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase):
# There is only one VTI test so no need to delete this globally in tearDown()
self.cli_delete(vti_path)
+
+ def test_06_flex_vpn_vips(self):
+ local_address = '192.0.2.5'
+ local_id = 'vyos-r1'
+ remote_id = 'vyos-r2'
+ peer_base_path = base_path + ['site-to-site', 'peer', peer_ip]
+
+ self.cli_set(tunnel_path + ['tun1', 'encapsulation', 'gre'])
+ self.cli_set(tunnel_path + ['tun1', 'source-address', local_address])
+
+ self.cli_set(base_path + ['interface', interface])
+ self.cli_set(base_path + ['options', 'flexvpn'])
+ self.cli_set(base_path + ['options', 'interface', 'tun1'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'ikev2-reauth', 'no'])
+ self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2'])
+
+ self.cli_set(peer_base_path + ['authentication', 'id', local_id])
+ self.cli_set(peer_base_path + ['authentication', 'mode', 'pre-shared-secret'])
+ self.cli_set(peer_base_path + ['authentication', 'pre-shared-secret', secret])
+ self.cli_set(peer_base_path + ['authentication', 'remote-id', remote_id])
+ self.cli_set(peer_base_path + ['connection-type', 'initiate'])
+ self.cli_set(peer_base_path + ['ike-group', ike_group])
+ self.cli_set(peer_base_path + ['default-esp-group', esp_group])
+ self.cli_set(peer_base_path + ['local-address', local_address])
+ self.cli_set(peer_base_path + ['tunnel', '1', 'protocol', 'gre'])
+
+ self.cli_set(peer_base_path + ['virtual-address', '203.0.113.55'])
+ self.cli_set(peer_base_path + ['virtual-address', '203.0.113.56'])
+
+ self.cli_commit()
+
+ # Verify strongSwan configuration
+ swanctl_conf = read_file(swanctl_file)
+ swanctl_conf_lines = [
+ f'version = 2',
+ f'vips = 203.0.113.55, 203.0.113.56',
+ f'life_time = 3600s', # default value
+ f'local_addrs = {local_address} # dhcp:no',
+ f'remote_addrs = {peer_ip}',
+ f'peer_{peer_ip.replace(".","-")}_tunnel_1',
+ f'mode = tunnel',
+ ]
+
+ for line in swanctl_conf_lines:
+ self.assertIn(line, swanctl_conf)
+
+ swanctl_secrets_lines = [
+ f'id-local = {local_address} # dhcp:no',
+ f'id-remote = {peer_ip}',
+ f'id-localid = {local_id}',
+ f'id-remoteid = {remote_id}',
+ f'secret = "{secret}"',
+ ]
+
+ for line in swanctl_secrets_lines:
+ self.assertIn(line, swanctl_conf)
+
+ # Verify charon configuration
+ charon_conf = read_file(charon_file)
+ charon_conf_lines = [
+ f'# Cisco FlexVPN',
+ f'cisco_flexvpn = yes',
+ f'install_virtual_ip = yes',
+ f'install_virtual_ip_on = tun1',
+ ]
+
+ for line in charon_conf_lines:
+ self.assertIn(line, charon_conf)
+
+
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 b0e859b5c..bda279342 100755
--- a/smoketest/scripts/cli/test_vpn_openconnect.py
+++ b/smoketest/scripts/cli/test_vpn_openconnect.py
@@ -24,8 +24,27 @@ OCSERV_CONF = '/run/ocserv/ocserv.conf'
base_path = ['vpn', 'openconnect']
pki_path = ['pki']
-cert_data = 'MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIwWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIxMDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu+JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3LftzngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93+dm/LDnp7C0='
-key_data = 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww'
+
+cert_data = """
+MIICFDCCAbugAwIBAgIUfMbIsB/ozMXijYgUYG80T1ry+mcwCgYIKoZIzj0EAwIw
+WTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNv
+bWUtQ2l0eTENMAsGA1UECgwEVnlPUzESMBAGA1UEAwwJVnlPUyBUZXN0MB4XDTIx
+MDcyMDEyNDUxMloXDTI2MDcxOTEyNDUxMlowWTELMAkGA1UEBhMCR0IxEzARBgNV
+BAgMClNvbWUtU3RhdGUxEjAQBgNVBAcMCVNvbWUtQ2l0eTENMAsGA1UECgwEVnlP
+UzESMBAGA1UEAwwJVnlPUyBUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
+01HrLcNttqq4/PtoMua8rMWEkOdBu7vP94xzDO7A8C92ls1v86eePy4QllKCzIw3
+QxBIoCuH2peGRfWgPRdFsKNhMF8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
+BAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMB0GA1UdDgQWBBSu
++JnU5ZC4mkuEpqg2+Mk4K79oeDAKBggqhkjOPQQDAgNHADBEAiBEFdzQ/Bc3Lftz
+ngrY605UhA6UprHhAogKgROv7iR4QgIgEFUxTtW3xXJcnUPWhhUFhyZoqfn8dE93
++dm/LDnp7C0=
+"""
+
+key_data = """
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx
+2KMIuze7ucKUt/lBEB2wc03IxXyhRANCAATTUestw222qrj8+2gy5rysxYSQ50G7
+u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww
+"""
class TestVpnOpenconnect(VyOSUnitTestSHIM.TestCase):
def tearDown(self):
@@ -37,18 +56,21 @@ class TestVpnOpenconnect(VyOSUnitTestSHIM.TestCase):
def test_vpn(self):
user = 'vyos_user'
password = 'vyos_pass'
+ otp = '37500000026900000000200000000000'
+
self.cli_delete(pki_path)
self.cli_delete(base_path)
- self.cli_set(pki_path + ['ca', 'openconnect', 'certificate', cert_data])
- self.cli_set(pki_path + ['certificate', 'openconnect', 'certificate', cert_data])
- self.cli_set(pki_path + ['certificate', 'openconnect', 'private', 'key', key_data])
+ self.cli_set(pki_path + ['ca', 'openconnect', 'certificate', cert_data.replace('\n','')])
+ self.cli_set(pki_path + ['certificate', 'openconnect', 'certificate', cert_data.replace('\n','')])
+ self.cli_set(pki_path + ['certificate', 'openconnect', 'private', 'key', key_data.replace('\n','')])
- self.cli_set(base_path + ["authentication", "local-users", "username", user, "password", password])
- self.cli_set(base_path + ["authentication", "mode", "local"])
- self.cli_set(base_path + ["network-settings", "client-ip-settings", "subnet", "192.0.2.0/24"])
- self.cli_set(base_path + ["ssl", "ca-certificate", 'openconnect'])
- self.cli_set(base_path + ["ssl", "certificate", 'openconnect'])
+ self.cli_set(base_path + ['authentication', 'local-users', 'username', user, 'password', password])
+ self.cli_set(base_path + ['authentication', 'local-users', 'username', user, 'otp', 'key', otp])
+ self.cli_set(base_path + ['authentication', 'mode', 'local', 'password-otp'])
+ self.cli_set(base_path + ['network-settings', 'client-ip-settings', 'subnet', '192.0.2.0/24'])
+ self.cli_set(base_path + ['ssl', 'ca-certificate', 'openconnect'])
+ self.cli_set(base_path + ['ssl', 'certificate', 'openconnect'])
self.cli_commit()
diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py
index 5ffa9c086..176c095fb 100755
--- a/smoketest/scripts/cli/test_vrf.py
+++ b/smoketest/scripts/cli/test_vrf.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -25,9 +25,10 @@ from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
from vyos.ifconfig import Section
-from vyos.template import is_ipv6
+from vyos.template import is_ipv4
from vyos.util import cmd
from vyos.util import read_file
+from vyos.util import get_interface_config
from vyos.validate import is_intf_addr_assigned
base_path = ['vrf']
@@ -48,7 +49,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
if not '.' in tmp:
cls._interfaces.append(tmp)
# call base-classes classmethod
- super(cls, cls).setUpClass()
+ super(VRFTest, cls).setUpClass()
def tearDown(self):
# delete all VRFs
@@ -105,10 +106,13 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
frrconfig = self.getFRRconfig(f'vrf {vrf}')
self.assertIn(f' vni {table}', frrconfig)
+ tmp = get_interface_config(vrf)
+ self.assertEqual(int(table), tmp['linkinfo']['info_data']['table'])
+
# Increment table ID for the next run
table = str(int(table) + 1)
- def test_vrf_loopback_ips(self):
+ def test_vrf_loopbacks_ips(self):
table = '2000'
for vrf in vrfs:
base = base_path + ['name', vrf]
@@ -119,10 +123,16 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
# Verify VRF configuration
+ loopbacks = ['127.0.0.1', '::1']
for vrf in vrfs:
- self.assertTrue(vrf in interfaces())
- self.assertTrue(is_intf_addr_assigned(vrf, '127.0.0.1'))
- self.assertTrue(is_intf_addr_assigned(vrf, '::1'))
+ # 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')
+ # Test for proper loopback IP assignment
+ for addr in loopbacks:
+ self.assertTrue(is_intf_addr_assigned(vrf, addr))
def test_vrf_bind_all(self):
table = '2000'
@@ -174,11 +184,11 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# commit changes
self.cli_commit()
- # Verify & cleanup
+ # Verify VRF assignmant
for interface in self._interfaces:
- # os.readlink resolves to: '../../../../../virtual/net/foovrf'
- tmp = os.readlink(f'/sys/class/net/{interface}/master').split('/')[-1]
- self.assertEqual(tmp, vrf)
+ tmp = get_interface_config(interface)
+ self.assertEqual(vrf, tmp['master'])
+
# cleanup
section = Section.section(interface)
self.cli_delete(['interfaces', section, interface, 'vrf'])
@@ -220,5 +230,66 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):
# Increment table ID for the next run
table = str(int(table) + 1)
+ def test_vrf_link_local_ip_addresses(self):
+ # Testcase for issue T4331
+ table = '100'
+ vrf = 'orange'
+ interface = 'dum9998'
+ addresses = ['192.0.2.1/26', '2001:db8:9998::1/64', 'fe80::1/64']
+
+ for address in addresses:
+ self.cli_set(['interfaces', 'dummy', interface, 'address', address])
+
+ # Create dummy interfaces
+ self.cli_commit()
+
+ # ... and verify IP addresses got assigned
+ for address in addresses:
+ self.assertTrue(is_intf_addr_assigned(interface, address))
+
+ # Move interface to VRF
+ self.cli_set(base_path + ['name', vrf, 'table', table])
+ self.cli_set(['interfaces', 'dummy', interface, 'vrf', vrf])
+
+ # Apply VRF config
+ self.cli_commit()
+ # Ensure VRF got created
+ self.assertIn(vrf, interfaces())
+ # ... and IP addresses are still assigned
+ for address in addresses:
+ self.assertTrue(is_intf_addr_assigned(interface, address))
+ # Verify VRF table ID
+ tmp = get_interface_config(vrf)
+ self.assertEqual(int(table), tmp['linkinfo']['info_data']['table'])
+
+ # Verify interface is assigned to VRF
+ tmp = get_interface_config(interface)
+ self.assertEqual(vrf, tmp['master'])
+
+ # Delete Interface
+ self.cli_delete(['interfaces', 'dummy', interface])
+ self.cli_commit()
+
+ def test_vrf_disable_forwarding(self):
+ table = '2000'
+ for vrf in vrfs:
+ base = base_path + ['name', vrf]
+ self.cli_set(base + ['table', table])
+ self.cli_set(base + ['ip', 'disable-forwarding'])
+ self.cli_set(base + ['ipv6', 'disable-forwarding'])
+ table = str(int(table) + 1)
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify VRF configuration
+ loopbacks = ['127.0.0.1', '::1']
+ for vrf in vrfs:
+ # 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')
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_zone_policy.py b/smoketest/scripts/cli/test_zone_policy.py
new file mode 100755
index 000000000..2c580e2f1
--- /dev/null
+++ b/smoketest/scripts/cli/test_zone_policy.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.util import cmd
+
+class TestZonePolicy(VyOSUnitTestSHIM.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TestZonePolicy, cls).setUpClass()
+ cls.cli_set(cls, ['firewall', 'name', 'smoketest', 'default-action', 'drop'])
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.cli_delete(cls, ['firewall'])
+ super(TestZonePolicy, cls).tearDownClass()
+
+ def tearDown(self):
+ self.cli_delete(['zone-policy'])
+ self.cli_commit()
+
+ def test_basic_zone(self):
+ self.cli_set(['zone-policy', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
+ self.cli_set(['zone-policy', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
+ self.cli_set(['zone-policy', 'zone', 'smoketest-local', 'local-zone'])
+ self.cli_set(['zone-policy', 'zone', 'smoketest-local', 'from', 'smoketest-eth0', 'firewall', 'name', 'smoketest'])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['chain VZONE_smoketest-eth0'],
+ ['chain VZONE_smoketest-local_IN'],
+ ['chain VZONE_smoketest-local_OUT'],
+ ['oifname { "eth0" }', 'jump VZONE_smoketest-eth0'],
+ ['jump VZONE_smoketest-local_IN'],
+ ['jump VZONE_smoketest-local_OUT'],
+ ['iifname { "eth0" }', 'jump NAME_smoketest'],
+ ['oifname { "eth0" }', 'jump NAME_smoketest']
+ ]
+
+ nftables_output = cmd('sudo nft list table ip filter')
+
+ for search in nftables_search:
+ matched = False
+ for line in nftables_output.split("\n"):
+ if all(item in line for item in search):
+ matched = True
+ break
+ self.assertTrue(matched)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)