diff options
Diffstat (limited to 'smoketest/scripts/cli/base_interfaces_test.py')
-rw-r--r-- | smoketest/scripts/cli/base_interfaces_test.py | 354 |
1 files changed, 275 insertions, 79 deletions
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 55343b893..51ccbc9e6 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2022 VyOS maintainers and contributors +# Copyright (C) 2019-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -26,15 +26,23 @@ from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSession from vyos.configsession import ConfigSessionError +from vyos.defaults import directories from vyos.ifconfig import Interface from vyos.ifconfig import Section -from vyos.util import read_file -from vyos.util import cmd -from vyos.util import dict_search -from vyos.util import process_named_running -from vyos.util import get_interface_config -from vyos.validate import is_intf_addr_assigned -from vyos.validate import is_ipv6_link_local +from vyos.utils.file import read_file +from vyos.utils.dict import dict_search +from vyos.utils.process import process_named_running +from vyos.utils.network import get_interface_config +from vyos.utils.network import get_interface_vrf +from vyos.utils.process import cmd +from vyos.utils.network import is_intf_addr_assigned +from vyos.utils.network import is_ipv6_link_local +from vyos.xml_ref import cli_defined + +dhclient_base_dir = directories['isc_dhclient_dir'] +dhclient_process_name = 'dhclient' +dhcp6c_base_dir = directories['dhcp6_client_dir'] +dhcp6c_process_name = 'dhcp6c' def is_mirrored_to(interface, mirror_if, qdisc): """ @@ -65,6 +73,7 @@ class BasicInterfaceTest: _test_ipv6_pd = False _test_ipv6_dhcpc6 = False _test_mirror = False + _test_vrf = False _base_path = [] _options = {} @@ -82,6 +91,18 @@ class BasicInterfaceTest: def setUpClass(cls): super(BasicInterfaceTest.TestCase, cls).setUpClass() + # XXX the case of test_vif_8021q_mtu_limits, below, shows that + # we should extend cli_defined to support more complex queries + cls._test_vlan = cli_defined(cls._base_path, 'vif') + cls._test_qinq = cli_defined(cls._base_path, 'vif-s') + cls._test_dhcp = cli_defined(cls._base_path, 'dhcp-options') + cls._test_ip = cli_defined(cls._base_path, 'ip') + cls._test_ipv6 = cli_defined(cls._base_path, 'ipv6') + cls._test_ipv6_dhcpc6 = cli_defined(cls._base_path, 'dhcpv6-options') + cls._test_ipv6_pd = cli_defined(cls._base_path + ['dhcpv6-options'], 'pd') + cls._test_mtu = cli_defined(cls._base_path, 'mtu') + cls._test_vrf = cli_defined(cls._base_path, 'vrf') + # Setup mirror interfaces for SPAN (Switch Port Analyzer) for span in cls._mirror_interfaces: section = Section.section(span) @@ -104,9 +125,15 @@ class BasicInterfaceTest: for intf in self._interfaces: self.assertNotIn(intf, interfaces()) - # No daemon that was started during a test should remain running + # No daemon started during tests should remain running for daemon in ['dhcp6c', 'dhclient']: - self.assertFalse(process_named_running(daemon)) + # if _interface list is populated do a more fine grained search + # by also checking the cmd arguments passed to the daemon + if self._interfaces: + for tmp in self._interfaces: + self.assertFalse(process_named_running(daemon, tmp)) + else: + self.assertFalse(process_named_running(daemon)) def test_dhcp_disable_interface(self): if not self._test_dhcp: @@ -119,11 +146,9 @@ class BasicInterfaceTest: 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 + # https://vyos.dev/T2767 self.cli_set(self._base_path + [interface, 'address', 'dhcp']) self.cli_commit() @@ -133,6 +158,99 @@ class BasicInterfaceTest: flags = read_file(f'/sys/class/net/{interface}/flags') self.assertEqual(int(flags, 16) & 1, 0) + def test_dhcp_client_options(self): + if not self._test_dhcp or not self._test_vrf: + self.skipTest('not supported') + + distance = '100' + + for interface in self._interfaces: + for option in self._options.get(interface, []): + self.cli_set(self._base_path + [interface] + option.split()) + + self.cli_set(self._base_path + [interface, 'address', 'dhcp']) + self.cli_set(self._base_path + [interface, 'dhcp-options', 'default-route-distance', distance]) + + self.cli_commit() + + for interface in self._interfaces: + # Check if dhclient process runs + dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface) + self.assertTrue(dhclient_pid) + + dhclient_config = read_file(f'{dhclient_base_dir}/dhclient_{interface}.conf') + self.assertIn('request subnet-mask, broadcast-address, routers, domain-name-servers', dhclient_config) + self.assertIn('require subnet-mask;', dhclient_config) + + # and the commandline has the appropriate options + cmdline = read_file(f'/proc/{dhclient_pid}/cmdline') + self.assertIn(f'-e\x00IF_METRIC={distance}', cmdline) + + def test_dhcp_vrf(self): + if not self._test_dhcp or not self._test_vrf: + self.skipTest('not supported') + + vrf_name = 'purple4' + self.cli_set(['vrf', 'name', vrf_name, 'table', '65000']) + + for interface in self._interfaces: + for option in self._options.get(interface, []): + self.cli_set(self._base_path + [interface] + option.split()) + + self.cli_set(self._base_path + [interface, 'address', 'dhcp']) + self.cli_set(self._base_path + [interface, 'vrf', vrf_name]) + + self.cli_commit() + + # Validate interface state + for interface in self._interfaces: + tmp = get_interface_vrf(interface) + self.assertEqual(tmp, vrf_name) + + # Check if dhclient process runs + dhclient_pid = process_named_running(dhclient_process_name, cmdline=interface) + self.assertTrue(dhclient_pid) + # .. inside the appropriate VRF instance + vrf_pids = cmd(f'ip vrf pids {vrf_name}') + self.assertIn(str(dhclient_pid), vrf_pids) + # and the commandline has the appropriate options + cmdline = read_file(f'/proc/{dhclient_pid}/cmdline') + self.assertIn('-e\x00IF_METRIC=210', cmdline) # 210 is the default value + + self.cli_delete(['vrf', 'name', vrf_name]) + + def test_dhcpv6_vrf(self): + if not self._test_ipv6_dhcpc6 or not self._test_vrf: + self.skipTest('not supported') + + vrf_name = 'purple6' + self.cli_set(['vrf', 'name', vrf_name, 'table', '65001']) + + # 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: + for option in self._options.get(interface, []): + self.cli_set(self._base_path + [interface] + option.split()) + + self.cli_set(self._base_path + [interface, 'address', 'dhcpv6']) + self.cli_set(self._base_path + [interface, 'vrf', vrf_name]) + + self.cli_commit() + + # Validate interface state + for interface in self._interfaces: + tmp = get_interface_vrf(interface) + self.assertEqual(tmp, vrf_name) + + # Check if dhclient process runs + tmp = process_named_running(dhcp6c_process_name, cmdline=interface) + self.assertTrue(tmp) + # .. inside the appropriate VRF instance + vrf_pids = cmd(f'ip vrf pids {vrf_name}') + self.assertIn(str(tmp), vrf_pids) + + self.cli_delete(['vrf', 'name', vrf_name]) + def test_span_mirror(self): if not self._mirror_interfaces: self.skipTest('not supported') @@ -360,7 +478,7 @@ class BasicInterfaceTest: # is the Wireless test will first load the wifi kernel hwsim module # which creates a wlan0 and wlan1 interface which will fail the # tearDown() test in the end that no interface is allowed to survive! - if not self._test_vlan: + if not self._test_vlan or not self._test_mtu: self.skipTest('not supported') mtu_1500 = '1500' @@ -476,7 +594,7 @@ class BasicInterfaceTest: self.assertEqual(to_key, new_egress_qos_to) def test_vif_8021q_lower_up_down(self): - # Testcase for https://phabricator.vyos.net/T3349 + # Testcase for https://vyos.dev/T3349 if not self._test_vlan: self.skipTest('not supported') @@ -634,58 +752,94 @@ class BasicInterfaceTest: self.cli_set(path + option.split()) # Options - self.cli_set(path + ['ip', 'adjust-mss', mss]) - self.cli_set(path + ['ip', 'arp-cache-timeout', arp_tmo]) - self.cli_set(path + ['ip', 'disable-arp-filter']) - self.cli_set(path + ['ip', 'disable-forwarding']) - self.cli_set(path + ['ip', 'enable-directed-broadcast']) - self.cli_set(path + ['ip', 'enable-arp-accept']) - self.cli_set(path + ['ip', 'enable-arp-announce']) - self.cli_set(path + ['ip', 'enable-arp-ignore']) - self.cli_set(path + ['ip', 'enable-proxy-arp']) - self.cli_set(path + ['ip', 'proxy-arp-pvlan']) - self.cli_set(path + ['ip', 'source-validation', 'loose']) + if cli_defined(self._base_path + ['ip'], 'adjust-mss'): + self.cli_set(path + ['ip', 'adjust-mss', mss]) + + if cli_defined(self._base_path + ['ip'], 'arp-cache-timeout'): + self.cli_set(path + ['ip', 'arp-cache-timeout', arp_tmo]) + + if cli_defined(self._base_path + ['ip'], 'disable-arp-filter'): + self.cli_set(path + ['ip', 'disable-arp-filter']) + + if cli_defined(self._base_path + ['ip'], 'disable-forwarding'): + self.cli_set(path + ['ip', 'disable-forwarding']) + + if cli_defined(self._base_path + ['ip'], 'enable-directed-broadcast'): + self.cli_set(path + ['ip', 'enable-directed-broadcast']) + + if cli_defined(self._base_path + ['ip'], 'enable-arp-accept'): + self.cli_set(path + ['ip', 'enable-arp-accept']) + + if cli_defined(self._base_path + ['ip'], 'enable-arp-announce'): + self.cli_set(path + ['ip', 'enable-arp-announce']) + + if cli_defined(self._base_path + ['ip'], 'enable-arp-ignore'): + self.cli_set(path + ['ip', 'enable-arp-ignore']) + + if cli_defined(self._base_path + ['ip'], 'enable-proxy-arp'): + self.cli_set(path + ['ip', 'enable-proxy-arp']) + + if cli_defined(self._base_path + ['ip'], 'proxy-arp-pvlan'): + self.cli_set(path + ['ip', 'proxy-arp-pvlan']) + + if cli_defined(self._base_path + ['ip'], 'source-validation'): + self.cli_set(path + ['ip', 'source-validation', 'loose']) self.cli_commit() for interface in self._interfaces: - 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'tcp option maxseg size set {mss}', line) + if cli_defined(self._base_path + ['ip'], 'adjust-mss'): + 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'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 + if cli_defined(self._base_path + ['ip'], 'arp-cache-timeout'): + 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 proc_base = f'/proc/sys/net/ipv4/conf/{interface}' - tmp = read_file(f'{proc_base}/arp_filter') - self.assertEqual('0', tmp) + if cli_defined(self._base_path + ['ip'], 'disable-arp-filter'): + tmp = read_file(f'{proc_base}/arp_filter') + self.assertEqual('0', tmp) - tmp = read_file(f'{proc_base}/arp_accept') - self.assertEqual('1', tmp) + if cli_defined(self._base_path + ['ip'], 'enable-arp-accept'): + tmp = read_file(f'{proc_base}/arp_accept') + self.assertEqual('1', tmp) - tmp = read_file(f'{proc_base}/arp_announce') - self.assertEqual('1', tmp) + if cli_defined(self._base_path + ['ip'], 'enable-arp-announce'): + tmp = read_file(f'{proc_base}/arp_announce') + self.assertEqual('1', tmp) - tmp = read_file(f'{proc_base}/arp_ignore') - self.assertEqual('1', tmp) + if cli_defined(self._base_path + ['ip'], 'enable-arp-ignore'): + tmp = read_file(f'{proc_base}/arp_ignore') + self.assertEqual('1', tmp) - tmp = read_file(f'{proc_base}/forwarding') - self.assertEqual('0', tmp) + if cli_defined(self._base_path + ['ip'], 'disable-forwarding'): + tmp = read_file(f'{proc_base}/forwarding') + self.assertEqual('0', tmp) - tmp = read_file(f'{proc_base}/bc_forwarding') - self.assertEqual('1', tmp) + if cli_defined(self._base_path + ['ip'], 'enable-directed-broadcast'): + tmp = read_file(f'{proc_base}/bc_forwarding') + self.assertEqual('1', tmp) - tmp = read_file(f'{proc_base}/proxy_arp') - self.assertEqual('1', tmp) + if cli_defined(self._base_path + ['ip'], 'enable-proxy-arp'): + tmp = read_file(f'{proc_base}/proxy_arp') + self.assertEqual('1', tmp) - tmp = read_file(f'{proc_base}/proxy_arp_pvlan') - self.assertEqual('1', tmp) + if cli_defined(self._base_path + ['ip'], 'proxy-arp-pvlan'): + tmp = read_file(f'{proc_base}/proxy_arp_pvlan') + self.assertEqual('1', tmp) - tmp = read_file(f'{proc_base}/rp_filter') - self.assertEqual('2', tmp) + if cli_defined(self._base_path + ['ip'], 'source-validation'): + base_options = f'iifname "{interface}"' + out = cmd('sudo nft list chain ip raw vyos_rpfilter') + for line in out.splitlines(): + if line.startswith(base_options): + self.assertIn('fib saddr oif 0', line) + self.assertIn('drop', line) def test_interface_ipv6_options(self): if not self._test_ipv6: @@ -693,6 +847,8 @@ class BasicInterfaceTest: mss = '1400' dad_transmits = '10' + accept_dad = '0' + source_validation = 'strict' for interface in self._interfaces: path = self._base_path + [interface] @@ -700,26 +856,51 @@ class BasicInterfaceTest: self.cli_set(path + option.split()) # Options - self.cli_set(path + ['ipv6', 'adjust-mss', mss]) - self.cli_set(path + ['ipv6', 'disable-forwarding']) - self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits]) + if cli_defined(self._base_path + ['ipv6'], 'adjust-mss'): + self.cli_set(path + ['ipv6', 'adjust-mss', mss]) - self.cli_commit() + if cli_defined(self._base_path + ['ipv6'], 'accept-dad'): + self.cli_set(path + ['ipv6', 'accept-dad', accept_dad]) - for interface in self._interfaces: - 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'tcp option maxseg size set {mss}', line) + if cli_defined(self._base_path + ['ipv6'], 'dup-addr-detect-transmits'): + self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits]) - proc_base = f'/proc/sys/net/ipv6/conf/{interface}' + if cli_defined(self._base_path + ['ipv6'], 'disable-forwarding'): + self.cli_set(path + ['ipv6', 'disable-forwarding']) - tmp = read_file(f'{proc_base}/forwarding') - self.assertEqual('0', tmp) + if cli_defined(self._base_path + ['ipv6'], 'source-validation'): + self.cli_set(path + ['ipv6', 'source-validation', source_validation]) - tmp = read_file(f'{proc_base}/dad_transmits') - self.assertEqual(dad_transmits, tmp) + self.cli_commit() + + for interface in self._interfaces: + proc_base = f'/proc/sys/net/ipv6/conf/{interface}' + if cli_defined(self._base_path + ['ipv6'], 'adjust-mss'): + 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'tcp option maxseg size set {mss}', line) + + if cli_defined(self._base_path + ['ipv6'], 'accept-dad'): + tmp = read_file(f'{proc_base}/accept_dad') + self.assertEqual(accept_dad, tmp) + + if cli_defined(self._base_path + ['ipv6'], 'dup-addr-detect-transmits'): + tmp = read_file(f'{proc_base}/dad_transmits') + self.assertEqual(dad_transmits, tmp) + + if cli_defined(self._base_path + ['ipv6'], 'disable-forwarding'): + tmp = read_file(f'{proc_base}/forwarding') + self.assertEqual('0', tmp) + + if cli_defined(self._base_path + ['ipv6'], 'source-validation'): + base_options = f'iifname "{interface}"' + out = cmd('sudo nft list chain ip6 raw vyos_rpfilter') + for line in out.splitlines(): + if line.startswith(base_options): + self.assertIn('fib saddr . iif oif 0', line) + self.assertIn('drop', line) def test_dhcpv6_client_options(self): if not self._test_ipv6_dhcpc6: @@ -734,6 +915,7 @@ class BasicInterfaceTest: # Enable DHCPv6 client self.cli_set(path + ['address', 'dhcpv6']) + self.cli_set(path + ['dhcpv6-options', 'no-release']) self.cli_set(path + ['dhcpv6-options', 'rapid-commit']) self.cli_set(path + ['dhcpv6-options', 'parameters-only']) self.cli_set(path + ['dhcpv6-options', 'duid', duid]) @@ -744,7 +926,7 @@ class BasicInterfaceTest: duid_base = 10 for interface in self._interfaces: duid = '00:01:00:01:27:71:db:f0:00:50:00:00:00:{}'.format(duid_base) - dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf') + dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf') self.assertIn(f'interface {interface} ' + '{', dhcpc6_config) self.assertIn(f' request domain-name-servers;', dhcpc6_config) self.assertIn(f' request domain-name;', dhcpc6_config) @@ -755,8 +937,12 @@ class BasicInterfaceTest: self.assertIn('};', dhcpc6_config) duid_base += 1 - # Check for running process - self.assertTrue(process_named_running('dhcp6c')) + # Better ask the process about it's commandline in the future + pid = process_named_running(dhcp6c_process_name, cmdline=interface) + self.assertTrue(pid) + + dhcp6c_options = read_file(f'/proc/{pid}/cmdline') + self.assertIn('-n', dhcp6c_options) def test_dhcpv6pd_auto_sla_id(self): if not self._test_ipv6_pd: @@ -765,7 +951,14 @@ class BasicInterfaceTest: prefix_len = '56' sla_len = str(64 - int(prefix_len)) + # Create delegatee interfaces first to avoid any confusion by dhcpc6 + # this is mainly an "issue" with virtual-ethernet interfaces delegatees = ['dum2340', 'dum2341', 'dum2342', 'dum2343', 'dum2344'] + for delegatee in delegatees: + section = Section.section(delegatee) + self.cli_set(['interfaces', section, delegatee]) + + self.cli_commit() for interface in self._interfaces: path = self._base_path + [interface] @@ -778,8 +971,6 @@ class BasicInterfaceTest: self.cli_set(pd_base + ['length', prefix_len]) for delegatee in delegatees: - section = Section.section(delegatee) - self.cli_set(['interfaces', section, delegatee]) self.cli_set(pd_base + ['interface', delegatee, 'address', address]) # increment interface address address = str(int(address) + 1) @@ -787,7 +978,7 @@ class BasicInterfaceTest: self.cli_commit() for interface in self._interfaces: - dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf') + dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf') # verify DHCPv6 prefix delegation self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config) @@ -805,8 +996,8 @@ class BasicInterfaceTest: # increment interface address address = str(int(address) + 1) - # Check for running process - self.assertTrue(process_named_running('dhcp6c')) + # Check for running process + self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface)) for delegatee in delegatees: # we can already cleanup the test delegatee interface here @@ -821,7 +1012,14 @@ class BasicInterfaceTest: prefix_len = '56' sla_len = str(64 - int(prefix_len)) + # Create delegatee interfaces first to avoid any confusion by dhcpc6 + # this is mainly an "issue" with virtual-ethernet interfaces delegatees = ['dum3340', 'dum3341', 'dum3342', 'dum3343', 'dum3344'] + for delegatee in delegatees: + section = Section.section(delegatee) + self.cli_set(['interfaces', section, delegatee]) + + self.cli_commit() for interface in self._interfaces: path = self._base_path + [interface] @@ -835,8 +1033,6 @@ class BasicInterfaceTest: self.cli_set(pd_base + ['length', prefix_len]) for delegatee in delegatees: - section = Section.section(delegatee) - self.cli_set(['interfaces', section, delegatee]) self.cli_set(pd_base + ['interface', delegatee, 'address', address]) self.cli_set(pd_base + ['interface', delegatee, 'sla-id', sla_id]) @@ -850,7 +1046,7 @@ class BasicInterfaceTest: for interface in self._interfaces: address = '1' sla_id = '1' - dhcpc6_config = read_file(f'/run/dhcp6c/dhcp6c.{interface}.conf') + dhcpc6_config = read_file(f'{dhcp6c_base_dir}/dhcp6c.{interface}.conf') # verify DHCPv6 prefix delegation self.assertIn(f'prefix ::/{prefix_len} infinity;', dhcpc6_config) @@ -866,8 +1062,8 @@ class BasicInterfaceTest: # increment interface address address = str(int(address) + 1) - # Check for running process - self.assertTrue(process_named_running('dhcp6c')) + # Check for running process + self.assertTrue(process_named_running(dhcp6c_process_name, cmdline=interface)) for delegatee in delegatees: # we can already cleanup the test delegatee interface here |