diff --git a/nixos/doc/manual/release-notes/rl-2003.xml b/nixos/doc/manual/release-notes/rl-2003.xml index 918906d27e7..b77f67b664b 100644 --- a/nixos/doc/manual/release-notes/rl-2003.xml +++ b/nixos/doc/manual/release-notes/rl-2003.xml @@ -712,6 +712,14 @@ auth required pam_succeed_if.so uid >= 1000 quiet For further reference, please read #68953 or the corresponding discourse thread. + + + The systemd.network.links option is now respected + even when systemd-networkd is disabled. + This mirrors the behaviour of systemd - It's udev that parses .link files, + not systemd-networkd. + + diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix index 042c4d5addd..cf39ed065a7 100644 --- a/nixos/modules/services/networking/zerotierone.nix +++ b/nixos/modules/services/networking/zerotierone.nix @@ -69,13 +69,14 @@ in environment.systemPackages = [ cfg.package ]; # Prevent systemd from potentially changing the MAC address - environment.etc."systemd/network/50-zerotier.link".text = '' - [Match] - OriginalName=zt* - - [Link] - AutoNegotiation=false - MACAddressPolicy=none - ''; + systemd.network.links."50-zerotier" = { + matchConfig = { + OriginalName = "zt*"; + }; + linkConfig = { + AutoNegotiation = false; + MACAddressPolicy = "none"; + }; + }; }; } diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 6dfbe66fc64..3078f84f6e9 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -355,6 +355,14 @@ let }; linkOptions = commonNetworkOptions // { + # overwrite enable option from above + enable = mkOption { + default = true; + type = types.bool; + description = '' + Whether to enable this .link unit. It's handled by udev no matter if systemd-networkd is enabled or not + ''; + }; linkConfig = mkOption { default = {}; @@ -1045,44 +1053,49 @@ in }; - config = mkIf config.systemd.network.enable { + config = 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; + } - users.users.systemd-network.group = "systemd-network"; + (mkIf config.systemd.network.enable { - systemd.additionalUpstreamSystemUnits = [ - "systemd-networkd.service" "systemd-networkd-wait-online.service" - ]; + users.users.systemd-network.group = "systemd-network"; - systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links - // mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs - // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks; + systemd.additionalUpstreamSystemUnits = [ + "systemd-networkd.service" "systemd-networkd-wait-online.service" + ]; - environment.etc = unitFiles; + 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; - systemd.services.systemd-networkd = { - wantedBy = [ "multi-user.target" ]; - restartTriggers = attrNames unitFiles; - # prevent race condition with interface renaming (#39069) - requires = [ "systemd-udev-settle.service" ]; - after = [ "systemd-udev-settle.service" ]; - }; - - systemd.services.systemd-networkd-wait-online = { - wantedBy = [ "network-online.target" ]; - }; - - systemd.services."systemd-network-wait-online@" = { - description = "Wait for Network Interface %I to be Configured"; - conflicts = [ "shutdown.target" ]; - requisite = [ "systemd-networkd.service" ]; - after = [ "systemd-networkd.service" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I"; + systemd.services.systemd-networkd = { + wantedBy = [ "multi-user.target" ]; + restartTriggers = attrNames unitFiles; + # prevent race condition with interface renaming (#39069) + requires = [ "systemd-udev-settle.service" ]; + after = [ "systemd-udev-settle.service" ]; }; - }; - services.resolved.enable = mkDefault true; - }; + systemd.services.systemd-networkd-wait-online = { + wantedBy = [ "network-online.target" ]; + }; + + systemd.services."systemd-network-wait-online@" = { + description = "Wait for Network Interface %I to be Configured"; + conflicts = [ "shutdown.target" ]; + requisite = [ "systemd-networkd.service" ]; + after = [ "systemd-networkd.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I"; + }; + }; + + services.resolved.enable = mkDefault true; + }) + ]; } diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix index 933a4451af9..a519ca06594 100644 --- a/nixos/tests/networking.nix +++ b/nixos/tests/networking.nix @@ -5,11 +5,10 @@ , networkd }: with import ../lib/testing-python.nix { inherit system pkgs; }; -with pkgs.lib; let - router = { config, pkgs, ... }: - with pkgs.lib; + router = { config, pkgs, lib, ... }: + with lib; let vlanIfs = range 1 (length config.virtualisation.vlans); in { @@ -85,7 +84,7 @@ let static = { name = "Static"; nodes.router = router; - nodes.client = { pkgs, ... }: with pkgs.lib; { + nodes.client = { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 2 ]; networking = { useNetworkd = networkd; @@ -137,7 +136,7 @@ let dhcpSimple = { name = "SimpleDHCP"; nodes.router = router; - nodes.client = { pkgs, ... }: with pkgs.lib; { + nodes.client = { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 2 ]; networking = { useNetworkd = networkd; @@ -193,7 +192,7 @@ let dhcpOneIf = { name = "OneInterfaceDHCP"; nodes.router = router; - nodes.client = { pkgs, ... }: with pkgs.lib; { + nodes.client = { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 2 ]; networking = { useNetworkd = networkd; @@ -232,7 +231,7 @@ let ''; }; bond = let - node = address: { pkgs, ... }: with pkgs.lib; { + node = address: { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 2 ]; networking = { useNetworkd = networkd; @@ -268,7 +267,7 @@ let ''; }; bridge = let - node = { address, vlan }: { pkgs, ... }: with pkgs.lib; { + node = { address, vlan }: { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ vlan ]; networking = { useNetworkd = networkd; @@ -281,7 +280,7 @@ let name = "Bridge"; nodes.client1 = node { address = "192.168.1.2"; vlan = 1; }; nodes.client2 = node { address = "192.168.1.3"; vlan = 2; }; - nodes.router = { pkgs, ... }: with pkgs.lib; { + nodes.router = { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 2 ]; networking = { useNetworkd = networkd; @@ -318,7 +317,7 @@ let macvlan = { name = "MACVLAN"; nodes.router = router; - nodes.client = { pkgs, ... }: with pkgs.lib; { + nodes.client = { pkgs, lib, ... }: with lib; { environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules virtualisation.vlans = [ 1 ]; networking = { @@ -372,7 +371,7 @@ let ''; }; sit = let - node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; { + node = { address4, remote, address6 }: { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 ]; networking = { useNetworkd = networkd; @@ -414,7 +413,7 @@ let ''; }; vlan = let - node = address: { pkgs, ... }: with pkgs.lib; { + node = address: { pkgs, lib, ... }: with lib; { #virtualisation.vlans = [ 1 ]; networking = { useNetworkd = networkd; @@ -527,7 +526,7 @@ let ''; }; }; - nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; { + nodes.client_with_privacy = { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 ]; networking = { useNetworkd = networkd; @@ -540,7 +539,7 @@ let }; }; }; - nodes.client = { pkgs, ... }: with pkgs.lib; { + nodes.client = { pkgs, lib, ... }: with lib; { virtualisation.vlans = [ 1 ]; networking = { useNetworkd = networkd; @@ -603,9 +602,9 @@ let testScript = '' targetIPv4Table = """ - 10.0.0.0/16 proto static scope link mtu 1500 - 192.168.1.0/24 proto kernel scope link src 192.168.1.2 - 192.168.2.0/24 via 192.168.1.1 proto static + 10.0.0.0/16 proto static scope link mtu 1500 + 192.168.1.0/24 proto kernel scope link src 192.168.1.2 + 192.168.2.0/24 via 192.168.1.1 proto static """.strip() targetIPv6Table = """ @@ -655,8 +654,33 @@ let ), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue) ''; }; + # even with disabled networkd, systemd.network.links should work + # (as it's handled by udev, not networkd) + link = { + name = "Link"; + nodes.client = { pkgs, ... }: { + virtualisation.vlans = [ 1 ]; + networking = { + useNetworkd = networkd; + useDHCP = false; + }; + systemd.network.links."50-foo" = { + matchConfig = { + Name = "foo"; + Driver = "dummy"; + }; + linkConfig.MTUBytes = "1442"; + }; + }; + testScript = '' + print(client.succeed("ip l add name foo type dummy")) + print(client.succeed("stat /etc/systemd/network/50-foo.link")) + client.succeed("udevadm settle") + assert "mtu 1442" in client.succeed("ip l show dummy0") + ''; + }; }; -in mapAttrs (const (attrs: makeTest (attrs // { +in pkgs.lib.mapAttrs (pkgs.lib.const (attrs: makeTest (attrs // { name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}"; }))) testCases