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