From a47e58577910a47b59885af4ac0599b3bc60bfcd Mon Sep 17 00:00:00 2001 From: Molly Miller Date: Mon, 11 Nov 2024 10:44:07 +0100 Subject: [PATCH] network: simplify network interface units to make them more reliable All network interface now use "-netdev.service" units, even if they have underlying physical devices and were previously depending on systemd device units. This proved to be unreliable when trying to transform systems between complex configuration states without requiring a reboot and ended up in undefined states. This is a forward port of: 1c4192ada7504e673fe4e574ac7de64217c0d91d Co-authored-by: Christian Theune --- .../tasks/network-interfaces-scripted.nix | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index 83c0dc27885e3c..5167421583c231 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -68,23 +68,16 @@ let let deviceDependency = dev: - # Use systemd service if we manage device creation, else - # trust udev when not in a container - if (dev == null || dev == "lo") then [] - else if (hasAttr dev (filterAttrs (k: v: v.virtual) cfg.interfaces)) || - (hasAttr dev cfg.bridges) || - (hasAttr dev cfg.bonds) || - (hasAttr dev cfg.macvlans) || - (hasAttr dev cfg.sits) || - (hasAttr dev cfg.vlans) || - (hasAttr dev cfg.vswitches) - then [ "${dev}-netdev.service" ] - else optional (!config.boot.isContainer) (subsystemDevice dev); + if (dev == null || dev == "lo" || config.boot.isContainer) then [] + else [ "${dev}-netdev.service" ]; hasDefaultGatewaySet = (cfg.defaultGateway != null && cfg.defaultGateway.address != "") || (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != ""); - needNetworkSetup = cfg.resolvconf.enable || cfg.defaultGateway != null || cfg.defaultGateway6 != null; + # XXX we explicitly rely on network-setup.service being + # unconditionally present for triggering dependencies on + # interface units. + needNetworkSetup = true; networkLocalCommands = lib.mkIf needNetworkSetup { after = [ "network-setup.service" ]; @@ -97,8 +90,8 @@ let after = [ "network-pre.target" "systemd-udevd.service" "systemd-sysctl.service" ]; before = [ "network.target" "shutdown.target" ]; wants = [ "network.target" ]; - # exclude bridges from the partOf relationship to fix container networking bug #47210 - partOf = map (i: "network-addresses-${i.name}.service") (filter (i: !(hasAttr i.name cfg.bridges)) interfaces); + # exclude bridges from the requires relationship to fix container networking bug #47210 + requiredBy = map (i: "network-addresses-${i.name}.service") (filter (i: !(hasAttr i.name cfg.bridges)) interfaces); conflicts = [ "shutdown.target" ]; wantedBy = [ "multi-user.target" ] ++ optional hasDefaultGatewaySet "network-online.target"; @@ -177,10 +170,12 @@ let wantedBy = [ "network-setup.service" "network.target" + "multi-user.target" ]; # order before network-setup because the routes that are configured # there may need ip addresses configured before = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; bindsTo = deviceDependency i.name; after = [ "network-pre.target" ] ++ (deviceDependency i.name); serviceConfig.Type = "oneshot"; @@ -260,7 +255,7 @@ let bindsTo = optional (!config.boot.isContainer) "dev-net-tun.device"; after = optional (!config.boot.isContainer) "dev-net-tun.device" ++ [ "network-pre.target" ]; wantedBy = [ "network-setup.service" (subsystemDevice i.name) ]; - partOf = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; before = [ "network-setup.service" ]; path = [ pkgs.iproute2 ]; serviceConfig = { @@ -282,7 +277,7 @@ let { description = "Bridge Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; bindsTo = deps ++ optional v.rstp "mstpd.service"; - partOf = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service"; + requires = [ "network-setup.service" ] ++ optional v.rstp "mstpd.service"; after = [ "network-pre.target" ] ++ deps ++ optional v.rstp "mstpd.service" ++ map (i: "network-addresses-${i}.service") v.interfaces; before = [ "network-setup.service" ]; @@ -370,7 +365,7 @@ let # should work without internalConfigs dependencies because address/link configuration depends # on the device, which is created by ovs-vswitchd with type=internal, but it does not... before = [ "network-setup.service" ] ++ internalConfigs; - partOf = [ "network-setup.service" ]; # shutdown the bridge when network is shutdown + requires = [ "network-setup.service" ]; # shutdown the bridge when network is shutdown bindsTo = [ "ovs-vswitchd.service" ]; # requires ovs-vswitchd to be alive at all times after = [ "network-pre.target" "ovs-vswitchd.service" ] ++ deps; # start switch after physical interfaces and vswitch daemon wants = deps; # if one or more interface fails, the switch should continue to run @@ -411,7 +406,7 @@ let { description = "Bond Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; after = [ "network-pre.target" ] ++ deps ++ map (i: "network-addresses-${i}.service") v.interfaces; before = [ "network-setup.service" ]; @@ -450,7 +445,7 @@ let { description = "MACVLAN Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; @@ -485,7 +480,7 @@ let { description = "FOU endpoint ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; @@ -508,7 +503,7 @@ let { description = "6-to-4 Tunnel Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; @@ -542,7 +537,7 @@ let { description = "GRE Tunnel Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot"; @@ -570,7 +565,7 @@ let { description = "VLAN Interface ${n}"; wantedBy = [ "network-setup.service" (subsystemDevice n) ]; bindsTo = deps; - partOf = [ "network-setup.service" ]; + requires = [ "network-setup.service" ]; after = [ "network-pre.target" ] ++ deps; before = [ "network-setup.service" ]; serviceConfig.Type = "oneshot";