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