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