python312Packages.millheater: 0.11.8 -> 0.12.0
[NixPkgs.git] / nixos / tests / jool.nix
blob37a4ad6ce0111390758ffae5e30ef03b5ca97f55
1 { pkgs, runTest }:
3 let
4   inherit (pkgs) lib;
6   ipv6Only = {
7     networking.useDHCP = false;
8     networking.interfaces.eth1.ipv4.addresses = lib.mkVMOverride [ ];
9   };
11   ipv4Only = {
12     networking.useDHCP = false;
13     networking.interfaces.eth1.ipv6.addresses = lib.mkVMOverride [ ];
14   };
16   webserver = ip: msg: {
17     systemd.services.webserver = {
18       description = "Mock webserver";
19       wants = [ "network-online.target" ];
20       wantedBy = [ "multi-user.target" ];
21       script = ''
22         while true; do
23         {
24           printf 'HTTP/1.0 200 OK\n'
25           printf 'Content-Length: ${toString (1 + builtins.stringLength msg)}\n'
26           printf '\n${msg}\n\n'
27         } | ${pkgs.libressl.nc}/bin/nc -${toString ip}nvl 80
28         done
29       '';
30     };
31     networking.firewall.allowedTCPPorts = [ 80 ];
32   };
37   siit = runTest {
38     # This test simulates the setup described in [1] with two IPv6 and
39     # IPv4-only devices on different subnets communicating through a border
40     # relay running Jool in SIIT mode.
41     # [1]: https://nicmx.github.io/Jool/en/run-vanilla.html
42     name = "jool-siit";
43     meta.maintainers = with lib.maintainers; [ rnhmjoj ];
45     # Border relay
46     nodes.relay = {
47       virtualisation.vlans = [ 1 2 ];
49       # Enable packet routing
50       boot.kernel.sysctl = {
51         "net.ipv6.conf.all.forwarding" = 1;
52         "net.ipv4.conf.all.forwarding" = 1;
53       };
55       networking.useDHCP = false;
56       networking.interfaces = lib.mkVMOverride {
57         eth1.ipv6.addresses = [ { address = "fd::198.51.100.1"; prefixLength = 120; } ];
58         eth2.ipv4.addresses = [ { address = "192.0.2.1";  prefixLength = 24; } ];
59       };
61       networking.jool.enable = true;
62       networking.jool.siit.default.global.pool6 = "fd::/96";
63     };
65     # IPv6 only node
66     nodes.alice = {
67       imports = [ ipv6Only (webserver 6 "Hello, Bob!") ];
69       virtualisation.vlans = [ 1 ];
70       networking.interfaces.eth1.ipv6 = {
71         addresses = [ { address = "fd::198.51.100.8"; prefixLength = 120; } ];
72         routes    = [ { address = "fd::192.0.2.0"; prefixLength = 120;
73                         via = "fd::198.51.100.1"; } ];
74       };
75     };
77     # IPv4 only node
78     nodes.bob = {
79       imports = [ ipv4Only (webserver 4 "Hello, Alice!") ];
81       virtualisation.vlans = [ 2 ];
82       networking.interfaces.eth1.ipv4 = {
83         addresses = [ { address = "192.0.2.16"; prefixLength = 24; } ];
84         routes    = [ { address = "198.51.100.0"; prefixLength = 24;
85                         via = "192.0.2.1"; } ];
86       };
87     };
89     testScript = ''
90       start_all()
92       relay.wait_for_unit("jool-siit-default.service")
93       alice.wait_for_unit("network-addresses-eth1.service")
94       bob.wait_for_unit("network-addresses-eth1.service")
96       with subtest("Alice and Bob can't ping each other"):
97         relay.systemctl("stop jool-siit-default.service")
98         alice.fail("ping -c1 fd::192.0.2.16")
99         bob.fail("ping -c1 198.51.100.8")
101       with subtest("Alice and Bob can ping using the relay"):
102         relay.systemctl("start jool-siit-default.service")
103         alice.wait_until_succeeds("ping -c1 fd::192.0.2.16")
104         bob.wait_until_succeeds("ping -c1 198.51.100.8")
106       with subtest("Alice can connect to Bob's webserver"):
107         bob.wait_for_open_port(80)
108         alice.succeed("curl -vvv http://[fd::192.0.2.16] >&2")
109         alice.succeed("curl --fail -s http://[fd::192.0.2.16] | grep -q Alice")
111       with subtest("Bob can connect to Alices's webserver"):
112         alice.wait_for_open_port(80)
113         bob.succeed("curl --fail -s http://198.51.100.8 | grep -q Bob")
114     '';
115   };
117   nat64 = runTest {
118     # This test simulates the setup described in [1] with two IPv6-only nodes
119     # (a client and a homeserver) on the LAN subnet and an IPv4 node on the WAN.
120     # The router runs Jool in stateful NAT64 mode, masquarading the LAN and
121     # forwarding ports using static BIB entries.
122     # [1]: https://nicmx.github.io/Jool/en/run-nat64.html
123     name = "jool-nat64";
124     meta.maintainers = with lib.maintainers; [ rnhmjoj ];
126     # Router
127     nodes.router = {
128       virtualisation.vlans = [ 1 2 ];
130       # Enable packet routing
131       boot.kernel.sysctl = {
132         "net.ipv6.conf.all.forwarding" = 1;
133         "net.ipv4.conf.all.forwarding" = 1;
134       };
136       networking.useDHCP = false;
137       networking.interfaces = lib.mkVMOverride {
138         eth1.ipv6.addresses = [ { address = "2001:db8::1"; prefixLength = 96; } ];
139         eth2.ipv4.addresses = [ { address = "203.0.113.1"; prefixLength = 24; } ];
140       };
142       networking.jool.enable = true;
143       networking.jool.nat64.default = {
144         bib = [
145           { # forward HTTP 203.0.113.1 (router) → 2001:db8::9 (homeserver)
146             "protocol"     = "TCP";
147             "ipv4 address" = "203.0.113.1#80";
148             "ipv6 address" = "2001:db8::9#80";
149           }
150         ];
151         pool4 = [
152           # Ports for dynamic translation
153           { protocol =  "TCP";  prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
154           { protocol =  "UDP";  prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
155           { protocol = "ICMP";  prefix = "203.0.113.1/32"; "port range" = "40001-65535"; }
156           # Ports for static BIB entries
157           { protocol =  "TCP";  prefix = "203.0.113.1/32"; "port range" = "80"; }
158         ];
159       };
160     };
162     # LAN client (IPv6 only)
163     nodes.client = {
164       imports = [ ipv6Only ];
165       virtualisation.vlans = [ 1 ];
167       networking.interfaces.eth1.ipv6 = {
168         addresses = lib.mkForce [ { address = "2001:db8::8"; prefixLength = 96; } ];
169         routes = lib.mkForce [ {
170           address = "64:ff9b::";
171           prefixLength = 96;
172           via = "2001:db8::1";
173         } ];
174       };
175     };
177     # LAN server (IPv6 only)
178     nodes.homeserver = {
179       imports = [ ipv6Only (webserver 6 "Hello from IPv6!") ];
181       virtualisation.vlans = [ 1 ];
182       networking.interfaces.eth1.ipv6 = {
183         addresses = lib.mkForce [ { address = "2001:db8::9"; prefixLength = 96; } ];
184         routes    = lib.mkForce [ {
185           address = "64:ff9b::";
186           prefixLength = 96;
187           via = "2001:db8::1";
188         } ];
189       };
190     };
192     # WAN server (IPv4 only)
193     nodes.server = {
194       imports = [ ipv4Only (webserver 4 "Hello from IPv4!") ];
196       virtualisation.vlans = [ 2 ];
197       networking.interfaces.eth1.ipv4.addresses =
198         [ { address = "203.0.113.16"; prefixLength = 24; } ];
199     };
201     testScript = ''
202       start_all()
204       for node in [client, homeserver, server]:
205         node.wait_for_unit("network-addresses-eth1.service")
207       with subtest("Client can ping the WAN server"):
208         router.wait_for_unit("jool-nat64-default.service")
209         client.succeed("ping -c1 64:ff9b::203.0.113.16")
211       with subtest("Client can connect to the WAN webserver"):
212         server.wait_for_open_port(80)
213         client.succeed("curl --fail -s http://[64:ff9b::203.0.113.16] | grep -q IPv4!")
215       with subtest("Router BIB entries are correctly populated"):
216         router.succeed("jool bib display | grep -q 'Dynamic TCP.*2001:db8::8'")
217         router.succeed("jool bib display | grep -q 'Static TCP.*2001:db8::9'")
219       with subtest("WAN server can reach the LAN server"):
220         homeserver.wait_for_open_port(80)
221         server.succeed("curl --fail -s http://203.0.113.1 | grep -q IPv6!")
222     '';
224   };