systemd-initrd: networkd

Changed files
+184 -53
nixos
-11
nixos/modules/services/hardware/udev.nix
···
'';
-
# 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;
···
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 = [
+107 -25
nixos/modules/system/boot/networkd.nix
···
let
-
cfg = config.systemd.network;
-
check = {
global = {
···
+ 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;
···
};
-
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}" ]
···
(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;
···
# 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" ];
···
};
};
+
})
+
];
+
+
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 = ''
···
};
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;
})
];
-9
nixos/modules/system/boot/systemd/initrd.nix
···
"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 = [
+1
nixos/tests/all-tests.nix
···
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 {};
+31 -8
nixos/tests/predictable-interface-names.nix
···
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 = ''
+45
nixos/tests/systemd-initrd-networkd.nix
···
+
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")
+
'';
+
})