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