diff options
Diffstat (limited to 'smoketest')
42 files changed, 1950 insertions, 522 deletions
diff --git a/smoketest/bin/vyos-smoketest b/smoketest/bin/vyos-smoketest index cb039db42..135388afe 100755 --- a/smoketest/bin/vyos-smoketest +++ b/smoketest/bin/vyos-smoketest @@ -26,7 +26,7 @@ for root, dirs, files in os.walk('/usr/libexec/vyos/tests/smoke'): test_file = os.path.join(root, name) mode = os.stat(test_file).st_mode - if mode & S_IXOTH: + if name.startswith("test_") and mode & S_IXOTH: print('Running Testcase: ' + test_file) process = Popen([test_file], stdout=PIPE) (output, err) = process.communicate() diff --git a/smoketest/configs/basic-qos b/smoketest/configs/basic-qos deleted file mode 100644 index d9baa4a1f..000000000 --- a/smoketest/configs/basic-qos +++ /dev/null @@ -1,194 +0,0 @@ -interfaces { - ethernet eth0 { - address 100.64.0.1/20 - duplex auto - smp-affinity auto - speed auto - } - ethernet eth1 { - duplex auto - speed auto - vif 10 { - traffic-policy { - in M2 - } - } - vif 20 { - traffic-policy { - out FS - } - } - vif 30 { - traffic-policy { - out MY-HTB - } - } - vif 40 { - traffic-policy { - out SHAPER-FOO - } - } - } -} -system { - config-management { - commit-revisions 100 - } - console { - device ttyS0 { - speed 115200 - } - } - host-name vyos - login { - user vyos { - authentication { - encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 - plaintext-password "" - } - } - } - name-server 192.168.0.1 - syslog { - global { - archive { - file 5 - size 512 - } - facility all { - level info - } - } - } - time-zone Europe/Berlin -} -traffic-policy { - limiter M2 { - class 10 { - bandwidth 120mbit - burst 15k - match ADDRESS10 { - ip { - dscp CS4 - } - } - priority 20 - } - default { - bandwidth 100mbit - burst 15k - } - } - shaper FS { - bandwidth auto - class 10 { - bandwidth 100% - burst 15k - match ADDRESS10 { - ip { - source { - address 172.17.1.2/32 - } - } - } - queue-type fair-queue - set-dscp CS4 - } - class 20 { - bandwidth 100% - burst 15k - match ADDRESS20 { - ip { - source { - address 172.17.1.3/32 - } - } - } - queue-type fair-queue - set-dscp CS5 - } - class 30 { - bandwidth 100% - burst 15k - match ADDRESS30 { - ip { - source { - address 172.17.1.4/32 - } - } - } - queue-type fair-queue - set-dscp CS6 - } - default { - bandwidth 10% - burst 15k - ceiling 100% - priority 7 - queue-type fair-queue - } - } - shaper MY-HTB { - bandwidth 10mbit - class 30 { - bandwidth 10% - burst 15k - ceiling 50% - match ADDRESS30 { - ip { - source { - address 10.1.1.0/24 - } - } - } - priority 5 - queue-type fair-queue - } - class 40 { - bandwidth 90% - burst 15k - ceiling 100% - match ADDRESS40 { - ip { - dscp CS4 - source { - address 10.2.1.0/24 - } - } - } - priority 5 - queue-type fair-queue - } - class 50 { - bandwidth 100% - burst 15k - match ADDRESS50 { - ip { - dscp CS5 - } - } - queue-type fair-queue - set-dscp CS7 - } - default { - bandwidth 10% - burst 15k - ceiling 100% - priority 7 - queue-type fair-queue - set-dscp CS1 - } - } - shaper SHAPER-FOO { - bandwidth 1000mbit - default { - bandwidth 100mbit - burst 15k - queue-type fair-queue - set-dscp CS4 - } - } -} -// Warning: Do not remove the following line. -// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" -// Release version: 1.3.2 diff --git a/smoketest/configs/dialup-router-medium-vpn b/smoketest/configs/dialup-router-medium-vpn index 56722d222..503280017 100644 --- a/smoketest/configs/dialup-router-medium-vpn +++ b/smoketest/configs/dialup-router-medium-vpn @@ -68,9 +68,6 @@ interfaces { mtu 1500 name-server auto password password - traffic-policy { - out shape-17mbit - } user-id vyos password vyos } @@ -96,9 +93,6 @@ interfaces { } smp-affinity auto speed auto - traffic-policy { - out shape-94mbit - } } loopback lo { } @@ -719,24 +713,6 @@ system { } time-zone Pacific/Auckland } -traffic-policy { - shaper shape-17mbit { - bandwidth 17mbit - default { - bandwidth 100% - burst 15k - queue-type fq-codel - } - } - shaper shape-94mbit { - bandwidth 94mbit - default { - bandwidth 100% - burst 15k - queue-type fq-codel - } - } -} /* Warning: Do not remove the following line. */ /* === vyatta-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack-sync@1:conntrack@1:dhcp-relay@2:dhcp-server@5:dns-forwarding@1:firewall@5:ipsec@5:l2tp@1:mdns@1:nat@4:ntp@1:pptp@1:qos@1:quagga@6:snmp@1:ssh@1:system@9:vrrp@2:wanloadbalance@3:webgui@1:webproxy@1:webproxy@2:zone-policy@1" === */ /* Release version: 1.2.6 */ diff --git a/smoketest/configs/egb-igp-route-maps b/smoketest/configs/egb-igp-route-maps new file mode 100644 index 000000000..ca36691d4 --- /dev/null +++ b/smoketest/configs/egb-igp-route-maps @@ -0,0 +1,127 @@ +interfaces { + ethernet eth0 { + address 192.0.2.1/25 + duplex auto + smp-affinity auto + speed auto + } + ethernet eth1 { + address 192.0.2.129/25 + address 2001:db8::1234/64 + duplex auto + smp-affinity auto + speed auto + } + loopback lo { + } +} +policy { + route-map zebra-bgp { + rule 10 { + action permit + } + } + route-map zebra-isis { + rule 10 { + action permit + } + } + route-map zebra-ospf { + rule 10 { + action permit + } + } + route-map zebra-ospfv3 { + rule 10 { + action permit + } + } + route-map zebra-ripng { + rule 10 { + action permit + } + } + route-map zebra-static { + rule 10 { + action permit + } + } +} +protocols { + bgp 100 { + route-map zebra-bgp + } + isis { + interface eth0 { + } + net 49.0001.1921.6800.1002.00 + route-map zebra-isis + } + ospf { + area 0 { + network 192.0.2.0/25 + network 192.0.2.128/25 + } + log-adjacency-changes { + } + parameters { + abr-type cisco + router-id 1.1.1.1 + } + passive-interface default + passive-interface-exclude eth0 + passive-interface-exclude eth1 + route-map zebra-ospf + } + ospfv3 { + area 0 { + interface eth1 + } + parameters { + router-id 1.1.1.1 + } + route-map zebra-ospfv3 + } + ripng { + interface eth1 + route-map zebra-ripng + } + static { + route-map zebra-static + } +} +system { + config-management { + commit-revisions 100 + } + console { + device ttyS0 { + speed 115200 + } + } + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + name-server 192.168.0.1 + syslog { + global { + archive { + file 5 + size 512 + } + facility all { + level info + } + } + } + time-zone Europe/Berlin +} +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3.2 diff --git a/smoketest/configs/qos-basic b/smoketest/configs/qos-basic index f94a5650d..65a888d38 100644 --- a/smoketest/configs/qos-basic +++ b/smoketest/configs/qos-basic @@ -8,7 +8,7 @@ interfaces { ethernet eth1 { address 10.2.1.1/24 traffic-policy { - out M2 + out ISPC } } ethernet eth2 { @@ -16,19 +16,14 @@ interfaces { traffic-policy { out MY-HTB } - } - loopback lo { - } -} -protocols { - static { - route 0.0.0.0/0 { - next-hop 10.9.9.2 { - } - next-hop 10.1.1.1 { + vif 200 { + traffic-policy { + out foo-emulate } } } + loopback lo { + } } system { config-management { @@ -79,24 +74,15 @@ system { } } traffic-policy { - shaper M2 { - bandwidth auto - class 10 { - bandwidth 100% - burst 15k - match ADDRESS10 { - ip { - dscp CS4 - } - } - queue-type fair-queue - set-dscp CS5 - } + shaper ISPC { + bandwidth 600Mbit default { - bandwidth 10mbit - burst 15k - queue-type fair-queue + bandwidth 50% + burst 768k + ceiling 100% + queue-type fq-codel } + description "Outbound Traffic Shaper - ISPC" } shaper MY-HTB { bandwidth 10mbit @@ -120,7 +106,6 @@ traffic-policy { ceiling 100% match ADDRESS40 { ip { - dscp CS4 source { address 10.2.1.0/24 } @@ -133,12 +118,13 @@ traffic-policy { bandwidth 100% burst 15k match ADDRESS50 { - ip { - dscp CS5 + ipv6 { + source { + address "2001:db8::1/64" + } } } queue-type fair-queue - set-dscp CS7 } default { bandwidth 10% @@ -146,7 +132,6 @@ traffic-policy { ceiling 100% priority 7 queue-type fair-queue - set-dscp CS1 } } shaper FS { @@ -162,7 +147,6 @@ traffic-policy { } } queue-type fair-queue - set-dscp CS4 } class 20 { bandwidth 100% @@ -175,7 +159,6 @@ traffic-policy { } } queue-type fair-queue - set-dscp CS5 } class 30 { bandwidth 100% @@ -188,7 +171,6 @@ traffic-policy { } } queue-type fair-queue - set-dscp CS6 } default { bandwidth 10% @@ -198,6 +180,10 @@ traffic-policy { queue-type fair-queue } } + network-emulator foo-emulate { + bandwidth 300mbit + burst 20000 + } } // Warning: Do not remove the following line. // vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 55343b893..2f730abfb 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -123,7 +123,7 @@ class BasicInterfaceTest: # 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() @@ -476,7 +476,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') diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index f1c18d761..99d3b3ca1 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -198,6 +198,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): def test_ipv4_basic_rules(self): name = 'smoketest' interface = 'eth0' + interface_wc = 'l2tp*' mss_range = '501-1460' conn_mark = '555' @@ -207,13 +208,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '1', 'source', 'address', '172.16.20.10']) self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '172.16.10.10']) self.cli_set(['firewall', 'name', name, 'rule', '1', 'log', 'enable']) - self.cli_set(['firewall', 'name', name, 'rule', '1', 'log-level', 'debug']) + self.cli_set(['firewall', 'name', name, 'rule', '1', 'log-options', 'level', 'debug']) self.cli_set(['firewall', 'name', name, 'rule', '1', 'ttl', 'eq', '15']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'reject']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'protocol', 'tcp']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'destination', 'port', '8888']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'log', 'enable']) - self.cli_set(['firewall', 'name', name, 'rule', '2', 'log-level', 'err']) + self.cli_set(['firewall', 'name', name, 'rule', '2', 'log-options', 'level', 'err']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'syn']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'tcp', 'flags', 'not', 'ack']) self.cli_set(['firewall', 'name', name, 'rule', '2', 'ttl', 'gt', '102']) @@ -227,10 +228,12 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '4', 'destination', 'port', '22']) self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'count', '10']) self.cli_set(['firewall', 'name', name, 'rule', '4', 'recent', 'time', 'minute']) + self.cli_set(['firewall', 'name', name, 'rule', '4', 'packet-type', 'host']) self.cli_set(['firewall', 'name', name, 'rule', '5', 'action', 'accept']) self.cli_set(['firewall', 'name', name, 'rule', '5', 'protocol', 'tcp']) self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'flags', 'syn']) self.cli_set(['firewall', 'name', name, 'rule', '5', 'tcp', 'mss', mss_range]) + self.cli_set(['firewall', 'name', name, 'rule', '5', 'packet-type', 'broadcast']) self.cli_set(['firewall', 'name', name, 'rule', '5', 'inbound-interface', 'interface-name', interface]) self.cli_set(['firewall', 'name', name, 'rule', '6', 'action', 'return']) self.cli_set(['firewall', 'name', name, 'rule', '6', 'protocol', 'gre']) @@ -238,6 +241,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '6', 'connection-mark', conn_mark]) self.cli_set(['firewall', 'interface', interface, 'in', 'name', name]) + self.cli_set(['firewall', 'interface', interface_wc, 'in', 'name', name]) self.cli_commit() @@ -245,12 +249,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ [f'iifname "{interface}"', f'jump NAME_{name}'], - ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" level debug', 'ip ttl 15', 'return'], - ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" level err', 'ip ttl > 102', 'reject'], + [f'iifname "{interface_wc}"', f'jump NAME_{name}'], + ['saddr 172.16.20.10', 'daddr 172.16.10.10', 'log prefix "[smoketest-1-A]" log level debug', 'ip ttl 15', 'return'], + ['tcp flags syn / syn,ack', 'tcp dport 8888', 'log prefix "[smoketest-2-R]" log level err', 'ip ttl > 102', 'reject'], ['tcp dport 22', 'limit rate 5/minute', 'return'], ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop'], - ['tcp dport 22', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'drop'], - ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface}"'], + ['tcp dport 22', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'meta pkttype host', 'drop'], + ['tcp flags & syn == syn', f'tcp option maxseg size {mss_range}', f'iifname "{interface}"', 'meta pkttype broadcast'], ['meta l4proto gre', f'oifname "{interface}"', f'ct mark {mark_hex}', 'return'] ] @@ -270,6 +275,10 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name, 'rule', '6', 'packet-length', '1024']) self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '17']) self.cli_set(['firewall', 'name', name, 'rule', '6', 'dscp', '52']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log', 'enable']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'group', '66']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'snapshot-length', '6666']) + self.cli_set(['firewall', 'name', name, 'rule', '6', 'log-options', 'queue-threshold','32000']) self.cli_set(['firewall', 'name', name, 'rule', '7', 'action', 'accept']) self.cli_set(['firewall', 'name', name, 'rule', '7', 'packet-length', '1-30000']) @@ -284,17 +293,28 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'name', name2, 'rule', '1', 'action', 'jump']) self.cli_set(['firewall', 'name', name2, 'rule', '1', 'jump-target', name]) + self.cli_set(['firewall', 'name', name2, 'rule', '2', 'protocol', 'tcp']) + self.cli_set(['firewall', 'name', name2, 'rule', '2', 'action', 'queue']) + self.cli_set(['firewall', 'name', name2, 'rule', '2', 'queue', '3']) + self.cli_set(['firewall', 'name', name2, 'rule', '3', 'protocol', 'udp']) + self.cli_set(['firewall', 'name', name2, 'rule', '3', 'action', 'queue']) + self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue-options', 'fanout']) + self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue-options', 'bypass']) + self.cli_set(['firewall', 'name', name2, 'rule', '3', 'queue', '0-15']) + self.cli_set(['firewall', 'interface', interface, 'in', 'name', name]) self.cli_commit() nftables_search = [ [f'iifname "{interface}"', f'jump NAME_{name}'], - ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', 'return'], + ['ip length { 64, 512, 1024 }', 'ip dscp { 0x11, 0x34 }', f'log prefix "[{name}-6-A]" log group 66 snaplen 6666 queue-threshold 32000', 'return'], ['ip length 1-30000', 'ip length != 60000-65535', 'ip dscp 0x03-0x0b', 'ip dscp != 0x15-0x19', 'return'], [f'log prefix "[{name}-default-D]"', 'drop'], ['ip saddr 198.51.100.1', f'jump NAME_{name}'], - [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}'] + [f'log prefix "[{name2}-default-J]"', f'jump NAME_{name}'], + [f'meta l4proto tcp','queue to 3'], + [f'meta l4proto udp','queue flags bypass,fanout to 0-15'] ] self.verify_nftables(nftables_search, 'ip vyos_filter') @@ -344,7 +364,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'source', 'address', '2002::1']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '2002::1:1']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log', 'enable']) - self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log-level', 'crit']) + self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'log-options', 'level', 'crit']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'reject']) self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'protocol', 'tcp_udp']) @@ -361,7 +381,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): nftables_search = [ [f'iifname "{interface}"', f'jump NAME6_{name}'], - ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" level crit', 'return'], + ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" log level crit', 'return'], ['meta l4proto { tcp, udp }', 'th dport 8888', f'iifname "{interface}"', 'reject'], ['meta l4proto gre', f'oifname "{interface}"', 'return'], ['smoketest default-action', f'log prefix "[{name}-default-D]"', 'drop'] diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py index 68905e447..3a4de2d8d 100755 --- a/smoketest/scripts/cli/test_ha_vrrp.py +++ b/smoketest/scripts/cli/test_ha_vrrp.py @@ -87,11 +87,21 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): advertise_interval = '77' priority = '123' preempt_delay = '400' + startup_delay = '120' + garp_master_delay = '2' + garp_master_repeat = '3' + garp_master_refresh = '4' + garp_master_refresh_repeat = '5' + garp_interval = '1.5' + group_garp_master_delay = '12' + group_garp_master_repeat = '13' + group_garp_master_refresh = '14' for group in groups: vlan_id = group.lstrip('VLAN') vip = f'100.64.{vlan_id}.1/24' group_base = base_path + ['vrrp', 'group', group] + global_param_base = base_path + ['vrrp', 'global-parameters'] self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]]) @@ -110,9 +120,32 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.cli_set(group_base + ['authentication', 'type', 'plaintext-password']) self.cli_set(group_base + ['authentication', 'password', f'{group}']) + # GARP + self.cli_set(group_base + ['garp', 'master-delay', group_garp_master_delay]) + self.cli_set(group_base + ['garp', 'master-repeat', group_garp_master_repeat]) + self.cli_set(group_base + ['garp', 'master-refresh', group_garp_master_refresh]) + + # Global parameters + #config = getConfig(f'global_defs') + self.cli_set(global_param_base + ['startup-delay', f'{startup_delay}']) + self.cli_set(global_param_base + ['garp', 'interval', f'{garp_interval}']) + self.cli_set(global_param_base + ['garp', 'master-delay', f'{garp_master_delay}']) + self.cli_set(global_param_base + ['garp', 'master-repeat', f'{garp_master_repeat}']) + self.cli_set(global_param_base + ['garp', 'master-refresh', f'{garp_master_refresh}']) + self.cli_set(global_param_base + ['garp', 'master-refresh-repeat', f'{garp_master_refresh_repeat}']) + # commit changes self.cli_commit() + # Check Global parameters + config = getConfig(f'global_defs') + self.assertIn(f'vrrp_startup_delay {startup_delay}', config) + self.assertIn(f'vrrp_garp_interval {garp_interval}', config) + self.assertIn(f'vrrp_garp_master_delay {garp_master_delay}', config) + self.assertIn(f'vrrp_garp_master_repeat {garp_master_repeat}', config) + self.assertIn(f'vrrp_garp_master_refresh {garp_master_refresh}', config) + self.assertIn(f'vrrp_garp_master_refresh_repeat {garp_master_refresh_repeat}', config) + for group in groups: vlan_id = group.lstrip('VLAN') vip = f'100.64.{vlan_id}.1/24' @@ -132,6 +165,11 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'auth_pass "{group}"', config) self.assertIn(f'auth_type PASS', config) + #GARP + self.assertIn(f'garp_master_delay {group_garp_master_delay}', config) + self.assertIn(f'garp_master_refresh {group_garp_master_refresh}', config) + self.assertIn(f'garp_master_repeat {group_garp_master_repeat}', config) + def test_03_sync_group(self): sync_group = 'VyOS' diff --git a/smoketest/scripts/cli/test_interfaces_dummy.py b/smoketest/scripts/cli/test_interfaces_dummy.py index d96ec2c5d..a79e4cb1b 100755 --- a/smoketest/scripts/cli/test_interfaces_dummy.py +++ b/smoketest/scripts/cli/test_interfaces_dummy.py @@ -21,6 +21,7 @@ from base_interfaces_test import BasicInterfaceTest class DummyInterfaceTest(BasicInterfaceTest.TestCase): @classmethod def setUpClass(cls): + cls._test_mtu = True cls._base_path = ['interfaces', 'dummy'] cls._interfaces = ['dum435', 'dum8677', 'dum0931', 'dum089'] # call base-classes classmethod diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py index ed611062a..e53413f0d 100755 --- a/smoketest/scripts/cli/test_interfaces_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_ethernet.py @@ -160,7 +160,7 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): 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, + # enable RPS on all available CPUs, RPS works with a CPU bitmask, # where each bit represents a CPU (core/thread). The formula below # expands to rps_cpus = 255 for a 8 core system rps_cpus = (1 << os.cpu_count()) -1 diff --git a/smoketest/scripts/cli/test_interfaces_input.py b/smoketest/scripts/cli/test_interfaces_input.py new file mode 100755 index 000000000..c6d7febec --- /dev/null +++ b/smoketest/scripts/cli/test_interfaces_input.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import unittest + +from vyos.util import read_file +from vyos.ifconfig import Interface +from base_vyostest_shim import VyOSUnitTestSHIM + +base_path = ['interfaces', 'input'] + +# add a classmethod to setup a temporaray PPPoE server for "proper" validation +class InputInterfaceTest(VyOSUnitTestSHIM.TestCase): + @classmethod + def setUpClass(cls): + super(InputInterfaceTest, cls).setUpClass() + + cls._interfaces = ['ifb10', 'ifb20', 'ifb30'] + + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + + def test_01_description(self): + # Check if PPPoE dialer can be configured and runs + for interface in self._interfaces: + self.cli_set(base_path + [interface, 'description', f'foo-{interface}']) + + # commit changes + self.cli_commit() + + # Validate remove interface description "empty" + for interface in self._interfaces: + tmp = read_file(f'/sys/class/net/{interface}/ifalias') + self.assertEqual(tmp, f'foo-{interface}') + self.assertEqual(Interface(interface).get_alias(), f'foo-{interface}') + +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 b2143d16e..c80c7cf80 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-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -368,6 +368,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase): self.cli_set(path + ['hash', auth_hash]) self.cli_set(path + ['mode', 'server']) self.cli_set(path + ['local-port', port]) + self.cli_set(path + ['server', 'mfa', 'totp']) self.cli_set(path + ['server', 'subnet', subnet]) self.cli_set(path + ['server', 'topology', 'subnet']) self.cli_set(path + ['keep-alive', 'failure-count', '5']) @@ -388,6 +389,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase): for ii in num_range: interface = f'vtun{ii}' + plugin = f'plugin "/usr/lib/openvpn/openvpn-otp.so" "otp_secrets=/config/auth/openvpn/{interface}-otp-secrets otp_slop=180 totp_t0=0 totp_step=30 totp_digits=6 password_is_cr=1"' subnet = f'192.0.{ii}.0/24' start_addr = inc_ip(subnet, '2') @@ -411,6 +413,7 @@ class TestInterfacesOpenVPN(VyOSUnitTestSHIM.TestCase): self.assertIn(f'topology subnet', config) self.assertIn(f'lport {port}', config) self.assertIn(f'push "redirect-gateway def1"', config) + self.assertIn(f'{plugin}', config) self.assertIn(f'keepalive 5 25', config) # TLS options diff --git a/smoketest/scripts/cli/test_interfaces_pppoe.py b/smoketest/scripts/cli/test_interfaces_pppoe.py index 8927121a8..f4efed641 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-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 @@ -57,11 +57,11 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): def test_01_pppoe_client(self): # Check if PPPoE dialer can be configured and runs for interface in self._interfaces: - user = 'VyOS-user-' + interface - passwd = 'VyOS-passwd-' + interface + user = f'VyOS-user-{interface}' + passwd = f'VyOS-passwd-{interface}' mtu = '1400' - self.cli_set(base_path + [interface, 'authentication', 'user', user]) + self.cli_set(base_path + [interface, 'authentication', 'username', user]) self.cli_set(base_path + [interface, 'authentication', 'password', passwd]) self.cli_set(base_path + [interface, 'mtu', mtu]) self.cli_set(base_path + [interface, 'no-peer-dns']) @@ -76,23 +76,26 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): # verify configuration file(s) for interface in self._interfaces: - user = 'VyOS-user-' + interface - password = 'VyOS-passwd-' + interface + user = f'VyOS-user-{interface}' + passwd = f'VyOS-passwd-{interface}' tmp = get_config_value(interface, 'mtu')[1] self.assertEqual(tmp, mtu) tmp = get_config_value(interface, 'user')[1].replace('"', '') self.assertEqual(tmp, user) tmp = get_config_value(interface, 'password')[1].replace('"', '') - self.assertEqual(tmp, password) + self.assertEqual(tmp, passwd) tmp = get_config_value(interface, 'ifname')[1] self.assertEqual(tmp, interface) def test_02_pppoe_client_disabled_interface(self): # Check if PPPoE Client can be disabled for interface in self._interfaces: - self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) - self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) + user = f'VyOS-user-{interface}' + passwd = f'VyOS-passwd-{interface}' + + self.cli_set(base_path + [interface, 'authentication', 'username', user]) + self.cli_set(base_path + [interface, 'authentication', 'password', passwd]) self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) self.cli_set(base_path + [interface, 'disable']) @@ -117,7 +120,10 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): def test_03_pppoe_authentication(self): # When username or password is set - so must be the other for interface in self._interfaces: - self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) + user = f'VyOS-user-{interface}' + passwd = f'VyOS-passwd-{interface}' + + self.cli_set(base_path + [interface, 'authentication', 'username', user]) self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) self.cli_set(base_path + [interface, 'ipv6', 'address', 'autoconf']) @@ -125,7 +131,7 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): with self.assertRaises(ConfigSessionError): self.cli_commit() - self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) + self.cli_set(base_path + [interface, 'authentication', 'password', passwd]) self.cli_commit() @@ -136,8 +142,11 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): sla_len = '8' for interface in self._interfaces: - self.cli_set(base_path + [interface, 'authentication', 'user', 'vyos']) - self.cli_set(base_path + [interface, 'authentication', 'password', 'vyos']) + user = f'VyOS-user-{interface}' + passwd = f'VyOS-passwd-{interface}' + + self.cli_set(base_path + [interface, 'authentication', 'username', user]) + self.cli_set(base_path + [interface, 'authentication', 'password', passwd]) 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]) @@ -149,18 +158,54 @@ class PPPoEInterfaceTest(VyOSUnitTestSHIM.TestCase): self.cli_set(dhcpv6_pd_base + ['interface', self._source_interface, 'address', address]) self.cli_set(dhcpv6_pd_base + ['interface', self._source_interface, 'sla-id', sla_id]) - # commit changes - self.cli_commit() + # commit changes + self.cli_commit() + + for interface in self._interfaces: + user = f'VyOS-user-{interface}' + passwd = f'VyOS-passwd-{interface}' # verify "normal" PPPoE value - 1492 is default MTU tmp = get_config_value(interface, 'mtu')[1] self.assertEqual(tmp, '1492') tmp = get_config_value(interface, 'user')[1].replace('"', '') - self.assertEqual(tmp, 'vyos') + self.assertEqual(tmp, user) tmp = get_config_value(interface, 'password')[1].replace('"', '') - self.assertEqual(tmp, 'vyos') + self.assertEqual(tmp, passwd) tmp = get_config_value(interface, '+ipv6 ipv6cp-use-ipaddr') self.assertListEqual(tmp, ['+ipv6', 'ipv6cp-use-ipaddr']) + def test_05_pppoe_options(self): + # Check if PPPoE dialer can be configured with DHCPv6-PD + for interface in self._interfaces: + user = f'VyOS-user-{interface}' + passwd = f'VyOS-passwd-{interface}' + ac_name = f'AC{interface}' + service_name = f'SRV{interface}' + host_uniq = 'cafebeefBABE123456' + + self.cli_set(base_path + [interface, 'authentication', 'username', user]) + self.cli_set(base_path + [interface, 'authentication', 'password', passwd]) + self.cli_set(base_path + [interface, 'source-interface', self._source_interface]) + + self.cli_set(base_path + [interface, 'access-concentrator', ac_name]) + self.cli_set(base_path + [interface, 'service-name', service_name]) + self.cli_set(base_path + [interface, 'host-uniq', host_uniq]) + + # commit changes + self.cli_commit() + + for interface in self._interfaces: + ac_name = f'AC{interface}' + service_name = f'SRV{interface}' + host_uniq = 'cafebeefBABE123456' + + tmp = get_config_value(interface, 'pppoe-ac')[1] + self.assertEqual(tmp, f'"{ac_name}"') + tmp = get_config_value(interface, 'pppoe-service')[1] + self.assertEqual(tmp, f'"{service_name}"') + tmp = get_config_value(interface, 'pppoe-host-uniq')[1] + self.assertEqual(tmp, f'"{host_uniq}"') + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_load_balancning_wan.py b/smoketest/scripts/cli/test_load_balancing_wan.py index 23020b9b1..8df3471f7 100755 --- a/smoketest/scripts/cli/test_load_balancning_wan.py +++ b/smoketest/scripts/cli/test_load_balancing_wan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -46,7 +46,6 @@ def cmd_in_netns(netns, cmd): def delete_netns(name): return call(f'sudo ip netns del {name}') - class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): @@ -61,7 +60,6 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): self.cli_commit() def test_table_routes(self): - ns1 = 'ns201' ns2 = 'ns202' ns3 = 'ns203' @@ -79,6 +77,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): create_veth_pair(iface1, container_iface1) create_veth_pair(iface2, container_iface2) create_veth_pair(iface3, container_iface3) + move_interface_to_netns(container_iface1, ns1) move_interface_to_netns(container_iface2, ns2) move_interface_to_netns(container_iface3, ns3) @@ -125,7 +124,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): self.assertEqual(tmp, original) # Delete veth interfaces and netns - for iface in [iface1, iface2]: + for iface in [iface1, iface2, iface3, container_iface1, container_iface2, container_iface3]: call(f'sudo ip link del dev {iface}') delete_netns(ns1) @@ -144,15 +143,15 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): container_iface3 = 'ceth2' mangle_isp1 = """table ip mangle { chain ISP_veth1 { - counter ct mark set 0xc9 - counter meta mark set 0xc9 + counter ct mark set 0xc9 + counter meta mark set 0xc9 counter accept } }""" mangle_isp2 = """table ip mangle { chain ISP_veth2 { - counter ct mark set 0xca - counter meta mark set 0xca + counter ct mark set 0xca + counter meta mark set 0xca counter accept } }""" @@ -164,7 +163,7 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): }""" mangle_wanloadbalance_pre = """table ip mangle { chain WANLOADBALANCE_PRE { - iifname "veth3" ip saddr 198.51.100.0/24 ct state new counter jump ISP_veth1 + iifname "veth3" ip saddr 198.51.100.0/24 ct state new meta random & 2147483647 < 1073741824 counter jump ISP_veth1 iifname "veth3" ip saddr 198.51.100.0/24 ct state new counter jump ISP_veth2 iifname "veth3" ip saddr 198.51.100.0/24 counter meta mark set ct mark } @@ -179,7 +178,6 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): chain VYOS_PRE_SNAT_HOOK { type nat hook postrouting priority srcnat - 1; policy accept; counter jump WANLOADBALANCE - return } }""" @@ -196,9 +194,10 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): call(f'sudo ip address add 203.0.113.10/24 dev {iface1}') call(f'sudo ip address add 192.0.2.10/24 dev {iface2}') call(f'sudo ip address add 198.51.100.10/24 dev {iface3}') - call(f'sudo ip link set dev {iface1} up') - call(f'sudo ip link set dev {iface2} up') - call(f'sudo ip link set dev {iface3} up') + + for iface in [iface1, iface2, iface3]: + call(f'sudo ip link set dev {iface} up') + cmd_in_netns(ns1, f'ip link set {container_iface1} name eth0') cmd_in_netns(ns2, f'ip link set {container_iface2} name eth0') cmd_in_netns(ns3, f'ip link set {container_iface3} name eth0') @@ -247,12 +246,11 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase): self.assertEqual(tmp, nat_vyos_pre_snat_hook) # Delete veth interfaces and netns - for iface in [iface1, iface2]: + for iface in [iface1, iface2, iface3, container_iface1, container_iface2, container_iface3]: call(f'sudo ip link del dev {iface}') delete_netns(ns1) delete_netns(ns2) - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_nat.py b/smoketest/scripts/cli/test_nat.py index 9f4e3b831..1f2b777a8 100755 --- a/smoketest/scripts/cli/test_nat.py +++ b/smoketest/scripts/cli/test_nat.py @@ -194,12 +194,13 @@ class TestNAT(VyOSUnitTestSHIM.TestCase): self.cli_set(dst_path + ['rule', '1', 'inbound-interface', 'eth1']) self.cli_set(dst_path + ['rule', '1', 'destination', 'port', '443']) self.cli_set(dst_path + ['rule', '1', 'protocol', 'tcp']) + self.cli_set(dst_path + ['rule', '1', 'packet-type', 'host']) self.cli_set(dst_path + ['rule', '1', 'translation', 'port', '443']) self.cli_commit() nftables_search = [ - ['iifname "eth1"', 'tcp dport 443', 'dnat to :443'] + ['iifname "eth1"', 'tcp dport 443', 'pkttype host', 'dnat to :443'] ] self.verify_nftables(nftables_search, 'ip vyos_nat') diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py index cb48a84ff..a3df6bf4d 100755 --- a/smoketest/scripts/cli/test_policy_route.py +++ b/smoketest/scripts/cli/test_policy_route.py @@ -26,6 +26,7 @@ conn_mark_set = '111' table_mark_offset = 0x7fffffff table_id = '101' interface = 'eth0' +interface_wc = 'ppp*' interface_ip = '172.16.10.1/24' class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): @@ -204,6 +205,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'icmp', 'type-name', 'echo-request']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '128']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-length', '1024-2048']) + self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'packet-type', 'other']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'log', 'enable']) self.cli_set(['policy', 'route', 'smoketest', 'rule', '4', 'set', 'table', table_id]) self.cli_set(['policy', 'route', 'smoketest', 'rule', '5', 'dscp', '41']) @@ -226,6 +228,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'icmpv6', 'type', 'echo-request']) self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '128']) self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-length-exclude', '1024-2048']) + self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'packet-type', 'multicast']) + self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'log', 'enable']) self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '4', 'set', 'table', table_id]) self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'dscp-exclude', '61']) @@ -233,7 +237,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '5', 'set', 'table', table_id]) self.cli_set(['policy', 'route', 'smoketest', 'interface', interface]) - self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface]) + self.cli_set(['policy', 'route', 'smoketest', 'interface', interface_wc]) + self.cli_set(['policy', 'route6', 'smoketest6', 'interface', interface_wc]) self.cli_commit() @@ -241,11 +246,11 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): # IPv4 nftables_search = [ - [f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'], + ['iifname { "' + interface + '", "' + interface_wc + '" }', 'jump VYOS_PBR_smoketest'], ['meta l4proto udp', 'drop'], ['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip saddr 198.51.100.0/24', 'ip ttl > 2', 'meta mark set ' + mark_hex], - ['meta l4proto icmp', 'log prefix "[smoketest-4-A]"', 'icmp type echo-request', 'ip length { 128, 1024-2048 }', 'meta mark set ' + mark_hex], + ['meta l4proto icmp', 'log prefix "[smoketest-4-A]"', 'icmp type echo-request', 'ip length { 128, 1024-2048 }', 'meta pkttype other', 'meta mark set ' + mark_hex], ['ip dscp { 0x29, 0x39-0x3b }', 'meta mark set ' + mark_hex] ] @@ -253,11 +258,11 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): # IPv6 nftables6_search = [ - [f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'], + [f'iifname "{interface_wc}"', 'jump VYOS_PBR6_smoketest'], ['meta l4proto udp', 'drop'], ['tcp flags syn / syn,ack', 'meta mark set ' + mark_hex], ['ct state new', 'tcp dport 22', 'ip6 saddr 2001:db8::/64', 'ip6 hoplimit > 2', 'meta mark set ' + mark_hex], - ['meta l4proto ipv6-icmp', 'log prefix "[smoketest6-4-A]"', 'icmpv6 type echo-request', 'ip6 length != { 128, 1024-2048 }', 'meta mark set ' + mark_hex], + ['meta l4proto ipv6-icmp', 'log prefix "[smoketest6-4-A]"', 'icmpv6 type echo-request', 'ip6 length != { 128, 1024-2048 }', 'meta pkttype multicast', 'meta mark set ' + mark_hex], ['ip6 dscp != { 0x0e-0x13, 0x3d }', 'meta mark set ' + mark_hex] ] diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index debc8270c..2fd5d0c9b 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -34,6 +34,10 @@ prefix_list_in6 = 'pfx-foo-in6' prefix_list_out6 = 'pfx-foo-out6' bfd_profile = 'foo-bar-baz' +import_afi = 'ipv4-unicast' +import_vrf = 'red' +import_rd = ASN + ':100' +import_vrf_base = ['vrf', 'name'] neighbor_config = { '192.0.2.1' : { 'bfd' : '', @@ -51,6 +55,7 @@ neighbor_config = { 'route_map_out' : route_map_out, 'no_send_comm_ext' : '', 'addpath_all' : '', + 'p_attr_discard' : '123', }, '192.0.2.2' : { 'bfd_profile' : bfd_profile, @@ -64,6 +69,7 @@ neighbor_config = { 'pfx_list_in' : prefix_list_in, 'pfx_list_out' : prefix_list_out, 'no_send_comm_std' : '', + 'local_role' : 'rs-client', }, '192.0.2.3' : { 'advertise_map' : route_map_in, @@ -94,6 +100,8 @@ neighbor_config = { 'no_send_comm_std' : '', 'addpath_per_as' : '', 'peer_group' : 'foo-bar', + 'local_role' : 'customer', + 'local_role_strict': '', }, '2001:db8::2' : { 'remote_as' : '456', @@ -122,10 +130,12 @@ peer_group_config = { 'cap_over' : '', 'ttl_security' : '5', 'disable_conn_chk' : '', + 'p_attr_discard' : '250', }, 'bar' : { 'remote_as' : '111', - 'graceful_rst_no' : '' + 'graceful_rst_no' : '', + 'port' : '667', }, 'foo-bar' : { 'advertise_map' : route_map_in, @@ -150,6 +160,8 @@ peer_group_config = { 'update_src' : 'lo', 'route_map_in' : route_map_in, 'route_map_out' : route_map_out, + 'local_role' : 'peer', + 'local_role_strict': '', }, } @@ -189,6 +201,15 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) + + def create_bgp_instances_for_import_test(self): + table = '1000' + self.cli_set(base_path + ['system-as', ASN]) + # testing only one AFI is sufficient as it's generic code + + self.cli_set(import_vrf_base + [import_vrf, 'table', table]) + self.cli_set(import_vrf_base + [import_vrf, 'protocols', 'bgp', 'system-as', ASN]) + 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: @@ -208,12 +229,19 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' neighbor {peer} ebgp-multihop {peer_config["multi_hop"]}', frrconfig) if 'local_as' in peer_config: self.assertIn(f' neighbor {peer} local-as {peer_config["local_as"]} no-prepend replace-as', frrconfig) + if 'local_role' in peer_config: + tmp = f' neighbor {peer} local-role {peer_config["local_role"]}' + if 'local_role_strict' in peer_config: + tmp += ' strict' + self.assertIn(tmp, frrconfig) if 'cap_over' in peer_config: self.assertIn(f' neighbor {peer} override-capability', frrconfig) if 'passive' in peer_config: self.assertIn(f' neighbor {peer} passive', frrconfig) if 'password' in peer_config: self.assertIn(f' neighbor {peer} password {peer_config["password"]}', frrconfig) + if 'port' in peer_config: + self.assertIn(f' neighbor {peer} port {peer_config["port"]}', frrconfig) if 'remote_as' in peer_config: self.assertIn(f' neighbor {peer} remote-as {peer_config["remote_as"]}', frrconfig) if 'solo' in peer_config: @@ -238,6 +266,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' no neighbor {peer} send-community extended', frrconfig) if 'addpath_all' in peer_config: self.assertIn(f' neighbor {peer} addpath-tx-all-paths', frrconfig) + if 'p_attr_discard' in peer_config: + self.assertIn(f' neighbor {peer} path-attribute discard {peer_config["p_attr_discard"]}', frrconfig) if 'addpath_per_as' in peer_config: self.assertIn(f' neighbor {peer} addpath-tx-bestpath-per-AS', frrconfig) if 'advertise_map' in peer_config: @@ -267,6 +297,9 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): max_path_v6ibgp = '16' cond_adv_timer = '30' min_hold_time = '2' + tcp_keepalive_idle = '66' + tcp_keepalive_interval = '77' + tcp_keepalive_probes = '22' self.cli_set(base_path + ['parameters', 'router-id', router_id]) self.cli_set(base_path + ['parameters', 'log-neighbor-changes']) @@ -294,9 +327,12 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): 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', 'route-reflector-allow-outbound-policy']) + self.cli_set(base_path + ['parameters', 'route-reflector-allow-outbound-policy']) self.cli_set(base_path + ['parameters', 'shutdown']) self.cli_set(base_path + ['parameters', 'suppress-fib-pending']) + self.cli_set(base_path + ['parameters', 'tcp-keepalive', 'idle', tcp_keepalive_idle]) + self.cli_set(base_path + ['parameters', 'tcp-keepalive', 'interval', tcp_keepalive_interval]) + self.cli_set(base_path + ['parameters', 'tcp-keepalive', 'probes', tcp_keepalive_probes]) # AFI maximum path support self.cli_set(base_path + ['address-family', 'ipv4-unicast', 'maximum-paths', 'ebgp', max_path_v4]) @@ -326,6 +362,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' bgp route-reflector allow-outbound-policy', frrconfig) self.assertIn(f' bgp shutdown', frrconfig) self.assertIn(f' bgp suppress-fib-pending', frrconfig) + self.assertIn(f' bgp tcp-keepalive {tcp_keepalive_idle} {tcp_keepalive_interval} {tcp_keepalive_probes}', frrconfig) self.assertNotIn(f'bgp ebgp-requires-policy', frrconfig) self.assertIn(f' no bgp suppress-duplicates', frrconfig) @@ -367,6 +404,10 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['neighbor', peer, 'ebgp-multihop', peer_config["multi_hop"]]) if 'local_as' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'local-as', peer_config["local_as"], 'no-prepend', 'replace-as']) + if 'local_role' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'local-role', peer_config["local_role"]]) + if 'local_role_strict' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'local-role', peer_config["local_role"], 'strict']) if 'cap_over' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'override-capability']) if 'passive' in peer_config: @@ -387,6 +428,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['neighbor', peer, 'ttl-security', 'hops', peer_config["ttl_security"]]) if 'update_src' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'update-source', peer_config["update_src"]]) + if 'p_attr_discard' in peer_config: + self.cli_set(base_path + ['neighbor', peer, 'path-attribute', 'discard', peer_config["p_attr_discard"]]) if 'route_map_in' in peer_config: self.cli_set(base_path + ['neighbor', peer, 'address-family', afi, 'route-map', 'import', peer_config["route_map_in"]]) if 'route_map_out' in peer_config: @@ -436,8 +479,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): for peer, peer_config in neighbor_config.items(): if 'adv_interv' in peer_config: self.assertIn(f' neighbor {peer} advertisement-interval {peer_config["adv_interv"]}', frrconfig) - if 'port' in peer_config: - self.assertIn(f' neighbor {peer} port {peer_config["port"]}', frrconfig) if 'cap_strict' in peer_config: self.assertIn(f' neighbor {peer} strict-capability-match', frrconfig) @@ -463,12 +504,18 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['peer-group', peer_group, 'ebgp-multihop', config["multi_hop"]]) if 'local_as' in config: self.cli_set(base_path + ['peer-group', peer_group, 'local-as', config["local_as"], 'no-prepend', 'replace-as']) + if 'local_role' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'local-role', config["local_role"]]) + if 'local_role_strict' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'local-role', config["local_role"], 'strict']) if 'cap_over' in config: self.cli_set(base_path + ['peer-group', peer_group, 'override-capability']) if 'passive' in config: self.cli_set(base_path + ['peer-group', peer_group, 'passive']) if 'password' in config: self.cli_set(base_path + ['peer-group', peer_group, 'password', config["password"]]) + if 'port' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'port', config["port"]]) if 'remote_as' in config: self.cli_set(base_path + ['peer-group', peer_group, 'remote-as', config["remote_as"]]) if 'shutdown' in config: @@ -501,6 +548,8 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['peer-group', peer_group, 'graceful-restart', 'restart-helper']) if 'disable_conn_chk' in config: self.cli_set(base_path + ['peer-group', peer_group, 'disable-connected-check']) + if 'p_attr_discard' in config: + self.cli_set(base_path + ['peer-group', peer_group, 'path-attribute', 'discard', config["p_attr_discard"]]) # Conditional advertisement if 'advertise_map' in config: @@ -664,7 +713,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): for prefix in listen_ranges: self.assertIn(f' bgp listen range {prefix} peer-group {peer_group}', frrconfig) - def test_bgp_07_l2vpn_evpn(self): vnis = ['10010', '10020', '10030'] neighbors = ['192.0.2.10', '192.0.2.20', '192.0.2.30'] @@ -694,26 +742,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' advertise-default-gw', vniconfig) self.assertIn(f' advertise-svi-ip', vniconfig) - def test_bgp_08_zebra_route_map(self): - # Implemented because of T3328 - self.cli_set(base_path + ['route-map', route_map_in]) - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol bgp route-map {route_map_in}' - 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) - def test_bgp_09_distance_and_flowspec(self): distance_external = '25' distance_internal = '30' @@ -781,7 +809,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.cli_set(vrf_base + ['table', table]) self.cli_set(vrf_base + ['protocols', 'bgp', 'system-as', ASN]) self.cli_set(vrf_base + ['protocols', 'bgp', 'parameters', 'router-id', router_id]) - self.cli_set(vrf_base + ['protocols', 'bgp', 'route-map', route_map_in]) table = str(int(table) + 1000) # import VRF routes do main RIB @@ -794,7 +821,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'router bgp {ASN}', frrconfig) self.assertIn(f' address-family ipv6 unicast', frrconfig) - for vrf in vrfs: self.assertIn(f' import vrf {vrf}', frrconfig) @@ -803,15 +829,6 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f'router bgp {ASN} vrf {vrf}', frr_vrf_config) self.assertIn(f' bgp router-id {router_id}', frr_vrf_config) - # XXX: Currently this is not working as FRR() class does not support - # route-maps for multiple vrfs because the modify_section() only works - # on lines and not text blocks. - # - # vrfconfig = self.getFRRconfig(f'vrf {vrf}') - # zebra_route_map = f' ip protocol bgp route-map {route_map_in}' - # self.assertIn(zebra_route_map, vrfconfig) - - def test_bgp_11_confederation(self): router_id = '127.10.10.2' confed_id = str(int(ASN) + 1) @@ -935,7 +952,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' neighbor {peer_group} remote-as {remote_asn}', frrconfig) def test_bgp_15_local_as_ebgp(self): - # https://phabricator.vyos.net/T4560 + # https://vyos.dev/T4560 # local-as allowed only for ebgp peers neighbor = '192.0.2.99' @@ -959,6 +976,101 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' neighbor {neighbor} remote-as {remote_asn}', frrconfig) self.assertIn(f' neighbor {neighbor} local-as {local_asn}', frrconfig) + def test_bgp_16_import_rd_rt_compatibility(self): + # Verify if import vrf and rd vpn export + # exist in the same address family + self.create_bgp_instances_for_import_test() + self.cli_set( + base_path + ['address-family', import_afi, 'import', 'vrf', + import_vrf]) + self.cli_set( + base_path + ['address-family', import_afi, 'rd', 'vpn', 'export', + import_rd]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + def test_bgp_17_import_rd_rt_compatibility(self): + # Verify if vrf that is in import vrf list contains rd vpn export + self.create_bgp_instances_for_import_test() + self.cli_set( + base_path + ['address-family', import_afi, 'import', 'vrf', + import_vrf]) + self.cli_commit() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}') + + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f'address-family ipv4 unicast', frrconfig) + self.assertIn(f' import vrf {import_vrf}', frrconfig) + self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf) + + self.cli_set( + import_vrf_base + [import_vrf] + base_path + ['address-family', + import_afi, 'rd', + 'vpn', 'export', + import_rd]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + def test_bgp_18_deleting_import_vrf(self): + # Verify deleting vrf that is in import vrf list + self.create_bgp_instances_for_import_test() + self.cli_set( + base_path + ['address-family', import_afi, 'import', 'vrf', + import_vrf]) + self.cli_commit() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f'address-family ipv4 unicast', frrconfig) + self.assertIn(f' import vrf {import_vrf}', frrconfig) + self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf) + self.cli_delete(import_vrf_base + [import_vrf]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + def test_bgp_19_deleting_default_vrf(self): + # Verify deleting existent vrf default if other vrfs were created + self.create_bgp_instances_for_import_test() + self.cli_commit() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf) + self.cli_delete(base_path) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + def test_bgp_20_import_rd_rt_compatibility(self): + # Verify if vrf that has rd vpn export is in import vrf of other vrfs + self.create_bgp_instances_for_import_test() + self.cli_set( + import_vrf_base + [import_vrf] + base_path + ['address-family', + import_afi, 'rd', + 'vpn', 'export', + import_rd]) + self.cli_commit() + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + frrconfig_vrf = self.getFRRconfig(f'router bgp {ASN} vrf {import_vrf}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f'router bgp {ASN} vrf {import_vrf}', frrconfig_vrf) + self.assertIn(f'address-family ipv4 unicast', frrconfig_vrf) + self.assertIn(f' rd vpn export {import_rd}', frrconfig_vrf) + + self.cli_set( + base_path + ['address-family', import_afi, 'import', 'vrf', + import_vrf]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + def test_bgp_21_import_unspecified_vrf(self): + # Verify if vrf that is in import is unspecified + self.create_bgp_instances_for_import_test() + self.cli_set( + base_path + ['address-family', import_afi, 'import', 'vrf', + 'test']) + with self.assertRaises(ConfigSessionError): + self.cli_commit() if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_isis.py b/smoketest/scripts/cli/test_protocols_isis.py index d11d80a1f..f1a030e77 100755 --- a/smoketest/scripts/cli/test_protocols_isis.py +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -119,39 +119,6 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_delete(['vrf', 'name', vrf]) self.cli_delete(['interfaces', 'ethernet', vrf_iface, 'vrf']) - def test_isis_03_zebra_route_map(self): - # Implemented because of T3328 - route_map = 'foo-isis-in' - - self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) - - 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, 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, daemon='zebra') - self.assertNotIn(zebra_route_map, frrconfig) - - self.cli_delete(['policy', 'route-map', route_map]) - def test_isis_04_default_information(self): metric = '50' route_map = 'default-foo-' @@ -293,7 +260,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['segment-routing', 'prefix', prefix_three, 'absolute', 'explicit-null']) self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'value', prefix_four_value]) self.cli_set(base_path + ['segment-routing', 'prefix', prefix_four, 'absolute', 'no-php-flag']) - + # Commit all changes self.cli_commit() @@ -308,5 +275,48 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.assertIn(f' segment-routing prefix {prefix_three} absolute {prefix_three_value} explicit-null', tmp) self.assertIn(f' segment-routing prefix {prefix_four} absolute {prefix_four_value} no-php-flag', tmp) + def test_isis_08_ldp_sync(self): + holddown = "500" + interface = 'lo' + + self.cli_set(base_path + ['net', net]) + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['ldp-sync', 'holddown', holddown]) + + # Commit main ISIS changes + self.cli_commit() + + # Verify main ISIS changes + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') + self.assertIn(f' net {net}', tmp) + self.assertIn(f' mpls ldp-sync', tmp) + self.assertIn(f' mpls ldp-sync holddown {holddown}', tmp) + + for interface in self._interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown]) + + # Commit interface changes for holddown + self.cli_commit() + + # Verify interface changes for holddown + tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') + self.assertIn(f'interface {interface}', tmp) + self.assertIn(f' ip router isis {domain}', tmp) + self.assertIn(f' ipv6 router isis {domain}', tmp) + self.assertIn(f' isis mpls ldp-sync holddown {holddown}', tmp) + + for interface in self._interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable']) + + # Commit interface changes for disable + self.cli_commit() + + # Verify interface changes for disable + tmp = self.getFRRconfig(f'interface {interface}', daemon='isisd') + self.assertIn(f'interface {interface}', tmp) + self.assertIn(f' ip router isis {domain}', tmp) + self.assertIn(f' ipv6 router isis {domain}', tmp) + self.assertIn(f' no isis mpls ldp-sync', tmp) + 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 59252875b..7dbe836f7 100755 --- a/smoketest/scripts/cli/test_protocols_nhrp.py +++ b/smoketest/scripts/cli/test_protocols_nhrp.py @@ -54,7 +54,7 @@ class TestProtocolsNHRP(VyOSUnitTestSHIM.TestCase): self.cli_set(tunnel_path + [tunnel_if, "address", "172.16.253.134/29"]) self.cli_set(tunnel_path + [tunnel_if, "encapsulation", tunnel_encapsulation]) self.cli_set(tunnel_path + [tunnel_if, "source-address", tunnel_source]) - self.cli_set(tunnel_path + [tunnel_if, "multicast", "enable"]) + self.cli_set(tunnel_path + [tunnel_if, "enable-multicast"]) self.cli_set(tunnel_path + [tunnel_if, "parameters", "ip", "key", "1"]) # NHRP diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 339713bf6..6fe6dd979 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -74,6 +74,11 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['parameters', 'rfc1583-compatibility']) self.cli_set(base_path + ['log-adjacency-changes', 'detail']) self.cli_set(base_path + ['default-metric', metric]) + self.cli_set(base_path + ['passive-interface', 'default']) + self.cli_set(base_path + ['area', '10', 'area-type', 'stub']) + self.cli_set(base_path + ['area', '10', 'network', '10.0.0.0/16']) + self.cli_set(base_path + ['area', '10', 'range', '10.0.1.0/24']) + self.cli_set(base_path + ['area', '10', 'range', '10.0.2.0/24', 'not-advertise']) # commit changes self.cli_commit() @@ -88,6 +93,12 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) # defaults self.assertIn(f' capability opaque', frrconfig) self.assertIn(f' default-metric {metric}', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) + self.assertIn(f' area 10 stub', frrconfig) + self.assertIn(f' network 10.0.0.0/16 area 10', frrconfig) + self.assertIn(f' area 10 range 10.0.1.0/24', frrconfig) + self.assertNotIn(f' area 10 range 10.0.1.0/24 not-advertise', frrconfig) + self.assertIn(f' area 10 range 10.0.2.0/24 not-advertise', frrconfig) def test_ospf_03_access_list(self): @@ -272,6 +283,10 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' passive-interface default', frrconfig) + for interface in interfaces: config = self.getFRRconfig(f'interface {interface}') self.assertIn(f'interface {interface}', config) @@ -285,26 +300,6 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' no ip ospf passive', config) self.assertIn(f' bandwidth {bandwidth}', config) - def test_ospf_10_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'ip protocol ospf 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) - def test_ospf_11_interface_area(self): area = '0' interfaces = Section.interfaces('ethernet') @@ -419,6 +414,47 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): self.assertIn(f' segment-routing prefix {prefix_one} index {prefix_one_value} explicit-null', frrconfig) self.assertIn(f' segment-routing prefix {prefix_two} index {prefix_two_value} no-php-flag', frrconfig) + def test_ospf_15_ldp_sync(self): + holddown = "500" + interface = 'lo' + interfaces = Section.interfaces('ethernet') + + self.cli_set(base_path + ['interface', interface]) + self.cli_set(base_path + ['ldp-sync', 'holddown', holddown]) + + # Commit main OSPF changes + self.cli_commit() + + # Verify main OSPF changes + frrconfig = self.getFRRconfig('router ospf') + self.assertIn(f'router ospf', frrconfig) + self.assertIn(f' timers throttle spf 200 1000 10000', frrconfig) + self.assertIn(f' mpls ldp-sync holddown {holddown}', frrconfig) + + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'holddown', holddown]) + + # Commit interface changes for holddown + self.cli_commit() + + # Verify interface changes for holddown + config = self.getFRRconfig(f'interface {interface}') + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ip ospf dead-interval 40', config) + self.assertIn(f' ip ospf mpls ldp-sync', config) + self.assertIn(f' ip ospf mpls ldp-sync holddown {holddown}', config) + + for interface in interfaces: + self.cli_set(base_path + ['interface', interface, 'ldp-sync', 'disable']) + + # Commit interface changes for disable + self.cli_commit() + + # Verify interface changes for disable + config = self.getFRRconfig(f'interface {interface}') + self.assertIn(f'interface {interface}', config) + self.assertIn(f' ip ospf dead-interval 40', config) + self.assertIn(f' no ip ospf mpls ldp-sync', config) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_static.py b/smoketest/scripts/cli/test_protocols_static.py index 19efe7786..706663ce5 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-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -95,6 +95,7 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): super(TestProtocolsStatic, cls).setUpClass() + cls.cli_delete(cls, ['vrf']) cls.cli_set(cls, ['vrf', 'name', 'black', 'table', '43210']) @classmethod @@ -433,30 +434,5 @@ class TestProtocolsStatic(VyOSUnitTestSHIM.TestCase): self.assertIn(tmp, frrconfig) - def test_04_static_zebra_route_map(self): - # Implemented because of T3328 - route_map = 'foo-static-in' - self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) - - self.cli_set(base_path + ['route-map', route_map]) - # commit changes - self.cli_commit() - - # Verify FRR configuration - zebra_route_map = f'ip protocol static 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) - - self.cli_delete(['policy', 'route-map', route_map]) - if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py new file mode 100755 index 000000000..0092473d6 --- /dev/null +++ b/smoketest/scripts/cli/test_qos.py @@ -0,0 +1,547 @@ +#!/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 json import loads +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Section +from vyos.util import cmd + +base_path = ['qos'] + +def get_tc_qdisc_json(interface) -> dict: + tmp = cmd(f'tc -detail -json qdisc show dev {interface}') + tmp = loads(tmp) + return next(iter(tmp)) + +def get_tc_filter_json(interface, direction) -> list: + if direction not in ['ingress', 'egress']: + raise ValueError() + tmp = cmd(f'tc -detail -json filter show dev {interface} {direction}') + tmp = loads(tmp) + return tmp + +class TestQoS(VyOSUnitTestSHIM.TestCase): + @classmethod + def setUpClass(cls): + super(TestQoS, 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 only test on physical interfaces and not VLAN (sub-)interfaces + cls._interfaces = [] + if 'TEST_ETH' in os.environ: + tmp = os.environ['TEST_ETH'].split() + cls._interfaces = tmp + else: + for tmp in Section.interfaces('ethernet', vlan=False): + cls._interfaces.append(tmp) + + def tearDown(self): + # delete testing SSH config + self.cli_delete(base_path) + self.cli_commit() + + def test_01_cake(self): + bandwidth = 1000000 + rtt = 200 + + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + self.cli_set(base_path + ['policy', 'cake', policy_name, 'bandwidth', str(bandwidth)]) + self.cli_set(base_path + ['policy', 'cake', policy_name, 'rtt', str(rtt)]) + self.cli_set(base_path + ['policy', 'cake', policy_name, 'flow-isolation', 'dual-src-host']) + + bandwidth += 1000000 + rtt += 20 + + # commit changes + self.cli_commit() + + bandwidth = 1000000 + rtt = 200 + for interface in self._interfaces: + tmp = get_tc_qdisc_json(interface) + + self.assertEqual('cake', tmp['kind']) + # TC store rates as a 32-bit unsigned integer in bps (Bytes per second) + self.assertEqual(int(bandwidth *125), tmp['options']['bandwidth']) + # RTT internally is in us + self.assertEqual(int(rtt *1000), tmp['options']['rtt']) + self.assertEqual('dual-srchost', tmp['options']['flowmode']) + self.assertFalse(tmp['options']['ingress']) + self.assertFalse(tmp['options']['nat']) + self.assertTrue(tmp['options']['raw']) + + bandwidth += 1000000 + rtt += 20 + + def test_02_drop_tail(self): + queue_limit = 50 + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + self.cli_set(base_path + ['policy', 'drop-tail', policy_name, 'queue-limit', str(queue_limit)]) + + queue_limit += 10 + + # commit changes + self.cli_commit() + + queue_limit = 50 + for interface in self._interfaces: + tmp = get_tc_qdisc_json(interface) + + self.assertEqual('pfifo', tmp['kind']) + self.assertEqual(queue_limit, tmp['options']['limit']) + + queue_limit += 10 + + def test_03_fair_queue(self): + hash_interval = 10 + queue_limit = 5 + policy_type = 'fair-queue' + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'hash-interval', str(hash_interval)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'queue-limit', str(queue_limit)]) + + hash_interval += 1 + queue_limit += 1 + + # commit changes + self.cli_commit() + + hash_interval = 10 + queue_limit = 5 + for interface in self._interfaces: + tmp = get_tc_qdisc_json(interface) + + self.assertEqual('sfq', tmp['kind']) + self.assertEqual(hash_interval, tmp['options']['perturb']) + self.assertEqual(queue_limit, tmp['options']['limit']) + + hash_interval += 1 + queue_limit += 1 + + def test_04_fq_codel(self): + policy_type = 'fq-codel' + codel_quantum = 1500 + flows = 512 + interval = 100 + queue_limit = 2048 + target = 5 + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'codel-quantum', str(codel_quantum)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'flows', str(flows)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'interval', str(interval)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'queue-limit', str(queue_limit)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'target', str(target)]) + + codel_quantum += 10 + flows += 2 + interval += 10 + queue_limit += 512 + target += 1 + + # commit changes + self.cli_commit() + + codel_quantum = 1500 + flows = 512 + interval = 100 + queue_limit = 2048 + target = 5 + for interface in self._interfaces: + tmp = get_tc_qdisc_json(interface) + + self.assertEqual('fq_codel', tmp['kind']) + self.assertEqual(codel_quantum, tmp['options']['quantum']) + self.assertEqual(flows, tmp['options']['flows']) + self.assertEqual(queue_limit, tmp['options']['limit']) + + # due to internal rounding we need to substract 1 from interval and target after converting to milliseconds + # configuration of: + # tc qdisc add dev eth0 root fq_codel quantum 1500 flows 512 interval 100ms limit 2048 target 5ms noecn + # results in: tc -j qdisc show dev eth0 + # [{"kind":"fq_codel","handle":"8046:","root":true,"refcnt":3,"options":{"limit":2048,"flows":512, + # "quantum":1500,"target":4999,"interval":99999,"memory_limit":33554432,"drop_batch":64}}] + self.assertAlmostEqual(tmp['options']['interval'], interval *1000, delta=1) + self.assertAlmostEqual(tmp['options']['target'], target *1000 -1, delta=1) + + codel_quantum += 10 + flows += 2 + interval += 10 + queue_limit += 512 + target += 1 + + def test_05_limiter(self): + qos_config = { + '1' : { + 'bandwidth' : '1000000', + 'match4' : { + 'ssh' : { 'dport' : '22', }, + }, + }, + '2' : { + 'bandwidth' : '1000000', + 'match6' : { + 'ssh' : { 'dport' : '22', }, + }, + }, + } + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'egress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # set default bandwidth parameter for all remaining connections + self.cli_set(base_path + ['policy', 'limiter', policy_name, 'default', 'bandwidth', '500000']) + + for qos_class, qos_class_config in qos_config.items(): + qos_class_base = base_path + ['policy', 'limiter', policy_name, 'class', qos_class] + + if 'match4' in qos_class_config: + for match, match_config in qos_class_config['match4'].items(): + if 'dport' in match_config: + self.cli_set(qos_class_base + ['match', match, 'ip', 'destination', 'port', match_config['dport']]) + + if 'match6' in qos_class_config: + for match, match_config in qos_class_config['match6'].items(): + if 'dport' in match_config: + self.cli_set(qos_class_base + ['match', match, 'ipv6', 'destination', 'port', match_config['dport']]) + + if 'bandwidth' in qos_class_config: + self.cli_set(qos_class_base + ['bandwidth', qos_class_config['bandwidth']]) + + + # commit changes + self.cli_commit() + + for interface in self._interfaces: + for filter in get_tc_filter_json(interface, 'ingress'): + # bail out early if filter has no attached action + if 'options' not in filter or 'actions' not in filter['options']: + continue + + for qos_class, qos_class_config in qos_config.items(): + # Every flowid starts with ffff and we encopde the class number after the colon + if 'flowid' not in filter['options'] or filter['options']['flowid'] != f'ffff:{qos_class}': + continue + + ip_hdr_offset = 20 + if 'match6' in qos_class_config: + ip_hdr_offset = 40 + + self.assertEqual(ip_hdr_offset, filter['options']['match']['off']) + if 'dport' in match_config: + dport = int(match_config['dport']) + self.assertEqual(f'{dport:x}', filter['options']['match']['value']) + + def test_06_network_emulator(self): + policy_type = 'network-emulator' + + bandwidth = 1000000 + corruption = 1 + delay = 2 + duplicate = 3 + loss = 4 + queue_limit = 5 + reordering = 6 + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + + self.cli_set(base_path + ['policy', policy_type, policy_name, 'bandwidth', str(bandwidth)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'corruption', str(corruption)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'delay', str(delay)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'duplicate', str(duplicate)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'loss', str(loss)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'queue-limit', str(queue_limit)]) + self.cli_set(base_path + ['policy', policy_type, policy_name, 'reordering', str(reordering)]) + + bandwidth += 1000000 + corruption += 1 + delay += 1 + duplicate +=1 + loss += 1 + queue_limit += 1 + reordering += 1 + + # commit changes + self.cli_commit() + + bandwidth = 1000000 + corruption = 1 + delay = 2 + duplicate = 3 + loss = 4 + queue_limit = 5 + reordering = 6 + for interface in self._interfaces: + tmp = get_tc_qdisc_json(interface) + self.assertEqual('netem', tmp['kind']) + + self.assertEqual(int(bandwidth *125), tmp['options']['rate']['rate']) + # values are in % + self.assertEqual(corruption/100, tmp['options']['corrupt']['corrupt']) + self.assertEqual(duplicate/100, tmp['options']['duplicate']['duplicate']) + self.assertEqual(loss/100, tmp['options']['loss-random']['loss']) + self.assertEqual(reordering/100, tmp['options']['reorder']['reorder']) + self.assertEqual(delay/1000, tmp['options']['delay']['delay']) + + self.assertEqual(queue_limit, tmp['options']['limit']) + + bandwidth += 1000000 + corruption += 1 + delay += 1 + duplicate += 1 + loss += 1 + queue_limit += 1 + reordering += 1 + + def test_07_priority_queue(self): + priorities = ['1', '2', '3', '4', '5'] + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + self.cli_set(base_path + ['policy', 'priority-queue', policy_name, 'default', 'queue-limit', '10']) + + for priority in priorities: + prio_base = base_path + ['policy', 'priority-queue', policy_name, 'class', priority] + self.cli_set(prio_base + ['match', f'prio-{priority}', 'ip', 'destination', 'port', str(1000 + int(priority))]) + + # commit changes + self.cli_commit() + + def test_08_random_detect(self): + self.skipTest('tc returns invalid JSON here - needs iproute2 fix') + bandwidth = 5000 + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + self.cli_set(base_path + ['policy', 'random-detect', policy_name, 'bandwidth', str(bandwidth)]) + + bandwidth += 1000 + + # commit changes + self.cli_commit() + + bandwidth = 5000 + for interface in self._interfaces: + tmp = get_tc_qdisc_json(interface) + import pprint + pprint.pprint(tmp) + + def test_09_rate_control(self): + bandwidth = 5000 + burst = 20 + latency = 5 + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + self.cli_set(base_path + ['policy', 'rate-control', policy_name, 'bandwidth', str(bandwidth)]) + self.cli_set(base_path + ['policy', 'rate-control', policy_name, 'burst', str(burst)]) + self.cli_set(base_path + ['policy', 'rate-control', policy_name, 'latency', str(latency)]) + + bandwidth += 1000 + burst += 5 + latency += 1 + # commit changes + self.cli_commit() + + bandwidth = 5000 + burst = 20 + latency = 5 + for interface in self._interfaces: + tmp = get_tc_qdisc_json(interface) + + self.assertEqual('tbf', tmp['kind']) + self.assertEqual(0, tmp['options']['mpu']) + # TC store rates as a 32-bit unsigned integer in bps (Bytes per second) + self.assertEqual(int(bandwidth * 125), tmp['options']['rate']) + + bandwidth += 1000 + burst += 5 + latency += 1 + + def test_10_round_robin(self): + qos_config = { + '1' : { + 'match4' : { + 'ssh' : { 'dport' : '22', }, + }, + }, + '2' : { + 'match6' : { + 'ssh' : { 'dport' : '22', }, + }, + }, + } + + first = True + for interface in self._interfaces: + policy_name = f'qos-policy-{interface}' + + if first: + self.cli_set(base_path + ['interface', interface, 'ingress', policy_name]) + # verify() - selected QoS policy on interface only supports egress + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface', interface, 'ingress', policy_name]) + first = False + + self.cli_set(base_path + ['interface', interface, 'egress', policy_name]) + + for qos_class, qos_class_config in qos_config.items(): + qos_class_base = base_path + ['policy', 'round-robin', policy_name, 'class', qos_class] + + if 'match4' in qos_class_config: + for match, match_config in qos_class_config['match4'].items(): + if 'dport' in match_config: + self.cli_set(qos_class_base + ['match', match, 'ip', 'destination', 'port', match_config['dport']]) + + if 'match6' in qos_class_config: + for match, match_config in qos_class_config['match6'].items(): + if 'dport' in match_config: + self.cli_set(qos_class_base + ['match', match, 'ipv6', 'destination', 'port', match_config['dport']]) + + + # commit changes + self.cli_commit() + + for interface in self._interfaces: + import pprint + tmp = get_tc_qdisc_json(interface) + self.assertEqual('drr', tmp['kind']) + + for filter in get_tc_filter_json(interface, 'ingress'): + # bail out early if filter has no attached action + if 'options' not in filter or 'actions' not in filter['options']: + continue + + for qos_class, qos_class_config in qos_config.items(): + # Every flowid starts with ffff and we encopde the class number after the colon + if 'flowid' not in filter['options'] or filter['options']['flowid'] != f'ffff:{qos_class}': + continue + + ip_hdr_offset = 20 + if 'match6' in qos_class_config: + ip_hdr_offset = 40 + + self.assertEqual(ip_hdr_offset, filter['options']['match']['off']) + if 'dport' in match_config: + dport = int(match_config['dport']) + self.assertEqual(f'{dport:x}', filter['options']['match']['value']) + +if __name__ == '__main__': + unittest.main(verbosity=2, failfast=True) diff --git a/smoketest/scripts/cli/test_service_dhcp-relay.py b/smoketest/scripts/cli/test_service_dhcp-relay.py index bbfd9e032..92f87c06c 100755 --- a/smoketest/scripts/cli/test_service_dhcp-relay.py +++ b/smoketest/scripts/cli/test_service_dhcp-relay.py @@ -82,6 +82,43 @@ class TestServiceDHCPRelay(VyOSUnitTestSHIM.TestCase): # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) + def test_relay_interfaces(self): + max_size = '800' + hop_count = '20' + agents_packets = 'append' + servers = ['192.0.2.1', '192.0.2.2'] + listen_iface = 'eth0' + up_iface = 'eth1' + + self.cli_set(base_path + ['interface', up_iface]) + self.cli_set(base_path + ['listen-interface', listen_iface]) + # check validate() - backward interface plus listen_interface + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(base_path + ['interface']) + + self.cli_set(base_path + ['upstream-interface', up_iface]) + + for server in servers: + self.cli_set(base_path + ['server', server]) + + # commit changes + self.cli_commit() + + # Check configured port + config = read_file(RELAY_CONF) + + # Test configured relay interfaces + self.assertIn(f'-id {listen_iface}', config) + self.assertIn(f'-iu {up_iface}', config) + + # Test relay servers + for server in servers: + self.assertIn(f' {server}', config) + + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_dhcpv6-relay.py b/smoketest/scripts/cli/test_service_dhcpv6-relay.py index fc206435b..8bb58d296 100755 --- a/smoketest/scripts/cli/test_service_dhcpv6-relay.py +++ b/smoketest/scripts/cli/test_service_dhcpv6-relay.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -34,22 +34,30 @@ listen_addr = '2001:db8:ffff::1/64' interfaces = [] class TestServiceDHCPv6Relay(VyOSUnitTestSHIM.TestCase): - def setUp(self): - for tmp in interfaces: + @classmethod + def setUpClass(cls): + super(TestServiceDHCPv6Relay, 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) + + for tmp in Section.interfaces('ethernet', vlan=False): + interfaces.append(tmp) listen = listen_addr if tmp == upstream_if: listen = upstream_if_addr - self.cli_set(['interfaces', 'ethernet', tmp, 'address', listen]) + cls.cli_set(cls, ['interfaces', 'ethernet', tmp, 'address', listen]) - def tearDown(self): - self.cli_delete(base_path) + @classmethod + def tearDownClass(cls): for tmp in interfaces: listen = listen_addr if tmp == upstream_if: listen = upstream_if_addr - self.cli_delete(['interfaces', 'ethernet', tmp, 'address', listen]) + cls.cli_delete(cls, ['interfaces', 'ethernet', tmp, 'address', listen]) - self.cli_commit() + super(TestServiceDHCPv6Relay, cls).tearDownClass() def test_relay_default(self): dhcpv6_server = '2001:db8::ffff' @@ -100,9 +108,5 @@ class TestServiceDHCPv6Relay(VyOSUnitTestSHIM.TestCase): self.assertTrue(process_named_running(PROCESS_NAME)) if __name__ == '__main__': - for tmp in Section.interfaces('ethernet'): - if '.' not in tmp: - interfaces.append(tmp) - unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index 90d10d40b..57705e26f 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -155,7 +155,7 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): self.assertEqual(login, user) self.assertEqual(pwd, f"'{password}'") self.assertEqual(server, srv) - self.assertEqual(usev6, f"if, if={interface}") + self.assertEqual(usev6, f"ifv6, if={interface}") if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py index 94e0597ad..88492e348 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -20,6 +20,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError +from vyos.template import bracketize_ipv6 from vyos.util import read_file from vyos.util import process_named_running @@ -141,15 +142,20 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase): for address in listen_adress: self.cli_set(base_path + ['listen-address', address]) - nameservers = ['192.0.2.1', '192.0.2.2'] - for nameserver in nameservers: - self.cli_set(base_path + ['name-server', nameserver]) + nameservers = {'192.0.2.1': {}, '192.0.2.2': {'port': '53'}, '2001:db8::1': {'port': '853'}} + for h,p in nameservers.items(): + if 'port' in p: + self.cli_set(base_path + ['name-server', h, 'port', p['port']]) + else: + self.cli_set(base_path + ['name-server', h]) # commit changes self.cli_commit() tmp = get_config_value(r'\+.', file=FORWARD_FILE) - self.assertEqual(tmp, ', '.join(nameservers)) + canonical_entries = [(lambda h, p: f"{bracketize_ipv6(h)}:{p['port'] if 'port' in p else 53}")(h, p) + for (h, p) in nameservers.items()] + self.assertEqual(tmp, ', '.join(canonical_entries)) # Do not use local /etc/hosts file in name resolution # default: yes @@ -163,10 +169,13 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['listen-address', address]) domains = ['vyos.io', 'vyos.net', 'vyos.com'] - nameservers = ['192.0.2.1', '192.0.2.2'] + nameservers = {'192.0.2.1': {}, '192.0.2.2': {'port': '53'}, '2001:db8::1': {'port': '853'}} for domain in domains: - for nameserver in nameservers: - self.cli_set(base_path + ['domain', domain, 'server', nameserver]) + for h,p in nameservers.items(): + if 'port' in p: + self.cli_set(base_path + ['domain', domain, 'name-server', h, 'port', p['port']]) + else: + self.cli_set(base_path + ['domain', domain, 'name-server', h]) # Test 'recursion-desired' flag for only one domain if domain == domains[0]: @@ -186,7 +195,9 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase): if domain == domains[0]: key =f'\+{domain}' else: key =f'{domain}' tmp = get_config_value(key, file=FORWARD_FILE) - self.assertEqual(tmp, ', '.join(nameservers)) + canonical_entries = [(lambda h, p: f"{bracketize_ipv6(h)}:{p['port'] if 'port' in p else 53}")(h, p) + for (h, p) in nameservers.items()] + self.assertEqual(tmp, ', '.join(canonical_entries)) # Test 'negative trust anchor' flag for the second domain only if domain == domains[1]: diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py index 0f4b1393c..1adf1f5cf 100755 --- a/smoketest/scripts/cli/test_service_https.py +++ b/smoketest/scripts/cli/test_service_https.py @@ -193,7 +193,8 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase): """ r = request('POST', graphql_url, verify=False, headers=headers, json={'query': query_no_key}) - self.assertEqual(r.status_code, 400) + success = r.json()['data']['SystemStatus']['success'] + self.assertFalse(success) # GraphQL token authentication test: request token; pass in header # of query. diff --git a/smoketest/scripts/cli/test_service_ipoe-server.py b/smoketest/scripts/cli/test_service_ipoe-server.py index bdab35834..8a141b8f0 100755 --- a/smoketest/scripts/cli/test_service_ipoe-server.py +++ b/smoketest/scripts/cli/test_service_ipoe-server.py @@ -26,6 +26,13 @@ from configparser import ConfigParser ac_name = 'ACN' interface = 'eth0' + +def getConfig(string, end='cli'): + command = f'cat /run/accel-pppd/ipoe.conf | sed -n "/^{string}/,/^{end}/p"' + out = cmd(command) + return out + + class TestServiceIPoEServer(BasicAccelPPPTest.TestCase): @classmethod def setUpClass(cls): @@ -86,6 +93,92 @@ class TestServiceIPoEServer(BasicAccelPPPTest.TestCase): tmp = re.findall(regex, tmp) self.assertTrue(tmp) + def test_accel_named_pool(self): + first_pool = 'VyOS-pool1' + first_subnet = '192.0.2.0/25' + first_gateway = '192.0.2.1' + second_pool = 'Vyos-pool2' + second_subnet = '203.0.113.0/25' + second_gateway = '203.0.113.1' + + self.set(['authentication', 'mode', 'noauth']) + self.set(['client-ip-pool', 'name', first_pool, 'gateway-address', first_gateway]) + self.set(['client-ip-pool', 'name', first_pool, 'subnet', first_subnet]) + self.set(['client-ip-pool', 'name', second_pool, 'gateway-address', second_gateway]) + self.set(['client-ip-pool', 'name', second_pool, 'subnet', second_subnet]) + self.set(['interface', interface]) + + # commit changes + self.cli_commit() + + + # Validate configuration values + conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False) + conf.read(self._config_file) + + self.assertTrue(conf['ipoe']['interface'], f'{interface},shared=1,mode=L2,ifcfg=1,start=dhcpv4,ipv6=1') + self.assertTrue(conf['ipoe']['noauth'], '1') + self.assertTrue(conf['ipoe']['ip-pool'], first_pool) + self.assertTrue(conf['ipoe']['ip-pool'], second_pool) + self.assertTrue(conf['ipoe']['gw-ip-address'], f'{first_gateway}/25') + self.assertTrue(conf['ipoe']['gw-ip-address'], f'{second_gateway}/25') + + config = getConfig('[ip-pool]') + pool_config = f'''{second_subnet},name={second_pool} +{first_subnet},name={first_pool} +gw-ip-address={second_gateway}/25 +gw-ip-address={first_gateway}/25''' + self.assertIn(pool_config, config) + + + def test_accel_next_pool(self): + first_pool = 'VyOS-pool1' + first_subnet = '192.0.2.0/25' + first_gateway = '192.0.2.1' + second_pool = 'Vyos-pool2' + second_subnet = '203.0.113.0/25' + second_gateway = '203.0.113.1' + third_pool = 'Vyos-pool3' + third_subnet = '198.51.100.0/24' + third_gateway = '198.51.100.1' + + self.set(['authentication', 'mode', 'noauth']) + self.set(['client-ip-pool', 'name', first_pool, 'gateway-address', first_gateway]) + self.set(['client-ip-pool', 'name', first_pool, 'subnet', first_subnet]) + self.set(['client-ip-pool', 'name', first_pool, 'next-pool', second_pool]) + self.set(['client-ip-pool', 'name', second_pool, 'gateway-address', second_gateway]) + self.set(['client-ip-pool', 'name', second_pool, 'subnet', second_subnet]) + self.set(['client-ip-pool', 'name', second_pool, 'next-pool', third_pool]) + self.set(['client-ip-pool', 'name', third_pool, 'gateway-address', third_gateway]) + self.set(['client-ip-pool', 'name', third_pool, 'subnet', third_subnet]) + self.set(['interface', interface]) + + # commit changes + self.cli_commit() + + + # Validate configuration values + conf = ConfigParser(allow_no_value=True, delimiters='=', strict=False) + conf.read(self._config_file) + + self.assertTrue(conf['ipoe']['interface'], f'{interface},shared=1,mode=L2,ifcfg=1,start=dhcpv4,ipv6=1') + self.assertTrue(conf['ipoe']['noauth'], '1') + self.assertTrue(conf['ipoe']['ip-pool'], first_pool) + self.assertTrue(conf['ipoe']['gw-ip-address'], f'{first_gateway}/25') + self.assertTrue(conf['ipoe']['gw-ip-address'], f'{second_gateway}/25') + self.assertTrue(conf['ipoe']['gw-ip-address'], f'{third_gateway}/24') + + config = getConfig('[ip-pool]') + # T5099 required specific order + pool_config = f'''{third_subnet},name={third_pool} +{second_subnet},name={second_pool},next={third_pool} +{first_subnet},name={first_pool},next={second_pool} +gw-ip-address={third_gateway}/24 +gw-ip-address={second_gateway}/25 +gw-ip-address={first_gateway}/25''' + self.assertIn(pool_config, config) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_ntp.py b/smoketest/scripts/cli/test_service_ntp.py index a0806acf0..046e5eea6 100755 --- a/smoketest/scripts/cli/test_system_ntp.py +++ b/smoketest/scripts/cli/test_service_ntp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# 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 @@ -19,14 +19,12 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError -from vyos.template import address_from_cidr -from vyos.template import netmask_from_cidr -from vyos.util import read_file +from vyos.util import cmd from vyos.util import process_named_running -PROCESS_NAME = 'ntpd' -NTP_CONF = '/run/ntpd/ntpd.conf' -base_path = ['system', 'ntp'] +PROCESS_NAME = 'chronyd' +NTP_CONF = '/run/chrony/chrony.conf' +base_path = ['service', 'ntp'] class TestSystemNTP(VyOSUnitTestSHIM.TestCase): @classmethod @@ -38,6 +36,8 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): cls.cli_delete(cls, base_path) def tearDown(self): + self.assertTrue(process_named_running(PROCESS_NAME)) + self.cli_delete(base_path) self.cli_commit() @@ -46,7 +46,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): 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'] + options = ['nts', 'noselect', 'prefer'] pools = ['pool.vyos.io'] for server in servers: @@ -61,12 +61,15 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Check generated configuration - 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) + # this file must be read with higher permissions + config = cmd(f'sudo cat {NTP_CONF}') + self.assertIn('driftfile /run/chrony/drift', config) + self.assertIn('dumpdir /run/chrony', config) + self.assertIn('ntsdumpdir /run/chrony', config) + self.assertIn('clientloglimit 1048576', config) + self.assertIn('rtcsync', config) + self.assertIn('makestep 1.0 3', config) + self.assertIn('leapsectz right/UTC', config) for server in servers: self.assertIn(f'server {server} iburst ' + ' '.join(options), config) @@ -80,9 +83,9 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): for listen in listen_address: self.cli_set(base_path + ['listen-address', listen]) - networks = ['192.0.2.0/24', '2001:db8:1000::/64'] + networks = ['192.0.2.0/24', '2001:db8:1000::/64', '100.64.0.0', '2001:db8::ffff'] for network in networks: - self.cli_set(base_path + ['allow-clients', 'address', network]) + self.cli_set(base_path + ['allow-client', 'address', network]) # Verify "NTP server not configured" verify() statement with self.assertRaises(ConfigSessionError): @@ -95,18 +98,14 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Check generated client address configuration - config = read_file(NTP_CONF) - self.assertIn('restrict default ignore', config) - + # this file must be read with higher permissions + config = cmd(f'sudo cat {NTP_CONF}') for network in networks: - network_address = address_from_cidr(network) - network_netmask = netmask_from_cidr(network) - self.assertIn(f'restrict {network_address} mask {network_netmask} nomodify notrap nopeer', config) + self.assertIn(f'allow {network}', config) # Check listen address - self.assertIn('interface ignore wildcard', config) for listen in listen_address: - self.assertIn(f'interface listen {listen}', config) + self.assertIn(f'bindaddress {listen}', config) def test_03_ntp_interface(self): interfaces = ['eth0', 'eth1'] @@ -120,10 +119,28 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Check generated client address configuration - config = read_file(NTP_CONF) - self.assertIn('interface ignore wildcard', config) + # this file must be read with higher permissions + config = cmd(f'sudo cat {NTP_CONF}') for interface in interfaces: - self.assertIn(f'interface listen {interface}', config) + self.assertIn(f'binddevice {interface}', config) + + def test_04_ntp_vrf(self): + vrf_name = 'vyos-mgmt' + + self.cli_set(['vrf', 'name', vrf_name, 'table', '12345']) + self.cli_set(base_path + ['vrf', vrf_name]) + + servers = ['time1.vyos.net', 'time2.vyos.net'] + for server in servers: + self.cli_set(base_path + ['server', server]) + + self.cli_commit() + + # Check for process in VRF + tmp = cmd(f'ip vrf pids {vrf_name}') + self.assertIn(PROCESS_NAME, tmp) + + self.cli_delete(['vrf', 'name', vrf_name]) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 7546c2e3d..4f9181704 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -143,6 +143,9 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): self.basic_config() subnet = '172.18.0.0/24' + fwmark = '223' + limiter = 'htb' + self.set(['client-ip-pool', 'subnet', subnet]) start = '192.0.2.10' @@ -151,6 +154,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): start_stop = f'{start}-{stop_octet}' self.set(['client-ip-pool', 'start', start]) self.set(['client-ip-pool', 'stop', stop]) + self.set(['shaper', 'fwmark', fwmark]) # commit changes self.cli_commit() @@ -163,6 +167,37 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase): self.assertEqual(conf['ip-pool'][subnet], None) self.assertEqual(conf['ip-pool'][start_stop], None) self.assertEqual(conf['ip-pool']['gw-ip-address'], self._gateway) + self.assertEqual(conf['shaper']['fwmark'], fwmark) + self.assertEqual(conf['shaper']['down-limiter'], limiter) + + + def test_pppoe_server_client_ip_pool_name(self): + # Test configuration of named client pools + self.basic_config() + + subnet = '192.0.2.0/24' + gateway = '192.0.2.1' + pool = 'VYOS' + + subnet_name = f'{subnet},name' + gw_ip_prefix = f'{gateway}/24' + + self.set(['client-ip-pool', 'name', pool, 'subnet', subnet]) + self.set(['client-ip-pool', 'name', pool, 'gateway-address', gateway]) + self.cli_delete(self._base_path + ['gateway-address']) + + # commit changes + self.cli_commit() + + # Validate configuration values + conf = ConfigParser(allow_no_value=True, delimiters='=') + conf.read(self._config_file) + + # Validate configuration + self.assertEqual(conf['ip-pool'][subnet_name], pool) + self.assertEqual(conf['ip-pool']['gw-ip-address'], gateway) + self.assertEqual(conf['pppoe']['ip-pool'], pool) + self.assertEqual(conf['pppoe']['gw-ip-address'], gw_ip_prefix) def test_pppoe_server_client_ipv6_pool(self): diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py index 873be7df0..0169b7934 100755 --- a/smoketest/scripts/cli/test_service_router-advert.py +++ b/smoketest/scripts/cli/test_service_router-advert.py @@ -37,7 +37,6 @@ def get_config_value(key): return tmp[0].split()[0].replace(';','') class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): - @classmethod def setUpClass(cls): super(TestServiceRADVD, cls).setUpClass() @@ -114,7 +113,6 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): tmp = get_config_value('DecrementLifetimes') self.assertEqual(tmp, 'off') - def test_dns(self): nameserver = ['2001:db8::1', '2001:db8::2'] dnssl = ['vyos.net', 'vyos.io'] @@ -150,7 +148,6 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): tmp = 'DNSSL ' + ' '.join(dnssl) + ' {' self.assertIn(tmp, config) - def test_deprecate_prefix(self): self.cli_set(base_path + ['prefix', prefix, 'valid-lifetime', 'infinity']) self.cli_set(base_path + ['prefix', prefix, 'deprecate-prefix']) @@ -159,13 +156,45 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(RADVD_CONF) - tmp = get_config_value('DeprecatePrefix') self.assertEqual(tmp, 'on') tmp = get_config_value('DecrementLifetimes') self.assertEqual(tmp, 'on') + def test_route(self): + route = '2001:db8:1000::/64' + + self.cli_set(base_path + ['prefix', prefix]) + self.cli_set(base_path + ['route', route]) + + # commit changes + self.cli_commit() + + config = read_file(RADVD_CONF) + + tmp = f'route {route}' + ' {' + self.assertIn(tmp, config) + + self.assertIn('AdvRouteLifetime 1800;', config) + self.assertIn('AdvRoutePreference medium;', config) + self.assertIn('RemoveRoute on;', config) + + def test_rasrcaddress(self): + ra_src = ['fe80::1', 'fe80::2'] + + self.cli_set(base_path + ['prefix', prefix]) + for src in ra_src: + self.cli_set(base_path + ['source-address', src]) + + # commit changes + self.cli_commit() + + config = read_file(RADVD_CONF) + self.assertIn('AdvRASrcAddress {', config) + for src in ra_src: + self.assertIn(f' {src};', config) + + 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 e80c689cc..b18b9e7a1 100755 --- a/smoketest/scripts/cli/test_service_snmp.py +++ b/smoketest/scripts/cli/test_service_snmp.py @@ -123,6 +123,28 @@ class TestSNMPService(VyOSUnitTestSHIM.TestCase): self.assertTrue(process_named_running(PROCESS_NAME)) self.cli_delete(['interfaces', 'dummy', dummy_if]) + ## Check communities and default view RESTRICTED + for auth in ['ro', 'rw']: + community = 'VyOS' + auth + for addr in clients: + if is_ipv4(addr): + entry = auth + 'community ' + community + ' ' + addr + ' -V' + else: + entry = auth + 'community6 ' + community + ' ' + addr + ' -V' + config = get_config_value(entry) + expected = 'RESTRICTED' + self.assertIn(expected, config) + for addr in networks: + if is_ipv4(addr): + entry = auth + 'community ' + community + ' ' + addr + ' -V' + else: + entry = auth + 'community6 ' + community + ' ' + addr + ' -V' + config = get_config_value(entry) + expected = 'RESTRICTED' + self.assertIn(expected, config) + # And finally check global entry for RESTRICTED view + config = get_config_value('view RESTRICTED included .1') + self.assertIn('80', config) def test_snmpv3_sha(self): # Check if SNMPv3 can be configured with SHA authentication diff --git a/smoketest/scripts/cli/test_service_tftp-server.py b/smoketest/scripts/cli/test_service_tftp-server.py index b57c33f26..99d81e203 100755 --- a/smoketest/scripts/cli/test_service_tftp-server.py +++ b/smoketest/scripts/cli/test_service_tftp-server.py @@ -33,15 +33,32 @@ address_ipv6 = '2001:db8::1' vrf = 'mgmt' class TestServiceTFTPD(VyOSUnitTestSHIM.TestCase): - def setUp(self): - self.cli_set(dummy_if_path + ['address', address_ipv4 + '/32']) - self.cli_set(dummy_if_path + ['address', address_ipv6 + '/128']) + @classmethod + def setUpClass(cls): + super(TestServiceTFTPD, 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, dummy_if_path + ['address', address_ipv4 + '/32']) + cls.cli_set(cls, dummy_if_path + ['address', address_ipv6 + '/128']) + + @classmethod + def tearDownClass(cls): + cls.cli_delete(cls, dummy_if_path) + super(TestServiceTFTPD, cls).tearDownClass() def tearDown(self): + # Check for running process + self.assertTrue(process_named_running(PROCESS_NAME)) + self.cli_delete(base_path) - self.cli_delete(dummy_if_path) self.cli_commit() + # Check for no longer running process + self.assertFalse(process_named_running(PROCESS_NAME)) + def test_01_tftpd_single(self): directory = '/tmp' port = '69' # default port @@ -61,9 +78,6 @@ class TestServiceTFTPD(VyOSUnitTestSHIM.TestCase): # verify upload self.assertIn('--create --umask 000', config) - # Check for running process - self.assertTrue(process_named_running(PROCESS_NAME)) - def test_02_tftpd_multi(self): directory = '/tmp' address = [address_ipv4, address_ipv6] @@ -125,9 +139,6 @@ class TestServiceTFTPD(VyOSUnitTestSHIM.TestCase): # 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) diff --git a/smoketest/scripts/cli/test_system_ip.py b/smoketest/scripts/cli/test_system_ip.py index f71ef5b3f..e7f7e3345 100755 --- a/smoketest/scripts/cli/test_system_ip.py +++ b/smoketest/scripts/cli/test_system_ip.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -17,6 +17,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError from vyos.util import read_file base_path = ['system', 'ip'] @@ -82,5 +83,31 @@ class TestSystemIP(VyOSUnitTestSHIM.TestCase): self.assertEqual(read_file(gc_thresh2), str(size // 2)) self.assertEqual(read_file(gc_thresh1), str(size // 8)) + def test_system_ip_protocol_route_map(self): + protocols = ['any', 'babel', 'bgp', 'connected', 'eigrp', 'isis', + 'kernel', 'ospf', 'rip', 'static', 'table'] + + for protocol in protocols: + self.cli_set(['policy', 'route-map', f'route-map-{protocol}', 'rule', '10', 'action', 'permit']) + self.cli_set(base_path + ['protocol', protocol, 'route-map', f'route-map-{protocol}']) + + self.cli_commit() + + # Verify route-map properly applied to FRR + frrconfig = self.getFRRconfig('ip protocol', end='', daemon='zebra') + for protocol in protocols: + self.assertIn(f'ip protocol {protocol} route-map route-map-{protocol}', frrconfig) + + def test_system_ip_protocol_non_existing_route_map(self): + non_existing = 'non-existing' + self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing]) + + # VRF does yet not exist - an error must be thrown + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny']) + # Commit again + self.cli_commit() + 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 c8aea9100..e91b924fc 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-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -18,6 +18,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError from vyos.template import is_ipv4 from vyos.util import read_file from vyos.util import get_interface_config @@ -88,5 +89,36 @@ class TestSystemIPv6(VyOSUnitTestSHIM.TestCase): self.assertEqual(read_file(gc_thresh2), str(size // 2)) self.assertEqual(read_file(gc_thresh1), str(size // 8)) + def test_system_ipv6_protocol_route_map(self): + protocols = ['any', 'babel', 'bgp', 'connected', 'isis', + 'kernel', 'ospfv3', 'ripng', 'static', 'table'] + + for protocol in protocols: + route_map = 'route-map-' + protocol.replace('ospfv3', 'ospf6') + + self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + self.cli_set(base_path + ['protocol', protocol, 'route-map', route_map]) + + self.cli_commit() + + # Verify route-map properly applied to FRR + frrconfig = self.getFRRconfig('ipv6 protocol', end='', daemon='zebra') + for protocol in protocols: + # VyOS and FRR use a different name for OSPFv3 (IPv6) + if protocol == 'ospfv3': + protocol = 'ospf6' + self.assertIn(f'ipv6 protocol {protocol} route-map route-map-{protocol}', frrconfig) + + def test_system_ipv6_protocol_non_existing_route_map(self): + non_existing = 'non-existing6' + self.cli_set(base_path + ['protocol', 'static', 'route-map', non_existing]) + + # VRF does yet not exist - an error must be thrown + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', non_existing, 'rule', '10', 'action', 'deny']) + # Commit again + self.cli_commit() + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py index 6006fe0f6..a1d2ba2ad 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-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 @@ -264,5 +264,26 @@ class TestSystemLogin(VyOSUnitTestSHIM.TestCase): tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf) self.assertTrue(tmp) + def test_system_login_max_login_session(self): + max_logins = '2' + timeout = '600' + + self.cli_set(base_path + ['max-login-session', max_logins]) + + # 'max-login-session' must be only with 'timeout' option + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(base_path + ['timeout', timeout]) + + self.cli_commit() + + security_limits = read_file('/etc/security/limits.d/10-vyos.conf') + self.assertIn(f'* - maxsyslogins {max_logins}', security_limits) + + self.cli_delete(base_path + ['timeout']) + self.cli_delete(base_path + ['max-login-session']) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_sflow.py b/smoketest/scripts/cli/test_system_sflow.py new file mode 100755 index 000000000..1aec050a4 --- /dev/null +++ b/smoketest/scripts/cli/test_system_sflow.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSessionError +from vyos.ifconfig import Section +from vyos.util import cmd +from vyos.util import process_named_running +from vyos.util import read_file + +PROCESS_NAME = 'hsflowd' +base_path = ['system', 'sflow'] + +hsflowd_conf = '/run/sflow/hsflowd.conf' + + +class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase): + + @classmethod + def setUpClass(cls): + 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() + + # after service removal process must no longer run + self.assertFalse(process_named_running(PROCESS_NAME)) + + def test_sflow(self): + agent_address = '192.0.2.5' + agent_interface = 'eth0' + polling = '24' + sampling_rate = '128' + server = '192.0.2.254' + local_server = '127.0.0.1' + port = '8192' + default_port = '6343' + mon_limit = '50' + + self.cli_set( + ['interfaces', 'dummy', 'dum0', 'address', f'{agent_address}/24']) + self.cli_set(base_path + ['agent-address', agent_address]) + self.cli_set(base_path + ['agent-interface', agent_interface]) + + # You need to configure at least one interface for sflow + with self.assertRaises(ConfigSessionError): + self.cli_commit() + for interface in Section.interfaces('ethernet'): + self.cli_set(base_path + ['interface', interface]) + + self.cli_set(base_path + ['polling', polling]) + self.cli_set(base_path + ['sampling-rate', sampling_rate]) + self.cli_set(base_path + ['server', server, 'port', port]) + self.cli_set(base_path + ['server', local_server]) + self.cli_set(base_path + ['drop-monitor-limit', mon_limit]) + + # commit changes + self.cli_commit() + + # verify configuration + hsflowd = read_file(hsflowd_conf) + + self.assertIn(f'polling={polling}', hsflowd) + self.assertIn(f'sampling={sampling_rate}', hsflowd) + self.assertIn(f'agentIP={agent_address}', hsflowd) + self.assertIn(f'agent={agent_interface}', hsflowd) + self.assertIn(f'collector {{ ip = {server} udpport = {port} }}', hsflowd) + self.assertIn(f'collector {{ ip = {local_server} udpport = {default_port} }}', hsflowd) + self.assertIn(f'dropmon {{ limit={mon_limit} start=on sw=on hw=off }}', hsflowd) + self.assertIn('dbus { }', hsflowd) + + for interface in Section.interfaces('ethernet'): + self.assertIn(f'pcap {{ dev={interface} }}', hsflowd) + + +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 bd242104f..b677f0e45 100755 --- a/smoketest/scripts/cli/test_vpn_ipsec.py +++ b/smoketest/scripts/cli/test_vpn_ipsec.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -34,11 +34,15 @@ swanctl_file = '/etc/swanctl/swanctl.conf' peer_ip = '203.0.113.45' connection_name = 'main-branch' +local_id = 'left' +remote_id = 'right' interface = 'eth1' vif = '100' esp_group = 'MyESPGroup' ike_group = 'MyIKEGroup' secret = 'MYSECRETKEY' +PROCESS_NAME = 'charon' +regex_uuid4 = '[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}' ca_pem = """ MIIDSzCCAjOgAwIBAgIUQHK+ZgTUYZksvXY2/MyW+Jiels4wDQYJKoZIhvcNAQEL @@ -113,6 +117,8 @@ rgiyCHemtMepq57Pl1Nmj49eEA== """ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): + skip_process_check = False + @classmethod def setUpClass(cls): super(TestVPNIPsec, cls).setUpClass() @@ -137,23 +143,34 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): def tearDown(self): # Check for running process - self.assertTrue(process_named_running('charon')) + if not self.skip_process_check: + self.assertTrue(process_named_running(PROCESS_NAME)) + else: + self.skip_process_check = False # Reset self.cli_delete(base_path) self.cli_delete(tunnel_path) self.cli_commit() # Check for no longer running process - self.assertFalse(process_named_running('charon')) + self.assertFalse(process_named_running(PROCESS_NAME)) def test_01_dhcp_fail_handling(self): + # Skip process check - connection is not created for this test + self.skip_process_check = True + # Interface for dhcp-interface self.cli_set(ethernet_path + [interface, 'vif', vif, 'address', 'dhcp']) # Use VLAN to avoid getting IP from qemu dhcp server + # vpn ipsec auth psk <tag> id <x.x.x.x> + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', remote_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', peer_ip]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'secret', secret]) + # Site to site peer_base_path = base_path + ['site-to-site', 'peer', connection_name] 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]) self.cli_set(peer_base_path + ['default-esp-group', esp_group]) self.cli_set(peer_base_path + ['dhcp-interface', f'{interface}.{vif}']) @@ -166,21 +183,30 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): dhcp_waiting = read_file(dhcp_waiting_file) self.assertIn(f'{interface}.{vif}', dhcp_waiting) # Ensure dhcp-failed interface was added for dhclient hook + self.cli_delete(ethernet_path + [interface, 'vif', vif, 'address']) + def test_02_site_to_site(self): self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) - # Site to site local_address = '192.0.2.10' priority = '20' life_bytes = '100000' life_packets = '2000000' + + # vpn ipsec auth psk <tag> id <x.x.x.x> + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', remote_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_address]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', peer_ip]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'secret', secret]) + + # Site to site peer_base_path = base_path + ['site-to-site', 'peer', connection_name] 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]) self.cli_set(peer_base_path + ['default-esp-group', esp_group]) self.cli_set(peer_base_path + ['local-address', local_address]) @@ -227,12 +253,14 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.assertIn(line, swanctl_conf) swanctl_secrets_lines = [ - f'id-local = {local_address} # dhcp:no', - f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}', + f'id-{regex_uuid4} = "{local_id}"', + f'id-{regex_uuid4} = "{remote_id}"', + f'id-{regex_uuid4} = "{local_address}"', + f'id-{regex_uuid4} = "{peer_ip}"', f'secret = "{secret}"' ] for line in swanctl_secrets_lines: - self.assertIn(line, swanctl_conf) + self.assertRegex(swanctl_conf, fr'{line}') def test_03_site_to_site_vti(self): @@ -246,10 +274,15 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): # VTI interface self.cli_set(vti_path + [vti, 'address', '10.1.1.1/24']) + # vpn ipsec auth psk <tag> id <x.x.x.x> + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', remote_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', peer_ip]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'secret', secret]) + # Site to site peer_base_path = base_path + ['site-to-site', 'peer', connection_name] 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 + ['force-udp-encapsulation']) self.cli_set(peer_base_path + ['ike-group', ike_group]) @@ -292,12 +325,12 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.assertIn(line, swanctl_conf) swanctl_secrets_lines = [ - f'id-local = {local_address} # dhcp:no', - f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}', + f'id-{regex_uuid4} = "{local_id}"', + f'id-{regex_uuid4} = "{remote_id}"', f'secret = "{secret}"' ] for line in swanctl_secrets_lines: - self.assertIn(line, swanctl_conf) + self.assertRegex(swanctl_conf, fr'{line}') def test_04_dmvpn(self): @@ -310,7 +343,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(tunnel_path + [tunnel_if, 'address', '172.16.253.134/29']) self.cli_set(tunnel_path + [tunnel_if, 'encapsulation', 'gre']) self.cli_set(tunnel_path + [tunnel_if, 'source-address', '192.0.2.1']) - self.cli_set(tunnel_path + [tunnel_if, 'multicast', 'enable']) + self.cli_set(tunnel_path + [tunnel_if, 'enable-multicast']) self.cli_set(tunnel_path + [tunnel_if, 'parameters', 'ip', 'key', '1']) # NHRP @@ -334,6 +367,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'dh-group', '2']) self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'encryption', 'aes256']) self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'hash', 'sha1']) + self.cli_set(base_path + ['ike-group', ike_group, 'proposal', '2', 'prf', 'prfsha1']) # Profile self.cli_set(base_path + ['profile', 'NHRPVPN', 'authentication', 'mode', 'pre-shared-secret']) @@ -346,7 +380,7 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): swanctl_conf = read_file(swanctl_file) swanctl_lines = [ - f'proposals = aes128-sha1-modp1024,aes256-sha1-modp1024', + f'proposals = aes128-sha1-modp1024,aes256-sha1-prfsha1-modp1024', f'version = 1', f'rekey_time = {ike_lifetime}s', f'rekey_time = {esp_lifetime}s', @@ -450,9 +484,15 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['options', 'interface', 'tun1']) self.cli_set(base_path + ['ike-group', ike_group, 'key-exchange', 'ikev2']) + # vpn ipsec auth psk <tag> id <x.x.x.x> + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', remote_id]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', local_address]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'id', peer_ip]) + self.cli_set(base_path + ['authentication', 'psk', connection_name, 'secret', secret]) + self.cli_set(peer_base_path + ['authentication', 'local-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]) @@ -482,15 +522,15 @@ class TestVPNIPsec(VyOSUnitTestSHIM.TestCase): self.assertIn(line, swanctl_conf) swanctl_secrets_lines = [ - f'id-local = {local_address} # dhcp:no', - f'id-remote_{peer_ip.replace(".","-")} = {peer_ip}', - f'id-localid = {local_id}', - f'id-remoteid = {remote_id}', + f'id-{regex_uuid4} = "{local_id}"', + f'id-{regex_uuid4} = "{remote_id}"', + f'id-{regex_uuid4} = "{peer_ip}"', + f'id-{regex_uuid4} = "{local_address}"', f'secret = "{secret}"', ] for line in swanctl_secrets_lines: - self.assertIn(line, swanctl_conf) + self.assertRegex(swanctl_conf, fr'{line}') # Verify charon configuration charon_conf = read_file(charon_file) diff --git a/smoketest/scripts/cli/test_vpn_openconnect.py b/smoketest/scripts/cli/test_vpn_openconnect.py index 8572d6d66..ec8ecacb9 100755 --- a/smoketest/scripts/cli/test_vpn_openconnect.py +++ b/smoketest/scripts/cli/test_vpn_openconnect.py @@ -18,6 +18,7 @@ import unittest from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.template import ip_from_cidr from vyos.util import process_named_running from vyos.util import read_file @@ -52,6 +53,9 @@ config_file = '/run/ocserv/ocserv.conf' auth_file = '/run/ocserv/ocpasswd' otp_file = '/run/ocserv/users.oath' +listen_if = 'dum116' +listen_address = '100.64.0.1/32' + class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): @@ -61,6 +65,8 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase): # out the current configuration :) cls.cli_delete(cls, base_path) + cls.cli_set(cls, ['interfaces', 'dummy', listen_if, 'address', listen_address]) + cls.cli_set(cls, pki_path + ['ca', 'openconnect', 'certificate', cert_data.replace('\n','')]) cls.cli_set(cls, pki_path + ['certificate', 'openconnect', 'certificate', cert_data.replace('\n','')]) cls.cli_set(cls, pki_path + ['certificate', 'openconnect', 'private', 'key', key_data.replace('\n','')]) @@ -68,6 +74,7 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase): @classmethod def tearDownClass(cls): cls.cli_delete(cls, pki_path) + cls.cli_delete(cls, ['interfaces', 'dummy', listen_if]) super(TestVPNOpenConnect, cls).tearDownClass() def tearDown(self): @@ -104,6 +111,9 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['ssl', 'ca-certificate', 'openconnect']) self.cli_set(base_path + ['ssl', 'certificate', 'openconnect']) + listen_ip_no_cidr = ip_from_cidr(listen_address) + self.cli_set(base_path + ['listen-address', listen_ip_no_cidr]) + self.cli_commit() # Verify configuration @@ -111,10 +121,15 @@ class TestVPNOpenConnect(VyOSUnitTestSHIM.TestCase): # authentication mode local password-otp self.assertIn(f'auth = "plain[passwd=/run/ocserv/ocpasswd,otp=/run/ocserv/users.oath]"', daemon_config) + self.assertIn(f'listen-host = {listen_ip_no_cidr}', daemon_config) self.assertIn(f'ipv4-network = {v4_subnet}', daemon_config) self.assertIn(f'ipv6-network = {v6_prefix}', daemon_config) self.assertIn(f'ipv6-subnet-prefix = {v6_len}', daemon_config) + # defaults + self.assertIn(f'tcp-port = 443', daemon_config) + self.assertIn(f'udp-port = 443', daemon_config) + for ns in name_server: self.assertIn(f'dns = {ns}', daemon_config) for domain in split_dns: diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index 176c095fb..926616727 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-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -33,6 +33,8 @@ from vyos.validate import is_intf_addr_assigned base_path = ['vrf'] vrfs = ['red', 'green', 'blue', 'foo-bar', 'baz_foo'] +v4_protocols = ['any', 'babel', 'bgp', 'connected', 'eigrp', 'isis', 'kernel', 'ospf', 'rip', 'static', 'table'] +v6_protocols = ['any', 'babel', 'bgp', 'connected', 'isis', 'kernel', 'ospfv3', 'ripng', 'static', 'table'] class VRFTest(VyOSUnitTestSHIM.TestCase): _interfaces = [] @@ -59,7 +61,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.assertNotIn(vrf, interfaces()) def test_vrf_vni_and_table_id(self): - table = '1000' + base_table = '1000' + table = base_table for vrf in vrfs: base = base_path + ['name', vrf] description = f'VyOS-VRF-{vrf}' @@ -80,7 +83,7 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify VRF configuration - table = '1000' + table = base_table iproute2_config = read_file('/etc/iproute2/rt_tables.d/vyos-vrf.conf') for vrf in vrfs: description = f'VyOS-VRF-{vrf}' @@ -194,7 +197,8 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_delete(['interfaces', section, interface, 'vrf']) def test_vrf_static_route(self): - table = '100' + base_table = '100' + table = base_table for vrf in vrfs: next_hop = f'192.0.{table}.1' prefix = f'10.0.{table}.0/24' @@ -215,13 +219,12 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): self.cli_commit() # Verify VRF configuration - table = '100' + table = base_table for vrf in vrfs: next_hop = f'192.0.{table}.1' prefix = f'10.0.{table}.0/24' self.assertTrue(vrf in interfaces()) - vrf_if = Interface(vrf) frrconfig = self.getFRRconfig(f'vrf {vrf}') self.assertIn(f' vni {table}', frrconfig) @@ -291,5 +294,174 @@ class VRFTest(VyOSUnitTestSHIM.TestCase): 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') + def test_vrf_ip_protocol_route_map(self): + table = '6000' + + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + + for protocol in v4_protocols: + self.cli_set(['policy', 'route-map', f'route-map-{vrf}-{protocol}', 'rule', '10', 'action', 'permit']) + self.cli_set(base + ['ip', 'protocol', protocol, 'route-map', f'route-map-{vrf}-{protocol}']) + + table = str(int(table) + 1) + + self.cli_commit() + + # Verify route-map properly applied to FRR + for vrf in vrfs: + frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon='zebra') + self.assertIn(f'vrf {vrf}', frrconfig) + for protocol in v4_protocols: + self.assertIn(f' ip protocol {protocol} route-map route-map-{vrf}-{protocol}', frrconfig) + + def test_vrf_ip_ipv6_protocol_non_existing_route_map(self): + table = '6100' + non_existing = 'non-existing' + + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + for protocol in v4_protocols: + self.cli_set(base + ['ip', 'protocol', protocol, 'route-map', f'v4-{non_existing}']) + for protocol in v6_protocols: + self.cli_set(base + ['ipv6', 'protocol', protocol, 'route-map', f'v6-{non_existing}']) + + table = str(int(table) + 1) + + # Both v4 and v6 route-maps do not exist yet + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', f'v4-{non_existing}', 'rule', '10', 'action', 'deny']) + + # v6 route-map does not exist yet + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(['policy', 'route-map', f'v6-{non_existing}', 'rule', '10', 'action', 'deny']) + + # Commit again + self.cli_commit() + + def test_vrf_ipv6_protocol_route_map(self): + table = '6200' + + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', table]) + + for protocol in v6_protocols: + route_map = f'route-map-{vrf}-{protocol.replace("ospfv3", "ospf6")}' + self.cli_set(['policy', 'route-map', route_map, 'rule', '10', 'action', 'permit']) + self.cli_set(base + ['ipv6', 'protocol', protocol, 'route-map', route_map]) + + table = str(int(table) + 1) + + self.cli_commit() + + # Verify route-map properly applied to FRR + for vrf in vrfs: + frrconfig = self.getFRRconfig(f'vrf {vrf}', daemon='zebra') + self.assertIn(f'vrf {vrf}', frrconfig) + for protocol in v6_protocols: + # VyOS and FRR use a different name for OSPFv3 (IPv6) + if protocol == 'ospfv3': + protocol = 'ospf6' + route_map = f'route-map-{vrf}-{protocol}' + self.assertIn(f' ipv6 protocol {protocol} route-map {route_map}', frrconfig) + + def test_vrf_vni_duplicates(self): + base_table = '6300' + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', str(table)]) + self.cli_set(base + ['vni', '100']) + table = str(int(table) + 1) + + # L3VNIs can only be used once + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['vni', str(table)]) + table = str(int(table) + 1) + + # commit changes + self.cli_commit() + + # Verify VRF configuration + table = base_table + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertIn(f' vni {table}', frrconfig) + # Increment table ID for the next run + table = str(int(table) + 1) + + def test_vrf_vni_add_change_remove(self): + base_table = '6300' + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['table', str(table)]) + self.cli_set(base + ['vni', str(table)]) + table = str(int(table) + 1) + + # commit changes + self.cli_commit() + + # Verify VRF configuration + table = base_table + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertIn(f' vni {table}', frrconfig) + # Increment table ID for the next run + table = str(int(table) + 1) + + # Now change all L3VNIs (increment 2) + # We must also change the base_table number as we probably could get + # duplicate VNI's during the test as VNIs are applied 1:1 to FRR + base_table = '5000' + table = base_table + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_set(base + ['vni', str(table)]) + table = str(int(table) + 2) + + # commit changes + self.cli_commit() + + # Verify VRF configuration + table = base_table + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertIn(f' vni {table}', frrconfig) + # Increment table ID for the next run + table = str(int(table) + 2) + + # Now delete all the VNIs + for vrf in vrfs: + base = base_path + ['name', vrf] + self.cli_delete(base + ['vni']) + + # commit changes + self.cli_commit() + + # Verify no VNI is defined + for vrf in vrfs: + self.assertTrue(vrf in interfaces()) + + frrconfig = self.getFRRconfig(f'vrf {vrf}') + self.assertNotIn('vni', frrconfig) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/system/test_kernel_options.py b/smoketest/scripts/system/test_kernel_options.py index 4d9cbacbe..94be0483a 100755 --- a/smoketest/scripts/system/test_kernel_options.py +++ b/smoketest/scripts/system/test_kernel_options.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -14,14 +14,19 @@ # 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 gzip import re +import os import platform import unittest +from vyos.util import call from vyos.util import read_file kernel = platform.release() config = read_file(f'/boot/config-{kernel}') +CONFIG = '/proc/config.gz' + class TestKernelModules(unittest.TestCase): """ VyOS makes use of a lot of Kernel drivers, modules and features. The @@ -42,6 +47,22 @@ class TestKernelModules(unittest.TestCase): tmp = re.findall(f'{option}=(y|m)', config) self.assertTrue(tmp) + def test_dropmon_enabled(self): + options_to_check = [ + 'CONFIG_NET_DROP_MONITOR=y', + 'CONFIG_UPROBE_EVENTS=y', + 'CONFIG_BPF_EVENTS=y', + 'CONFIG_TRACEPOINTS=y' + ] + if not os.path.isfile(CONFIG): + call('sudo modprobe configs') + + with gzip.open(CONFIG, 'rt') as f: + config_data = f.read() + for option in options_to_check: + self.assertIn(option, config_data, + f"Option {option} is not present in /proc/config.gz") + def test_qemu_support(self): # The bond/lacp interface must be enabled in the OS Kernel for option in ['CONFIG_VIRTIO_BLK', 'CONFIG_SCSI_VIRTIO', @@ -58,6 +79,7 @@ class TestKernelModules(unittest.TestCase): tmp = re.findall(f'{option}=(y|m)', config) self.assertTrue(tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/system/test_module_load.py b/smoketest/scripts/system/test_module_load.py index 76a41ac4d..bd30c57ec 100755 --- a/smoketest/scripts/system/test_module_load.py +++ b/smoketest/scripts/system/test_module_load.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2020 VyOS maintainers and contributors +# Copyright (C) 2019-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -23,8 +23,7 @@ modules = { "intel_qat": ["qat_200xx", "qat_200xxvf", "qat_c3xxx", "qat_c3xxxvf", "qat_c62x", "qat_c62xvf", "qat_d15xx", "qat_d15xxvf", "qat_dh895xcc", "qat_dh895xccvf"], - "accel_ppp": ["ipoe", "vlan_mon"], - "misc": ["wireguard"] + "accel_ppp": ["ipoe", "vlan_mon"] } class TestKernelModules(unittest.TestCase): |