From 2be59edf63a4d5963e246dd150f51e6ae35d79fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 27 Nov 2022 18:56:24 +0000 Subject: [PATCH] kexec-installer: fix static ip tests --- nix/kexec-installer/module.nix | 10 ++- nix/kexec-installer/restore_routes.py | 11 ++-- nix/kexec-installer/test.nix | 91 +++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/nix/kexec-installer/module.nix b/nix/kexec-installer/module.nix index c9e7f28..72b2714 100644 --- a/nix/kexec-installer/module.nix +++ b/nix/kexec-installer/module.nix @@ -98,9 +98,13 @@ in { wants = [ "network-pre.target" ]; wantedBy = [ "multi-user.target" ]; - serviceConfig.ExecStart = [ - "${restoreNetwork} /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json" - ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = [ + "${restoreNetwork} /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json /etc/systemd/network" + ]; + }; unitConfig.ConditionPathExists = [ "/root/network/addrs.json" diff --git a/nix/kexec-installer/restore_routes.py b/nix/kexec-installer/restore_routes.py index 152ccbe..9578f10 100644 --- a/nix/kexec-installer/restore_routes.py +++ b/nix/kexec-installer/restore_routes.py @@ -58,7 +58,7 @@ def generate_networkd_units( if route["dev"] != interface["ifname"]: continue - route_section = "[Route]" + route_section = "[Route]\n" if route["dst"] != "default": # can be skipped for default routes route_section += f"Destination = {route['dst']}\n" @@ -85,9 +85,9 @@ IPv6AcceptRA = yes def main() -> None: - if len(sys.argv) < 4: + if len(sys.argv) < 5: print( - f"USAGE: {sys.argv[0]} addresses routes-v4 routes-v6 [networkd-directory]", + f"USAGE: {sys.argv[0]} addresses routes-v4 routes-v6 networkd-directory", file=sys.stderr, ) sys.exit(1) @@ -99,10 +99,7 @@ def main() -> None: with open(sys.argv[3]) as f: v6_routes = json.load(f) - if len(sys.argv) >= 4: - networkd_directory = Path(sys.argv[4]) - else: - networkd_directory = Path("/etc/systemd/network") + networkd_directory = Path(sys.argv[4]) relevant_interfaces = filter_interfaces(addresses) relevant_routes = filter_routes(v4_routes) + filter_routes(v6_routes) diff --git a/nix/kexec-installer/test.nix b/nix/kexec-installer/test.nix index 69ab562..2d46285 100644 --- a/nix/kexec-installer/test.nix +++ b/nix/kexec-installer/test.nix @@ -28,14 +28,43 @@ in makeTest' { boot.loader.systemd-boot.enable = true; boot.loader.efi.canTouchEfiVariables = true; services.openssh.enable = true; + networking = { useNetworkd = true; useDHCP = false; }; + + systemd.network = { + networks = { + # systemd-networkd will load the first network unit file + # that matches, ordered lexiographically by filename. + # /etc/systemd/network/{40-eth1,99-main}.network already + # exists. This network unit must be loaded for the test, + # however, hence why this network is named such. + "01-eth1" = { + name = "eth1"; + address = [ + # Some static addresses that we want to see in the kexeced image + "192.168.42.1/24" + "42::1/64" + ]; + routes = [ + # Some static routes that we want to see in the kexeced image + { routeConfig = { Destination = "192.168.43.0/24"; }; } + { routeConfig = { Destination = "192.168.44.0/24"; Gateway = "192.168.43.1"; }; } + { routeConfig = { Destination = "43::0/64"; }; } + { routeConfig = { Destination = "44::1/64"; Gateway = "43::1"; }; } + ]; + networkConfig = { + DHCP = "yes"; + IPv6AcceptRA = true; + }; + }; + }; + }; }; node2 = { pkgs, modulesPath, ... }: { - virtualisation.vlans = [ 1 ]; environment.systemPackages = [ pkgs.hello ]; imports = [ ./module.nix @@ -59,7 +88,13 @@ in makeTest' { "01-eth1" = { name = "eth1"; address = [ - "2001:DB8::1/64" + "2001:db8::1/64" + ]; + ipv6Prefixes = [ + { ipv6PrefixConfig = { Prefix = "2001:db8::/64"; AddressAutoconfiguration = true; OnLink = true; }; } + ]; + ipv6RoutePrefixes = [ + { ipv6RoutePrefixConfig = { Route = "::/0"; LifetimeSec = 3600; }; } ]; networkConfig = { DHCPServer = true; @@ -69,6 +104,7 @@ in makeTest' { dhcpServerConfig = { PoolOffset = 100; PoolSize = 1; + EmitRouter = true; }; }; }; @@ -79,22 +115,24 @@ in makeTest' { testScript = { nodes, ... }: '' # Test whether reboot via kexec works. - node1.wait_for_unit("multi-user.target") - node1.succeed('kexec --load /run/current-system/kernel --initrd /run/current-system/initrd --command-line "$(&2 &", check_return=False) - node1.connected = False - node1.connect() - node1.wait_for_unit("multi-user.target") - node1.wait_for_unit("network-online.target") + + router.wait_for_unit("network-online.target") + router.succeed("ip addr >&2") + router.succeed("ip route >&2") + router.succeed("ip -6 route >&2") + router.succeed("networkctl status eth1 >&2") + + node1.wait_until_succeeds("ping -c1 10.0.0.1") + node1.wait_until_succeeds("ping -c1 2001:db8::1") node1.succeed("ip addr >&2") node1.succeed("ip route >&2") node1.succeed("ip -6 route >&2") + node1.succeed("networkctl status eth1 >&2") - node1.wait_for_unit("sshd.service") host_ed25519_before = node1.succeed("cat /etc/ssh/ssh_host_ed25519_key.pub") - node1.succeed('ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -q -N ""') root_ed25519_before = node1.succeed('tee /root/.ssh/authorized_keys < /root/.ssh/id_ed25519.pub') + # Kexec node1 to the toplevel of node2 via the kexec-boot script node1.succeed('touch /run/foo') node1.fail('hello') @@ -113,6 +151,37 @@ in makeTest' { root_ed25519_after = node1.succeed("cat /root/.ssh/authorized_keys") assert root_ed25519_before == root_ed25519_after, f"{root_ed25519_before} != {root_ed25519_after}" + # See if we can reach the router after kexec + node1.wait_for_unit("restoreNetwork.service") + node1.wait_until_succeeds("cat /etc/systemd/network/eth1.network >&2") + node1.wait_until_succeeds("ping -c1 10.0.0.1") + node1.wait_until_succeeds("ping -c1 2001:db8::1") + + # Check if static addresses have been restored + node1.wait_until_succeeds("ping -c1 42::1") + node1.wait_until_succeeds("ping -c1 192.168.42.1") + + out = node1.wait_until_succeeds("ip route get 192.168.43.2") + print(out) + assert "192.168.43.2 dev eth1" in out + + out = node1.wait_until_succeeds("ip route get 192.168.44.2") + print(out) + assert "192.168.44.2 via 192.168.43.1" in out + + out = node1.wait_until_succeeds("ip route get 43::2") + print(out) + assert "43::2 from :: dev eth1" in out + + out = node1.wait_until_succeeds("ip route get 44::2") + print(out) + assert "44::2 from :: via 43::1" in out + + node1.succeed("ip addr >&2") + node1.succeed("ip route >&2") + node1.succeed("ip -6 route >&2") + node1.succeed("networkctl status eth1 >&2") + node1.shutdown() ''; }