diff options
Diffstat (limited to 'nixpkgs/nixos/tests/ipv6.nix')
-rw-r--r-- | nixpkgs/nixos/tests/ipv6.nix | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/nixpkgs/nixos/tests/ipv6.nix b/nixpkgs/nixos/tests/ipv6.nix new file mode 100644 index 000000000000..75faa6f60201 --- /dev/null +++ b/nixpkgs/nixos/tests/ipv6.nix @@ -0,0 +1,130 @@ +# Test of IPv6 functionality in NixOS, including whether router +# solicication/advertisement using radvd works. + +import ./make-test-python.nix ({ pkgs, lib, ...} : { + name = "ipv6"; + meta = with pkgs.lib.maintainers; { + maintainers = [ eelco ]; + }; + + nodes = + { + # We use lib.mkForce here to remove the interface configuration + # provided by makeTest, so that the interfaces are all configured + # implicitly. + + # This client should use privacy extensions fully, having a + # completely-default network configuration. + client_defaults.networking.interfaces = lib.mkForce {}; + + # Both of these clients should obtain temporary addresses, but + # not use them as the default source IP. We thus run the same + # checks against them — but the configuration resulting in this + # behaviour is different. + + # Here, by using an altered default value for the global setting... + client_global_setting = { + networking.interfaces = lib.mkForce {}; + networking.tempAddresses = "enabled"; + }; + # and here, by setting this on the interface explicitly. + client_interface_setting = { + networking.tempAddresses = "disabled"; + networking.interfaces = lib.mkForce { + eth1.tempAddress = "enabled"; + }; + }; + + server = + { services.httpd.enable = true; + services.httpd.adminAddr = "foo@example.org"; + networking.firewall.allowedTCPPorts = [ 80 ]; + }; + + router = + { ... }: + { services.radvd.enable = true; + services.radvd.config = + '' + interface eth1 { + AdvSendAdvert on; + # ULA prefix (RFC 4193). + prefix fd60:cc69:b537:1::/64 { }; + }; + ''; + }; + }; + + testScript = + '' + import re + + # Start the router first so that it respond to router solicitations. + router.wait_for_unit("radvd") + + clients = [client_defaults, client_global_setting, client_interface_setting] + + start_all() + + for client in clients: + client.wait_for_unit("network.target") + server.wait_for_unit("network.target") + server.wait_for_unit("httpd.service") + + # Wait until the given interface has a non-tentative address of + # the desired scope (i.e. has completed Duplicate Address + # Detection). + def wait_for_address(machine, iface, scope, temporary=False): + temporary_flag = "temporary" if temporary else "-temporary" + cmd = f"ip -o -6 addr show dev {iface} scope {scope} -tentative {temporary_flag}" + + machine.wait_until_succeeds(f"[ `{cmd} | wc -l` -eq 1 ]") + output = machine.succeed(cmd) + ip = re.search(r"inet6 ([0-9a-f:]{2,})/", output).group(1) + + if temporary: + scope = scope + " temporary" + machine.log(f"{scope} address on {iface} is {ip}") + return ip + + + with subtest("Loopback address can be pinged"): + client_defaults.succeed("ping -c 1 ::1 >&2") + client_defaults.fail("ping -c 1 2001:db8:: >&2") + + with subtest("Local link addresses can be obtained and pinged"): + for client in clients: + client_ip = wait_for_address(client, "eth1", "link") + server_ip = wait_for_address(server, "eth1", "link") + client.succeed(f"ping -c 1 {client_ip}%eth1 >&2") + client.succeed(f"ping -c 1 {server_ip}%eth1 >&2") + + with subtest("Global addresses can be obtained, pinged, and reached via http"): + for client in clients: + client_ip = wait_for_address(client, "eth1", "global") + server_ip = wait_for_address(server, "eth1", "global") + client.succeed(f"ping -c 1 {client_ip} >&2") + client.succeed(f"ping -c 1 {server_ip} >&2") + client.succeed(f"curl --fail -g http://[{server_ip}]") + client.fail(f"curl --fail -g http://[{client_ip}]") + + with subtest( + "Privacy extensions: Global temporary address is used as default source address" + ): + ip = wait_for_address(client_defaults, "eth1", "global", temporary=True) + # Default route should have "src <temporary address>" in it + client_defaults.succeed(f"ip route get 2001:db8:: | grep 'src {ip}'") + + for client, setting_desc in ( + (client_global_setting, "global"), + (client_interface_setting, "interface"), + ): + with subtest(f'Privacy extensions: "enabled" through {setting_desc} setting)'): + # We should be obtaining both a temporary address and an EUI-64 address... + ip = wait_for_address(client, "eth1", "global") + assert "ff:fe" in ip + ip_temp = wait_for_address(client, "eth1", "global", temporary=True) + # But using the EUI-64 one. + client.succeed(f"ip route get 2001:db8:: | grep 'src {ip}'") + ''; +}) |