about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorRyan Lahfa <masterancpp@gmail.com>2023-05-26 15:57:01 +0200
committerGitHub <noreply@github.com>2023-05-26 15:57:01 +0200
commit435237d641c9e195572ac40057fc32d62c42f965 (patch)
tree15e38fe6ced3fda50f9af45086a0cbb58e11d4c4 /nixos
parent1aca05b5e347fb456173d35d39fe131deef9ac23 (diff)
parent8e58daad0227de3886bd073e09e47b0308b53e15 (diff)
downloadnixlib-435237d641c9e195572ac40057fc32d62c42f965.tar
nixlib-435237d641c9e195572ac40057fc32d62c42f965.tar.gz
nixlib-435237d641c9e195572ac40057fc32d62c42f965.tar.bz2
nixlib-435237d641c9e195572ac40057fc32d62c42f965.tar.lz
nixlib-435237d641c9e195572ac40057fc32d62c42f965.tar.xz
nixlib-435237d641c9e195572ac40057fc32d62c42f965.tar.zst
nixlib-435237d641c9e195572ac40057fc32d62c42f965.zip
Merge pull request #233350 from GrahamDennis/grahamdennis/testing-networks
nixos/qemu-vm: add option for named network interfaces
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2311.section.md2
-rw-r--r--nixos/lib/testing/driver.nix4
-rw-r--r--nixos/lib/testing/network.nix44
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix32
-rw-r--r--nixos/tests/networking.nix154
5 files changed, 140 insertions, 96 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md
index 33c3e14bb6a3..972a3777bb79 100644
--- a/nixos/doc/manual/release-notes/rl-2311.section.md
+++ b/nixos/doc/manual/release-notes/rl-2311.section.md
@@ -16,4 +16,4 @@
 
 ## Other Notable Changes {#sec-release-23.11-notable-changes}
 
-- Create the first release note entry in this section!
+- A new option was added to the virtualisation module that enables specifying explicitly named network interfaces in QEMU VMs. The existing `virtualisation.vlans` is still supported for cases where the name of the network interface is irrelevant.
diff --git a/nixos/lib/testing/driver.nix b/nixos/lib/testing/driver.nix
index 25759a91dda3..444236efb1e7 100644
--- a/nixos/lib/testing/driver.nix
+++ b/nixos/lib/testing/driver.nix
@@ -12,7 +12,9 @@ let
   };
 
 
