From 68729958e889b7db114fcb3f5ca297d28ae59165 Mon Sep 17 00:00:00 2001 From: Arnold Krille Date: Sat, 11 Feb 2017 16:46:55 +0100 Subject: [PATCH] network-interfaces: reload bridges on conf changes And adopt the tests to add an interface and remove it again. It should work when deactivating rstp, it will not work when activating rstp for the first bridge as then the userspace daemon is not yet available. But once one bridge is active with stp, it should work with the reload for any further bridge. Fixes #21745. Also see #22547. --- .../tasks/network-interfaces-scripted.nix | 25 ++++ nixos/release.nix | 1 + nixos/tests/containers-restart_networking.nix | 114 ++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 nixos/tests/containers-restart_networking.nix diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 3571e00d04e..f30906b84a2 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -239,6 +239,10 @@ let ip link set "${i}" master "${n}" ip link set "${i}" up '')} + # Save list of enslaved interfaces + echo "${flip concatMapStrings v.interfaces (i: '' + ${i} + '')}" > /run/${n}.interfaces # Enable stp on the interface ${optionalString v.rstp '' @@ -250,7 +254,28 @@ let postStop = '' ip link set "${n}" down || true ip link del "${n}" || true + rm -f /run/${n}.interfaces ''; + reload = '' + # Un-enslave child interfaces (old list of interfaces) + for interface in `cat /run/${n}.interfaces`; do + ip link set "$interface" nomaster up + done + + # Enslave child interfaces (new list of interfaces) + ${flip concatMapStrings v.interfaces (i: '' + ip link set "${i}" master "${n}" + ip link set "${i}" up + '')} + # Save list of enslaved interfaces + echo "${flip concatMapStrings v.interfaces (i: '' + ${i} + '')}" > /run/${n}.interfaces + + # (Un-)set stp on the bridge + echo ${if v.rstp then "2" else "0"} > /sys/class/net/${n}/bridge/stp_state + ''; + reloadIfChanged = true; }); createVswitchDevice = n: v: nameValuePair "${n}-netdev" diff --git a/nixos/release.nix b/nixos/release.nix index 3535690a8e6..523d6e291ac 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -228,6 +228,7 @@ in rec { tests.containers-imperative = callTest tests/containers-imperative.nix {}; tests.containers-extra_veth = callTest tests/containers-extra_veth.nix {}; tests.containers-physical_interfaces = callTest tests/containers-physical_interfaces.nix {}; + tests.containers-restart_networking = callTest tests/containers-restart_networking.nix {}; tests.containers-tmpfs = callTest tests/containers-tmpfs.nix {}; tests.containers-hosts = callTest tests/containers-hosts.nix {}; tests.containers-macvlans = callTest tests/containers-macvlans.nix {}; diff --git a/nixos/tests/containers-restart_networking.nix b/nixos/tests/containers-restart_networking.nix new file mode 100644 index 00000000000..086d056c51c --- /dev/null +++ b/nixos/tests/containers-restart_networking.nix @@ -0,0 +1,114 @@ +# Test for NixOS' container support. + +let + client_base = rec { + networking.firewall.enable = false; + + containers.webserver = { + autoStart = true; + privateNetwork = true; + hostBridge = "br0"; + config = { + networking.firewall.enable = false; + networking.firewall.allowPing = true; + networking.interfaces.eth0.ip4 = [ + { address = "192.168.1.122"; prefixLength = 24; } + ]; + }; + }; + }; +in import ./make-test.nix ({ pkgs, lib, ...} : +{ + name = "containers-restart_networking"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ kampfschlaefer ]; + }; + + nodes = { + client = { lib, pkgs, ... }: client_base // { + virtualisation.vlans = [ 1 ]; + + networking.bridges.br0 = { + interfaces = []; + rstp = false; + }; + networking.interfaces = { + eth1.ip4 = lib.mkOverride 0 [ ]; + br0.ip4 = [{ address = "192.168.1.1"; prefixLength = 24; }]; + }; + + }; + client_eth1 = { lib, pkgs, ... }: client_base // { + networking.bridges.br0 = { + interfaces = [ "eth1" ]; + rstp = false; + }; + networking.interfaces = { + eth1.ip4 = lib.mkOverride 0 [ ]; + br0.ip4 = [{ address = "192.168.1.2"; prefixLength = 24; }]; + }; + }; + client_eth1_rstp = { lib, pkgs, ... }: client_base // { + networking.bridges.br0 = { + interfaces = [ "eth1" ]; + rstp = true; + }; + networking.interfaces = { + eth1.ip4 = lib.mkOverride 0 [ ]; + br0.ip4 = [{ address = "192.168.1.2"; prefixLength = 24; }]; + }; + }; + }; + + testScript = {nodes, ...}: let + originalSystem = nodes.client.config.system.build.toplevel; + eth1_bridged = nodes.client_eth1.config.system.build.toplevel; + eth1_rstp = nodes.client_eth1_rstp.config.system.build.toplevel; + in '' + $client->start(); + + $client->waitForUnit("default.target"); + + subtest "initial state", sub { + $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.1 >&2"); + + $client->fail("ip l show eth1 |grep \"master br0\" >&2"); + $client->fail("grep eth1 /run/br0.interfaces >&2"); + }; + + subtest "interfaces without stp", sub { + $client->succeed("${eth1_bridged}/bin/switch-to-configuration test >&2"); + + $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.2 >&2"); + + $client->succeed("ip l show eth1 |grep \"master br0\" >&2"); + $client->succeed("grep eth1 /run/br0.interfaces >&2"); + }; + + # activating rstp needs another service, therefor the bridge will restart and the container will loose its connectivity + #subtest "interfaces with rstp", sub { + # $client->succeed("${eth1_rstp}/bin/switch-to-configuration test >&2"); + # $client->execute("ip -4 a >&2"); + # $client->execute("ip l >&2"); + # + # $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + # $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.2 >&2"); + # + # $client->succeed("ip l show eth1 |grep \"master br0\" >&2"); + # $client->succeed("grep eth1 /run/br0.interfaces >&2"); + #}; + + subtest "back to no interfaces and no stp", sub { + $client->succeed("${originalSystem}/bin/switch-to-configuration test >&2"); + + $client->succeed("ping 192.168.1.122 -c 1 -n >&2"); + $client->succeed("nixos-container run webserver -- ping -c 1 -n 192.168.1.1 >&2"); + + $client->fail("ip l show eth1 |grep \"master br0\" >&2"); + $client->fail("grep eth1 /run/br0.interfaces >&2"); + }; + ''; + +})