nixpkgs/nixos/tests/networking.nix
Jörg Thalheim b5c1deca8a
treewide: remove wkennington as maintainer
He prefers to contribute to his own nixpkgs fork triton.
Since he is still marked as maintainer in many packages
this leaves the wrong impression he still maintains those.
2019-01-26 10:05:32 +00:00

610 lines
22 KiB
Nix

{ system ? builtins.currentSystem
, config ? {}
, pkgs ? import ../.. { inherit system config; }
# bool: whether to use networkd in the tests
, networkd }:
with import ../lib/testing.nix { inherit system pkgs; };
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"}";
}))) testCases