Commit 3cb9534d authored by Will Fancher's avatar Will Fancher
Browse files

systemd-initrd: Flush networkd

parent 85982346
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -67,11 +67,15 @@ in

    boot.initrd.network.flushBeforeStage2 = mkOption {
      type = types.bool;
      default = true;
      default = !config.boot.initrd.systemd.enable;
      defaultText = "!config.boot.initrd.systemd.enable";
      description = lib.mdDoc ''
        Whether to clear the configuration of the interfaces that were set up in
        the initrd right before stage 2 takes over. Stage 2 will do the regular network
        configuration based on the NixOS networking options.

        The default is false when systemd is enabled in initrd,
        because the systemd-networkd documentation suggests it.
      '';
    };

+33 −0
Original line number Diff line number Diff line
@@ -3186,6 +3186,39 @@ let
      ];
      kernelModules = [ "af_packet" ];

      systemd.services.nixos-flush-networkd = mkIf config.boot.initrd.network.flushBeforeStage2 {
        description = "Flush Network Configuration";
        wantedBy = ["initrd.target"];
        after = ["systemd-networkd.service" "dbus.socket" "dbus.service"];
        before = ["shutdown.target" "initrd-switch-root.target"];
        conflicts = ["shutdown.target" "initrd-switch-root.target"];
        unitConfig.DefaultDependencies = false;
        serviceConfig = {
          # This service does nothing when starting, but brings down
          # interfaces when switching root. This is the easiest way to
          # ensure proper ordering while stopping. See systemd.unit(5)
          # section on Before= and After=. The important part is that
          # we are stopped before units we need, like dbus.service,
          # and that we are stopped before starting units like
          # initrd-switch-root.target
          Type = "oneshot";
          RemainAfterExit = true;
          ExecStart = "/bin/true";
        };
        # systemd-networkd doesn't bring down interfaces on its own
        # when it exits (see: systemd-networkd(8)), so we have to do
        # it ourselves. The networkctl command doesn't have a way to
        # bring all interfaces down, so we have to iterate over the
        # list and filter out unmanaged interfaces to bring them down
        # individually.
        preStop = ''
          networkctl list --full --no-legend | while read _idx link _type _operational setup _; do
            [ "$setup" = unmanaged ] && continue
            networkctl down "$link"
          done
        '';
      };

    })
  ];

+33 −1
Original line number Diff line number Diff line
@@ -2,7 +2,23 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
  name = "systemd-initrd-network";
  meta.maintainers = [ lib.maintainers.elvishjerricco ];

  nodes = {
  nodes = let
    mkFlushTest = flush: script: { ... }: {
      boot.initrd.systemd.enable = true;
      boot.initrd.network = {
        enable = true;
        flushBeforeStage2 = flush;
      };
      systemd.services.check-flush = {
        requiredBy = ["multi-user.target"];
        before = ["network-pre.target" "multi-user.target"];
        unitConfig.DefaultDependencies = false;
        serviceConfig.Type = "oneshot";
        path = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ];
        inherit script;
      };
    };
  in {
    basic = { ... }: {
      boot.initrd.network.enable = true;

@@ -29,11 +45,27 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
          };
      };
    };

    doFlush = mkFlushTest true ''
      if ip addr | grep 10.0.2.15; then
        echo "Network configuration survived switch-root; flushBeforeStage2 failed"
        exit 1
      fi
    '';

    dontFlush = mkFlushTest false ''
      if ! (ip addr | grep 10.0.2.15); then
        echo "Network configuration didn't survive switch-root"
        exit 1
      fi
    '';
  };

  testScript = ''
    start_all()
    basic.wait_for_unit("multi-user.target")
    doFlush.wait_for_unit("multi-user.target")
    dontFlush.wait_for_unit("multi-user.target")
    # Make sure the systemd-network user was set correctly in initrd
    basic.succeed("[ $(stat -c '%U,%G' /run/systemd/netif/links) = systemd-network,systemd-network ]")
    basic.succeed("ip addr show >&2")