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