summary refs log blame commit diff
path: root/nixos/tests/networking.nix
blob: d1d4fd41dda61730b483a56c39fa379b6c3ed2de (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13


                                            









                                                             
                                                                               
                                       
                                                               


                               

                                           

                                                                    

                                                                                                     

               
                         


                                                         
                        

                                                                
                                                   


                                                                              



                                                                    
























                                                                                   







                                                







                                                                           
                                                    
                                       
                      
                                 

                                         
                                                         



                                                            
                                                         

                                                           

          
                           
          
                   
 
                                                 
                                                        
























                                                               
           



                            
                                                    


                                       
                         
                             

                                              

                             

                                              
            
          
        
                           

                   
 
                                                 
                                                        
 

                                                                                    
                                                                                            
                                                                                    
                                                                                            
 


                                                              

                                                                      
 

                                                              

                                                                      
 


                                                              

                                                                      
 

                                                              

                                                                      




                                
                                                    


                                       

                             
                                              
                           
            
                                                            
          
        
                           

                   
 
                                          
                                                 
                                                 
 

                                                                                    
 


                                                              
 

                                                              
 


                                                              
 




                                                              
                                                     


                                       

                          
                                           
                                              
            


                                                            
                                                        
          
        



                                         
                           



                                          
                                                  










                                                               
                                                               


                                        
                          
                                                       
                                                        
          




                                                                  
                                                    


                                       

                                                        


                                                            
                                                                
          
        
                           



                                          
                                                  
                                                  


















                                                               
                                                    
                                                                                 


                                     



                                                                           

                                              
                                                            
          
        
                           



                                          
                                                 





                                                                                       

                                                                         
                                          


                                                                         
                                          


                                                                         











                                                              
                                                                            








                                     
            
                                                       
                                                           
                                                      
                                                           
          
        



                                                                                                       
                           



                                                
                                                  






                                                  

                                                           
 

                                                           


              
                                                     


                                      



                               
            


                                                            
                                                        
          
        



                                         
                           



                                                
                                                  





                                                         
      



                                        

                                                                                         


                                        

                                                                                         





                                 

                                



                                        
                                                       










                                                                              
                            
                                                                   
                            






                                                                    

                       
                               



                                                                 


                                                      

















                                            
                                                    





                                     

                                              


            
                           












                                                                                            
                                                                                                                 

           




                                        


                                                                                         


                                                                                           
                         








                                                                                     
                                                     
                                                               
                                                    



                                                                    

                                                                                      
































                                                                                     







                                                                                   
{ system ? builtins.currentSystem
# bool: whether to use networkd in the tests
, networkd }:

with import ../lib/testing.nix { inherit system; };
with pkgs.lib;

let
  router = { config, pkgs, ... }:
    with pkgs.lib;
    let
      vlanIfs = range 1 (length config.virtualisation.vlans);
    in {
      environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
      virtualisation.vlans = [ 1 2 3 ];
      boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
      networking = {
        useDHCP = false;
        useNetworkd = networkd;
        firewall.checkReversePath = true;
        firewall.allowedUDPPorts = [ 547 ];
        interfaces = mkOverride 0 (listToAttrs (flip map vlanIfs (n:
          nameValuePair "eth${toString n}" {
            ipv4.addresses = [ { address = "192.168.${toString n}.1"; prefixLength = 24; } ];
            ipv6.addresses = [ { address = "fd00:1234:5678:${toString n}::1"; prefixLength = 64; } ];
          })));
      };
      services.dhcpd4 = {
        enable = true;
        interfaces = map (n: "eth${toString n}") vlanIfs;
        extraConfig = ''
          authoritative;
        '' + flip concatMapStrings vlanIfs (n: ''
          subnet 192.168.${toString n}.0 netmask 255.255.255.0 {
            option routers 192.168.${toString n}.1;
            # XXX: technically it's _not guaranteed_ that IP addresses will be
            # issued from the first item in range onwards! We assume that in
            # our tests however.
            range 192.168.${toString n}.2 192.168.${toString n}.254;
          }
        '');
      };
      services.radvd = {
        enable = true;
        config = flip concatMapStrings vlanIfs (n: ''
          interface eth${toString n} {
            AdvSendAdvert on;
            AdvManagedFlag on;
            AdvOtherConfigFlag on;

            prefix fd00:1234:5678:${toString n}::/64 {
              AdvAutonomous off;
            };
          };
        '');
      };
      services.dhcpd6 = {
        enable = true;
        interfaces = map (n: "eth${toString n}") vlanIfs;
        extraConfig = ''
          authoritative;
        '' + flip concatMapStrings vlanIfs (n: ''
          subnet6 fd00:1234:5678:${toString n}::/64 {
            range6 fd00:1234:5678:${toString n}::2 fd00:1234:5678:${toString n}::2;
          }
        '');
      };
    };

  testCases = {
    loopback = {
      name = "Loopback";
      machine.networking.useNetworkd = networkd;
      testScript = ''
        startAll;
        $machine->waitForUnit("network.target");
        $machine->succeed("ip addr show lo | grep -q 'inet 127.0.0.1/8 '");
        $machine->succeed("ip addr show lo | grep -q 'inet6 ::1/128 '");
      '';
    };
    static = {
      name = "Static";
      nodes.router = router;
      nodes.client = { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ 1 2 ];
        networking = {
          useNetworkd = networkd;
          useDHCP = false;
          defaultGateway = "192.168.1.1";
          interfaces.eth1.ipv4.addresses = mkOverride 0 [
            { address = "192.168.1.2"; prefixLength = 24; }
            { address = "192.168.1.3"; prefixLength = 32; }
            { address = "192.168.1.10"; prefixLength = 32; }
          ];
          interfaces.eth2.ipv4.addresses = mkOverride 0 [
            { address = "192.168.2.2"; prefixLength = 24; }
          ];
        };
      };
      testScript = { ... }:
        ''
          startAll;

          $client->waitForUnit("network.target");
          $router->waitForUnit("network-online.target");

          # Make sure dhcpcd is not started
          $client->fail("systemctl status dhcpcd.service");

          # Test vlan 1
          $client->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $client->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $client->waitUntilSucceeds("ping -c 1 192.168.1.3");
          $client->waitUntilSucceeds("ping -c 1 192.168.1.10");

          $router->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.3");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.10");

          # Test vlan 2
          $client->waitUntilSucceeds("ping -c 1 192.168.2.1");
          $client->waitUntilSucceeds("ping -c 1 192.168.2.2");

          $router->waitUntilSucceeds("ping -c 1 192.168.2.1");
          $router->waitUntilSucceeds("ping -c 1 192.168.2.2");

          # Test default gateway
          $router->waitUntilSucceeds("ping -c 1 192.168.3.1");
          $client->waitUntilSucceeds("ping -c 1 192.168.3.1");
        '';
    };
    dhcpSimple = {
      name = "SimpleDHCP";
      nodes.router = router;
      nodes.client = { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ 1 2 ];
        networking = {
          useNetworkd = networkd;
          useDHCP = true;
          interfaces.eth1 = {
            ipv4.addresses = mkOverride 0 [ ];
            ipv6.addresses = mkOverride 0 [ ];
          };
          interfaces.eth2 = {
            ipv4.addresses = mkOverride 0 [ ];
            ipv6.addresses = mkOverride 0 [ ];
          };
        };
      };
      testScript = { ... }:
        ''
          startAll;

          $client->waitForUnit("network.target");
          $router->waitForUnit("network-online.target");

          # Wait until we have an ip address on each interface
          $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'");
          $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");
          $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q '192.168.2'");
          $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'");

          # Test vlan 1
          $client->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $client->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2");

          $router->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");
          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2");

          # Test vlan 2
          $client->waitUntilSucceeds("ping -c 1 192.168.2.1");
          $client->waitUntilSucceeds("ping -c 1 192.168.2.2");
          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1");
          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2");

          $router->waitUntilSucceeds("ping -c 1 192.168.2.1");
          $router->waitUntilSucceeds("ping -c 1 192.168.2.2");
          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1");
          $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2");
        '';
    };
    dhcpOneIf = {
      name = "OneInterfaceDHCP";
      nodes.router = router;
      nodes.client = { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ 1 2 ];
        networking = {
          useNetworkd = networkd;
          useDHCP = false;
          interfaces.eth1 = {
            ipv4.addresses = mkOverride 0 [ ];
            useDHCP = true;
          };
          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
        };
      };
      testScript = { ... }:
        ''
          startAll;

          # Wait for networking to come up
          $client->waitForUnit("network.target");
          $router->waitForUnit("network.target");

          # Wait until we have an ip address on each interface
          $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'");

          # Test vlan 1
          $client->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $client->waitUntilSucceeds("ping -c 1 192.168.1.2");

          $router->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.2");

          # Test vlan 2
          $client->waitUntilSucceeds("ping -c 1 192.168.2.1");
          $client->fail("ping -c 1 192.168.2.2");

          $router->waitUntilSucceeds("ping -c 1 192.168.2.1");
          $router->fail("ping -c 1 192.168.2.2");
        '';
    };
    bond = let
      node = address: { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ 1 2 ];
        networking = {
          useNetworkd = networkd;
          useDHCP = false;
          bonds.bond = {
            interfaces = [ "eth1" "eth2" ];
            driverOptions.mode = "balance-rr";
          };
          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
          interfaces.bond.ipv4.addresses = mkOverride 0
            [ { inherit address; prefixLength = 30; } ];
        };
      };
    in {
      name = "Bond";
      nodes.client1 = node "192.168.1.1";
      nodes.client2 = node "192.168.1.2";
      testScript = { ... }:
        ''
          startAll;

          # Wait for networking to come up
          $client1->waitForUnit("network.target");
          $client2->waitForUnit("network.target");

          # Test bonding
          $client1->waitUntilSucceeds("ping -c 2 192.168.1.1");
          $client1->waitUntilSucceeds("ping -c 2 192.168.1.2");

          $client2->waitUntilSucceeds("ping -c 2 192.168.1.1");
          $client2->waitUntilSucceeds("ping -c 2 192.168.1.2");
        '';
    };
    bridge = let
      node = { address, vlan }: { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ vlan ];
        networking = {
          useNetworkd = networkd;
          useDHCP = false;
          interfaces.eth1.ipv4.addresses = mkOverride 0
            [ { inherit address; prefixLength = 24; } ];
        };
      };
    in {
      name = "Bridge";
      nodes.client1 = node { address = "192.168.1.2"; vlan = 1; };
      nodes.client2 = node { address = "192.168.1.3"; vlan = 2; };
      nodes.router = { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ 1 2 ];
        networking = {
          useNetworkd = networkd;
          useDHCP = false;
          bridges.bridge.interfaces = [ "eth1" "eth2" ];
          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
          interfaces.bridge.ipv4.addresses = mkOverride 0
            [ { address = "192.168.1.1"; prefixLength = 24; } ];
        };
      };
      testScript = { ... }:
        ''
          startAll;

          # Wait for networking to come up
          $client1->waitForUnit("network.target");
          $client2->waitForUnit("network.target");
          $router->waitForUnit("network.target");

          # Test bridging
          $client1->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $client1->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $client1->waitUntilSucceeds("ping -c 1 192.168.1.3");

          $client2->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $client2->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $client2->waitUntilSucceeds("ping -c 1 192.168.1.3");

          $router->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.3");
        '';
    };
    macvlan = {
      name = "MACVLAN";
      nodes.router = router;
      nodes.client = { pkgs, ... }: with pkgs.lib; {
        environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
        virtualisation.vlans = [ 1 ];
        networking = {
          useNetworkd = networkd;
          firewall.logReversePathDrops = true; # to debug firewall rules
          # reverse path filtering rules for the macvlan interface seem
          # to be incorrect, causing the test to fail. Disable temporarily.
          firewall.checkReversePath = false;
          useDHCP = true;
          macvlans.macvlan.interface = "eth1";
          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
        };
      };
      testScript = { ... }:
        ''
          startAll;

          # Wait for networking to come up
          $client->waitForUnit("network.target");
          $router->waitForUnit("network.target");

          # Wait until we have an ip address on each interface
          $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'");
          $client->waitUntilSucceeds("ip addr show dev macvlan | grep -q '192.168.1'");

          # Print lots of diagnostic information
          $router->log('**********************************************');
          $router->succeed("ip addr >&2");
          $router->succeed("ip route >&2");
          $router->execute("iptables-save >&2");
          $client->log('==============================================');
          $client->succeed("ip addr >&2");
          $client->succeed("ip route >&2");
          $client->execute("iptables-save >&2");
          $client->log('##############################################');

          # Test macvlan creates routable ips
          $client->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $client->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $client->waitUntilSucceeds("ping -c 1 192.168.1.3");

          $router->waitUntilSucceeds("ping -c 1 192.168.1.1");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.2");
          $router->waitUntilSucceeds("ping -c 1 192.168.1.3");
        '';
    };
    sit = let
      node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ 1 ];
        networking = {
          useNetworkd = networkd;
          firewall.enable = false;
          useDHCP = false;
          sits.sit = {
            inherit remote;
            local = address4;
            dev = "eth1";
          };
          interfaces.eth1.ipv4.addresses = mkOverride 0
            [ { address = address4; prefixLength = 24; } ];
          interfaces.sit.ipv6.addresses = mkOverride 0
            [ { address = address6; prefixLength = 64; } ];
        };
      };
    in {
      name = "Sit";
      nodes.client1 = node { address4 = "192.168.1.1"; remote = "192.168.1.2"; address6 = "fc00::1"; };
      nodes.client2 = node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; };
      testScript = { ... }:
        ''
          startAll;

          # Wait for networking to be configured
          $client1->waitForUnit("network.target");
          $client2->waitForUnit("network.target");

          # Print diagnostic information
          $client1->succeed("ip addr >&2");
          $client2->succeed("ip addr >&2");

          # Test ipv6
          $client1->waitUntilSucceeds("ping -c 1 fc00::1");
          $client1->waitUntilSucceeds("ping -c 1 fc00::2");

          $client2->waitUntilSucceeds("ping -c 1 fc00::1");
          $client2->waitUntilSucceeds("ping -c 1 fc00::2");
        '';
    };
    vlan = let
      node = address: { pkgs, ... }: with pkgs.lib; {
        #virtualisation.vlans = [ 1 ];
        networking = {
          useNetworkd = networkd;
          useDHCP = false;
          vlans.vlan = {
            id = 1;
            interface = "eth0";
          };
          interfaces.eth0.ipv4.addresses = mkOverride 0 [ ];
          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
          interfaces.vlan.ipv4.addresses = mkOverride 0
            [ { inherit address; prefixLength = 24; } ];
        };
      };
    in {
      name = "vlan";
      nodes.client1 = node "192.168.1.1";
      nodes.client2 = node "192.168.1.2";
      testScript = { ... }:
        ''
          startAll;

          # Wait for networking to be configured
          $client1->waitForUnit("network.target");
          $client2->waitForUnit("network.target");

          # Test vlan is setup
          $client1->succeed("ip addr show dev vlan >&2");
          $client2->succeed("ip addr show dev vlan >&2");
        '';
    };
    virtual = {
      name = "Virtual";
      machine = {
        networking.interfaces."tap0" = {
          ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
          ipv6.addresses = [ { address = "2001:1470:fffd:2096::"; prefixLength = 64; } ];
          virtual = true;
        };
        networking.interfaces."tun0" = {
          ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
          ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
          virtual = true;
        };
      };

      testScript = ''
        my $targetList = <<'END';
        tap0: tap persist user 0
        tun0: tun persist user 0
        END

        # Wait for networking to come up
        $machine->start;
        $machine->waitForUnit("network-online.target");

        # Test interfaces set up
        my $list = $machine->succeed("ip tuntap list | sort");
        "$list" eq "$targetList" or die(
          "The list of virtual interfaces does not match the expected one:\n",
          "Result:\n", "$list\n",
          "Expected:\n", "$targetList\n"
        );

        # Test interfaces clean up
        $machine->succeed("systemctl stop network-addresses-tap0");
        $machine->sleep(10);
        $machine->succeed("systemctl stop network-addresses-tun0");
        $machine->sleep(10);
        my $residue = $machine->succeed("ip tuntap list");
        $residue eq "" or die(
          "Some virtual interface has not been properly cleaned:\n",
          "$residue\n"
        );
      '';
    };
    privacy = {
      name = "Privacy";
      nodes.router = { ... }: {
        virtualisation.vlans = [ 1 ];
        boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
        networking = {
          useNetworkd = networkd;
          interfaces.eth1.ipv6.addresses = singleton {
            address = "fd00:1234:5678:1::1";
            prefixLength = 64;
          };
        };
        services.radvd = {
          enable = true;
          config = ''
            interface eth1 {
              AdvSendAdvert on;
              AdvManagedFlag on;
              AdvOtherConfigFlag on;

              prefix fd00:1234:5678:1::/64 {
                AdvAutonomous on;
                AdvOnLink on;
              };
            };
          '';
        };
      };
      nodes.client = { pkgs, ... }: with pkgs.lib; {
        virtualisation.vlans = [ 1 ];
        networking = {
          useNetworkd = networkd;
          useDHCP = true;
          interfaces.eth1 = {
            preferTempAddress = true;
            ipv4.addresses = mkOverride 0 [ ];
            ipv6.addresses = mkOverride 0 [ ];
          };
        };
      };
      testScript = { ... }:
        ''
          startAll;

          $client->waitForUnit("network.target");
          $router->waitForUnit("network-online.target");

          # Wait until we have an ip address
          $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'");

          # Test vlan 1
          $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1");

          # Test address used is temporary
          $client->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'");
        '';
    };
    routes = {
      name = "routes";
      machine = {
        networking.useDHCP = false;
        networking.interfaces."eth0" = {
          ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
          ipv6.addresses = [ { address = "2001:1470:fffd:2097::"; prefixLength = 64; } ];
          ipv6.routes = [
            { address = "fdfd:b3f0::"; prefixLength = 48; }
            { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
          ];
          ipv4.routes = [
            { address = "10.0.0.0"; prefixLength = 16; options = { mtu = "1500"; }; }
            { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
          ];
        };
        virtualisation.vlans = [ ];
      };

      testScript = ''
        my $targetIPv4Table = <<'END';
        10.0.0.0/16 proto static scope link mtu 1500 
        192.168.1.0/24 proto kernel scope link src 192.168.1.2 
        192.168.2.0/24 via 192.168.1.1 proto static 
        END

        my $targetIPv6Table = <<'END';
        2001:1470:fffd:2097::/64 proto kernel metric 256 pref medium
        2001:1470:fffd:2098::/64 via fdfd:b3f0::1 proto static metric 1024 pref medium
        fdfd:b3f0::/48 proto static metric 1024 pref medium
        END

        $machine->start;
        $machine->waitForUnit("network.target");

        # test routing tables
        my $ipv4Table = $machine->succeed("ip -4 route list dev eth0 | head -n3");
        my $ipv6Table = $machine->succeed("ip -6 route list dev eth0 | head -n3");
        "$ipv4Table" eq "$targetIPv4Table" or die(
          "The IPv4 routing table does not match the expected one:\n",
          "Result:\n", "$ipv4Table\n",
          "Expected:\n", "$targetIPv4Table\n"
        );
        "$ipv6Table" eq "$targetIPv6Table" or die(
          "The IPv6 routing table does not match the expected one:\n",
          "Result:\n", "$ipv6Table\n",
          "Expected:\n", "$targetIPv6Table\n"
        );

        # test clean-up of the tables
        $machine->succeed("systemctl stop network-addresses-eth0");
        my $ipv4Residue = $machine->succeed("ip -4 route list dev eth0 | head -n-3");
        my $ipv6Residue = $machine->succeed("ip -6 route list dev eth0 | head -n-3");
        $ipv4Residue eq "" or die(
          "The IPv4 routing table has not been properly cleaned:\n",
          "$ipv4Residue\n"
        );
        $ipv6Residue eq "" or die(
          "The IPv6 routing table has not been properly cleaned:\n",
          "$ipv6Residue\n"
        );
      '';
    };
  };

in mapAttrs (const (attrs: makeTest (attrs // {
  name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}";
  meta = with pkgs.stdenv.lib.maintainers; {
    maintainers = [ wkennington ];
  };
}))) testCases