nixVersions.stable: 2.15 -> 2.17
[NixPkgs.git] / nixos / tests / networking.nix
blob46fc715d0891dc3b7190e0f9db3a28d9ce76d1e9
1 { system ? builtins.currentSystem
2 , config ? {}
3 , pkgs ? import ../.. { inherit system config; }
4 # bool: whether to use networkd in the tests
5 , networkd }:
7 with import ../lib/testing-python.nix { inherit system pkgs; };
8 with pkgs.lib;
10 let
11   qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
13   router = { config, pkgs, lib, ... }:
14     with pkgs.lib;
15     let
16       vlanIfs = range 1 (length config.virtualisation.vlans);
17     in {
18       environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
19       virtualisation.vlans = [ 1 2 3 ];
20       boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
21       networking = {
22         useDHCP = false;
23         useNetworkd = networkd;
24         firewall.checkReversePath = true;
25         firewall.allowedUDPPorts = [ 547 ];
26         interfaces = mkOverride 0 (listToAttrs (forEach vlanIfs (n:
27           nameValuePair "eth${toString n}" {
28             ipv4.addresses = [ { address = "192.168.${toString n}.1"; prefixLength = 24; } ];
29             ipv6.addresses = [ { address = "fd00:1234:5678:${toString n}::1"; prefixLength = 64; } ];
30           })));
31       };
32       services.kea = {
33         dhcp4 = {
34           enable = true;
35           settings = {
36             interfaces-config = {
37               interfaces = map (n: "eth${toString n}") vlanIfs;
38               dhcp-socket-type = "raw";
39               service-sockets-require-all = true;
40               service-sockets-max-retries = 5;
41               service-sockets-retry-wait-time = 2500;
42             };
43             subnet4 = map (n: {
44               id = n;
45               subnet = "192.168.${toString n}.0/24";
46               pools = [{ pool = "192.168.${toString n}.3 - 192.168.${toString n}.254"; }];
47               option-data = [{ name = "routers"; data = "192.168.${toString n}.1"; }];
49               reservations = [{
50                 hw-address = qemu-common.qemuNicMac n 1;
51                 hostname = "client${toString n}";
52                 ip-address = "192.168.${toString n}.2";
53               }];
54             }) vlanIfs;
55           };
56         };
57         dhcp6 = {
58           enable = true;
59           settings = {
60             interfaces-config = {
61               interfaces = map (n: "eth${toString n}") vlanIfs;
62               service-sockets-require-all = true;
63               service-sockets-max-retries = 5;
64               service-sockets-retry-wait-time = 2500;
65             };
67             subnet6 = map (n: {
68               id = n;
69               subnet = "fd00:1234:5678:${toString n}::/64";
70               interface = "eth${toString n}";
71               pools = [{ pool = "fd00:1234:5678:${toString n}::2-fd00:1234:5678:${toString n}::2"; }];
72             }) vlanIfs;
73           };
74         };
75       };
76       services.radvd = {
77         enable = true;
78         config = flip concatMapStrings vlanIfs (n: ''
79           interface eth${toString n} {
80             AdvSendAdvert on;
81             AdvManagedFlag on;
82             AdvOtherConfigFlag on;
84             prefix fd00:1234:5678:${toString n}::/64 {
85               AdvAutonomous off;
86             };
87           };
88         '');
89       };
90     };
92   testCases = {
93     loopback = {
94       name = "Loopback";
95       nodes.client = { pkgs, ... }: with pkgs.lib; {
96         networking.useDHCP = false;
97         networking.useNetworkd = networkd;
98       };
99       testScript = ''
100         start_all()
101         client.wait_for_unit("network.target")
102         loopback_addresses = client.succeed("ip addr show lo")
103         assert "inet 127.0.0.1/8" in loopback_addresses
104         assert "inet6 ::1/128" in loopback_addresses
105       '';
106     };
107     static = {
108       name = "Static";
109       nodes.router = router;
110       nodes.client = { pkgs, ... }: with pkgs.lib; {
111         virtualisation.interfaces.enp1s0.vlan = 1;
112         virtualisation.interfaces.enp2s0.vlan = 2;
113         networking = {
114           useNetworkd = networkd;
115           useDHCP = false;
116           defaultGateway = "192.168.1.1";
117           defaultGateway6 = "fd00:1234:5678:1::1";
118           interfaces.enp1s0.ipv4.addresses = [
119             { address = "192.168.1.2"; prefixLength = 24; }
120             { address = "192.168.1.3"; prefixLength = 32; }
121             { address = "192.168.1.10"; prefixLength = 32; }
122           ];
123           interfaces.enp2s0.ipv4.addresses = [
124             { address = "192.168.2.2"; prefixLength = 24; }
125           ];
126         };
127       };
128       testScript = { ... }:
129         ''
130           start_all()
132           client.wait_for_unit("network.target")
133           router.wait_for_unit("network-online.target")
135           with subtest("Make sure DHCP server is not started"):
136               client.fail("systemctl status kea-dhcp4-server.service")
137               client.fail("systemctl status kea-dhcp6-server.service")
139           with subtest("Test vlan 1"):
140               client.wait_until_succeeds("ping -c 1 192.168.1.1")
141               client.wait_until_succeeds("ping -c 1 192.168.1.2")
142               client.wait_until_succeeds("ping -c 1 192.168.1.3")
143               client.wait_until_succeeds("ping -c 1 192.168.1.10")
145               router.wait_until_succeeds("ping -c 1 192.168.1.1")
146               router.wait_until_succeeds("ping -c 1 192.168.1.2")
147               router.wait_until_succeeds("ping -c 1 192.168.1.3")
148               router.wait_until_succeeds("ping -c 1 192.168.1.10")
150           with subtest("Test vlan 2"):
151               client.wait_until_succeeds("ping -c 1 192.168.2.1")
152               client.wait_until_succeeds("ping -c 1 192.168.2.2")
154               router.wait_until_succeeds("ping -c 1 192.168.2.1")
155               router.wait_until_succeeds("ping -c 1 192.168.2.2")
157           with subtest("Test default gateway"):
158               router.wait_until_succeeds("ping -c 1 192.168.3.1")
159               client.wait_until_succeeds("ping -c 1 192.168.3.1")
160               router.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1")
161               client.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1")
162         '';
163     };
164     routeType = {
165       name = "RouteType";
166       nodes.client = { pkgs, ... }: with pkgs.lib; {
167         networking = {
168           useDHCP = false;
169           useNetworkd = networkd;
170           interfaces.eth1.ipv4.routes = [{
171             address = "192.168.1.127";
172             prefixLength = 32;
173             type = "local";
174           }];
175         };
176       };
177       testScript = ''
178         start_all()
179         client.wait_for_unit("network.target")
180         client.succeed("ip -4 route list table local | grep 'local 192.168.1.127'")
181       '';
182     };
183     dhcpDefault = {
184       name = "useDHCP-by-default";
185       nodes.router = router;
186       nodes.client = { lib, ... }: {
187         # Disable test driver default config
188         networking.interfaces = lib.mkForce {};
189         networking.useNetworkd = networkd;
190         virtualisation.interfaces.enp1s0.vlan = 1;
191       };
192       testScript = ''
193         start_all()
194         client.wait_for_unit("multi-user.target")
195         client.wait_until_succeeds("ip addr show dev enp1s0 | grep '192.168.1'")
196         client.shell_interact()
197         client.succeed("ping -c 1 192.168.1.1")
198         router.succeed("ping -c 1 192.168.1.1")
199         router.succeed("ping -c 1 192.168.1.2")
200         client.succeed("ping -c 1 192.168.1.2")
201       '';
202     };
203     dhcpSimple = {
204       name = "SimpleDHCP";
205       nodes.router = router;
206       nodes.client = { pkgs, ... }: with pkgs.lib; {
207         virtualisation.interfaces.enp1s0.vlan = 1;
208         virtualisation.interfaces.enp2s0.vlan = 2;
209         networking = {
210           useNetworkd = networkd;
211           useDHCP = false;
212           interfaces.enp1s0.useDHCP = true;
213           interfaces.enp2s0.useDHCP = true;
214         };
215       };
216       testScript = { ... }:
217         ''
218           start_all()
220           client.wait_for_unit("network.target")
221           router.wait_for_unit("network-online.target")
223           with subtest("Wait until we have an ip address on each interface"):
224               client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
225               client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
226               client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q '192.168.2'")
227               client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q 'fd00:1234:5678:2:'")
229           with subtest("Test vlan 1"):
230               client.wait_until_succeeds("ping -c 1 192.168.1.1")
231               client.wait_until_succeeds("ping -c 1 192.168.1.2")
232               client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
233               client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2")
235               router.wait_until_succeeds("ping -c 1 192.168.1.1")
236               router.wait_until_succeeds("ping -c 1 192.168.1.2")
237               router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
238               router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2")
240           with subtest("Test vlan 2"):
241               client.wait_until_succeeds("ping -c 1 192.168.2.1")
242               client.wait_until_succeeds("ping -c 1 192.168.2.2")
243               client.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::1")
244               client.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2")
246               router.wait_until_succeeds("ping -c 1 192.168.2.1")
247               router.wait_until_succeeds("ping -c 1 192.168.2.2")
248               router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::1")
249               router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2")
250         '';
251     };
252     dhcpOneIf = {
253       name = "OneInterfaceDHCP";
254       nodes.router = router;
255       nodes.client = { pkgs, ... }: with pkgs.lib; {
256         virtualisation.interfaces.enp1s0.vlan = 1;
257         virtualisation.interfaces.enp2s0.vlan = 2;
258         networking = {
259           useNetworkd = networkd;
260           useDHCP = false;
261           interfaces.enp1s0 = {
262             mtu = 1343;
263             useDHCP = true;
264           };
265         };
266       };
267       testScript = { ... }:
268         ''
269           start_all()
271           with subtest("Wait for networking to come up"):
272               client.wait_for_unit("network.target")
273               router.wait_for_unit("network.target")
275           with subtest("Wait until we have an ip address on each interface"):
276               client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
278           with subtest("ensure MTU is set"):
279               assert "mtu 1343" in client.succeed("ip link show dev enp1s0")
281           with subtest("Test vlan 1"):
282               client.wait_until_succeeds("ping -c 1 192.168.1.1")
283               client.wait_until_succeeds("ping -c 1 192.168.1.2")
285               router.wait_until_succeeds("ping -c 1 192.168.1.1")
286               router.wait_until_succeeds("ping -c 1 192.168.1.2")
288           with subtest("Test vlan 2"):
289               client.wait_until_succeeds("ping -c 1 192.168.2.1")
290               client.fail("ping -c 1 192.168.2.2")
292               router.wait_until_succeeds("ping -c 1 192.168.2.1")
293               router.fail("ping -c 1 192.168.2.2")
294         '';
295     };
296     bond = let
297       node = address: { pkgs, ... }: with pkgs.lib; {
298         virtualisation.interfaces.enp1s0.vlan = 1;
299         virtualisation.interfaces.enp2s0.vlan = 2;
300         networking = {
301           useNetworkd = networkd;
302           useDHCP = false;
303           bonds.bond0 = {
304             interfaces = [ "enp1s0" "enp2s0" ];
305             driverOptions.mode = "802.3ad";
306           };
307           interfaces.bond0.ipv4.addresses = mkOverride 0
308             [ { inherit address; prefixLength = 30; } ];
309         };
310       };
311     in {
312       name = "Bond";
313       nodes.client1 = node "192.168.1.1";
314       nodes.client2 = node "192.168.1.2";
315       testScript = { ... }:
316         ''
317           start_all()
319           with subtest("Wait for networking to come up"):
320               client1.wait_for_unit("network.target")
321               client2.wait_for_unit("network.target")
323           with subtest("Test bonding"):
324               client1.wait_until_succeeds("ping -c 2 192.168.1.1")
325               client1.wait_until_succeeds("ping -c 2 192.168.1.2")
327               client2.wait_until_succeeds("ping -c 2 192.168.1.1")
328               client2.wait_until_succeeds("ping -c 2 192.168.1.2")
330           with subtest("Verify bonding mode"):
331               for client in client1, client2:
332                   client.succeed('grep -q "Bonding Mode: IEEE 802.3ad Dynamic link aggregation" /proc/net/bonding/bond0')
333         '';
334     };
335     bridge = let
336       node = { address, vlan }: { pkgs, ... }: with pkgs.lib; {
337         virtualisation.interfaces.enp1s0.vlan = vlan;
338         networking = {
339           useNetworkd = networkd;
340           useDHCP = false;
341           interfaces.enp1s0.ipv4.addresses = [ { inherit address; prefixLength = 24; } ];
342         };
343       };
344     in {
345       name = "Bridge";
346       nodes.client1 = node { address = "192.168.1.2"; vlan = 1; };
347       nodes.client2 = node { address = "192.168.1.3"; vlan = 2; };
348       nodes.router = { pkgs, ... }: with pkgs.lib; {
349         virtualisation.interfaces.enp1s0.vlan = 1;
350         virtualisation.interfaces.enp2s0.vlan = 2;
351         networking = {
352           useNetworkd = networkd;
353           useDHCP = false;
354           bridges.bridge.interfaces = [ "enp1s0" "enp2s0" ];
355           interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
356           interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
357           interfaces.bridge.ipv4.addresses = mkOverride 0
358             [ { address = "192.168.1.1"; prefixLength = 24; } ];
359         };
360       };
361       testScript = { ... }:
362         ''
363           start_all()
365           with subtest("Wait for networking to come up"):
366               for machine in client1, client2, router:
367                   machine.wait_for_unit("network.target")
369           with subtest("Test bridging"):
370               client1.wait_until_succeeds("ping -c 1 192.168.1.1")
371               client1.wait_until_succeeds("ping -c 1 192.168.1.2")
372               client1.wait_until_succeeds("ping -c 1 192.168.1.3")
374               client2.wait_until_succeeds("ping -c 1 192.168.1.1")
375               client2.wait_until_succeeds("ping -c 1 192.168.1.2")
376               client2.wait_until_succeeds("ping -c 1 192.168.1.3")
378               router.wait_until_succeeds("ping -c 1 192.168.1.1")
379               router.wait_until_succeeds("ping -c 1 192.168.1.2")
380               router.wait_until_succeeds("ping -c 1 192.168.1.3")
381         '';
382     };
383     macvlan = {
384       name = "MACVLAN";
385       nodes.router = router;
386       nodes.client = { pkgs, ... }: with pkgs.lib; {
387         environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
388         virtualisation.interfaces.enp1s0.vlan = 1;
389         networking = {
390           useNetworkd = networkd;
391           useDHCP = false;
392           firewall.logReversePathDrops = true; # to debug firewall rules
393           # reverse path filtering rules for the macvlan interface seem
394           # to be incorrect, causing the test to fail. Disable temporarily.
395           firewall.checkReversePath = false;
396           macvlans.macvlan.interface = "enp1s0";
397           interfaces.enp1s0.useDHCP = true;
398           interfaces.macvlan.useDHCP = true;
399         };
400       };
401       testScript = { ... }:
402         ''
403           start_all()
405           with subtest("Wait for networking to come up"):
406               client.wait_for_unit("network.target")
407               router.wait_for_unit("network.target")
409           with subtest("Wait until we have an ip address on each interface"):
410               client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
411               client.wait_until_succeeds("ip addr show dev macvlan | grep -q '192.168.1'")
413           with subtest("Print lots of diagnostic information"):
414               router.log("**********************************************")
415               router.succeed("ip addr >&2")
416               router.succeed("ip route >&2")
417               router.execute("iptables-save >&2")
418               client.log("==============================================")
419               client.succeed("ip addr >&2")
420               client.succeed("ip route >&2")
421               client.execute("iptables-save >&2")
422               client.log("##############################################")
424           with subtest("Test macvlan creates routable ips"):
425               client.wait_until_succeeds("ping -c 1 192.168.1.1")
426               client.wait_until_succeeds("ping -c 1 192.168.1.2")
427               client.wait_until_succeeds("ping -c 1 192.168.1.3")
429               router.wait_until_succeeds("ping -c 1 192.168.1.1")
430               router.wait_until_succeeds("ping -c 1 192.168.1.2")
431               router.wait_until_succeeds("ping -c 1 192.168.1.3")
432         '';
433     };
434     fou = {
435       name = "foo-over-udp";
436       nodes.machine = { ... }: {
437         virtualisation.interfaces.enp1s0.vlan = 1;
438         networking = {
439           useNetworkd = networkd;
440           useDHCP = false;
441           interfaces.enp1s0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
442           fooOverUDP = {
443             fou1 = { port = 9001; };
444             fou2 = { port = 9002; protocol = 41; };
445             fou3 = mkIf (!networkd)
446               { port = 9003; local.address = "192.168.1.1"; };
447             fou4 = mkIf (!networkd)
448               { port = 9004; local = { address = "192.168.1.1"; dev = "enp1s0"; }; };
449           };
450         };
451         systemd.services = {
452           fou3-fou-encap.after = optional (!networkd) "network-addresses-enp1s0.service";
453         };
454       };
455       testScript = { ... }:
456         ''
457           import json
459           machine.wait_for_unit("network.target")
460           fous = json.loads(machine.succeed("ip -json fou show"))
461           assert {"port": 9001, "gue": None, "family": "inet"} in fous, "fou1 exists"
462           assert {"port": 9002, "ipproto": 41, "family": "inet"} in fous, "fou2 exists"
463         '' + optionalString (!networkd) ''
464           assert {
465               "port": 9003,
466               "gue": None,
467               "family": "inet",
468               "local": "192.168.1.1",
469           } in fous, "fou3 exists"
470           assert {
471               "port": 9004,
472               "gue": None,
473               "family": "inet",
474               "local": "192.168.1.1",
475               "dev": "enp1s0",
476           } in fous, "fou4 exists"
477         '';
478     };
479     sit = let
480       node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; {
481         virtualisation.interfaces.enp1s0.vlan = 1;
482         networking = {
483           useNetworkd = networkd;
484           useDHCP = false;
485           sits.sit = {
486             inherit remote;
487             local = address4;
488             dev = "enp1s0";
489           };
490           interfaces.enp1s0.ipv4.addresses = mkOverride 0
491             [ { address = address4; prefixLength = 24; } ];
492           interfaces.sit.ipv6.addresses = mkOverride 0
493             [ { address = address6; prefixLength = 64; } ];
494         };
495       };
496     in {
497       name = "Sit";
498       # note on firewalling: the two nodes are explicitly asymmetric.
499       # client1 sends SIT packets in UDP, but accepts only proto-41 incoming.
500       # client2 does the reverse, sending in proto-41 and accepting only UDP incoming.
501       # that way we'll notice when either SIT itself or FOU breaks.
502       nodes.client1 = args@{ pkgs, ... }:
503         mkMerge [
504           (node { address4 = "192.168.1.1"; remote = "192.168.1.2"; address6 = "fc00::1"; } args)
505           {
506             networking = {
507               firewall.extraCommands = "iptables -A INPUT -p 41 -j ACCEPT";
508               sits.sit.encapsulation = { type = "fou"; port = 9001; };
509             };
510           }
511         ];
512       nodes.client2 = args@{ pkgs, ... }:
513         mkMerge [
514           (node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; } args)
515           {
516             networking = {
517               firewall.allowedUDPPorts = [ 9001 ];
518               fooOverUDP.fou1 = { port = 9001; protocol = 41; };
519             };
520           }
521         ];
522       testScript = { ... }:
523         ''
524           start_all()
526           with subtest("Wait for networking to be configured"):
527               client1.wait_for_unit("network.target")
528               client2.wait_for_unit("network.target")
530               # Print diagnostic information
531               client1.succeed("ip addr >&2")
532               client2.succeed("ip addr >&2")
534           with subtest("Test ipv6"):
535               client1.wait_until_succeeds("ping -c 1 fc00::1")
536               client1.wait_until_succeeds("ping -c 1 fc00::2")
538               client2.wait_until_succeeds("ping -c 1 fc00::1")
539               client2.wait_until_succeeds("ping -c 1 fc00::2")
540         '';
541     };
542     gre = let
543       node = { pkgs, ... }: with pkgs.lib; {
544         networking = {
545           useNetworkd = networkd;
546           useDHCP = false;
547           firewall.extraCommands = "ip6tables -A nixos-fw -p gre -j nixos-fw-accept";
548         };
549       };
550     in {
551       name = "GRE";
552       nodes.client1 = args@{ pkgs, ... }:
553         mkMerge [
554           (node args)
555           {
556             virtualisation.vlans = [ 1 2 4 ];
557             networking = {
558               greTunnels = {
559                 greTunnel = {
560                   local = "192.168.2.1";
561                   remote = "192.168.2.2";
562                   dev = "eth2";
563                   ttl = 225;
564                   type = "tap";
565                 };
566                 gre6Tunnel = {
567                   local = "fd00:1234:5678:4::1";
568                   remote = "fd00:1234:5678:4::2";
569                   dev = "eth3";
570                   ttl = 255;
571                   type = "tun6";
572                 };
573               };
574               bridges.bridge.interfaces = [ "greTunnel" "eth1" ];
575               interfaces.eth1.ipv4.addresses = mkOverride 0 [];
576               interfaces.bridge.ipv4.addresses = mkOverride 0 [
577                 { address = "192.168.1.1"; prefixLength = 24; }
578               ];
579               interfaces.eth3.ipv6.addresses = [
580                 { address = "fd00:1234:5678:4::1"; prefixLength = 64; }
581               ];
582               interfaces.gre6Tunnel.ipv6.addresses = mkOverride 0 [
583                 { address = "fc00::1"; prefixLength = 64; }
584               ];
585             };
586           }
587         ];
588       nodes.client2 = args@{ pkgs, ... }:
589         mkMerge [
590           (node args)
591           {
592             virtualisation.vlans = [ 2 3 4 ];
593             networking = {
594               greTunnels = {
595                 greTunnel = {
596                   local = "192.168.2.2";
597                   remote = "192.168.2.1";
598                   dev = "eth1";
599                   ttl = 225;
600                   type = "tap";
601                 };
602                 gre6Tunnel = {
603                   local = "fd00:1234:5678:4::2";
604                   remote = "fd00:1234:5678:4::1";
605                   dev = "eth3";
606                   ttl = 255;
607                   type = "tun6";
608                 };
609               };
610               bridges.bridge.interfaces = [ "greTunnel" "eth2" ];
611               interfaces.eth2.ipv4.addresses = mkOverride 0 [];
612               interfaces.bridge.ipv4.addresses = mkOverride 0 [
613                 { address = "192.168.1.2"; prefixLength = 24; }
614               ];
615               interfaces.eth3.ipv6.addresses = [
616                 { address = "fd00:1234:5678:4::2"; prefixLength = 64; }
617               ];
618               interfaces.gre6Tunnel.ipv6.addresses = mkOverride 0 [
619                 { address = "fc00::2"; prefixLength = 64; }
620               ];
621             };
622           }
623         ];
624       testScript = { ... }:
625         ''
626           import json
627           start_all()
629           with subtest("Wait for networking to be configured"):
630               client1.wait_for_unit("network.target")
631               client2.wait_for_unit("network.target")
633               # Print diagnostic information
634               client1.succeed("ip addr >&2")
635               client2.succeed("ip addr >&2")
637           with subtest("Test GRE tunnel bridge over VLAN"):
638               client1.wait_until_succeeds("ping -c 1 192.168.1.2")
640               client2.wait_until_succeeds("ping -c 1 192.168.1.1")
642               client1.wait_until_succeeds("ping -c 1 fc00::2")
644               client2.wait_until_succeeds("ping -c 1 fc00::1")
646           with subtest("Test GRE tunnel TTL"):
647               links = json.loads(client1.succeed("ip -details -json link show greTunnel"))
648               assert links[0]['linkinfo']['info_data']['ttl'] == 225, "ttl not set for greTunnel"
650               links = json.loads(client2.succeed("ip -details -json link show gre6Tunnel"))
651               assert links[0]['linkinfo']['info_data']['ttl'] == 255, "ttl not set for gre6Tunnel"
652         '';
653     };
654     vlan = let
655       node = address: { pkgs, ... }: with pkgs.lib; {
656         #virtualisation.vlans = [ 1 ];
657         networking = {
658           useNetworkd = networkd;
659           useDHCP = false;
660           vlans.vlan = {
661             id = 1;
662             interface = "eth0";
663           };
664           interfaces.eth0.ipv4.addresses = mkOverride 0 [ ];
665           interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
666           interfaces.vlan.ipv4.addresses = mkOverride 0
667             [ { inherit address; prefixLength = 24; } ];
668         };
669       };
670     in {
671       name = "vlan";
672       nodes.client1 = node "192.168.1.1";
673       nodes.client2 = node "192.168.1.2";
674       testScript = { ... }:
675         ''
676           start_all()
678           with subtest("Wait for networking to be configured"):
679               client1.wait_for_unit("network.target")
680               client2.wait_for_unit("network.target")
682           with subtest("Test vlan is setup"):
683               client1.succeed("ip addr show dev vlan >&2")
684               client2.succeed("ip addr show dev vlan >&2")
685         '';
686     };
687     vlan-ping = let
688         baseIP = number: "10.10.10.${number}";
689         vlanIP = number: "10.1.1.${number}";
690         baseInterface = "enp1s0";
691         vlanInterface = "vlan42";
692         node = number: {pkgs, ... }: with pkgs.lib; {
693           virtualisation.interfaces.enp1s0.vlan = 1;
694           networking = {
695             #useNetworkd = networkd;
696             useDHCP = false;
697             vlans.${vlanInterface} = { id = 42; interface = baseInterface; };
698             interfaces.${baseInterface}.ipv4.addresses = mkOverride 0 [{ address = baseIP number; prefixLength = 24; }];
699             interfaces.${vlanInterface}.ipv4.addresses = mkOverride 0 [{ address = vlanIP number; prefixLength = 24; }];
700           };
701         };
703         serverNodeNum = "1";
704         clientNodeNum = "2";
706     in {
707       name = "vlan-ping";
708       nodes.server = node serverNodeNum;
709       nodes.client = node clientNodeNum;
710       testScript = { ... }:
711         ''
712           start_all()
714           with subtest("Wait for networking to be configured"):
715               server.wait_for_unit("network.target")
716               client.wait_for_unit("network.target")
718           with subtest("Test ping on base interface in setup"):
719               client.succeed("ping -I ${baseInterface} -c 1 ${baseIP serverNodeNum}")
720               server.succeed("ping -I ${baseInterface} -c 1 ${baseIP clientNodeNum}")
722           with subtest("Test ping on vlan subinterface in setup"):
723               client.succeed("ping -I ${vlanInterface} -c 1 ${vlanIP serverNodeNum}")
724               server.succeed("ping -I ${vlanInterface} -c 1 ${vlanIP clientNodeNum}")
725         '';
726     };
727     virtual = {
728       name = "Virtual";
729       nodes.machine = {
730         networking.useNetworkd = networkd;
731         networking.useDHCP = false;
732         networking.interfaces.tap0 = {
733           ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
734           ipv6.addresses = [ { address = "2001:1470:fffd:2096::"; prefixLength = 64; } ];
735           virtual = true;
736           mtu = 1342;
737           macAddress = "02:de:ad:be:ef:01";
738         };
739         networking.interfaces.tun0 = {
740           ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
741           ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
742           virtual = true;
743           mtu = 1343;
744         };
745       };
747       testScript = ''
748         targetList = """
749         tap0: tap persist user 0
750         tun0: tun persist user 0
751         """.strip()
753         with subtest("Wait for networking to come up"):
754             machine.start()
755             machine.wait_for_unit("network.target")
757         with subtest("Test interfaces set up"):
758             list = machine.succeed("ip tuntap list | sort").strip()
759             assert (
760                 list == targetList
761             ), """
762             The list of virtual interfaces does not match the expected one:
763             Result:
764               {}
765             Expected:
766               {}
767             """.format(
768                 list, targetList
769             )
770         with subtest("Test MTU and MAC Address are configured"):
771             machine.wait_until_succeeds("ip link show dev tap0 | grep 'mtu 1342'")
772             machine.wait_until_succeeds("ip link show dev tun0 | grep 'mtu 1343'")
773             assert "02:de:ad:be:ef:01" in machine.succeed("ip link show dev tap0")
774       '' # network-addresses-* only exist in scripted networking
775       + optionalString (!networkd) ''
776         with subtest("Test interfaces clean up"):
777             machine.succeed("systemctl stop network-addresses-tap0")
778             machine.sleep(10)
779             machine.succeed("systemctl stop network-addresses-tun0")
780             machine.sleep(10)
781             residue = machine.succeed("ip tuntap list")
782             assert (
783                 residue == ""
784             ), "Some virtual interface has not been properly cleaned:\n{}".format(residue)
785       '';
786     };
787     privacy = {
788       name = "Privacy";
789       nodes.router = { ... }: {
790         virtualisation.interfaces.enp1s0.vlan = 1;
791         boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
792         networking = {
793           useNetworkd = networkd;
794           useDHCP = false;
795           interfaces.enp1s0.ipv6.addresses = singleton {
796             address = "fd00:1234:5678:1::1";
797             prefixLength = 64;
798           };
799         };
800         services.radvd = {
801           enable = true;
802           config = ''
803             interface enp1s0 {
804               AdvSendAdvert on;
805               AdvManagedFlag on;
806               AdvOtherConfigFlag on;
808               prefix fd00:1234:5678:1::/64 {
809                 AdvAutonomous on;
810                 AdvOnLink on;
811               };
812             };
813           '';
814         };
815       };
816       nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; {
817         virtualisation.interfaces.enp1s0.vlan = 1;
818         networking = {
819           useNetworkd = networkd;
820           useDHCP = false;
821           interfaces.enp1s0 = {
822             tempAddress = "default";
823             ipv4.addresses = mkOverride 0 [ ];
824             ipv6.addresses = mkOverride 0 [ ];
825             useDHCP = true;
826           };
827         };
828       };
829       nodes.client = { pkgs, ... }: with pkgs.lib; {
830         virtualisation.interfaces.enp1s0.vlan = 1;
831         networking = {
832           useNetworkd = networkd;
833           useDHCP = false;
834           interfaces.enp1s0 = {
835             tempAddress = "enabled";
836             ipv4.addresses = mkOverride 0 [ ];
837             ipv6.addresses = mkOverride 0 [ ];
838             useDHCP = true;
839           };
840         };
841       };
842       testScript = { ... }:
843         ''
844           start_all()
846           client.wait_for_unit("network.target")
847           client_with_privacy.wait_for_unit("network.target")
848           router.wait_for_unit("network-online.target")
850           with subtest("Wait until we have an ip address"):
851               client_with_privacy.wait_until_succeeds(
852                   "ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'"
853               )
854               client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
856           with subtest("Test vlan 1"):
857               client_with_privacy.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
858               client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
860           with subtest("Test address used is temporary"):
861               client_with_privacy.wait_until_succeeds(
862                   "! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'"
863               )
865           with subtest("Test address used is EUI-64"):
866               client.wait_until_succeeds(
867                   "ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'"
868               )
869         '';
870     };
871     routes = {
872       name = "routes";
873       nodes.machine = {
874         networking.useNetworkd = networkd;
875         networking.useDHCP = false;
876         networking.interfaces.eth0 = {
877           ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
878           ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
879           ipv6.routes = [
880             { address = "fdfd:b3f0::"; prefixLength = 48; }
881             { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
882           ];
883           ipv4.routes = [
884             { address = "10.0.0.0"; prefixLength = 16; options = {
885               mtu = "1500";
886               # Explicitly set scope because iproute and systemd-networkd
887               # disagree on what the scope should be
888               # if the type is the default "unicast"
889               scope = "link";
890             }; }
891             { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
892           ];
893         };
894         virtualisation.vlans = [ ];
895       };
897       testScript = ''
898         targetIPv4Table = [
899             "10.0.0.0/16 proto static scope link mtu 1500",
900             "192.168.1.0/24 proto kernel scope link src 192.168.1.2",
901             "192.168.2.0/24 via 192.168.1.1 proto static",
902         ]
904         targetIPv6Table = [
905             "2001:1470:fffd:2097::/64 proto kernel metric 256 pref medium",
906             "2001:1470:fffd:2098::/64 via fdfd:b3f0::1 proto static metric 1024 pref medium",
907             "fdfd:b3f0::/48 proto static metric 1024 pref medium",
908         ]
910         machine.start()
911         machine.wait_for_unit("network.target")
913         with subtest("test routing tables"):
914             ipv4Table = machine.succeed("ip -4 route list dev eth0 | head -n3").strip()
915             ipv6Table = machine.succeed("ip -6 route list dev eth0 | head -n3").strip()
916             assert [
917                 l.strip() for l in ipv4Table.splitlines()
918             ] == targetIPv4Table, """
919               The IPv4 routing table does not match the expected one:
920                 Result:
921                   {}
922                 Expected:
923                   {}
924               """.format(
925                 ipv4Table, targetIPv4Table
926             )
927             assert [
928                 l.strip() for l in ipv6Table.splitlines()
929             ] == targetIPv6Table, """
930               The IPv6 routing table does not match the expected one:
931                 Result:
932                   {}
933                 Expected:
934                   {}
935               """.format(
936                 ipv6Table, targetIPv6Table
937             )
939       '' + optionalString (!networkd) ''
940         with subtest("test clean-up of the tables"):
941             machine.succeed("systemctl stop network-addresses-eth0")
942             ipv4Residue = machine.succeed("ip -4 route list dev eth0 | head -n-3").strip()
943             ipv6Residue = machine.succeed("ip -6 route list dev eth0 | head -n-3").strip()
944             assert (
945                 ipv4Residue == ""
946             ), "The IPv4 routing table has not been properly cleaned:\n{}".format(ipv4Residue)
947             assert (
948                 ipv6Residue == ""
949             ), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue)
950       '';
951     };
952     rename = if networkd then {
953       name = "RenameInterface";
954       nodes.machine = { pkgs, ... }: {
955         virtualisation.vlans = [ 1 ];
956         networking = {
957           useNetworkd = networkd;
958           useDHCP = false;
959         };
960         systemd.network.links."10-custom_name" = {
961           matchConfig.MACAddress = "52:54:00:12:01:01";
962           linkConfig.Name = "custom_name";
963         };
964       };
965       testScript = ''
966         machine.succeed("udevadm settle")
967         print(machine.succeed("ip link show dev custom_name"))
968       '';
969     } else {
970       name = "RenameInterface";
971       nodes = { };
972       testScript = "";
973     };
974     # even with disabled networkd, systemd.network.links should work
975     # (as it's handled by udev, not networkd)
976     link = {
977       name = "Link";
978       nodes.client = { pkgs, ... }: {
979         virtualisation.vlans = [ 1 ];
980         networking = {
981           useNetworkd = networkd;
982           useDHCP = false;
983         };
984         systemd.network.links."50-foo" = {
985           matchConfig = {
986             Name = "foo";
987             Driver = "dummy";
988           };
989           linkConfig.MTUBytes = "1442";
990         };
991       };
992       testScript = ''
993         print(client.succeed("ip l add name foo type dummy"))
994         print(client.succeed("stat /etc/systemd/network/50-foo.link"))
995         client.succeed("udevadm settle")
996         assert "mtu 1442" in client.succeed("ip l show dev foo")
997       '';
998     };
999     wlanInterface = let
1000       testMac = "06:00:00:00:02:00";
1001     in {
1002       name = "WlanInterface";
1003       nodes.machine = { pkgs, ... }: {
1004         boot.kernelModules = [ "mac80211_hwsim" ];
1005         networking.wlanInterfaces = {
1006           wlan0 = { device = "wlan0"; };
1007           wap0 = { device = "wlan0"; mac = testMac; };
1008         };
1009       };
1010       testScript = ''
1011         machine.start()
1012         machine.wait_for_unit("network.target")
1013         machine.wait_until_succeeds("ip address show wap0 | grep -q ${testMac}")
1014         machine.fail("ip address show wlan0 | grep -q ${testMac}")
1015       '';
1016     };
1017     naughtyInterfaceNames = let
1018       ifnames = [
1019         # flags of ip-address
1020         "home" "temporary" "optimistic"
1021         "bridge_slave" "flush"
1022         # flags of ip-route
1023         "up" "type" "nomaster" "address"
1024         # other
1025         "very_loong_name" "lowerUpper" "-"
1026       ];
1027     in {
1028       name = "naughtyInterfaceNames";
1029       nodes.machine = { pkgs, ... }: {
1030         networking.useNetworkd = networkd;
1031         networking.bridges = listToAttrs
1032           (flip map ifnames
1033              (name: { inherit name; value.interfaces = []; }));
1034       };
1035       testScript = ''
1036         machine.start()
1037         machine.wait_for_unit("network.target")
1038         for ifname in ${builtins.toJSON ifnames}:
1039             machine.wait_until_succeeds(f"ip link show dev '{ifname}' | grep -q '{ifname}'")
1040       '';
1041     };
1042     caseSensitiveRenaming = {
1043       name = "CaseSensitiveRenaming";
1044       nodes.machine = { pkgs, ... }: {
1045         virtualisation.interfaces.enCustom.vlan = 11;
1046         networking = {
1047           useNetworkd = networkd;
1048           useDHCP = false;
1049         };
1050       };
1051       testScript = ''
1052         machine.succeed("udevadm settle")
1053         print(machine.succeed("ip link show dev enCustom"))
1054         machine.wait_until_succeeds("ip link show dev enCustom | grep -q 52:54:00:12:0b:01")
1055       '';
1056     };
1057   };
1059 in mapAttrs (const (attrs: makeTest (attrs // {
1060   name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}";
1061 }))) testCases