-  vlans = map (m: m.virtualisation.vlans) (lib.attrValues config.nodes);
+  vlans = map (m: (
+    m.virtualisation.vlans ++
+    (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
   vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
 
   nodeHostNames =
diff --git a/nixos/lib/testing/network.nix b/nixos/lib/testing/network.nix
index 04ea9a2bc9f7..1edc9e276530 100644
--- a/nixos/lib/testing/network.nix
+++ b/nixos/lib/testing/network.nix
@@ -4,7 +4,7 @@ let
   inherit (lib)
     attrNames concatMap concatMapStrings flip forEach head
     listToAttrs mkDefault mkOption nameValuePair optionalString
-    range types zipListsWith zipLists
+    range toLower types zipListsWith zipLists
     mdDoc
     ;
 
@@ -18,24 +18,41 @@ let
 
   networkModule = { config, nodes, pkgs, ... }:
     let
-      interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
-      interfaces = forEach interfacesNumbered ({ fst, snd }:
-        nameValuePair "eth${toString snd}" {
-          ipv4.addresses =
-            [{
-              address = "192.168.${toString fst}.${toString config.virtualisation.test.nodeNumber}";
+      qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
+
+      # Convert legacy VLANs to named interfaces and merge with explicit interfaces.
+      vlansNumbered = forEach (zipLists config.virtualisation.vlans (range 1 255)) (v: {
+        name = "eth${toString v.snd}";
+        vlan = v.fst;
+        assignIP = true;
+      });
+      explicitInterfaces = lib.mapAttrsToList (n: v: v // { name = n; }) config.virtualisation.interfaces;
+      interfaces = vlansNumbered ++ explicitInterfaces;
+      interfacesNumbered = zipLists interfaces (range 1 255);
+
+      # Automatically assign IP addresses to requested interfaces.
+      assignIPs = lib.filter (i: i.assignIP) interfaces;
+      ipInterfaces = forEach assignIPs (i:
+        nameValuePair i.name { ipv4.addresses =
+          [ { address = "192.168.${toString i.vlan}.${toString config.virtualisation.test.nodeNumber}";
               prefixLength = 24;
             }];
         });
 
+      qemuOptions = lib.flatten (forEach interfacesNumbered ({ fst, snd }:
+        qemu-common.qemuNICFlags snd fst.vlan config.virtualisation.test.nodeNumber));
+      udevRules = forEach interfacesNumbered ({ fst, snd }:
+        # MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive.
+        ''SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="${toLower(qemu-common.qemuNicMac fst.vlan config.virtualisation.test.nodeNumber)}",NAME="${fst.name}"'');
+
       networkConfig =
         {
           networking.hostName = mkDefault config.virtualisation.test.nodeName;
 
-          networking.interfaces = listToAttrs interfaces;
+          networking.interfaces = listToAttrs ipInterfaces;
 
           networking.primaryIPAddress =
-            optionalString (interfaces != [ ]) (head (head interfaces).value.ipv4.addresses).address;
+            optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv4.addresses).address;
 
           # Put the IP addresses of all VMs in this machine's
           # /etc/hosts file.  If a machine has multiple
@@ -51,16 +68,13 @@ let
                     "${config.networking.hostName}.${config.networking.domain} " +
                   "${config.networking.hostName}\n"));
 
-          virtualisation.qemu.options =
-            let qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
-            in
-            flip concatMap interfacesNumbered
-              ({ fst, snd }: qemu-common.qemuNICFlags snd fst config.virtualisation.test.nodeNumber);
+          virtualisation.qemu.options = qemuOptions;
+          boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules;
         };
 
     in
     {
-      key = "ip-address";
+      key = "network-interfaces";
       config = networkConfig // {
         # Expose the networkConfig items for tests like nixops
         # that need to recreate the network config.
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 5f6bf4b39e97..a8ac478aab4c 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -564,7 +564,8 @@ in
     virtualisation.vlans =
       mkOption {
         type = types.listOf types.ints.unsigned;
-        default = [ 1 ];
+        default = if config.virtualisation.interfaces == {} then [ 1 ] else [ ];
+        defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
         example = [ 1 2 ];
         description =
           lib.mdDoc ''
@@ -579,6 +580,35 @@ in
           '';
       };
 
+    virtualisation.interfaces = mkOption {
+      default = {};
+      example = {
+        enp1s0.vlan = 1;
+      };
+      description = lib.mdDoc ''
+        Network interfaces to add to the VM.
+      '';
+      type = with types; attrsOf (submodule {
+        options = {
+          vlan = mkOption {
+            type = types.ints.unsigned;
+            description = lib.mdDoc ''
+              VLAN to which the network interface is connected.
+            '';
+          };
+
+          assignIP = mkOption {
+            type = types.bool;
+            default = false;
+            description = lib.mdDoc ''
+              Automatically assign an IP address to the network interface using the same scheme as
+              virtualisation.vlans.
+            '';
+          };
+        };
+      });
+    };
+
     virtualisation.writableStore =
       mkOption {
         type = types.bool;
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 441d258afc08..99f0b6db32af 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -93,18 +93,19 @@ let
       name = "Static";
       nodes.router = router;
       nodes.client = { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ 1 2 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
+        virtualisation.interfaces.enp2s0.vlan = 2;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
           defaultGateway = "192.168.1.1";
           defaultGateway6 = "fd00:1234:5678:1::1";
-          interfaces.eth1.ipv4.addresses = mkOverride 0 [
+          interfaces.enp1s0.ipv4.addresses = [
             { address = "192.168.1.2"; prefixLength = 24; }
             { address = "192.168.1.3"; prefixLength = 32; }
             { address = "192.168.1.10"; prefixLength = 32; }
           ];
-          interfaces.eth2.ipv4.addresses = mkOverride 0 [
+          interfaces.enp2s0.ipv4.addresses = [
             { address = "192.168.2.2"; prefixLength = 24; }
           ];
         };
@@ -170,12 +171,12 @@ let
         # Disable test driver default config
         networking.interfaces = lib.mkForce {};
         networking.useNetworkd = networkd;
-        virtualisation.vlans = [ 1 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
       };
       testScript = ''
         start_all()
         client.wait_for_unit("multi-user.target")
-        client.wait_until_succeeds("ip addr show dev eth1 | grep '192.168.1'")
+        client.wait_until_succeeds("ip addr show dev enp1s0 | grep '192.168.1'")
         client.shell_interact()
         client.succeed("ping -c 1 192.168.1.1")
         router.succeed("ping -c 1 192.168.1.1")
@@ -187,20 +188,13 @@ let
       name = "SimpleDHCP";
       nodes.router = router;
       nodes.client = { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ 1 2 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
+        virtualisation.interfaces.enp2s0.vlan = 2;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          interfaces.eth1 = {
-            ipv4.addresses = mkOverride 0 [ ];
-            ipv6.addresses = mkOverride 0 [ ];
-            useDHCP = true;
-          };
-          interfaces.eth2 = {
-            ipv4.addresses = mkOverride 0 [ ];
-            ipv6.addresses = mkOverride 0 [ ];
-            useDHCP = true;
-          };
+          interfaces.enp1s0.useDHCP = true;
+          interfaces.enp2s0.useDHCP = true;
         };
       };
       testScript = { ... }:
@@ -211,10 +205,10 @@ let
           router.wait_for_unit("network-online.target")
 
           with subtest("Wait until we have an ip address on each interface"):
-              client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
-              client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
-              client.wait_until_succeeds("ip addr show dev eth2 | grep -q '192.168.2'")
-              client.wait_until_succeeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'")
+              client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
+              client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
+              client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q '192.168.2'")
+              client.wait_until_succeeds("ip addr show dev enp2s0 | grep -q 'fd00:1234:5678:2:'")
 
           with subtest("Test vlan 1"):
               client.wait_until_succeeds("ping -c 1 192.168.1.1")
@@ -243,16 +237,15 @@ let
       name = "OneInterfaceDHCP";
       nodes.router = router;
       nodes.client = { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ 1 2 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
+        virtualisation.interfaces.enp2s0.vlan = 2;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          interfaces.eth1 = {
-            ipv4.addresses = mkOverride 0 [ ];
+          interfaces.enp1s0 = {
             mtu = 1343;
             useDHCP = true;
           };
-          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
         };
       };
       testScript = { ... }:
@@ -264,10 +257,10 @@ let
               router.wait_for_unit("network.target")
 
           with subtest("Wait until we have an ip address on each interface"):
-              client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
+              client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
 
           with subtest("ensure MTU is set"):
-              assert "mtu 1343" in client.succeed("ip link show dev eth1")
+              assert "mtu 1343" in client.succeed("ip link show dev enp1s0")
 
           with subtest("Test vlan 1"):
               client.wait_until_succeeds("ping -c 1 192.168.1.1")
@@ -286,16 +279,15 @@ let
     };
     bond = let
       node = address: { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ 1 2 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
+        virtualisation.interfaces.enp2s0.vlan = 2;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
           bonds.bond0 = {
-            interfaces = [ "eth1" "eth2" ];
+            interfaces = [ "enp1s0" "enp2s0" ];
             driverOptions.mode = "802.3ad";
           };
-          interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
-          interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
           interfaces.bond0.ipv4.addresses = mkOverride 0
             [ { inherit address; prefixLength = 30; } ];
         };
@@ -326,12 +318,11 @@ let
     };
     bridge = let
       node = { address, vlan }: { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ vlan ];
+        virtualisation.interfaces.enp1s0.vlan = vlan;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          interfaces.eth1.ipv4.addresses = mkOverride 0
-            [ { inherit address; prefixLength = 24; } ];
+          interfaces.enp1s0.ipv4.addresses = [ { inherit address; prefixLength = 24; } ];
         };
       };
     in {
@@ -339,11 +330,12 @@ let
       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; {
-        virtualisation.vlans = [ 1 2 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
+        virtualisation.interfaces.enp2s0.vlan = 2;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          bridges.bridge.interfaces = [ "eth1" "eth2" ];
+          bridges.bridge.interfaces = [ "enp1s0" "enp2s0" ];
           interfaces.eth1.ipv4.addresses = mkOverride 0 [ ];
           interfaces.eth2.ipv4.addresses = mkOverride 0 [ ];
           interfaces.bridge.ipv4.addresses = mkOverride 0
@@ -377,7 +369,7 @@ let
       nodes.router = router;
       nodes.client = { pkgs, ... }: with pkgs.lib; {
         environment.systemPackages = [ pkgs.iptables ]; # to debug firewall rules
-        virtualisation.vlans = [ 1 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
@@ -385,14 +377,9 @@ let
           # reverse path filtering rules for the macvlan interface seem
           # to be incorrect, causing the test to fail. Disable temporarily.
           firewall.checkReversePath = false;
-          macvlans.macvlan.interface = "eth1";
-          interfaces.eth1 = {
-            ipv4.addresses = mkOverride 0 [ ];
-            useDHCP = true;
-          };
-          interfaces.macvlan = {
-            useDHCP = true;
-          };
+          macvlans.macvlan.interface = "enp1s0";
+          interfaces.enp1s0.useDHCP = true;
+          interfaces.macvlan.useDHCP = true;
         };
       };
       testScript = { ... }:
@@ -404,7 +391,7 @@ let
               router.wait_for_unit("network.target")
 
           with subtest("Wait until we have an ip address on each interface"):
-              client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'")
+              client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q '192.168.1'")
               client.wait_until_succeeds("ip addr show dev macvlan | grep -q '192.168.1'")
 
           with subtest("Print lots of diagnostic information"):
@@ -431,23 +418,22 @@ let
     fou = {
       name = "foo-over-udp";
       nodes.machine = { ... }: {
-        virtualisation.vlans = [ 1 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          interfaces.eth1.ipv4.addresses = mkOverride 0
-            [ { address = "192.168.1.1"; prefixLength = 24; } ];
+          interfaces.enp1s0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
           fooOverUDP = {
             fou1 = { port = 9001; };
             fou2 = { port = 9002; protocol = 41; };
             fou3 = mkIf (!networkd)
               { port = 9003; local.address = "192.168.1.1"; };
             fou4 = mkIf (!networkd)
-              { port = 9004; local = { address = "192.168.1.1"; dev = "eth1"; }; };
+              { port = 9004; local = { address = "192.168.1.1"; dev = "enp1s0"; }; };
           };
         };
         systemd.services = {
-          fou3-fou-encap.after = optional (!networkd) "network-addresses-eth1.service";
+          fou3-fou-encap.after = optional (!networkd) "network-addresses-enp1s0.service";
         };
       };
       testScript = { ... }:
@@ -470,22 +456,22 @@ let
               "gue": None,
               "family": "inet",
               "local": "192.168.1.1",
-              "dev": "eth1",
+              "dev": "enp1s0",
           } in fous, "fou4 exists"
         '';
     };
     sit = let
       node = { address4, remote, address6 }: { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ 1 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
           sits.sit = {
             inherit remote;
             local = address4;
-            dev = "eth1";
+            dev = "enp1s0";
           };
-          interfaces.eth1.ipv4.addresses = mkOverride 0
+          interfaces.enp1s0.ipv4.addresses = mkOverride 0
             [ { address = address4; prefixLength = 24; } ];
           interfaces.sit.ipv6.addresses = mkOverride 0
             [ { address = address6; prefixLength = 64; } ];
@@ -685,10 +671,10 @@ let
     vlan-ping = let
         baseIP = number: "10.10.10.${number}";
         vlanIP = number: "10.1.1.${number}";
-        baseInterface = "eth1";
+        baseInterface = "enp1s0";
         vlanInterface = "vlan42";
         node = number: {pkgs, ... }: with pkgs.lib; {
-          virtualisation.vlans = [ 1 ];
+          virtualisation.interfaces.enp1s0.vlan = 1;
           networking = {
             #useNetworkd = networkd;
             useDHCP = false;
@@ -785,12 +771,12 @@ let
     privacy = {
       name = "Privacy";
       nodes.router = { ... }: {
-        virtualisation.vlans = [ 1 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
         boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          interfaces.eth1.ipv6.addresses = singleton {
+          interfaces.enp1s0.ipv6.addresses = singleton {
             address = "fd00:1234:5678:1::1";
             prefixLength = 64;
           };
@@ -798,7 +784,7 @@ let
         services.radvd = {
           enable = true;
           config = ''
-            interface eth1 {
+            interface enp1s0 {
               AdvSendAdvert on;
               AdvManagedFlag on;
               AdvOtherConfigFlag on;
@@ -812,11 +798,11 @@ let
         };
       };
       nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ 1 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          interfaces.eth1 = {
+          interfaces.enp1s0 = {
             tempAddress = "default";
             ipv4.addresses = mkOverride 0 [ ];
             ipv6.addresses = mkOverride 0 [ ];
@@ -825,11 +811,11 @@ let
         };
       };
       nodes.client = { pkgs, ... }: with pkgs.lib; {
-        virtualisation.vlans = [ 1 ];
+        virtualisation.interfaces.enp1s0.vlan = 1;
         networking = {
           useNetworkd = networkd;
           useDHCP = false;
-          interfaces.eth1 = {
+          interfaces.enp1s0 = {
             tempAddress = "enabled";
             ipv4.addresses = mkOverride 0 [ ];
             ipv6.addresses = mkOverride 0 [ ];
@@ -847,9 +833,9 @@ let
 
           with subtest("Wait until we have an ip address"):
               client_with_privacy.wait_until_succeeds(
-                  "ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"
+                  "ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'"
               )
-              client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'")
+              client.wait_until_succeeds("ip addr show dev enp1s0 | grep -q 'fd00:1234:5678:1:'")
 
           with subtest("Test vlan 1"):
               client_with_privacy.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
@@ -947,7 +933,7 @@ let
             ), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue)
       '';
     };
-    rename = {
+    rename = if networkd then {
       name = "RenameInterface";
       nodes.machine = { pkgs, ... }: {
         virtualisation.vlans = [ 1 ];
@@ -955,23 +941,20 @@ let
           useNetworkd = networkd;
           useDHCP = false;
         };
-      } //
-      (if networkd
-       then { systemd.network.links."10-custom_name" = {
-                matchConfig.MACAddress = "52:54:00:12:01:01";
-                linkConfig.Name = "custom_name";
-              };
-            }
-       else { boot.initrd.services.udev.rules = ''
-               SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="52:54:00:12:01:01", KERNEL=="eth*", NAME="custom_name"
-              '';
-            });
+        systemd.network.links."10-custom_name" = {
+          matchConfig.MACAddress = "52:54:00:12:01:01";
+          linkConfig.Name = "custom_name";
+        };
+      };
       testScript = ''
         machine.succeed("udevadm settle")
         print(machine.succeed("ip link show dev custom_name"))
       '';
-    };
+    } else {
+      name = "RenameInterface";
       nodes = { };
+      testScript = "";
+    };
     # even with disabled networkd, systemd.network.links should work
     # (as it's handled by udev, not networkd)
     link = {
@@ -1015,6 +998,21 @@ let
         machine.fail("ip address show wlan0 | grep -q ${testMac}")
       '';
     };
+    caseSensitiveRenaming = {
+      name = "CaseSensitiveRenaming";
+      nodes.machine = { pkgs, ... }: {
+        virtualisation.interfaces.enCustom.vlan = 11;
+        networking = {
+          useNetworkd = networkd;
+          useDHCP = false;
+        };
+      };
+      testScript = ''
+        machine.succeed("udevadm settle")
+        print(machine.succeed("ip link show dev enCustom"))
+        machine.wait_until_succeeds("ip link show dev enCustom | grep -q '52:54:00:12:0b:01")
+      '';
+    };
   };
 
 in mapAttrs (const (attrs: makeTest (attrs // {