about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorWill Fancher <elvishjerricco@gmail.com>2023-09-25 05:07:23 -0400
committerGitHub <noreply@github.com>2023-09-25 05:07:23 -0400
commit4bd4976b876060e1766db2531440be4174f2f178 (patch)
treeeefdbe7ac35d7307745aca7c7443524e496fe3b1 /nixos
parent801e33f56288bbbac28acdfe783e9229441fb9a8 (diff)
parent1f34babe84854576c936969f8a879403be9f2515 (diff)
downloadnixlib-4bd4976b876060e1766db2531440be4174f2f178.tar
nixlib-4bd4976b876060e1766db2531440be4174f2f178.tar.gz
nixlib-4bd4976b876060e1766db2531440be4174f2f178.tar.bz2
nixlib-4bd4976b876060e1766db2531440be4174f2f178.tar.lz
nixlib-4bd4976b876060e1766db2531440be4174f2f178.tar.xz
nixlib-4bd4976b876060e1766db2531440be4174f2f178.tar.zst
nixlib-4bd4976b876060e1766db2531440be4174f2f178.zip
Merge pull request #251290 from Majiir/systemd-initrd-networking-features
nixos/network-interfaces-systemd: support `vlans`, `bridges` in systemd-initrd
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix64
-rw-r--r--nixos/tests/all-tests.nix2
-rw-r--r--nixos/tests/systemd-initrd-bridge.nix63
-rw-r--r--nixos/tests/systemd-initrd-vlan.nix59
4 files changed, 162 insertions, 26 deletions
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index dfa883a2c336..679567cbb730 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -173,6 +173,33 @@ let
     }];
   }));
 
+  bridgeNetworks = mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: {
+    netdevs."40-${name}" = {
+      netdevConfig = {
+        Name = name;
+        Kind = "bridge";
+      };
+    };
+    networks = listToAttrs (forEach bridge.interfaces (bi:
+      nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
+        DHCP = mkOverride 0 (dhcpStr false);
+        networkConfig.Bridge = name;
+      } ])));
+  }));
+
+  vlanNetworks = mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: {
+    netdevs."40-${name}" = {
+      netdevConfig = {
+        Name = name;
+        Kind = "vlan";
+      };
+      vlanConfig.Id = vlan.id;
+    };
+    networks."40-${vlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
+      vlan = [ name ];
+    } ]);
+  }));
+
 in
 
 {
@@ -182,7 +209,15 @@ in
     # Note this is if initrd.network.enable, not if
     # initrd.systemd.network.enable. By setting the latter and not the
     # former, the user retains full control over the configuration.
-    boot.initrd.systemd.network = mkMerge [(genericDhcpNetworks true) interfaceNetworks];
+    boot.initrd.systemd.network = mkMerge [
+      (genericDhcpNetworks true)
+      interfaceNetworks
+      bridgeNetworks
+      vlanNetworks
+    ];
+    boot.initrd.availableKernelModules =
+      optional (cfg.bridges != {}) "bridge" ++
+      optional (cfg.vlans != {}) "8021q";
   })
 
   (mkIf cfg.useNetworkd {
@@ -212,19 +247,7 @@ in
       }
       (genericDhcpNetworks false)
       interfaceNetworks
-      (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: {
-        netdevs."40-${name}" = {
-          netdevConfig = {
-            Name = name;
-            Kind = "bridge";
-          };
-        };
-        networks = listToAttrs (forEach bridge.interfaces (bi:
-          nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
-            DHCP = mkOverride 0 (dhcpStr false);
-            networkConfig.Bridge = name;
-          } ])));
-      })))
+      bridgeNetworks
       (mkMerge (flip mapAttrsToList cfg.bonds (name: bond: {
         netdevs."40-${name}" = {
           netdevConfig = {
@@ -377,18 +400,7 @@ in
           } ]);
         };
       })))
