systemd-initrd: networkd

This commit is contained in:
Will Fancher 2022-06-29 01:01:59 -04:00
parent e70b42bf61
commit dd392d7c76
6 changed files with 184 additions and 53 deletions

View file

@ -16,16 +16,6 @@ let
'';
# networkd link files are used early by udev to set up interfaces early.
# This must be done in stage 1 to avoid race conditions between udev and
# network daemons.
# TODO move this into the initrd-network module when it exists
initrdLinkUnits = pkgs.runCommand "initrd-link-units" {} ''
mkdir -p $out
ln -s ${udev}/lib/systemd/network/*.link $out/
${lib.concatMapStringsSep "\n" (file: "ln -s ${file} $out/") (lib.mapAttrsToList (n: v: "${v.unit}/${n}") (lib.filterAttrs (n: _: hasSuffix ".link" n) config.systemd.network.units))}
'';
extraUdevRules = pkgs.writeTextFile {
name = "extra-udev-rules";
text = cfg.extraRules;
@ -398,7 +388,6 @@ in
systemd = config.boot.initrd.systemd.package;
binPackages = config.boot.initrd.services.udev.binPackages ++ [ config.boot.initrd.systemd.contents."/bin".source ];
};
"/etc/systemd/network".source = initrdLinkUnits;
};
# Insert initrd rules
boot.initrd.services.udev.packages = [

View file

@ -6,8 +6,6 @@ with lib;
let
cfg = config.systemd.network;
check = {
global = {
@ -2941,14 +2939,12 @@ let
+ def.extraConfig;
};
unitFiles = listToAttrs (map (name: {
name = "systemd/network/${name}";
mkUnitFiles = prefix: cfg: listToAttrs (map (name: {
name = "${prefix}systemd/network/${name}";
value.source = "${cfg.units.${name}.unit}/${name}";
}) (attrNames cfg.units));
in
{
options = {
commonOptions = {
systemd.network.enable = mkOption {
default = false;
@ -3051,12 +3047,11 @@ in
};
config = mkMerge [
commonConfig = config: let cfg = config.systemd.network; in mkMerge [
# .link units are honored by udev, no matter if systemd-networkd is enabled or not.
{
systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links;
environment.etc = unitFiles;
systemd.network.wait-online.extraArgs =
[ "--timeout=${toString cfg.wait-online.timeout}" ]
@ -3066,14 +3061,6 @@ in
(mkIf config.systemd.network.enable {
users.users.systemd-network.group = "systemd-network";
systemd.additionalUpstreamSystemUnits = [
"systemd-networkd-wait-online.service"
"systemd-networkd.service"
"systemd-networkd.socket"
];
systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
// mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
@ -3082,14 +3069,6 @@ in
# networkd.
systemd.sockets.systemd-networkd.wantedBy = [ "sockets.target" ];
systemd.services.systemd-networkd = {
wantedBy = [ "multi-user.target" ];
aliases = [ "dbus-org.freedesktop.network1.service" ];
restartTriggers = map (x: x.source) (attrValues unitFiles) ++ [
config.environment.etc."systemd/networkd.conf".source
];
};
systemd.services.systemd-networkd-wait-online = {
inherit (cfg.wait-online) enable;
wantedBy = [ "network-online.target" ];
@ -3111,8 +3090,37 @@ in
};
};
})
];
stage2Config = let
cfg = config.systemd.network;
unitFiles = mkUnitFiles "" cfg;
in mkMerge [
(commonConfig config)
{ environment.etc = unitFiles; }
(mkIf config.systemd.network.enable {
users.users.systemd-network.group = "systemd-network";
systemd.additionalUpstreamSystemUnits = [
"systemd-networkd-wait-online.service"
"systemd-networkd.service"
"systemd-networkd.socket"
];
environment.etc."systemd/networkd.conf" = renderConfig cfg.config;
systemd.services.systemd-networkd = {
wantedBy = [ "multi-user.target" ];
restartTriggers = map (x: x.source) (attrValues unitFiles) ++ [
config.environment.etc."systemd/networkd.conf".source
];
aliases = [ "dbus-org.freedesktop.network1.service" ];
};
networking.iproute2 = mkIf (cfg.config.addRouteTablesToIPRoute2 && cfg.config.routeTables != { }) {
enable = mkDefault true;
rttablesExtraConfig = ''
@ -3123,6 +3131,80 @@ in
};
services.resolved.enable = mkDefault true;
})
];
stage1Config = let
cfg = config.boot.initrd.systemd.network;
in mkMerge [
(commonConfig config.boot.initrd)
{
systemd.network.enable = mkDefault config.boot.initrd.network.enable;
systemd.contents = mkUnitFiles "/etc/" cfg;
# Networkd link files are used early by udev to set up interfaces early.
# This must be done in stage 1 to avoid race conditions between udev and
# network daemons.
systemd.network.units = lib.filterAttrs (n: _: hasSuffix ".link" n) config.systemd.network.units;
systemd.storePaths = ["${config.boot.initrd.systemd.package}/lib/systemd/network/99-default.link"];
}
(mkIf cfg.enable {
systemd.package = pkgs.systemdStage1Network;
systemd.additionalUpstreamUnits = [
"systemd-networkd-wait-online.service"
"systemd-networkd.service"
"systemd-networkd.socket"
"systemd-network-generator.service"
"network-online.target"
"network-pre.target"
"network.target"
"nss-lookup.target"
"nss-user-lookup.target"
"remote-fs-pre.target"
"remote-fs.target"
];
systemd.users.systemd-network = {};
systemd.groups.systemd-network = {};
systemd.contents."/etc/systemd/networkd.conf" = renderConfig cfg.config;
systemd.services.systemd-networkd.wantedBy = [ "initrd.target" ];
systemd.services.systemd-network-generator.wantedBy = [ "sysinit.target" ];
systemd.storePaths = [
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-networkd"
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-networkd-wait-online"
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-network-generator"
];
kernelModules = [ "af_packet" ];
})
];
in
{
options = commonOptions // {
boot.initrd = commonOptions;
};
config = mkMerge [
stage2Config
(mkIf config.boot.initrd.systemd.enable {
assertions = [{
assertion = config.boot.initrd.network.udhcpc.extraArgs == [];
message = ''
boot.initrd.network.udhcpc.extraArgs is not supported when
boot.initrd.systemd.enable is enabled
'';
}];
boot.initrd = stage1Config;
})
];
}

View file

@ -72,15 +72,6 @@ let
"systemd-tmpfiles-setup.service"
"timers.target"
"umount.target"
# TODO: Networking
# "network-online.target"
# "network-pre.target"
# "network.target"
# "nss-lookup.target"
# "nss-user-lookup.target"
# "remote-fs-pre.target"
# "remote-fs.target"
] ++ cfg.additionalUpstreamUnits;
upstreamWants = [

View file

@ -677,6 +677,7 @@ in {
systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {};
systemd-initrd-swraid = handleTest ./systemd-initrd-swraid.nix {};
systemd-initrd-vconsole = handleTest ./systemd-initrd-vconsole.nix {};
systemd-initrd-networkd = handleTest ./systemd-initrd-networkd.nix {};
systemd-journal = handleTest ./systemd-journal.nix {};
systemd-machinectl = handleTest ./systemd-machinectl.nix {};
systemd-networkd = handleTest ./systemd-networkd.nix {};

View file

@ -8,25 +8,48 @@ let
testCombinations = pkgs.lib.cartesianProductOfSets {
predictable = [true false];
withNetworkd = [true false];
systemdStage1 = [true false];
};
in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd }: {
in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd, systemdStage1 }: {
name = pkgs.lib.optionalString (!predictable) "un" + "predictable"
+ pkgs.lib.optionalString withNetworkd "Networkd";
+ pkgs.lib.optionalString withNetworkd "Networkd"
+ pkgs.lib.optionalString systemdStage1 "SystemdStage1";
value = makeTest {
name = "${pkgs.lib.optionalString (!predictable) "un"}predictableInterfaceNames${pkgs.lib.optionalString withNetworkd "-with-networkd"}";
name = pkgs.lib.optionalString (!predictable) "un" + "predictableInterfaceNames"
+ pkgs.lib.optionalString withNetworkd "-with-networkd"
+ pkgs.lib.optionalString systemdStage1 "-systemd-stage-1";
meta = {};
nodes.machine = { lib, ... }: {
nodes.machine = { lib, ... }: let
script = ''
ip link
if ${lib.optionalString predictable "!"} ip link show eth0; then
echo Success
else
exit 1
fi
'';
in {
networking.usePredictableInterfaceNames = lib.mkForce predictable;
networking.useNetworkd = withNetworkd;
networking.dhcpcd.enable = !withNetworkd;
networking.useDHCP = !withNetworkd;
# Check if predictable interface names are working in stage-1
boot.initrd.postDeviceCommands = ''
ip link
ip link show eth0 ${if predictable then "&&" else "||"} exit 1
'';
boot.initrd.postDeviceCommands = script;
boot.initrd.systemd = lib.mkIf systemdStage1 {
enable = true;
initrdBin = [ pkgs.iproute2 ];
services.systemd-udev-settle.wantedBy = ["initrd.target"];
services.check-interfaces = {
requiredBy = ["initrd.target"];
after = ["systemd-udev-settle.service"];
serviceConfig.Type = "oneshot";
path = [ pkgs.iproute2 ];
inherit script;
};
};
};
testScript = ''

View file

@ -0,0 +1,45 @@
import ./make-test-python.nix ({ pkgs, lib, ... }: {
name = "systemd-initrd-network";
meta.maintainers = [ lib.maintainers.elvishjerricco ];
nodes = {
basic = { ... }: {
boot.initrd.network.enable = true;
boot.initrd.systemd = {
enable = true;
network.networks."99-eth0" = {
matchConfig.Name = "eth0";
DHCP = "yes";
};
network.wait-online.timeout = 10;
# Drop the boot into emergency mode if we timeout
targets.network-online.requiredBy = [ "initrd.target" ];
services.systemd-networkd-wait-online.requiredBy =
[ "network-online.target" ];
initrdBin = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ];
services.check = {
requiredBy = [ "initrd.target" ];
before = [ "initrd.target" ];
after = [ "network-online.target" ];
serviceConfig.Type = "oneshot";
path = [ pkgs.iproute2 pkgs.iputils pkgs.gnugrep ];
script = ''
ip addr | grep 10.0.2.15 || exit 1
ping -c1 10.0.2.2 || exit 1
'';
};
};
};
};
testScript = ''
start_all()
basic.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")
basic.succeed("ip route show >&2")
'';
})