diff options
Diffstat (limited to 'smoketest')
-rw-r--r-- | smoketest/config-tests/container-simple | 1 | ||||
-rw-r--r-- | smoketest/config-tests/nat-basic | 85 | ||||
-rw-r--r-- | smoketest/configs/container-simple | 1 | ||||
-rw-r--r-- | smoketest/configs/nat-basic | 256 | ||||
-rw-r--r-- | smoketest/scripts/cli/base_accel_ppp_test.py | 41 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_cgnat.py | 138 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_container.py | 16 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_load-balancing_reverse-proxy.py | 78 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_isis.py | 17 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_protocols_ospf.py | 3 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_qos.py | 116 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_service_dns_forwarding.py | 10 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_service_https.py | 41 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_service_upnp.py | 103 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_vpn_l2tp.py | 23 | ||||
-rwxr-xr-x | smoketest/scripts/system/test_kernel_options.py | 17 |
16 files changed, 841 insertions, 105 deletions
diff --git a/smoketest/config-tests/container-simple b/smoketest/config-tests/container-simple index 299af64cb..cc80ef4cf 100644 --- a/smoketest/config-tests/container-simple +++ b/smoketest/config-tests/container-simple @@ -8,5 +8,6 @@ set container name c01 capability 'net-bind-service' set container name c01 capability 'net-raw' set container name c01 image 'busybox:stable' set container name c02 allow-host-networks +set container name c02 allow-host-pid set container name c02 capability 'sys-time' set container name c02 image 'busybox:stable' diff --git a/smoketest/config-tests/nat-basic b/smoketest/config-tests/nat-basic new file mode 100644 index 000000000..9fea08b02 --- /dev/null +++ b/smoketest/config-tests/nat-basic @@ -0,0 +1,85 @@ +set interfaces ethernet eth0 offload rps +set interfaces ethernet eth0 disable +set interfaces ethernet eth1 offload gro +set interfaces ethernet eth1 offload gso +set interfaces ethernet eth1 offload rps +set interfaces ethernet eth1 offload sg +set interfaces ethernet eth1 offload tso +set interfaces ethernet eth2 offload gro +set interfaces ethernet eth2 offload gso +set interfaces ethernet eth2 offload rps +set interfaces ethernet eth2 offload sg +set interfaces ethernet eth2 offload tso +set interfaces ethernet eth3 offload gro +set interfaces ethernet eth3 offload gso +set interfaces ethernet eth3 offload rps +set interfaces ethernet eth3 offload sg +set interfaces ethernet eth3 offload tso +set interfaces bonding bond10 hash-policy 'layer3+4' +set interfaces bonding bond10 member interface 'eth2' +set interfaces bonding bond10 member interface 'eth3' +set interfaces bonding bond10 mode '802.3ad' +set interfaces bonding bond10 vif 50 address '192.168.189.1/24' +set interfaces loopback lo +set interfaces pppoe pppoe7 authentication password 'vyos' +set interfaces pppoe pppoe7 authentication username 'vyos' +set interfaces pppoe pppoe7 dhcpv6-options pd 0 interface bond10.50 address '1' +set interfaces pppoe pppoe7 dhcpv6-options pd 0 length '56' +set interfaces pppoe pppoe7 ip adjust-mss '1452' +set interfaces pppoe pppoe7 ipv6 address autoconf +set interfaces pppoe pppoe7 ipv6 adjust-mss '1432' +set interfaces pppoe pppoe7 mtu '1492' +set interfaces pppoe pppoe7 no-peer-dns +set interfaces pppoe pppoe7 source-interface 'eth1' +set service lldp interface eth1 disable +set service ntp allow-client address '192.168.189.0/24' +set service ntp server time1.vyos.net +set service ntp server time2.vyos.net +set service ntp listen-address '192.168.189.1' +set service ssh dynamic-protection +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 lease '604800' +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option default-router '192.168.189.1' +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option domain-name 'vyos.net' +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '1.1.1.1' +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 option name-server '9.9.9.9' +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 start '192.168.189.20' +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 range 0 stop '192.168.189.254' +set service dhcp-server shared-network-name LAN subnet 192.168.189.0/24 subnet-id '1' +set service router-advert interface bond10.50 prefix ::/64 preferred-lifetime '2700' +set service router-advert interface bond10.50 prefix ::/64 valid-lifetime '5400' +set system config-management commit-revisions '100' +set system domain-name 'vyos.net' +set system host-name 'R1' +set system login user vyos authentication encrypted-password '$6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/' +set system login user vyos authentication plaintext-password '' +set system name-server '1.1.1.1' +set system name-server '9.9.9.9' +set system console device ttyS0 speed '115200' +set nat destination rule 1000 destination port '3389' +set nat destination rule 1000 inbound-interface name 'pppoe7' +set nat destination rule 1000 protocol 'tcp' +set nat destination rule 1000 translation address '192.168.189.5' +set nat destination rule 1000 translation port '3389' +set nat destination rule 10022 destination port '10022' +set nat destination rule 10022 inbound-interface name 'pppoe7' +set nat destination rule 10022 protocol 'tcp' +set nat destination rule 10022 translation address '192.168.189.2' +set nat destination rule 10022 translation port '22' +set nat destination rule 10300 destination port '10300' +set nat destination rule 10300 inbound-interface name 'pppoe7' +set nat destination rule 10300 protocol 'udp' +set nat destination rule 10300 translation address '192.168.189.2' +set nat destination rule 10300 translation port '10300' +set nat source rule 10 outbound-interface name 'eth1' +set nat source rule 10 source address '192.168.189.0/24' +set nat source rule 10 translation address 'masquerade' +set nat source rule 10 translation options port-mapping 'random' +set nat source rule 50 outbound-interface name 'pppoe7' +set nat source rule 50 protocol 'udp' +set nat source rule 50 source address '192.168.189.2' +set nat source rule 50 source port '10300' +set nat source rule 50 translation address 'masquerade' +set nat source rule 50 translation port '10300' +set nat source rule 100 outbound-interface name 'pppoe7' +set nat source rule 100 source address '192.168.189.0/24' +set nat source rule 100 translation address 'masquerade' diff --git a/smoketest/configs/container-simple b/smoketest/configs/container-simple index 05efe05e9..82983afb7 100644 --- a/smoketest/configs/container-simple +++ b/smoketest/configs/container-simple @@ -7,6 +7,7 @@ container { } name c02 { allow-host-networks + allow-host-pid cap-add sys-time image busybox:stable } diff --git a/smoketest/configs/nat-basic b/smoketest/configs/nat-basic new file mode 100644 index 000000000..52f369f34 --- /dev/null +++ b/smoketest/configs/nat-basic @@ -0,0 +1,256 @@ +interfaces { + bonding bond10 { + hash-policy "layer3+4" + member { + interface "eth2" + interface "eth3" + } + mode "802.3ad" + vif 50 { + address "192.168.189.1/24" + } + } + ethernet eth0 { + disable + offload { + gro + gso + rps + sg + tso + } + } + ethernet eth1 { + offload { + gro + gso + rps + sg + tso + } + } + ethernet eth2 { + offload { + gro + gso + rps + sg + tso + } + } + ethernet eth3 { + offload { + gro + gso + rps + sg + tso + } + } + loopback lo { + } + pppoe pppoe7 { + authentication { + password "vyos" + username "vyos" + } + dhcpv6-options { + pd 0 { + interface bond10.50 { + address "1" + } + length "56" + } + } + ip { + adjust-mss "1452" + } + ipv6 { + address { + autoconf + } + adjust-mss "1432" + } + mtu "1492" + no-peer-dns + source-interface "eth1" + } +} +nat { + destination { + rule 1000 { + destination { + port "3389" + } + inbound-interface { + name "pppoe7" + } + protocol "tcp" + translation { + address "192.168.189.5" + port "3389" + } + } + rule 10022 { + destination { + port "10022" + } + inbound-interface { + name "pppoe7" + } + protocol "tcp" + translation { + address "192.168.189.2" + port "22" + } + } + rule 10300 { + destination { + port "10300" + } + inbound-interface { + name "pppoe7" + } + protocol "udp" + translation { + address "192.168.189.2" + port "10300" + } + } + } + source { + rule 10 { + outbound-interface { + name "eth1" + } + source { + address "192.168.189.0/24" + } + translation { + address "masquerade" + options { + port-mapping fully-random + } + } + } + rule 50 { + outbound-interface { + name "pppoe7" + } + protocol "udp" + source { + address "192.168.189.2" + port "10300" + } + translation { + address "masquerade" + port "10300" + } + } + rule 100 { + outbound-interface { + name "pppoe7" + } + source { + address "192.168.189.0/24" + } + translation { + address "masquerade" + } + } + } +} +service { + dhcp-server { + shared-network-name LAN { + subnet 192.168.189.0/24 { + default-router "192.168.189.1" + domain-name "vyos.net" + lease "604800" + name-server "1.1.1.1" + name-server "9.9.9.9" + range 0 { + start "192.168.189.20" + stop "192.168.189.254" + } + } + } + } + lldp { + interface all { + } + interface eth1 { + disable + } + } + ntp { + allow-client { + address "192.168.189.0/24" + } + listen-address "192.168.189.1" + server time1.vyos.net { + } + server time2.vyos.net { + } + } + router-advert { + interface bond10.50 { + prefix ::/64 { + preferred-lifetime "2700" + valid-lifetime "5400" + } + } + } + ssh { + disable-host-validation + dynamic-protection { + } + } +} +system { + config-management { + commit-revisions "100" + } + conntrack { + modules { + ftp + h323 + nfs + pptp + sip + sqlnet + tftp + } + } + console { + device ttyS0 { + speed "115200" + } + } + domain-name "vyos.net" + host-name "R1" + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + name-server "1.1.1.1" + name-server "9.9.9.9" + syslog { + global { + facility all { + level "info" + } + facility local7 { + level "debug" + } + } + } +} + +// Warning: Do not remove the following line. +// vyos-config-version: "bgp@5:broadcast-relay@1:cluster@2:config-management@1:conntrack@5:conntrack-sync@2:container@2:dhcp-relay@2:dhcp-server@8:dhcpv6-server@1:dns-dynamic@4:dns-forwarding@4:firewall@15:flow-accounting@1:https@6:ids@1:interfaces@32:ipoe-server@3:ipsec@13:isis@3:l2tp@9:lldp@2:mdns@1:monitoring@1:nat@7:nat66@3:ntp@3:openconnect@3:ospf@2:pim@1:policy@8:pppoe-server@10:pptp@5:qos@2:quagga@11:rip@1:rpki@2:salt@1:snmp@3:ssh@2:sstp@6:system@27:vrf@3:vrrp@4:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2" +// Release version: 1.4.0-epa3 diff --git a/smoketest/scripts/cli/base_accel_ppp_test.py b/smoketest/scripts/cli/base_accel_ppp_test.py index 383adc445..212dc58ab 100644 --- a/smoketest/scripts/cli/base_accel_ppp_test.py +++ b/smoketest/scripts/cli/base_accel_ppp_test.py @@ -367,6 +367,27 @@ class BasicAccelPPPTest: ] ) + self.set( + [ + "authentication", + "radius", + "server", + radius_server, + "backup", + ] + ) + + self.set( + [ + "authentication", + "radius", + "server", + radius_server, + "priority", + "10", + ] + ) + # commit changes self.cli_commit() @@ -379,6 +400,8 @@ class BasicAccelPPPTest: self.assertEqual(f"acct-port=0", server[3]) self.assertEqual(f"req-limit=0", server[4]) self.assertEqual(f"fail-time=0", server[5]) + self.assertIn('weight=10', server) + self.assertIn('backup', server) def test_accel_ipv4_pool(self): self.basic_config(is_gateway=False, is_client_pool=False) @@ -605,3 +628,21 @@ delegate={delegate_2_prefix},{delegate_mask},name={pool_name}""" self.assertEqual(conf['connlimit']['limit'], limits) self.assertEqual(conf['connlimit']['burst'], burst) self.assertEqual(conf['connlimit']['timeout'], timeout) + + def test_accel_log_level(self): + self.basic_config() + self.cli_commit() + + # check default value + conf = ConfigParser(allow_no_value=True) + conf.read(self._config_file) + self.assertEqual(conf['log']['level'], '3') + + for log_level in range(0, 5): + self.set(['log', 'level', str(log_level)]) + self.cli_commit() + # Validate configuration values + conf = ConfigParser(allow_no_value=True) + conf.read(self._config_file) + + self.assertEqual(conf['log']['level'], str(log_level)) diff --git a/smoketest/scripts/cli/test_cgnat.py b/smoketest/scripts/cli/test_cgnat.py new file mode 100755 index 000000000..02dad3de5 --- /dev/null +++ b/smoketest/scripts/cli/test_cgnat.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configsession import ConfigSessionError + + +base_path = ['nat', 'cgnat'] +nftables_cgnat_config = '/run/nftables-cgnat.nft' + + +class TestCGNAT(VyOSUnitTestSHIM.TestCase): + @classmethod + def setUpClass(cls): + super(TestCGNAT, cls).setUpClass() + + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, base_path) + + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + self.assertFalse(os.path.exists(nftables_cgnat_config)) + + def test_cgnat(self): + internal_name = 'vyos-int-01' + external_name = 'vyos-ext-01' + internal_net = '100.64.0.0/29' + external_net = '192.0.2.1-192.0.2.2' + external_ports = '40000-60000' + ports_per_subscriber = '5000' + rule = '100' + + nftables_search = [ + ['map tcp_nat_map'], + ['map udp_nat_map'], + ['map icmp_nat_map'], + ['map other_nat_map'], + ['100.64.0.0 : 192.0.2.1 . 40000-44999'], + ['100.64.0.1 : 192.0.2.1 . 45000-49999'], + ['100.64.0.2 : 192.0.2.1 . 50000-54999'], + ['100.64.0.3 : 192.0.2.1 . 55000-59999'], + ['100.64.0.4 : 192.0.2.2 . 40000-44999'], + ['100.64.0.5 : 192.0.2.2 . 45000-49999'], + ['100.64.0.6 : 192.0.2.2 . 50000-54999'], + ['100.64.0.7 : 192.0.2.2 . 55000-59999'], + ['chain POSTROUTING'], + ['type nat hook postrouting priority srcnat'], + ['ip protocol tcp counter snat ip to ip saddr map @tcp_nat_map'], + ['ip protocol udp counter snat ip to ip saddr map @udp_nat_map'], + ['ip protocol icmp counter snat ip to ip saddr map @icmp_nat_map'], + ['counter snat ip to ip saddr map @other_nat_map'], + ] + + self.cli_set(base_path + ['pool', 'external', external_name, 'external-port-range', external_ports]) + self.cli_set(base_path + ['pool', 'external', external_name, 'range', external_net]) + + # allocation out of the available ports + with self.assertRaises(ConfigSessionError): + self.cli_set(base_path + ['pool', 'external', external_name, 'per-user-limit', 'port', '8000']) + self.cli_commit() + self.cli_set(base_path + ['pool', 'external', external_name, 'per-user-limit', 'port', ports_per_subscriber]) + + # internal pool not set + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_set(base_path + ['pool', 'internal', internal_name, 'range', internal_net]) + + self.cli_set(base_path + ['rule', rule, 'source', 'pool', internal_name]) + # non-exist translation pool + with self.assertRaises(ConfigSessionError): + self.cli_set(base_path + ['rule', rule, 'translation', 'pool', 'fake-pool']) + self.cli_commit() + + self.cli_set(base_path + ['rule', rule, 'translation', 'pool', external_name]) + self.cli_commit() + + self.verify_nftables(nftables_search, 'ip cgnat', inverse=False, args='-s') + + + def test_cgnat_sequence(self): + internal_name = 'earth' + external_name = 'milky_way' + internal_net = '100.64.0.0/28' + + ext_addr_alpha_proxima = '192.0.2.121/32' + ext_addr_beta_cygni = '198.51.100.23/32' + ext_addr_gamma_leonis = '203.0.113.102/32' + + ext_seq_beta_cygni = '3' + ext_seq_gamma_leonis = '10' + + external_ports = '1024-65535' + ports_per_subscriber = '10000' + rule = '100' + + nftables_search = [ + ['100.64.0.0 : 198.51.100.23 . 1024-11023, 100.64.0.1 : 198.51.100.23 . 11024-21023'], + ['100.64.0.4 : 198.51.100.23 . 41024-51023, 100.64.0.5 : 198.51.100.23 . 51024-61023'], + ['100.64.0.6 : 203.0.113.102 . 1024-11023, 100.64.0.7 : 203.0.113.102 . 11024-21023'], + ['100.64.0.8 : 203.0.113.102 . 21024-31023, 100.64.0.9 : 203.0.113.102 . 31024-41023'], + ['100.64.0.10 : 203.0.113.102 . 41024-51023, 100.64.0.11 : 203.0.113.102 . 51024-61023'], + ['100.64.0.12 : 192.0.2.121 . 1024-11023, 100.64.0.13 : 192.0.2.121 . 11024-21023'], + ['100.64.0.14 : 192.0.2.121 . 21024-31023, 100.64.0.15 : 192.0.2.121 . 31024-41023'], + ] + + self.cli_set(base_path + ['pool', 'external', external_name, 'external-port-range', external_ports]) + self.cli_set(base_path + ['pool', 'external', external_name, 'per-user-limit', 'port', ports_per_subscriber]) + self.cli_set(base_path + ['pool', 'external', external_name, 'range', ext_addr_alpha_proxima]) + self.cli_set(base_path + ['pool', 'external', external_name, 'range', ext_addr_beta_cygni, 'seq', ext_seq_beta_cygni]) + self.cli_set(base_path + ['pool', 'external', external_name, 'range', ext_addr_gamma_leonis, 'seq', ext_seq_gamma_leonis]) + self.cli_set(base_path + ['pool', 'internal', internal_name, 'range', internal_net]) + self.cli_set(base_path + ['rule', rule, 'source', 'pool', internal_name]) + self.cli_set(base_path + ['rule', rule, 'translation', 'pool', external_name]) + self.cli_commit() + + self.verify_nftables(nftables_search, 'ip cgnat', inverse=False, args='-s') + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_container.py b/smoketest/scripts/cli/test_container.py index 3201883b8..90f821c60 100755 --- a/smoketest/scripts/cli/test_container.py +++ b/smoketest/scripts/cli/test_container.py @@ -91,6 +91,22 @@ class TestContainer(VyOSUnitTestSHIM.TestCase): # Check for running process self.assertEqual(process_named_running(PROCESS_NAME), pid) + def test_cpu_limit(self): + cont_name = 'c2' + + self.cli_set(base_path + ['name', cont_name, 'allow-host-networks']) + self.cli_set(base_path + ['name', cont_name, 'image', cont_image]) + self.cli_set(base_path + ['name', cont_name, 'cpu-quota', '1.25']) + + self.cli_commit() + + pid = 0 + with open(PROCESS_PIDFILE.format(cont_name), 'r') as f: + pid = int(f.read()) + + # Check for running process + self.assertEqual(process_named_running(PROCESS_NAME), pid) + def test_ipv4_network(self): prefix = '192.0.2.0/24' base_name = 'ipv4' diff --git a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py index c8b17316f..aa796f59f 100755 --- a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py +++ b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py @@ -218,7 +218,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase): # Frontend self.assertIn(f'frontend {frontend}', config) - self.assertIn(f'bind :::{front_port} v4v6', config) + self.assertIn(f'bind [::]:{front_port} v4v6', config) self.assertIn(f'mode {mode}', config) for domain in domains_bk_first: self.assertIn(f'acl {rule_ten} hdr(host) -i {domain}', config) @@ -338,6 +338,11 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase): self.assertIn('http-check send meth GET uri /health', config) self.assertIn('http-check expect string success', config) + # Test configuring both http-check & health-check fails validation script + self.cli_set(base_path + ['backend', 'bk-01', 'health-check', 'ldap']) + with self.assertRaises(ConfigSessionError) as e: + self.cli_commit() + def test_06_lb_reverse_proxy_tcp_mode(self): frontend = 'tcp_8443' mode = 'tcp' @@ -371,7 +376,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase): # Frontend self.assertIn(f'frontend {frontend}', config) - self.assertIn(f'bind :::{front_port} v4v6', config) + self.assertIn(f'bind [::]:{front_port} v4v6', config) self.assertIn(f'mode {mode}', config) self.assertIn(f'tcp-request inspect-delay {tcp_request_delay}', config) @@ -385,5 +390,74 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase): self.assertIn(f'mode {mode}', config) self.assertIn(f'server {bk_name} {bk_server}:{bk_server_port}', config) + def test_07_lb_reverse_proxy_http_response_headers(self): + # Setup base + self.configure_pki() + self.base_config() + + # Set example headers in both frontend and backend + self.cli_set(base_path + ['service', 'https_front', 'http-response-headers', 'Cache-Control', 'value', 'max-age=604800']) + self.cli_set(base_path + ['backend', 'bk-01', 'http-response-headers', 'Proxy-Backend-ID', 'value', 'bk-01']) + self.cli_commit() + + # Test headers are present in generated configuration file + config = read_file(HAPROXY_CONF) + self.assertIn('http-response set-header Cache-Control \'max-age=604800\'', config) + self.assertIn('http-response set-header Proxy-Backend-ID \'bk-01\'', config) + + # Test setting alongside modes other than http is blocked by validation conditions + self.cli_set(base_path + ['service', 'https_front', 'mode', 'tcp']) + with self.assertRaises(ConfigSessionError) as e: + self.cli_commit() + + def test_08_lb_reverse_proxy_tcp_health_checks(self): + # Setup PKI + self.configure_pki() + + # Define variables + frontend = 'fe_ldaps' + mode = 'tcp' + health_check = 'ldap' + front_port = '636' + bk_name = 'bk_ldap' + bk_servers = ['192.0.2.11', '192.0.2.12'] + bk_server_port = '389' + + # Configure frontend + self.cli_set(base_path + ['service', frontend, 'mode', mode]) + self.cli_set(base_path + ['service', frontend, 'port', front_port]) + self.cli_set(base_path + ['service', frontend, 'ssl', 'certificate', 'smoketest']) + + # Configure backend + self.cli_set(base_path + ['backend', bk_name, 'mode', mode]) + self.cli_set(base_path + ['backend', bk_name, 'health-check', health_check]) + for index, bk_server in enumerate(bk_servers): + self.cli_set(base_path + ['backend', bk_name, 'server', f'srv-{index}', 'address', bk_server]) + self.cli_set(base_path + ['backend', bk_name, 'server', f'srv-{index}', 'port', bk_server_port]) + + # Commit & read config + self.cli_commit() + config = read_file(HAPROXY_CONF) + + # Validate Frontend + self.assertIn(f'frontend {frontend}', config) + self.assertIn(f'bind [::]:{front_port} v4v6 ssl crt /run/haproxy/smoketest.pem', config) + self.assertIn(f'mode {mode}', config) + self.assertIn(f'backend {bk_name}', config) + + # Validate Backend + self.assertIn(f'backend {bk_name}', config) + self.assertIn(f'option {health_check}-check', config) + self.assertIn(f'mode {mode}', config) + for index, bk_server in enumerate(bk_servers): + self.assertIn(f'server srv-{index} {bk_server}:{bk_server_port}', config) + + # Validate SMTP option renders correctly + self.cli_set(base_path + ['backend', bk_name, 'health-check', 'smtp']) + self.cli_commit() + config = read_file(HAPROXY_CONF) + self.assertIn(f'option smtpchk', config) + + 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 0fd18a6da..769f3dd33 100755 --- a/smoketest/scripts/cli/test_protocols_isis.py +++ b/smoketest/scripts/cli/test_protocols_isis.py @@ -60,6 +60,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): prefix_list = 'EXPORT-ISIS' route_map = 'EXPORT-ISIS' rule = '10' + metric_style = 'transition' self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'action', 'permit']) self.cli_set(['policy', 'prefix-list', prefix_list, 'rule', rule, 'prefix', '203.0.113.0/24']) @@ -80,6 +81,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_commit() self.cli_set(base_path + ['redistribute', 'ipv4', 'connected', 'level-2', 'route-map', route_map]) + self.cli_set(base_path + ['metric-style', metric_style]) self.cli_set(base_path + ['log-adjacency-changes']) # Commit all changes @@ -88,6 +90,7 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): # Verify all changes tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') self.assertIn(f' net {net}', tmp) + self.assertIn(f' metric-style {metric_style}', tmp) self.assertIn(f' log-adjacency-changes', tmp) self.assertIn(f' redistribute ipv4 connected level-2 route-map {route_map}', tmp) @@ -395,5 +398,19 @@ class TestProtocolsISIS(VyOSUnitTestSHIM.TestCase): self.cli_delete(['policy', 'prefix-list', prefix_list]) self.cli_commit() + def test_isis_10_topology(self): + topologies = ['ipv4-multicast', 'ipv4-mgmt', 'ipv6-unicast', 'ipv6-multicast', 'ipv6-mgmt'] + interface = 'lo' + + # Set a basic IS-IS config + self.cli_set(base_path + ['net', net]) + self.cli_set(base_path + ['interface', interface]) + for topology in topologies: + self.cli_set(base_path + ['topology', topology]) + self.cli_commit() + tmp = self.getFRRconfig(f'router isis {domain}', daemon='isisd') + self.assertIn(f' net {net}', tmp) + self.assertIn(f' topology {topology}', tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_ospf.py b/smoketest/scripts/cli/test_protocols_ospf.py index 1b9cc50fe..585c1dc89 100755 --- a/smoketest/scripts/cli/test_protocols_ospf.py +++ b/smoketest/scripts/cli/test_protocols_ospf.py @@ -16,6 +16,7 @@ import unittest +from time import sleep from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError @@ -480,6 +481,8 @@ class TestProtocolsOSPF(VyOSUnitTestSHIM.TestCase): # Commit main OSPF changes self.cli_commit() + sleep(10) + # Verify main OSPF changes frrconfig = self.getFRRconfig('router ospf', daemon=PROCESS_NAME) self.assertIn(f'router ospf', frrconfig) diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py index bcf5139c7..b98c0e9b7 100755 --- a/smoketest/scripts/cli/test_qos.py +++ b/smoketest/scripts/cli/test_qos.py @@ -738,6 +738,122 @@ class TestQoS(VyOSUnitTestSHIM.TestCase): self.cli_commit() self.assertEqual('', cmd(f'tc filter show dev {interface}')) + def test_14_policy_limiter_marked_traffic(self): + policy_name = 'smoke_test' + base_policy_path = ['qos', 'policy', 'limiter', policy_name] + + self.cli_set(['qos', 'interface', self._interfaces[0], 'ingress', policy_name]) + self.cli_set(base_policy_path + ['class', '100', 'bandwidth', '20gbit']) + self.cli_set(base_policy_path + ['class', '100', 'burst', '3760k']) + self.cli_set(base_policy_path + ['class', '100', 'match', 'INTERNAL', 'mark', '100']) + self.cli_set(base_policy_path + ['class', '100', 'priority', '20']) + self.cli_set(base_policy_path + ['default', 'bandwidth', '1gbit']) + self.cli_set(base_policy_path + ['default', 'burst', '125000000b']) + self.cli_commit() + + tc_filters = cmd(f'tc filter show dev {self._interfaces[0]} ingress') + # class 100 + self.assertIn('filter parent ffff: protocol all pref 20 fw chain 0', tc_filters) + self.assertIn('action order 1: police 0x1 rate 20Gbit burst 3847500b mtu 2Kb action drop overhead 0b', tc_filters) + # default + self.assertIn('filter parent ffff: protocol all pref 255 basic chain 0', tc_filters) + self.assertIn('action order 1: police 0x2 rate 1Gbit burst 125000000b mtu 2Kb action drop overhead 0b', tc_filters) + + def test_15_traffic_match_group(self): + interface = self._interfaces[0] + self.cli_set(['qos', 'interface', interface, 'egress', 'VyOS-HTB']) + base_policy_path = ['qos', 'policy', 'shaper', 'VyOS-HTB'] + + #old syntax + self.cli_set(base_policy_path + ['bandwidth', '100mbit']) + self.cli_set(base_policy_path + ['class', '10', 'bandwidth', '40%']) + self.cli_set(base_policy_path + ['class', '10', 'match', 'AF11', 'ip', 'dscp', 'AF11']) + self.cli_set(base_policy_path + ['class', '10', 'match', 'AF41', 'ip', 'dscp', 'AF41']) + self.cli_set(base_policy_path + ['class', '10', 'match', 'AF43', 'ip', 'dscp', 'AF43']) + self.cli_set(base_policy_path + ['class', '10', 'match', 'CS4', 'ip', 'dscp', 'CS4']) + self.cli_set(base_policy_path + ['class', '10', 'priority', '1']) + self.cli_set(base_policy_path + ['class', '10', 'queue-type', 'fair-queue']) + self.cli_set(base_policy_path + ['class', '20', 'bandwidth', '30%']) + self.cli_set(base_policy_path + ['class', '20', 'match', 'EF', 'ip', 'dscp', 'EF']) + self.cli_set(base_policy_path + ['class', '20', 'match', 'CS5', 'ip', 'dscp', 'CS5']) + self.cli_set(base_policy_path + ['class', '20', 'priority', '2']) + self.cli_set(base_policy_path + ['class', '20', 'queue-type', 'fair-queue']) + self.cli_set(base_policy_path + ['default', 'bandwidth', '20%']) + self.cli_set(base_policy_path + ['default', 'queue-type', 'fair-queue']) + self.cli_commit() + + tc_filters_old = cmd(f'tc -details filter show dev {interface}') + self.assertIn('match 00280000/00ff0000', tc_filters_old) + self.assertIn('match 00880000/00ff0000', tc_filters_old) + self.assertIn('match 00980000/00ff0000', tc_filters_old) + self.assertIn('match 00800000/00ff0000', tc_filters_old) + self.assertIn('match 00a00000/00ff0000', tc_filters_old) + self.assertIn('match 00b80000/00ff0000', tc_filters_old) + # delete config by old syntax + self.cli_delete(base_policy_path) + self.cli_delete(['qos', 'interface', interface, 'egress', 'VyOS-HTB']) + self.cli_commit() + self.assertEqual('', cmd(f'tc -s filter show dev {interface}')) + + self.cli_set(['qos', 'interface', interface, 'egress', 'VyOS-HTB']) + # prepare traffic match group + self.cli_set(['qos', 'traffic-match-group', 'VOICE', 'description', 'voice shaper']) + self.cli_set(['qos', 'traffic-match-group', 'VOICE', 'match', 'EF', 'ip', 'dscp', 'EF']) + self.cli_set(['qos', 'traffic-match-group', 'VOICE', 'match', 'CS5', 'ip', 'dscp', 'CS5']) + + self.cli_set(['qos', 'traffic-match-group', 'REAL_TIME_COMMON', 'description', 'real time common filters']) + self.cli_set(['qos', 'traffic-match-group', 'REAL_TIME_COMMON', 'match', 'AF43', 'ip', 'dscp', 'AF43']) + self.cli_set(['qos', 'traffic-match-group', 'REAL_TIME_COMMON', 'match', 'CS4', 'ip', 'dscp', 'CS4']) + + self.cli_set(['qos', 'traffic-match-group', 'REAL_TIME', 'description', 'real time shaper']) + self.cli_set(['qos', 'traffic-match-group', 'REAL_TIME', 'match', 'AF41', 'ip', 'dscp', 'AF41']) + self.cli_set(['qos', 'traffic-match-group', 'REAL_TIME', 'match-group', 'REAL_TIME_COMMON']) + + # new syntax + self.cli_set(base_policy_path + ['bandwidth', '100mbit']) + self.cli_set(base_policy_path + ['class', '10', 'bandwidth', '40%']) + self.cli_set(base_policy_path + ['class', '10', 'match', 'AF11', 'ip', 'dscp', 'AF11']) + self.cli_set(base_policy_path + ['class', '10', 'match-group', 'REAL_TIME']) + self.cli_set(base_policy_path + ['class', '10', 'priority', '1']) + self.cli_set(base_policy_path + ['class', '10', 'queue-type', 'fair-queue']) + self.cli_set(base_policy_path + ['class', '20', 'bandwidth', '30%']) + self.cli_set(base_policy_path + ['class', '20', 'match-group', 'VOICE']) + self.cli_set(base_policy_path + ['class', '20', 'priority', '2']) + self.cli_set(base_policy_path + ['class', '20', 'queue-type', 'fair-queue']) + self.cli_set(base_policy_path + ['default', 'bandwidth', '20%']) + self.cli_set(base_policy_path + ['default', 'queue-type', 'fair-queue']) + self.cli_commit() + + self.assertEqual(tc_filters_old, cmd(f'tc -details filter show dev {interface}')) + + def test_16_wrong_traffic_match_group(self): + interface = self._interfaces[0] + self.cli_set(['qos', 'interface', interface]) + + # Can not use both IPv6 and IPv4 in one match + self.cli_set(['qos', 'traffic-match-group', '1', 'match', 'one', 'ip', 'dscp', 'EF']) + self.cli_set(['qos', 'traffic-match-group', '1', 'match', 'one', 'ipv6', 'dscp', 'EF']) + with self.assertRaises(ConfigSessionError) as e: + self.cli_commit() + + # check contain itself, should commit success + self.cli_delete(['qos', 'traffic-match-group', '1', 'match', 'one', 'ipv6']) + self.cli_set(['qos', 'traffic-match-group', '1', 'match-group', '1']) + self.cli_commit() + + # check cycle dependency, should commit success + self.cli_set(['qos', 'traffic-match-group', '1', 'match-group', '3']) + self.cli_set(['qos', 'traffic-match-group', '2', 'match', 'one', 'ip', 'dscp', 'CS4']) + self.cli_set(['qos', 'traffic-match-group', '2', 'match-group', '1']) + + self.cli_set(['qos', 'traffic-match-group', '3', 'match', 'one', 'ipv6', 'dscp', 'CS4']) + self.cli_set(['qos', 'traffic-match-group', '3', 'match-group', '2']) + self.cli_commit() + + # inherit from non exist group, should commit success with warning + self.cli_set(['qos', 'traffic-match-group', '3', 'match-group', 'unexpected']) + self.cli_commit() + 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 079c584ba..4db1d7495 100755 --- a/smoketest/scripts/cli/test_service_dns_forwarding.py +++ b/smoketest/scripts/cli/test_service_dns_forwarding.py @@ -291,5 +291,15 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase): tmp = get_config_value('edns-subnet-allow-list') self.assertEqual(tmp, ','.join(options)) + def test_multiple_ns_records(self): + test_zone = 'example.com' + self.cli_set(base_path + ['authoritative-domain', test_zone, 'records', 'ns', 'test', 'target', f'ns1.{test_zone}']) + self.cli_set(base_path + ['authoritative-domain', test_zone, 'records', 'ns', 'test', 'target', f'ns2.{test_zone}']) + self.cli_commit() + zone_config = read_file(f'{PDNS_REC_RUN_DIR}/zone.{test_zone}.conf') + self.assertRegex(zone_config, fr'test\s+\d+\s+NS\s+ns1\.{test_zone}\.') + self.assertRegex(zone_config, fr'test\s+\d+\s+NS\s+ns2\.{test_zone}\.') + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py index f2a64627f..8a6386e4f 100755 --- a/smoketest/scripts/cli/test_service_https.py +++ b/smoketest/scripts/cli/test_service_https.py @@ -412,6 +412,47 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase): self.assertEqual(r.status_code, 200) @ignore_warning(InsecureRequestWarning) + def test_api_image(self): + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/image' + headers = {} + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + payload = { + 'data': '{"op": "add"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 400) + self.assertIn('Missing required field "url"', r.json().get('error')) + + payload = { + 'data': '{"op": "delete"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 400) + self.assertIn('Missing required field "name"', r.json().get('error')) + + payload = { + 'data': '{"op": "set_default"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 400) + self.assertIn('Missing required field "name"', r.json().get('error')) + + payload = { + 'data': '{"op": "show"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + @ignore_warning(InsecureRequestWarning) def test_api_config_file_load_http(self): # Test load config from HTTP URL address = '127.0.0.1' diff --git a/smoketest/scripts/cli/test_service_upnp.py b/smoketest/scripts/cli/test_service_upnp.py deleted file mode 100755 index fd67b0ced..000000000 --- a/smoketest/scripts/cli/test_service_upnp.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2021-2024 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# 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.template import ip_from_cidr -from vyos.utils.file import read_file -from vyos.utils.process import process_named_running - -UPNP_CONF = '/run/upnp/miniupnp.conf' -DAEMON = 'miniupnpd' -interface = 'eth0' -base_path = ['service', 'upnp'] -address_base = ['interfaces', 'ethernet', interface, 'address'] - -ipv4_addr = '100.64.0.1/24' -ipv6_addr = '2001:db8::1/64' - -class TestServiceUPnP(VyOSUnitTestSHIM.TestCase): - @classmethod - def setUpClass(cls): - super(TestServiceUPnP, cls).setUpClass() - - # ensure we can also run this test on a live system - so lets clean - # out the current configuration :) - cls.cli_delete(cls, base_path) - - cls.cli_set(cls, address_base + [ipv4_addr]) - cls.cli_set(cls, address_base + [ipv6_addr]) - - @classmethod - def tearDownClass(cls): - cls.cli_delete(cls, address_base) - cls._session.commit() - - super(TestServiceUPnP, cls).tearDownClass() - - def tearDown(self): - # Check for running process - self.assertTrue(process_named_running(DAEMON)) - - self.cli_delete(base_path) - self.cli_commit() - - # Check for running process - self.assertFalse(process_named_running(DAEMON)) - - def test_ipv4_base(self): - self.cli_set(base_path + ['nat-pmp']) - self.cli_set(base_path + ['listen', interface]) - - # check validate() - WAN interface is mandatory - with self.assertRaises(ConfigSessionError): - self.cli_commit() - self.cli_set(base_path + ['wan-interface', interface]) - - self.cli_commit() - - config = read_file(UPNP_CONF) - self.assertIn(f'ext_ifname={interface}', config) - self.assertIn(f'listening_ip={interface}', config) - self.assertIn(f'enable_natpmp=yes', config) - self.assertIn(f'enable_upnp=yes', config) - - def test_ipv6_base(self): - v6_addr = ip_from_cidr(ipv6_addr) - - self.cli_set(base_path + ['nat-pmp']) - self.cli_set(base_path + ['listen', interface]) - self.cli_set(base_path + ['listen', v6_addr]) - - # check validate() - WAN interface is mandatory - with self.assertRaises(ConfigSessionError): - self.cli_commit() - self.cli_set(base_path + ['wan-interface', interface]) - - self.cli_commit() - - config = read_file(UPNP_CONF) - self.assertIn(f'ext_ifname={interface}', config) - self.assertIn(f'listening_ip={interface}', config) - self.assertIn(f'ipv6_listening_ip={v6_addr}', config) - self.assertIn(f'enable_natpmp=yes', config) - self.assertIn(f'enable_upnp=yes', config) - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_vpn_l2tp.py b/smoketest/scripts/cli/test_vpn_l2tp.py index 8c4e53895..07a7e2906 100755 --- a/smoketest/scripts/cli/test_vpn_l2tp.py +++ b/smoketest/scripts/cli/test_vpn_l2tp.py @@ -95,6 +95,29 @@ class TestVPNL2TPServer(BasicAccelPPPTest.TestCase): self.cli_set(base_path + ['authentication', 'protocols', 'chap']) self.cli_commit() + def test_l2tp_radius_server(self): + base_path = ['vpn', 'l2tp', 'remote-access'] + radius_server = "192.0.2.22" + radius_key = "secretVyOS" + + self.cli_set(base_path + ['authentication', 'mode', 'radius']) + self.cli_set(base_path + ['gateway-address', '192.0.2.1']) + self.cli_set(base_path + ['client-ip-pool', 'SIMPLE-POOL', 'range', '192.0.2.0/24']) + self.cli_set(base_path + ['default-pool', 'SIMPLE-POOL']) + self.cli_set(base_path + ['authentication', 'radius', 'server', radius_server, 'key', radius_key]) + self.cli_set(base_path + ['authentication', 'radius', 'server', radius_server, 'priority', '10']) + self.cli_set(base_path + ['authentication', 'radius', 'server', radius_server, 'backup']) + + # commit changes + self.cli_commit() + + # Validate configuration values + conf = ConfigParser(allow_no_value=True) + conf.read(self._config_file) + server = conf["radius"]["server"].split(",") + self.assertIn('weight=10', server) + self.assertIn('backup', server) + 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 18922d93d..4666e98e7 100755 --- a/smoketest/scripts/system/test_kernel_options.py +++ b/smoketest/scripts/system/test_kernel_options.py @@ -111,5 +111,22 @@ class TestKernelModules(unittest.TestCase): tmp = re.findall(f'{option}=(y|m)', self._config_data) self.assertTrue(tmp) + def test_vfio(self): + options_to_check = [ + 'CONFIG_VFIO', 'CONFIG_VFIO_GROUP', 'CONFIG_VFIO_CONTAINER', + 'CONFIG_VFIO_IOMMU_TYPE1', 'CONFIG_VFIO_NOIOMMU', 'CONFIG_VFIO_VIRQFD' + ] + for option in options_to_check: + tmp = re.findall(f'{option}=(y|m)', self._config_data) + self.assertTrue(tmp) + + def test_container_cpu(self): + options_to_check = [ + 'CONFIG_CGROUP_SCHED', 'CONFIG_CPUSETS', 'CONFIG_CGROUP_CPUACCT', 'CONFIG_CFS_BANDWIDTH' + ] + for option in options_to_check: + tmp = re.findall(f'{option}=(y|m)', self._config_data) + self.assertTrue(tmp) + if __name__ == '__main__': unittest.main(verbosity=2) |