-      (mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: {
-        netdevs."40-${name}" = {
-          netdevConfig = {
-            Name = name;
-            Kind = "vlan";
-          };
-          vlanConfig.Id = vlan.id;
-        };
-        networks."40-${vlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
-          vlan = [ name ];
-        } ]);
-      })))
+      vlanNetworks
     ];
 
     # We need to prefill the slaved devices with networking options
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index a9bac3346b90..11cc12f071ce 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -764,6 +764,7 @@ in {
   systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {};
   systemd-credentials-tpm2 = handleTest ./systemd-credentials-tpm2.nix {};
   systemd-escaping = handleTest ./systemd-escaping.nix {};
+  systemd-initrd-bridge = handleTest ./systemd-initrd-bridge.nix {};
   systemd-initrd-btrfs-raid = handleTest ./systemd-initrd-btrfs-raid.nix {};
   systemd-initrd-luks-fido2 = handleTest ./systemd-initrd-luks-fido2.nix {};
   systemd-initrd-luks-keyfile = handleTest ./systemd-initrd-luks-keyfile.nix {};
@@ -778,6 +779,7 @@ in {
   systemd-initrd-networkd = handleTest ./systemd-initrd-networkd.nix {};
   systemd-initrd-networkd-ssh = handleTest ./systemd-initrd-networkd-ssh.nix {};
   systemd-initrd-networkd-openvpn = handleTest ./initrd-network-openvpn { systemdStage1 = true; };
+  systemd-initrd-vlan = handleTest ./systemd-initrd-vlan.nix {};
   systemd-journal = handleTest ./systemd-journal.nix {};
   systemd-machinectl = handleTest ./systemd-machinectl.nix {};
   systemd-networkd = handleTest ./systemd-networkd.nix {};
diff --git a/nixos/tests/systemd-initrd-bridge.nix b/nixos/tests/systemd-initrd-bridge.nix
new file mode 100644
index 000000000000..f48a46ff2b93
--- /dev/null
+++ b/nixos/tests/systemd-initrd-bridge.nix
@@ -0,0 +1,63 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "systemd-initrd-bridge";
+  meta.maintainers = [ lib.maintainers.majiir ];
+
+  # Tests bridge interface configuration in systemd-initrd.
+  #
+  # The 'a' and 'b' nodes are connected to a 'bridge' node through different
+  # links. The 'bridge' node configures a bridge across them. It waits forever
+  # in initrd (stage 1) with networking enabled. 'a' and 'b' ping 'bridge' to
+  # test connectivity with the bridge interface. Then, 'a' pings 'b' to test
+  # the bridge itself.
+
+  nodes = {
+    bridge = { config, lib, ... }: {
+      boot.initrd.systemd.enable = true;
+      boot.initrd.network.enable = true;
+      boot.initrd.systemd.services.boot-blocker = {
+        before = [ "initrd.target" ];
+        wantedBy = [ "initrd.target" ];
+        script = "sleep infinity";
+        serviceConfig.Type = "oneshot";
+      };
+
+      networking.primaryIPAddress = "192.168.1.${toString config.virtualisation.test.nodeNumber}";
+
+      virtualisation.vlans = [ 1 2 ];
+      networking.bridges.br0.interfaces = [ "eth1" "eth2" ];
+
+      networking.interfaces = {
+        eth1.ipv4.addresses = lib.mkForce [];
+        eth2.ipv4.addresses = lib.mkForce [];
+        br0.ipv4.addresses = [{
+          address = config.networking.primaryIPAddress;
+          prefixLength = 24;
+        }];
+      };
+    };
+
+    a = {
+      virtualisation.vlans = [ 1 ];
+    };
+
+    b = { config, ... }: {
+      virtualisation.vlans = [ 2 ];
+      networking.primaryIPAddress = lib.mkForce "192.168.1.${toString config.virtualisation.test.nodeNumber}";
+      networking.interfaces.eth1.ipv4.addresses = lib.mkForce [{
+        address = config.networking.primaryIPAddress;
+        prefixLength = 24;
+      }];
+    };
+  };
+
+  testScript = ''
+    start_all()
+    a.wait_for_unit("network.target")
+    b.wait_for_unit("network.target")
+
+    a.succeed("ping -n -w 10 -c 1 bridge >&2")
+    b.succeed("ping -n -w 10 -c 1 bridge >&2")
+
+    a.succeed("ping -n -w 10 -c 1 b >&2")
+  '';
+})
diff --git a/nixos/tests/systemd-initrd-vlan.nix b/nixos/tests/systemd-initrd-vlan.nix
new file mode 100644
index 000000000000..5060163a047d
--- /dev/null
+++ b/nixos/tests/systemd-initrd-vlan.nix
@@ -0,0 +1,59 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "systemd-initrd-vlan";
+  meta.maintainers = [ lib.maintainers.majiir ];
+
+  # Tests VLAN interface configuration in systemd-initrd.
+  #
+  # Two nodes are configured for a tagged VLAN. (Note that they also still have
+  # their ordinary eth0 and eth1 interfaces, which are not VLAN-tagged.)
+  #
+  # The 'server' node waits forever in initrd (stage 1) with networking
+  # enabled. The 'client' node pings it to test network connectivity.
+
+  nodes = let
+    network = id: {
+      networking = {
+        vlans."eth1.10" = {
+          id = 10;
+          interface = "eth1";
+        };
+        interfaces."eth1.10" = {
+          ipv4.addresses = [{
+            address = "192.168.10.${id}";
+            prefixLength = 24;
+          }];
+        };
+      };
+    };
+  in {
+    # Node that will use initrd networking.
+    server = network "1" // {
+      boot.initrd.systemd.enable = true;
+      boot.initrd.network.enable = true;
+      boot.initrd.systemd.services.boot-blocker = {
+        before = [ "initrd.target" ];
+        wantedBy = [ "initrd.target" ];
+        script = "sleep infinity";
+        serviceConfig.Type = "oneshot";
+      };
+    };
+
+    # Node that will ping the server.
+    client = network "2";
+  };
+
+  testScript = ''
+    start_all()
+    client.wait_for_unit("network.target")
+
+    # Wait for the regular (untagged) interface to be up.
+    def server_is_up(_) -> bool:
+        status, _ = client.execute("ping -n -c 1 server >&2")
+        return status == 0
+    with client.nested("waiting for server to come up"):
+        retry(server_is_up)
+
+    # Try to ping the (tagged) VLAN interface.
+    client.succeed("ping -n -w 10 -c 1 192.168.10.1 >&2")
+  '';
+})