at 23.11-pre 5.0 kB view raw
1# Test of IPv6 functionality in NixOS, including whether router 2# solicication/advertisement using radvd works. 3 4import ./make-test-python.nix ({ pkgs, lib, ...} : { 5 name = "ipv6"; 6 meta = with pkgs.lib.maintainers; { 7 maintainers = [ eelco ]; 8 }; 9 10 nodes = 11 { 12 # We use lib.mkForce here to remove the interface configuration 13 # provided by makeTest, so that the interfaces are all configured 14 # implicitly. 15 16 # This client should use privacy extensions fully, having a 17 # completely-default network configuration. 18 client_defaults.networking.interfaces = lib.mkForce {}; 19 20 # Both of these clients should obtain temporary addresses, but 21 # not use them as the default source IP. We thus run the same 22 # checks against them — but the configuration resulting in this 23 # behaviour is different. 24 25 # Here, by using an altered default value for the global setting... 26 client_global_setting = { 27 networking.interfaces = lib.mkForce {}; 28 networking.tempAddresses = "enabled"; 29 }; 30 # and here, by setting this on the interface explicitly. 31 client_interface_setting = { 32 networking.tempAddresses = "disabled"; 33 networking.interfaces = lib.mkForce { 34 eth1.tempAddress = "enabled"; 35 }; 36 }; 37 38 server = 39 { services.httpd.enable = true; 40 services.httpd.adminAddr = "foo@example.org"; 41 networking.firewall.allowedTCPPorts = [ 80 ]; 42 }; 43 44 router = 45 { ... }: 46 { services.radvd.enable = true; 47 services.radvd.config = 48 '' 49 interface eth1 { 50 AdvSendAdvert on; 51 # ULA prefix (RFC 4193). 52 prefix fd60:cc69:b537:1::/64 { }; 53 }; 54 ''; 55 }; 56 }; 57 58 testScript = 59 '' 60 import re 61 62 # Start the router first so that it respond to router solicitations. 63 router.wait_for_unit("radvd") 64 65 clients = [client_defaults, client_global_setting, client_interface_setting] 66 67 start_all() 68 69 for client in clients: 70 client.wait_for_unit("network.target") 71 server.wait_for_unit("network.target") 72 server.wait_for_unit("httpd.service") 73 74 # Wait until the given interface has a non-tentative address of 75 # the desired scope (i.e. has completed Duplicate Address 76 # Detection). 77 def wait_for_address(machine, iface, scope, temporary=False): 78 temporary_flag = "temporary" if temporary else "-temporary" 79 cmd = f"ip -o -6 addr show dev {iface} scope {scope} -tentative {temporary_flag}" 80 81 machine.wait_until_succeeds(f"[ `{cmd} | wc -l` -eq 1 ]") 82 output = machine.succeed(cmd) 83 ip = re.search(r"inet6 ([0-9a-f:]{2,})/", output).group(1) 84 85 if temporary: 86 scope = scope + " temporary" 87 machine.log(f"{scope} address on {iface} is {ip}") 88 return ip 89 90 91 with subtest("Loopback address can be pinged"): 92 client_defaults.succeed("ping -c 1 ::1 >&2") 93 client_defaults.fail("ping -c 1 2001:db8:: >&2") 94 95 with subtest("Local link addresses can be obtained and pinged"): 96 for client in clients: 97 client_ip = wait_for_address(client, "eth1", "link") 98 server_ip = wait_for_address(server, "eth1", "link") 99 client.succeed(f"ping -c 1 {client_ip}%eth1 >&2") 100 client.succeed(f"ping -c 1 {server_ip}%eth1 >&2") 101 102 with subtest("Global addresses can be obtained, pinged, and reached via http"): 103 for client in clients: 104 client_ip = wait_for_address(client, "eth1", "global") 105 server_ip = wait_for_address(server, "eth1", "global") 106 client.succeed(f"ping -c 1 {client_ip} >&2") 107 client.succeed(f"ping -c 1 {server_ip} >&2") 108 client.succeed(f"curl --fail -g http://[{server_ip}]") 109 client.fail(f"curl --fail -g http://[{client_ip}]") 110 111 with subtest( 112 "Privacy extensions: Global temporary address is used as default source address" 113 ): 114 ip = wait_for_address(client_defaults, "eth1", "global", temporary=True) 115 # Default route should have "src <temporary address>" in it 116 client_defaults.succeed(f"ip route get 2001:db8:: | grep 'src {ip}'") 117 118 for client, setting_desc in ( 119 (client_global_setting, "global"), 120 (client_interface_setting, "interface"), 121 ): 122 with subtest(f'Privacy extensions: "enabled" through {setting_desc} setting)'): 123 # We should be obtaining both a temporary address and an EUI-64 address... 124 ip = wait_for_address(client, "eth1", "global") 125 assert "ff:fe" in ip 126 ip_temp = wait_for_address(client, "eth1", "global", temporary=True) 127 # But using the EUI-64 one. 128 client.succeed(f"ip route get 2001:db8:: | grep 'src {ip}'") 129 ''; 130})