1{ system ? builtins.currentSystem
2, config ? {}
3, pkgs ? import ../.. { inherit system config; }
4# bool: whether to use networkd in the tests
5, networkd }:
6
7with import ../lib/testing-python.nix { inherit system pkgs; };
8with pkgs.lib;
9
10let
11 qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
12
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.dhcpd4 = {
33 enable = true;
34 interfaces = map (n: "eth${toString n}") vlanIfs;
35 extraConfig = flip concatMapStrings vlanIfs (n: ''
36 subnet 192.168.${toString n}.0 netmask 255.255.255.0 {
37 option routers 192.168.${toString n}.1;
38 range 192.168.${toString n}.3 192.168.${toString n}.254;
39 }
40 '')
41 ;
42 machines = flip map vlanIfs (vlan:
43 {
44 hostName = "client${toString vlan}";
45 ethernetAddress = qemu-common.qemuNicMac vlan 1;
46 ipAddress = "192.168.${toString vlan}.2";
47 }
48 );
49 };
50 services.radvd = {
51 enable = true;
52 config = flip concatMapStrings vlanIfs (n: ''
53 interface eth${toString n} {
54 AdvSendAdvert on;
55 AdvManagedFlag on;
56 AdvOtherConfigFlag on;
57
58 prefix fd00:1234:5678:${toString n}::/64 {
59 AdvAutonomous off;
60 };
61 };
62 '');
63 };
64 services.dhcpd6 = {
65 enable = true;
66 interfaces = map (n: "eth${toString n}") vlanIfs;
67 extraConfig = ''
68 authoritative;
69 '' + flip concatMapStrings vlanIfs (n: ''
70 subnet6 fd00:1234:5678:${toString n}::/64 {
71 range6 fd00:1234:5678:${toString n}::2 fd00:1234:5678:${toString n}::2;
72 }
73 '');
74 };
75 };
76
77 testCases = {
78 loopback = {
79 name = "Loopback";
80 machine.networking.useDHCP = false;
81 machine.networking.useNetworkd = networkd;
82 testScript = ''
83 start_all()
84 machine.wait_for_unit("network.target")
85 loopback_addresses = machine.succeed("ip addr show lo")
86 assert "inet 127.0.0.1/8" in loopback_addresses
87 assert "inet6 ::1/128" in loopback_addresses
88 '';
89 };
90 static = {
91 name = "Static";
92 nodes.router = router;
93 nodes.client = { pkgs, ... }: with pkgs.lib; {
94 virtualisation.vlans = [ 1 2 ];
95 networking = {
96 useNetworkd = networkd;
97 useDHCP = false;
98 defaultGateway = "192.168.1.1";
99 interfaces.eth1.ipv4.addresses = mkOverride 0 [
100 { address = "192.168.1.2"; prefixLength = 24; }
101 { address = "192.168.1.3"; prefixLength = 32; }
102 { address = "192.168.1.10"; prefixLength = 32; }
103 ];
104 interfaces.eth2.ipv4.addresses = mkOverride 0 [
105 { address = "192.168.2.2"; prefixLength = 24; }
106 ];
107 };
108 };
109 testScript = { ... }:
110 ''
111 start_all()
112
113 client.wait_for_unit("network.target")
114 router.wait_for_unit("network-online.target")
115
116 with subtest("Make sure dhcpcd is not started"):
117 client.fail("systemctl status dhcpcd.service")
118
119 with subtest("Test vlan 1"):
120 client.wait_until_succeeds("ping -c 1 192.168.1.1")
121 client.wait_until_succeeds("ping -c 1 192.168.1.2")
122 client.wait_until_succeeds("ping -c 1 192.168.1.3")
123 client.wait_until_succeeds("ping -c 1 192.168.1.10")
124
125 router.wait_until_succeeds("ping -c 1 192.168.1.1")
126 router.wait_until_succeeds("ping -c 1 192.168.1.2")
127 router.wait_until_succeeds("ping -c 1 192.168.1.3")
128 router.wait_until_succeeds("ping -c 1 192.168.1.10")
129
130 with subtest("Test vlan 2"):
131 client.wait_until_succeeds("ping -c 1 192.168.2.1")
132 client.wait_until_succeeds("ping -c 1 192.168.2.2")
133
134 router.wait_until_succeeds("ping -c 1 192.168.2.1")
135 router.wait_until_succeeds("ping -c 1 192.168.2.2")
136
137 with subtest("Test default gateway"):
138 router.wait_until_succeeds("ping -c 1 192.168.3.1")
139 client.wait_until_succeeds("ping -c 1 192.168.3.1")
140 '';
141 };
142 dhcpSimple = {
143 name = "SimpleDHCP";
144 nodes.router = router;
145 nodes.client = { pkgs, ... }: with pkgs.lib; {
146 virtualisation.vlans = [ 1 2 ];
147 networking = {
148 useNetworkd = networkd;
149 useDHCP = false;
150 interfaces.eth1 = {
151 ipv4.addresses = mkOverride 0 [ ];
152 ipv6.addresses = mkOverride 0 [ ];
153 useDHCP = true;
154 };
155 interfaces.eth2 = {
156 ipv4.addresses = mkOverride 0 [ ];
157 ipv6.addresses = mkOverride 0 [ ];
158 useDHCP = true;
159 };
160 };
161 };
162 testScript = { ... }:
163 ''
164 start_all()
165
166 client.wait_for_unit("network.target")
167 router.wait_for_unit("network-online.target")
168
169 with subtest("Wait until we have an ip address on each interface"):
170 client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
171 client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
172 client.wait_until_succeeds("ip addr show dev eth2 | grep -q '192.168.2'")
173 client.wait_until_succeeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'")
174
175 with subtest("Test vlan 1"):
176 client.wait_until_succeeds("ping -c 1 192.168.1.1")
177 client.wait_until_succeeds("ping -c 1 192.168.1.2")
178 client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
179 client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2")
180
181 router.wait_until_succeeds("ping -c 1 192.168.1.1")
182 router.wait_until_succeeds("ping -c 1 192.168.1.2")
183 router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
184 router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2")
185
186 with subtest("Test vlan 2"):
187 client.wait_until_succeeds("ping -c 1 192.168.2.1")
188 client.wait_until_succeeds("ping -c 1 192.168.2.2")
189 client.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::1")
190 client.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2")
191
192 router.wait_until_succeeds("ping -c 1 192.168.2.1")
193 router.wait_until_succeeds("ping -c 1 192.168.2.2")
194 router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::1")
195 router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2")
196 '';
197 };
198 dhcpOneIf = {
199 name = "OneInterfaceDHCP";
200 nodes.router = router;
201 nodes.client = { pkgs, ... }: with pkgs.lib; {
202 virtualisation.vlans = [ 1 2 ];
203 networking = {
204 useNetworkd = networkd;
205 useDHCP = false;
206 interfaces.eth1 = {
207 ipv4.addresses = mkOverride 0 [ ];
208 mtu = 1343;
209 useDHCP = true;
210 };
211 interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
212 };
213 };
214 testScript = { ... }:
215 ''
216 start_all()
217
218 with subtest("Wait for networking to come up"):
219 client.wait_for_unit("network.target")
220 router.wait_for_unit("network.target")
221
222 with subtest("Wait until we have an ip address on each interface"):
223 client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
224
225 with subtest("ensure MTU is set"):
226 assert "mtu 1343" in client.succeed("ip link show dev eth1")
227
228 with subtest("Test vlan 1"):
229 client.wait_until_succeeds("ping -c 1 192.168.1.1")
230 client.wait_until_succeeds("ping -c 1 192.168.1.2")
231
232 router.wait_until_succeeds("ping -c 1 192.168.1.1")
233 router.wait_until_succeeds("ping -c 1 192.168.1.2")
234
235 with subtest("Test vlan 2"):
236 client.wait_until_succeeds("ping -c 1 192.168.2.1")
237 client.fail("ping -c 1 192.168.2.2")
238
239 router.wait_until_succeeds("ping -c 1 192.168.2.1")
240 router.fail("ping -c 1 192.168.2.2")
241 '';
242 };
243 bond = let
244 node = address: { pkgs, ... }: with pkgs.lib; {
245 virtualisation.vlans = [ 1 2 ];
246 networking = {
247 useNetworkd = networkd;
248 useDHCP = false;
249 bonds.bond = {
250 interfaces = [ "eth1" "eth2" ];
251 driverOptions.mode = "balance-rr";
252 };
253 interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
254 interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
255 interfaces.bond.ipv4.addresses = mkOverride 0
256 [ { inherit address; prefixLength = 30; } ];
257 };
258 };
259 in {
260 name = "Bond";
261 nodes.client1 = node "192.168.1.1";
262 nodes.client2 = node "192.168.1.2";
263 testScript = { ... }:
264 ''
265 start_all()
266
267 with subtest("Wait for networking to come up"):
268 client1.wait_for_unit("network.target")
269 client2.wait_for_unit("network.target")
270
271 with subtest("Test bonding"):
272 client1.wait_until_succeeds("ping -c 2 192.168.1.1")
273 client1.wait_until_succeeds("ping -c 2 192.168.1.2")
274
275 client2.wait_until_succeeds("ping -c 2 192.168.1.1")
276 client2.wait_until_succeeds("ping -c 2 192.168.1.2")
277 '';
278 };
279 bridge = let
280 node = { address, vlan }: { pkgs, ... }: with pkgs.lib; {
281 virtualisation.vlans = [ vlan ];
282 networking = {
283 useNetworkd = networkd;
284 useDHCP = false;
285 interfaces.eth1.ipv4.addresses = mkOverride 0
286 [ { inherit address; prefixLength = 24; } ];
287 };
288 };
289 in {
290 name = "Bridge";
291 nodes.client1 = node { address = "192.168.1.2"; vlan = 1; };
292 nodes.client2 = node { address = "192.168.1.3"; vlan = 2; };
293 nodes.router = { pkgs, ... }: with pkgs.lib; {
294 virtualisation.vlans = [ 1 2 ];
295 networking = {
296 useNetworkd = networkd;
297 useDHCP = false;
298 bridges.bridge.interfaces = [ "eth1" "eth2" ];
299 interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
300 interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
301 interfaces.bridge.ipv4.addresses = mkOverride 0
302 [ { address = "192.168.1.1"; prefixLength = 24; } ];
303 };
304 };
305 testScript = { ... }:
306 ''
307 start_all()
308
309 with subtest("Wait for networking to come up"):
310 for machine in client1, client2, router:
311 machine.wait_for_unit("network.target")
312
313 with subtest("Test bridging"):
314 client1.wait_until_succeeds("ping -c 1 192.168.1.1")
315 client1.wait_until_succeeds("ping -c 1 192.168.1.2")
316 client1.wait_until_succeeds("ping -c 1 192.168.1.3")
317
318 client2.wait_until_succeeds("ping -c 1 192.168.1.1")
319 client2.wait_until_succeeds("ping -c 1 192.168.1.2")
320 client2.wait_until_succeeds("ping -c 1 192.168.1.3")
321
322 router.wait_until_succeeds("ping -c 1 192.168.1.1")
323 router.wait_until_succeeds("ping -c 1 192.168.1.2")
324 router.wait_until_succeeds("ping -c 1 192.168.1.3")
325 '';
326 };
327 macvlan = {
328 name = "MACVLAN";
329 nodes.router = router;
330 nodes.client = { pkgs, ... }: with pkgs.lib; {
331 environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
332 virtualisation.vlans = [ 1 ];
333 networking = {
334 useNetworkd = networkd;
335 useDHCP = false;
336 firewall.logReversePathDrops = true; # to debug firewall rules
337 # reverse path filtering rules for the macvlan interface seem
338 # to be incorrect, causing the test to fail. Disable temporarily.
339 firewall.checkReversePath = false;
340 macvlans.macvlan.interface = "eth1";
341 interfaces.eth1 = {
342 ipv4.addresses = mkOverride 0 [ ];
343 useDHCP = true;
344 };
345 interfaces.macvlan = {
346 useDHCP = true;
347 };
348 };
349 };
350 testScript = { ... }:
351 ''
352 start_all()
353
354 with subtest("Wait for networking to come up"):
355 client.wait_for_unit("network.target")
356 router.wait_for_unit("network.target")
357
358 with subtest("Wait until we have an ip address on each interface"):
359 client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
360 client.wait_until_succeeds("ip addr show dev macvlan | grep -q '192.168.1'")
361
362 with subtest("Print lots of diagnostic information"):
363 router.log("**********************************************")
364 router.succeed("ip addr >&2")
365 router.succeed("ip route >&2")
366 router.execute("iptables-save >&2")
367 client.log("==============================================")
368 client.succeed("ip addr >&2")
369 client.succeed("ip route >&2")
370 client.execute("iptables-save >&2")
371 client.log("##############################################")
372
373 with subtest("Test macvlan creates routable ips"):
374 client.wait_until_succeeds("ping -c 1 192.168.1.1")
375 client.wait_until_succeeds("ping -c 1 192.168.1.2")
376 client.wait_until_succeeds("ping -c 1 192.168.1.3")
377
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 fou = {
384 name = "foo-over-udp";
385 nodes.machine = { ... }: {
386 virtualisation.vlans = [ 1 ];
387 networking = {
388 useNetworkd = networkd;
389 useDHCP = false;
390 interfaces.eth1.ipv4.addresses = mkOverride 0
391 [ { address = "192.168.1.1"; prefixLength = 24; } ];
392 fooOverUDP = {
393 fou1 = { port = 9001; };
394 fou2 = { port = 9002; protocol = 41; };
395 fou3 = mkIf (!networkd)
396 { port = 9003; local.address = "192.168.1.1"; };
397 fou4 = mkIf (!networkd)
398 { port = 9004; local = { address = "192.168.1.1"; dev = "eth1"; }; };
399 };
400 };
401 systemd.services = {
402 fou3-fou-encap.after = optional (!networkd) "network-addresses-eth1.service";
403 };
404 };
405 testScript = { ... }:
406 ''
407 import json
408
409 machine.wait_for_unit("network.target")
410 fous = json.loads(machine.succeed("ip -json fou show"))
411 assert {"port": 9001, "gue": None, "family": "inet"} in fous, "fou1 exists"
412 assert {"port": 9002, "ipproto": 41, "family": "inet"} in fous, "fou2 exists"
413 '' + optionalString (!networkd) ''
414 assert {
415 "port": 9003,
416 "gue": None,
417 "family": "inet",
418 "local": "192.168.1.1",
419 } in fous, "fou3 exists"
420 assert {
421 "port": 9004,
422 "gue": None,
423 "family": "inet",
424 "local": "192.168.1.1",
425 "dev": "eth1",
426 } in fous, "fou4 exists"
427 '';
428 };
429 sit = let
430 node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; {
431 virtualisation.vlans = [ 1 ];
432 networking = {
433 useNetworkd = networkd;
434 useDHCP = false;
435 sits.sit = {
436 inherit remote;
437 local = address4;
438 dev = "eth1";
439 };
440 interfaces.eth1.ipv4.addresses = mkOverride 0
441 [ { address = address4; prefixLength = 24; } ];
442 interfaces.sit.ipv6.addresses = mkOverride 0
443 [ { address = address6; prefixLength = 64; } ];
444 };
445 };
446 in {
447 name = "Sit";
448 # note on firewalling: the two nodes are explicitly asymmetric.
449 # client1 sends SIT packets in UDP, but accepts only proto-41 incoming.
450 # client2 does the reverse, sending in proto-41 and accepting only UDP incoming.
451 # that way we'll notice when either SIT itself or FOU breaks.
452 nodes.client1 = args@{ pkgs, ... }:
453 mkMerge [
454 (node { address4 = "192.168.1.1"; remote = "192.168.1.2"; address6 = "fc00::1"; } args)
455 {
456 networking = {
457 firewall.extraCommands = "iptables -A INPUT -p 41 -j ACCEPT";
458 sits.sit.encapsulation = { type = "fou"; port = 9001; };
459 };
460 }
461 ];
462 nodes.client2 = args@{ pkgs, ... }:
463 mkMerge [
464 (node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; } args)
465 {
466 networking = {
467 firewall.allowedUDPPorts = [ 9001 ];
468 fooOverUDP.fou1 = { port = 9001; protocol = 41; };
469 };
470 }
471 ];
472 testScript = { ... }:
473 ''
474 start_all()
475
476 with subtest("Wait for networking to be configured"):
477 client1.wait_for_unit("network.target")
478 client2.wait_for_unit("network.target")
479
480 # Print diagnostic information
481 client1.succeed("ip addr >&2")
482 client2.succeed("ip addr >&2")
483
484 with subtest("Test ipv6"):
485 client1.wait_until_succeeds("ping -c 1 fc00::1")
486 client1.wait_until_succeeds("ping -c 1 fc00::2")
487
488 client2.wait_until_succeeds("ping -c 1 fc00::1")
489 client2.wait_until_succeeds("ping -c 1 fc00::2")
490 '';
491 };
492 vlan = let
493 node = address: { pkgs, ... }: with pkgs.lib; {
494 #virtualisation.vlans = [ 1 ];
495 networking = {
496 useNetworkd = networkd;
497 useDHCP = false;
498 vlans.vlan = {
499 id = 1;
500 interface = "eth0";
501 };
502 interfaces.eth0.ipv4.addresses = mkOverride 0 [ ];
503 interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
504 interfaces.vlan.ipv4.addresses = mkOverride 0
505 [ { inherit address; prefixLength = 24; } ];
506 };
507 };
508 in {
509 name = "vlan";
510 nodes.client1 = node "192.168.1.1";
511 nodes.client2 = node "192.168.1.2";
512 testScript = { ... }:
513 ''
514 start_all()
515
516 with subtest("Wait for networking to be configured"):
517 client1.wait_for_unit("network.target")
518 client2.wait_for_unit("network.target")
519
520 with subtest("Test vlan is setup"):
521 client1.succeed("ip addr show dev vlan >&2")
522 client2.succeed("ip addr show dev vlan >&2")
523 '';
524 };
525 virtual = {
526 name = "Virtual";
527 machine = {
528 networking.useNetworkd = networkd;
529 networking.useDHCP = false;
530 networking.interfaces.tap0 = {
531 ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
532 ipv6.addresses = [ { address = "2001:1470:fffd:2096::"; prefixLength = 64; } ];
533 virtual = true;
534 mtu = 1342;
535 macAddress = "02:de:ad:be:ef:01";
536 };
537 networking.interfaces.tun0 = {
538 ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
539 ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
540 virtual = true;
541 mtu = 1343;
542 };
543 };
544
545 testScript = ''
546 targetList = """
547 tap0: tap persist user 0
548 tun0: tun persist user 0
549 """.strip()
550
551 with subtest("Wait for networking to come up"):
552 machine.start()
553 machine.wait_for_unit("network.target")
554
555 with subtest("Test interfaces set up"):
556 list = machine.succeed("ip tuntap list | sort").strip()
557 assert (
558 list == targetList
559 ), """
560 The list of virtual interfaces does not match the expected one:
561 Result:
562 {}
563 Expected:
564 {}
565 """.format(
566 list, targetList
567 )
568 with subtest("Test MTU and MAC Address are configured"):
569 machine.wait_until_succeeds("ip link show dev tap0 | grep 'mtu 1342'")
570 machine.wait_until_succeeds("ip link show dev tun0 | grep 'mtu 1343'")
571 assert "02:de:ad:be:ef:01" in machine.succeed("ip link show dev tap0")
572 '' # network-addresses-* only exist in scripted networking
573 + optionalString (!networkd) ''
574 with subtest("Test interfaces clean up"):
575 machine.succeed("systemctl stop network-addresses-tap0")
576 machine.sleep(10)
577 machine.succeed("systemctl stop network-addresses-tun0")
578 machine.sleep(10)
579 residue = machine.succeed("ip tuntap list")
580 assert (
581 residue == ""
582 ), "Some virtual interface has not been properly cleaned:\n{}".format(residue)
583 '';
584 };
585 privacy = {
586 name = "Privacy";
587 nodes.router = { ... }: {
588 virtualisation.vlans = [ 1 ];
589 boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
590 networking = {
591 useNetworkd = networkd;
592 useDHCP = false;
593 interfaces.eth1.ipv6.addresses = singleton {
594 address = "fd00:1234:5678:1::1";
595 prefixLength = 64;
596 };
597 };
598 services.radvd = {
599 enable = true;
600 config = ''
601 interface eth1 {
602 AdvSendAdvert on;
603 AdvManagedFlag on;
604 AdvOtherConfigFlag on;
605
606 prefix fd00:1234:5678:1::/64 {
607 AdvAutonomous on;
608 AdvOnLink on;
609 };
610 };
611 '';
612 };
613 };
614 nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; {
615 virtualisation.vlans = [ 1 ];
616 networking = {
617 useNetworkd = networkd;
618 useDHCP = false;
619 interfaces.eth1 = {
620 tempAddress = "default";
621 ipv4.addresses = mkOverride 0 [ ];
622 ipv6.addresses = mkOverride 0 [ ];
623 useDHCP = true;
624 };
625 };
626 };
627 nodes.client = { pkgs, ... }: with pkgs.lib; {
628 virtualisation.vlans = [ 1 ];
629 networking = {
630 useNetworkd = networkd;
631 useDHCP = false;
632 interfaces.eth1 = {
633 tempAddress = "enabled";
634 ipv4.addresses = mkOverride 0 [ ];
635 ipv6.addresses = mkOverride 0 [ ];
636 useDHCP = true;
637 };
638 };
639 };
640 testScript = { ... }:
641 ''
642 start_all()
643
644 client.wait_for_unit("network.target")
645 client_with_privacy.wait_for_unit("network.target")
646 router.wait_for_unit("network-online.target")
647
648 with subtest("Wait until we have an ip address"):
649 client_with_privacy.wait_until_succeeds(
650 "ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"
651 )
652 client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
653
654 with subtest("Test vlan 1"):
655 client_with_privacy.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
656 client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
657
658 with subtest("Test address used is temporary"):
659 client_with_privacy.wait_until_succeeds(
660 "! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'"
661 )
662
663 with subtest("Test address used is EUI-64"):
664 client.wait_until_succeeds(
665 "ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'"
666 )
667 '';
668 };
669 routes = {
670 name = "routes";
671 machine = {
672 networking.useDHCP = false;
673 networking.interfaces.eth0 = {
674 ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
675 ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
676 ipv6.routes = [
677 { address = "fdfd:b3f0::"; prefixLength = 48; }
678 { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
679 ];
680 ipv4.routes = [
681 { address = "10.0.0.0"; prefixLength = 16; options = { mtu = "1500"; }; }
682 { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
683 ];
684 };
685 virtualisation.vlans = [ ];
686 };
687
688 testScript = ''
689 targetIPv4Table = [
690 "10.0.0.0/16 proto static scope link mtu 1500",
691 "192.168.1.0/24 proto kernel scope link src 192.168.1.2",
692 "192.168.2.0/24 via 192.168.1.1 proto static",
693 ]
694
695 targetIPv6Table = [
696 "2001:1470:fffd:2097::/64 proto kernel metric 256 pref medium",
697 "2001:1470:fffd:2098::/64 via fdfd:b3f0::1 proto static metric 1024 pref medium",
698 "fdfd:b3f0::/48 proto static metric 1024 pref medium",
699 ]
700
701 machine.start()
702 machine.wait_for_unit("network.target")
703
704 with subtest("test routing tables"):
705 ipv4Table = machine.succeed("ip -4 route list dev eth0 | head -n3").strip()
706 ipv6Table = machine.succeed("ip -6 route list dev eth0 | head -n3").strip()
707 assert [
708 l.strip() for l in ipv4Table.splitlines()
709 ] == targetIPv4Table, """
710 The IPv4 routing table does not match the expected one:
711 Result:
712 {}
713 Expected:
714 {}
715 """.format(
716 ipv4Table, targetIPv4Table
717 )
718 assert [
719 l.strip() for l in ipv6Table.splitlines()
720 ] == targetIPv6Table, """
721 The IPv6 routing table does not match the expected one:
722 Result:
723 {}
724 Expected:
725 {}
726 """.format(
727 ipv6Table, targetIPv6Table
728 )
729
730 with subtest("test clean-up of the tables"):
731 machine.succeed("systemctl stop network-addresses-eth0")
732 ipv4Residue = machine.succeed("ip -4 route list dev eth0 | head -n-3").strip()
733 ipv6Residue = machine.succeed("ip -6 route list dev eth0 | head -n-3").strip()
734 assert (
735 ipv4Residue == ""
736 ), "The IPv4 routing table has not been properly cleaned:\n{}".format(ipv4Residue)
737 assert (
738 ipv6Residue == ""
739 ), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue)
740 '';
741 };
742 rename = {
743 name = "RenameInterface";
744 machine = { pkgs, ... }: {
745 virtualisation.vlans = [ 1 ];
746 networking = {
747 useNetworkd = networkd;
748 useDHCP = false;
749 };
750 } //
751 (if networkd
752 then { systemd.network.links."10-custom_name" = {
753 matchConfig.MACAddress = "52:54:00:12:01:01";
754 linkConfig.Name = "custom_name";
755 };
756 }
757 else { services.udev.initrdRules = ''
758 SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="52:54:00:12:01:01", KERNEL=="eth*", NAME="custom_name"
759 '';
760 });
761 testScript = ''
762 machine.succeed("udevadm settle")
763 print(machine.succeed("ip link show dev custom_name"))
764 '';
765 };
766 # even with disabled networkd, systemd.network.links should work
767 # (as it's handled by udev, not networkd)
768 link = {
769 name = "Link";
770 nodes.client = { pkgs, ... }: {
771 virtualisation.vlans = [ 1 ];
772 networking = {
773 useNetworkd = networkd;
774 useDHCP = false;
775 };
776 systemd.network.links."50-foo" = {
777 matchConfig = {
778 Name = "foo";
779 Driver = "dummy";
780 };
781 linkConfig.MTUBytes = "1442";
782 };
783 };
784 testScript = ''
785 print(client.succeed("ip l add name foo type dummy"))
786 print(client.succeed("stat /etc/systemd/network/50-foo.link"))
787 client.succeed("udevadm settle")
788 assert "mtu 1442" in client.succeed("ip l show dummy0")
789 '';
790 };
791 wlanInterface = let
792 testMac = "06:00:00:00:02:00";
793 in {
794 name = "WlanInterface";
795 machine = { pkgs, ... }: {
796 boot.kernelModules = [ "mac80211_hwsim" ];
797 networking.wlanInterfaces = {
798 wlan0 = { device = "wlan0"; };
799 wap0 = { device = "wlan0"; mac = testMac; };
800 };
801 };
802 testScript = ''
803 machine.start()
804 machine.wait_for_unit("network.target")
805 machine.wait_until_succeeds("ip address show wap0 | grep -q ${testMac}")
806 machine.fail("ip address show wlan0 | grep -q ${testMac}")
807 '';
808 };
809 };
810
811in mapAttrs (const (attrs: makeTest (attrs // {
812 name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}";
813}))) testCases