base16-schemes: unstable-2024-06-21 -> unstable-2024-11-12
[NixPkgs.git] / nixos / modules / tasks / network-interfaces.nix
blobc9ea31a85beefefaeb4220c740d52150c97c7d26
1 { config, options, lib, pkgs, utils, ... }:
3 with lib;
4 with utils;
6 let
8   cfg = config.networking;
9   opt = options.networking;
10   interfaces = attrValues cfg.interfaces;
11   hasVirtuals = any (i: i.virtual) interfaces;
12   hasSits = cfg.sits != { };
13   hasGres = cfg.greTunnels != { };
14   hasBonds = cfg.bonds != { };
15   hasFous = cfg.fooOverUDP != { }
16     || filterAttrs (_: s: s.encapsulation != null) cfg.sits != { };
18   slaves = concatMap (i: i.interfaces) (attrValues cfg.bonds)
19     ++ concatMap (i: i.interfaces) (attrValues cfg.bridges)
20     ++ concatMap (i: attrNames (filterAttrs (name: config: ! (config.type == "internal" || hasAttr name cfg.interfaces)) i.interfaces)) (attrValues cfg.vswitches);
22   slaveIfs = map (i: cfg.interfaces.${i}) (filter (i: cfg.interfaces ? ${i}) slaves);
24   rstpBridges = flip filterAttrs cfg.bridges (_: { rstp, ... }: rstp);
26   needsMstpd = rstpBridges != { };
28   bridgeStp = optional needsMstpd (pkgs.writeTextFile {
29     name = "bridge-stp";
30     executable = true;
31     destination = "/bin/bridge-stp";
32     text = ''
33       #!${pkgs.runtimeShell} -e
34       export PATH="${pkgs.mstpd}/bin"
36       BRIDGES=(${concatStringsSep " " (attrNames rstpBridges)})
37       for BRIDGE in $BRIDGES; do
38         if [ "$BRIDGE" = "$1" ]; then
39           if [ "$2" = "start" ]; then
40             mstpctl addbridge "$BRIDGE"
41             exit 0
42           elif [ "$2" = "stop" ]; then
43             mstpctl delbridge "$BRIDGE"
44             exit 0
45           fi
46           exit 1
47         fi
48       done
49       exit 1
50     '';
51   });
53   # We must escape interfaces due to the systemd interpretation
54   subsystemDevice = interface:
55     "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
57   addrOpts = v:
58     assert v == 4 || v == 6;
59     { options = {
60         address = mkOption {
61           type = types.str;
62           description = ''
63             IPv${toString v} address of the interface. Leave empty to configure the
64             interface using DHCP.
65           '';
66         };
68         prefixLength = mkOption {
69           type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
70           description = ''
71             Subnet mask of the interface, specified as the number of
72             bits in the prefix (`${if v == 4 then "24" else "64"}`).
73           '';
74         };
75       };
76     };
78   routeOpts = v:
79   { options = {
80       address = mkOption {
81         type = types.str;
82         description = "IPv${toString v} address of the network.";
83       };
85       prefixLength = mkOption {
86         type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
87         description = ''
88           Subnet mask of the network, specified as the number of
89           bits in the prefix (`${if v == 4 then "24" else "64"}`).
90         '';
91       };
93       type = mkOption {
94         type = types.nullOr (types.enum [
95           "unicast" "local" "broadcast" "multicast"
96         ]);
97         default = null;
98         description = ''
99           Type of the route.  See the `Route types` section
100           in the `ip-route(8)` manual page for the details.
102           Note that `prohibit`, `blackhole`,
103           `unreachable`, and `throw` cannot
104           be configured per device, so they are not available here. Similarly,
105           `nat` hasn't been supported since kernel 2.6.
106         '';
107       };
109       via = mkOption {
110         type = types.nullOr types.str;
111         default = null;
112         description = "IPv${toString v} address of the next hop.";
113       };
115       options = mkOption {
116         type = types.attrsOf types.str;
117         default = { };
118         example = { mtu = "1492"; window = "524288"; };
119         description = ''
120           Other route options. See the symbol `OPTIONS`
121           in the `ip-route(8)` manual page for the details.
122           You may also specify `metric`,
123           `src`, `protocol`,
124           `scope`, `from`
125           and `table`, which are technically
126           not route options, in the sense used in the manual.
127         '';
128       };
130     };
131   };
133   gatewayCoerce = address: { inherit address; };
135   gatewayOpts = { ... }: {
137     options = {
139       address = mkOption {
140         type = types.str;
141         description = "The default gateway address.";
142       };
144       interface = mkOption {
145         type = types.nullOr types.str;
146         default = null;
147         example = "enp0s3";
148         description = "The default gateway interface.";
149       };
151       metric = mkOption {
152         type = types.nullOr types.int;
153         default = null;
154         example = 42;
155         description = "The default gateway metric/preference.";
156       };
158     };
160   };
162   interfaceOpts = { name, ... }: {
164     options = {
165       name = mkOption {
166         example = "eth0";
167         type = types.str;
168         description = "Name of the interface.";
169       };
171       tempAddress = mkOption {
172         type = types.enum (lib.attrNames tempaddrValues);
173         default = cfg.tempAddresses;
174         defaultText = literalExpression ''config.networking.tempAddresses'';
175         description = ''
176           When IPv6 is enabled with SLAAC, this option controls the use of
177           temporary address (aka privacy extensions) on this
178           interface. This is used to reduce tracking.
180           See also the global option
181           [](#opt-networking.tempAddresses), which
182           applies to all interfaces where this is not set.
184           Possible values are:
185           ${tempaddrDoc}
186         '';
187       };
189       useDHCP = mkOption {
190         type = types.nullOr types.bool;
191         default = null;
192         description = ''
193           Whether this interface should be configured with DHCP. Overrides the
194           default set by {option}`networking.useDHCP`. If `null` (the default),
195           DHCP is enabled if the interface has no IPv4 addresses configured
196           with {option}`networking.interfaces.<name>.ipv4.addresses`, and
197           disabled otherwise.
198         '';
199       };
201       ipv4.addresses = mkOption {
202         default = [ ];
203         example = [
204           { address = "10.0.0.1"; prefixLength = 16; }
205           { address = "192.168.1.1"; prefixLength = 24; }
206         ];
207         type = with types; listOf (submodule (addrOpts 4));
208         description = ''
209           List of IPv4 addresses that will be statically assigned to the interface.
210         '';
211       };
213       ipv6.addresses = mkOption {
214         default = [ ];
215         example = [
216           { address = "fdfd:b3f0:482::1"; prefixLength = 48; }
217           { address = "2001:1470:fffd:2098::e006"; prefixLength = 64; }
218         ];
219         type = with types; listOf (submodule (addrOpts 6));
220         description = ''
221           List of IPv6 addresses that will be statically assigned to the interface.
222         '';
223       };
225       ipv4.routes = mkOption {
226         default = [];
227         example = [
228           { address = "10.0.0.0"; prefixLength = 16; }
229           { address = "192.168.2.0"; prefixLength = 24; via = "192.168.1.1"; }
230         ];
231         type = with types; listOf (submodule (routeOpts 4));
232         description = ''
233           List of extra IPv4 static routes that will be assigned to the interface.
235           ::: {.warning}
236           If the route type is the default `unicast`, then the scope
237           is set differently depending on the value of {option}`networking.useNetworkd`:
238           the script-based backend sets it to `link`, while networkd sets
239           it to `global`.
240           :::
242           If you want consistency between the two implementations,
243           set the scope of the route manually with
244           `networking.interfaces.eth0.ipv4.routes = [{ options.scope = "global"; }]`
245           for example.
246         '';
247       };
249       ipv6.routes = mkOption {
250         default = [];
251         example = [
252           { address = "fdfd:b3f0::"; prefixLength = 48; }
253           { address = "2001:1470:fffd:2098::"; prefixLength = 64; via = "fdfd:b3f0::1"; }
254         ];
255         type = with types; listOf (submodule (routeOpts 6));
256         description = ''
257           List of extra IPv6 static routes that will be assigned to the interface.
258         '';
259       };
261       macAddress = mkOption {
262         default = null;
263         example = "00:11:22:33:44:55";
264         type = types.nullOr (types.str);
265         description = ''
266           MAC address of the interface. Leave empty to use the default.
267         '';
268       };
270       mtu = mkOption {
271         default = null;
272         example = 9000;
273         type = types.nullOr types.int;
274         description = ''
275           MTU size for packets leaving the interface. Leave empty to use the default.
276         '';
277       };
279       virtual = mkOption {
280         default = false;
281         type = types.bool;
282         description = ''
283           Whether this interface is virtual and should be created by tunctl.
284           This is mainly useful for creating bridges between a host and a virtual
285           network such as VPN or a virtual machine.
286         '';
287       };
289       virtualOwner = mkOption {
290         default = "root";
291         type = types.str;
292         description = ''
293           In case of a virtual device, the user who owns it.
294         '';
295       };
297       virtualType = mkOption {
298         default = if hasPrefix "tun" name then "tun" else "tap";
299         defaultText = literalExpression ''if hasPrefix "tun" name then "tun" else "tap"'';
300         type = with types; enum [ "tun" "tap" ];
301         description = ''
302           The type of interface to create.
303           The default is TUN for an interface name starting
304           with "tun", otherwise TAP.
305         '';
306       };
308       proxyARP = mkOption {
309         default = false;
310         type = types.bool;
311         description = ''
312           Turn on proxy_arp for this device.
313           This is mainly useful for creating pseudo-bridges between a real
314           interface and a virtual network such as VPN or a virtual machine for
315           interfaces that don't support real bridging (most wlan interfaces).
316           As ARP proxying acts slightly above the link-layer, below-ip traffic
317           isn't bridged, so things like DHCP won't work. The advantage above
318           using NAT lies in the fact that no IP addresses are shared, so all
319           hosts are reachable/routeable.
321           WARNING: turns on ip-routing, so if you have multiple interfaces, you
322           should think of the consequence and setup firewall rules to limit this.
323         '';
324       };
326       wakeOnLan = {
327         enable = mkOption {
328           type = types.bool;
329           default = false;
330           description = "Whether to enable wol on this interface.";
331         };
332         policy = mkOption {
333           type = with types; listOf (
334             enum ["phy" "unicast" "multicast" "broadcast" "arp" "magic" "secureon"]
335           );
336           default = ["magic"];
337           description = ''
338             The [Wake-on-LAN policy](https://www.freedesktop.org/software/systemd/man/systemd.link.html#WakeOnLan=)
339             to set for the device.
341             The options are
342             - `phy`: Wake on PHY activity
343             - `unicast`: Wake on unicast messages
344             - `multicast`: Wake on multicast messages
345             - `broadcast`: Wake on broadcast messages
346             - `arp`: Wake on ARP
347             - `magic`: Wake on receipt of a magic packet
348           '';
349         };
350       };
351     };
353     config = {
354       name = mkDefault name;
355     };
357     # Renamed or removed options
358     imports =
359       let
360         defined = x: x != "_mkMergedOptionModule";
361       in [
362         (mkChangedOptionModule [ "preferTempAddress" ] [ "tempAddress" ]
363          (config:
364           let bool = getAttrFromPath [ "preferTempAddress" ] config;
365           in if bool then "default" else "enabled"
366         ))
367         (mkRenamedOptionModule [ "ip4" ] [ "ipv4" "addresses"])
368         (mkRenamedOptionModule [ "ip6" ] [ "ipv6" "addresses"])
369         (mkRemovedOptionModule [ "subnetMask" ] ''
370           Supply a prefix length instead; use option
371           networking.interfaces.<name>.ipv{4,6}.addresses'')
372         (mkMergedOptionModule
373           [ [ "ipAddress" ] [ "prefixLength" ] ]
374           [ "ipv4" "addresses" ]
375           (cfg: with cfg;
376             optional (defined ipAddress && defined prefixLength)
377             { address = ipAddress; prefixLength = prefixLength; }))
378         (mkMergedOptionModule
379           [ [ "ipv6Address" ] [ "ipv6PrefixLength" ] ]
380           [ "ipv6" "addresses" ]
381           (cfg: with cfg;
382             optional (defined ipv6Address && defined ipv6PrefixLength)
383             { address = ipv6Address; prefixLength = ipv6PrefixLength; }))
385         ({ options.warnings = options.warnings; options.assertions = options.assertions; })
386       ];
388   };
390   vswitchInterfaceOpts = {name, ...}: {
392     options = {
394       name = mkOption {
395         description = "Name of the interface";
396         example = "eth0";
397         type = types.str;
398       };
400       vlan = mkOption {
401         description = "Vlan tag to apply to interface";
402         example = 10;
403         type = types.nullOr types.int;
404         default = null;
405       };
407       type = mkOption {
408         description = "Openvswitch type to assign to interface";
409         example = "internal";
410         type = types.nullOr types.str;
411         default = null;
412       };
413     };
414   };
416   hexChars = stringToCharacters "0123456789abcdef";
418   isHexString = s: all (c: elem c hexChars) (stringToCharacters (toLower s));
420   tempaddrValues = {
421     disabled = {
422       sysctl = "0";
423       description = "completely disable IPv6 temporary addresses";
424     };
425     enabled = {
426       sysctl = "1";
427       description = "generate IPv6 temporary addresses but still use EUI-64 addresses as source addresses";
428     };
429     default = {
430       sysctl = "2";
431       description = "generate IPv6 temporary addresses and use these as source addresses in routing";
432     };
433   };
434   tempaddrDoc = concatStringsSep "\n"
435     (mapAttrsToList
436       (name: { description, ... }: ''- `"${name}"` to ${description};'')
437       tempaddrValues);
439   hostidFile = pkgs.runCommand "gen-hostid" { preferLocalBuild = true; } ''
440       hi="${cfg.hostId}"
441       ${if pkgs.stdenv.hostPlatform.isBigEndian then ''
442         echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > $out
443       '' else ''
444         echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > $out
445       ''}
446     '';
452   ###### interface
454   options = {
456     networking.hostName = mkOption {
457       default = config.system.nixos.distroId;
458       defaultText = literalExpression "config.system.nixos.distroId";
459       # Only allow hostnames without the domain name part (i.e. no FQDNs, see
460       # e.g. "man 5 hostname") and require valid DNS labels (recommended
461       # syntax). Note: We also allow underscores for compatibility/legacy
462       # reasons (as undocumented feature):
463       type = types.strMatching
464         "^$|^[[:alnum:]]([[:alnum:]_-]{0,61}[[:alnum:]])?$";
465       description = ''
466         The name of the machine. Leave it empty if you want to obtain it from a
467         DHCP server (if using DHCP). The hostname must be a valid DNS label (see
468         RFC 1035 section 2.3.1: "Preferred name syntax", RFC 1123 section 2.1:
469         "Host Names and Numbers") and as such must not contain the domain part.
470         This means that the hostname must start with a letter or digit,
471         end with a letter or digit, and have as interior characters only
472         letters, digits, and hyphen. The maximum length is 63 characters.
473         Additionally it is recommended to only use lower-case characters.
474         If (e.g. for legacy reasons) a FQDN is required as the Linux kernel
475         network node hostname (uname --nodename) the option
476         boot.kernel.sysctl."kernel.hostname" can be used as a workaround (but
477         the 64 character limit still applies).
479         WARNING: Do not use underscores (_) or you may run into unexpected issues.
480       '';
481        # warning until the issues in https://github.com/NixOS/nixpkgs/pull/138978
482        # are resolved
483     };
485     networking.fqdn = mkOption {
486       readOnly = true;
487       type = types.str;
488       default = if (cfg.hostName != "" && cfg.domain != null)
489         then "${cfg.hostName}.${cfg.domain}"
490         else throw ''
491           The FQDN is required but cannot be determined. Please make sure that
492           both networking.hostName and networking.domain are set properly.
493         '';
494       defaultText = literalExpression ''"''${networking.hostName}.''${networking.domain}"'';
495       description = ''
496         The fully qualified domain name (FQDN) of this host. It is the result
497         of combining `networking.hostName` and `networking.domain.` Using this
498         option will result in an evaluation error if the hostname is empty or
499         no domain is specified.
501         Modules that accept a mere `networking.hostName` but prefer a fully qualified
502         domain name may use `networking.fqdnOrHostName` instead.
503       '';
504     };
506     networking.fqdnOrHostName = mkOption {
507       readOnly = true;
508       type = types.str;
509       default = if cfg.domain == null then cfg.hostName else cfg.fqdn;
510       defaultText = literalExpression ''
511         if cfg.domain == null then cfg.hostName else cfg.fqdn
512       '';
513       description = ''
514         Either the fully qualified domain name (FQDN), or just the host name if
515         it does not exists.
517         This is a convenience option for modules to read instead of `fqdn` when
518         a mere `hostName` is also an acceptable value; this option does not
519         throw an error when `domain` is unset.
520       '';
521     };
523     networking.hostId = mkOption {
524       default = null;
525       example = "4e98920d";
526       type = types.nullOr types.str;
527       description = ''
528         The 32-bit host ID of the machine, formatted as 8 hexadecimal characters.
530         You should try to make this ID unique among your machines. You can
531         generate a random 32-bit ID using the following commands:
533         `head -c 8 /etc/machine-id`
535         (this derives it from the machine-id that systemd generates) or
537         `head -c4 /dev/urandom | od -A none -t x4`
539         The primary use case is to ensure when using ZFS that a pool isn't imported
540         accidentally on a wrong machine.
541       '';
542     };
544     networking.enableIPv6 = mkOption {
545       default = true;
546       type = types.bool;
547       description = ''
548         Whether to enable support for IPv6.
549       '';
550     };
552     networking.defaultGateway = mkOption {
553       default = null;
554       example = {
555         address = "131.211.84.1";
556         interface = "enp3s0";
557       };
558       type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
559       description = ''
560         The default gateway. It can be left empty if it is auto-detected through DHCP.
561         It can be specified as a string or an option set along with a network interface.
562       '';
563     };
565     networking.defaultGateway6 = mkOption {
566       default = null;
567       example = {
568         address = "2001:4d0:1e04:895::1";
569         interface = "enp3s0";
570       };
571       type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
572       description = ''
573         The default ipv6 gateway. It can be left empty if it is auto-detected through DHCP.
574         It can be specified as a string or an option set along with a network interface.
575       '';
576     };
578     networking.defaultGatewayWindowSize = mkOption {
579       default = null;
580       example = 524288;
581       type = types.nullOr types.int;
582       description = ''
583         The window size of the default gateway. It limits maximal data bursts that TCP peers
584         are allowed to send to us.
585       '';
586     };
588     networking.nameservers = mkOption {
589       type = types.listOf types.str;
590       default = [];
591       example = ["130.161.158.4" "130.161.33.17"];
592       description = ''
593         The list of nameservers.  It can be left empty if it is auto-detected through DHCP.
594       '';
595     };
597     networking.search = mkOption {
598       default = [];
599       example = [ "example.com" "home.arpa" ];
600       type = types.listOf types.str;
601       description = ''
602         The list of search paths used when resolving domain names.
603       '';
604     };
606     networking.domain = mkOption {
607       default = null;
608       example = "home.arpa";
609       type = types.nullOr types.str;
610       description = ''
611         The domain.  It can be left empty if it is auto-detected through DHCP.
612       '';
613     };
615     networking.useHostResolvConf = mkOption {
616       type = types.bool;
617       default = false;
618       description = ''
619         In containers, whether to use the
620         {file}`resolv.conf` supplied by the host.
621       '';
622     };
624     networking.localCommands = mkOption {
625       type = types.lines;
626       default = "";
627       example = "text=anything; echo You can put $text here.";
628       description = ''
629         Shell commands to be executed at the end of the
630         `network-setup` systemd service.  Note that if
631         you are using DHCP to obtain the network configuration,
632         interfaces may not be fully configured yet.
633       '';
634     };
636     networking.interfaces = mkOption {
637       default = {};
638       example =
639         { eth0.ipv4.addresses = [ {
640             address = "131.211.84.78";
641             prefixLength = 25;
642           } ];
643         };
644       description = ''
645         The configuration for each network interface.
647         Please note that {option}`systemd.network.netdevs` has more features
648         and is better maintained. When building new things, it is advised to
649         use that instead.
650       '';
651       type = with types; attrsOf (submodule interfaceOpts);
652     };
654     networking.vswitches = mkOption {
655       default = { };
656       example =
657         { vs0.interfaces = { eth0 = { }; lo1 = { type="internal"; }; };
658           vs1.interfaces = [ { name = "eth2"; } { name = "lo2"; type="internal"; } ];
659         };
660       description = ''
661           This option allows you to define Open vSwitches that connect
662           physical networks together. The value of this option is an
663           attribute set. Each attribute specifies a vswitch, with the
664           attribute name specifying the name of the vswitch's network
665           interface.
666         '';
668       type = with types; attrsOf (submodule {
670         options = {
672           interfaces = mkOption {
673             description = "The physical network interfaces connected by the vSwitch.";
674             type = with types; attrsOf (submodule vswitchInterfaceOpts);
675           };
677           controllers = mkOption {
678             type = types.listOf types.str;
679             default = [];
680             example = [ "ptcp:6653:[::1]" ];
681             description = ''
682               Specify the controller targets. For the allowed options see `man 8 ovs-vsctl`.
683             '';
684           };
686           openFlowRules = mkOption {
687             type = types.lines;
688             default = "";
689             example = ''
690               actions=normal
691             '';
692             description = ''
693               OpenFlow rules to insert into the Open vSwitch. All `openFlowRules` are
694               loaded with `ovs-ofctl` within one atomic operation.
695             '';
696           };
698           # TODO: custom "openflow version" type, with list from existing openflow protocols
699           supportedOpenFlowVersions = mkOption {
700             type = types.listOf types.str;
701             example = [ "OpenFlow10" "OpenFlow13" "OpenFlow14" ];
702             default = [ "OpenFlow13" ];
703             description = ''
704               Supported versions to enable on this switch.
705             '';
706           };
708           # TODO: use same type as elements from supportedOpenFlowVersions
709           openFlowVersion = mkOption {
710             type = types.str;
711             default = "OpenFlow13";
712             description = ''
713               Version of OpenFlow protocol to use when communicating with the switch internally (e.g. with `openFlowRules`).
714             '';
715           };
717           extraOvsctlCmds = mkOption {
718             type = types.lines;
719             default = "";
720             example = ''
721               set-fail-mode <switch_name> secure
722               set Bridge <switch_name> stp_enable=true
723             '';
724             description = ''
725               Commands to manipulate the Open vSwitch database. Every line executed with `ovs-vsctl`.
726               All commands are bundled together with the operations for adding the interfaces
727               into one atomic operation.
728             '';
729           };
731         };
733       });
735     };
737     networking.bridges = mkOption {
738       default = { };
739       example =
740         { br0.interfaces = [ "eth0" "eth1" ];
741           br1.interfaces = [ "eth2" "wlan0" ];
742         };
743       description = ''
744           This option allows you to define Ethernet bridge devices
745           that connect physical networks together.  The value of this
746           option is an attribute set.  Each attribute specifies a
747           bridge, with the attribute name specifying the name of the
748           bridge's network interface.
749         '';
751       type = with types; attrsOf (submodule {
753         options = {
755           interfaces = mkOption {
756             example = [ "eth0" "eth1" ];
757             type = types.listOf types.str;
758             description = "The physical network interfaces connected by the bridge.";
759           };
761           rstp = mkOption {
762             default = false;
763             type = types.bool;
764             description = "Whether the bridge interface should enable rstp.";
765           };
767         };
769       });
771     };
773     networking.bonds =
774       let
775         driverOptionsExample =  ''
776           {
777             miimon = "100";
778             mode = "active-backup";
779           }
780         '';
781       in mkOption {
782         default = { };
783         example = literalExpression ''
784           {
785             bond0 = {
786               interfaces = [ "eth0" "wlan0" ];
787               driverOptions = ${driverOptionsExample};
788             };
789             anotherBond.interfaces = [ "enp4s0f0" "enp4s0f1" "enp5s0f0" "enp5s0f1" ];
790           }
791         '';
792         description = ''
793           This option allows you to define bond devices that aggregate multiple,
794           underlying networking interfaces together. The value of this option is
795           an attribute set. Each attribute specifies a bond, with the attribute
796           name specifying the name of the bond's network interface
797         '';
799         type = with types; attrsOf (submodule {
801           options = {
803             interfaces = mkOption {
804               example = [ "enp4s0f0" "enp4s0f1" "wlan0" ];
805               type = types.listOf types.str;
806               description = "The interfaces to bond together";
807             };
809             driverOptions = mkOption {
810               type = types.attrsOf types.str;
811               default = {};
812               example = literalExpression driverOptionsExample;
813               description = ''
814                 Options for the bonding driver.
815                 Documentation can be found in
816                 <https://www.kernel.org/doc/Documentation/networking/bonding.txt>
817               '';
819             };
821             lacp_rate = mkOption {
822               default = null;
823               example = "fast";
824               type = types.nullOr types.str;
825               description = ''
826                 DEPRECATED, use `driverOptions`.
827                 Option specifying the rate in which we'll ask our link partner
828                 to transmit LACPDU packets in 802.3ad mode.
829               '';
830             };
832             miimon = mkOption {
833               default = null;
834               example = 100;
835               type = types.nullOr types.int;
836               description = ''
837                 DEPRECATED, use `driverOptions`.
838                 Miimon is the number of millisecond in between each round of polling
839                 by the device driver for failed links. By default polling is not
840                 enabled and the driver is trusted to properly detect and handle
841                 failure scenarios.
842               '';
843             };
845             mode = mkOption {
846               default = null;
847               example = "active-backup";
848               type = types.nullOr types.str;
849               description = ''
850                 DEPRECATED, use `driverOptions`.
851                 The mode which the bond will be running. The default mode for
852                 the bonding driver is balance-rr, optimizing for throughput.
853                 More information about valid modes can be found at
854                 https://www.kernel.org/doc/Documentation/networking/bonding.txt
855               '';
856             };
858             xmit_hash_policy = mkOption {
859               default = null;
860               example = "layer2+3";
861               type = types.nullOr types.str;
862               description = ''
863                 DEPRECATED, use `driverOptions`.
864                 Selects the transmit hash policy to use for slave selection in
865                 balance-xor, 802.3ad, and tlb modes.
866               '';
867             };
869           };
871         });
872       };
874     networking.macvlans = mkOption {
875       default = { };
876       example = literalExpression ''
877         {
878           wan = {
879             interface = "enp2s0";
880             mode = "vepa";
881           };
882         }
883       '';
884       description = ''
885         This option allows you to define macvlan interfaces which should
886         be automatically created.
887       '';
888       type = with types; attrsOf (submodule {
889         options = {
891           interface = mkOption {
892             example = "enp4s0";
893             type = types.str;
894             description = "The interface the macvlan will transmit packets through.";
895           };
897           mode = mkOption {
898             default = null;
899             type = types.nullOr types.str;
900             example = "vepa";
901             description = "The mode of the macvlan device.";
902           };
904         };
906       });
907     };
909     networking.fooOverUDP = mkOption {
910       default = { };
911       example =
912         {
913           primary = { port = 9001; local = { address = "192.0.2.1"; dev = "eth0"; }; };
914           backup =  { port = 9002; };
915         };
916       description = ''
917         This option allows you to configure Foo Over UDP and Generic UDP Encapsulation
918         endpoints. See {manpage}`ip-fou(8)` for details.
919       '';
920       type = with types; attrsOf (submodule {
921         options = {
922           port = mkOption {
923             type = port;
924             description = ''
925               Local port of the encapsulation UDP socket.
926             '';
927           };
929           protocol = mkOption {
930             type = nullOr (ints.between 1 255);
931             default = null;
932             description = ''
933               Protocol number of the encapsulated packets. Specifying `null`
934               (the default) creates a GUE endpoint, specifying a protocol number will create
935               a FOU endpoint.
936             '';
937           };
939           local = mkOption {
940             type = nullOr (submodule {
941               options = {
942                 address = mkOption {
943                   type = types.str;
944                   description = ''
945                     Local address to bind to. The address must be available when the FOU
946                     endpoint is created, using the scripted network setup this can be achieved
947                     either by setting `dev` or adding dependency information to
948                     `systemd.services.<name>-fou-encap`; it isn't supported
949                     when using networkd.
950                   '';
951                 };
953                 dev = mkOption {
954                   type = nullOr str;
955                   default = null;
956                   example = "eth0";
957                   description = ''
958                     Network device to bind to.
959                   '';
960                 };
961               };
962             });
963             default = null;
964             example = { address = "203.0.113.22"; };
965             description = ''
966               Local address (and optionally device) to bind to using the given port.
967             '';
968           };
969         };
970       });
971     };
973     networking.sits = mkOption {
974       default = { };
975       example = literalExpression ''
976         {
977           hurricane = {
978             remote = "10.0.0.1";
979             local = "10.0.0.22";
980             ttl = 255;
981           };
982           msipv6 = {
983             remote = "192.168.0.1";
984             dev = "enp3s0";
985             ttl = 127;
986           };
987         }
988       '';
989       description = ''
990         This option allows you to define 6-to-4 interfaces which should be automatically created.
991       '';
992       type = with types; attrsOf (submodule {
993         options = {
995           remote = mkOption {
996             type = types.nullOr types.str;
997             default = null;
998             example = "10.0.0.1";
999             description = ''
1000               The address of the remote endpoint to forward traffic over.
1001             '';
1002           };
1004           local = mkOption {
1005             type = types.nullOr types.str;
1006             default = null;
1007             example = "10.0.0.22";
1008             description = ''
1009               The address of the local endpoint which the remote
1010               side should send packets to.
1011             '';
1012           };
1014           ttl = mkOption {
1015             type = types.nullOr types.int;
1016             default = null;
1017             example = 255;
1018             description = ''
1019               The time-to-live of the connection to the remote tunnel endpoint.
1020             '';
1021           };
1023           dev = mkOption {
1024             type = types.nullOr types.str;
1025             default = null;
1026             example = "enp4s0f0";
1027             description = ''
1028               The underlying network device on which the tunnel resides.
1029             '';
1030           };
1032           encapsulation = with types; mkOption {
1033             type = nullOr (submodule {
1034               options = {
1035                 type = mkOption {
1036                   type = enum [ "fou" "gue" ];
1037                   description = ''
1038                     Selects encapsulation type. See
1039                     {manpage}`ip-link(8)` for details.
1040                   '';
1041                 };
1043                 port = mkOption {
1044                   type = port;
1045                   example = 9001;
1046                   description = ''
1047                     Destination port for encapsulated packets.
1048                   '';
1049                 };
1051                 sourcePort = mkOption {
1052                   type = nullOr types.port;
1053                   default = null;
1054                   example = 9002;
1055                   description = ''
1056                     Source port for encapsulated packets. Will be chosen automatically by
1057                     the kernel if unset.
1058                   '';
1059                 };
1060               };
1061             });
1062             default = null;
1063             example = { type = "fou"; port = 9001; };
1064             description = ''
1065               Configures encapsulation in UDP packets.
1066             '';
1067           };
1069         };
1071       });
1072     };
1074     networking.greTunnels = mkOption {
1075       default = { };
1076       example = literalExpression ''
1077         {
1078           greBridge = {
1079             remote = "10.0.0.1";
1080             local = "10.0.0.22";
1081             dev = "enp4s0f0";
1082             type = "tap";
1083             ttl = 255;
1084           };
1085           gre6Tunnel = {
1086             remote = "fd7a:5634::1";
1087             local = "fd7a:5634::2";
1088             dev = "enp4s0f0";
1089             type = "tun6";
1090             ttl = 255;
1091           };
1092         }
1093       '';
1094       description = ''
1095         This option allows you to define Generic Routing Encapsulation (GRE) tunnels.
1096       '';
1097       type = with types; attrsOf (submodule {
1098         options = {
1100           remote = mkOption {
1101             type = types.nullOr types.str;
1102             default = null;
1103             example = "10.0.0.1";
1104             description = ''
1105               The address of the remote endpoint to forward traffic over.
1106             '';
1107           };
1109           local = mkOption {
1110             type = types.nullOr types.str;
1111             default = null;
1112             example = "10.0.0.22";
1113             description = ''
1114               The address of the local endpoint which the remote
1115               side should send packets to.
1116             '';
1117           };
1119           dev = mkOption {
1120             type = types.nullOr types.str;
1121             default = null;
1122             example = "enp4s0f0";
1123             description = ''
1124               The underlying network device on which the tunnel resides.
1125             '';
1126           };
1128           ttl = mkOption {
1129             type = types.nullOr types.int;
1130             default = null;
1131             example = 255;
1132             description = ''
1133               The time-to-live/hoplimit of the connection to the remote tunnel endpoint.
1134             '';
1135           };
1137           type = mkOption {
1138             type = with types; enum [ "tun" "tap" "tun6" "tap6" ];
1139             default = "tap";
1140             example = "tap";
1141             apply = v: {
1142               tun = "gre";
1143               tap = "gretap";
1144               tun6 = "ip6gre";
1145               tap6 = "ip6gretap";
1146             }.${v};
1147             description = ''
1148               Whether the tunnel routes layer 2 (tap) or layer 3 (tun) traffic.
1149             '';
1150           };
1151         };
1152       });
1153     };
1155     networking.vlans = mkOption {
1156       default = { };
1157       example = literalExpression ''
1158         {
1159           vlan0 = {
1160             id = 3;
1161             interface = "enp3s0";
1162           };
1163           vlan1 = {
1164             id = 1;
1165             interface = "wlan0";
1166           };
1167         }
1168       '';
1169       description = ''
1170           This option allows you to define vlan devices that tag packets
1171           on top of a physical interface. The value of this option is an
1172           attribute set. Each attribute specifies a vlan, with the name
1173           specifying the name of the vlan interface.
1174         '';
1176       type = with types; attrsOf (submodule {
1178         options = {
1180           id = mkOption {
1181             example = 1;
1182             type = types.int;
1183             description = "The vlan identifier";
1184           };
1186           interface = mkOption {
1187             example = "enp4s0";
1188             type = types.str;
1189             description = "The interface the vlan will transmit packets through.";
1190           };
1192         };
1194       });
1196     };
1198     networking.wlanInterfaces = mkOption {
1199       default = { };
1200       example = literalExpression ''
1201         {
1202           wlan-station0 = {
1203               device = "wlp6s0";
1204           };
1205           wlan-adhoc0 = {
1206               type = "ibss";
1207               device = "wlp6s0";
1208               mac = "02:00:00:00:00:01";
1209           };
1210           wlan-p2p0 = {
1211               device = "wlp6s0";
1212               mac = "02:00:00:00:00:02";
1213           };
1214           wlan-ap0 = {
1215               device = "wlp6s0";
1216               mac = "02:00:00:00:00:03";
1217           };
1218         }
1219       '';
1220       description = ''
1221           Creating multiple WLAN interfaces on top of one physical WLAN device (NIC).
1223           The name of the WLAN interface corresponds to the name of the attribute.
1224           A NIC is referenced by the persistent device name of the WLAN interface that
1225           `udev` assigns to a NIC by default.
1226           If a NIC supports multiple WLAN interfaces, then the one NIC can be used as
1227           `device` for multiple WLAN interfaces.
1228           If a NIC is used for creating WLAN interfaces, then the default WLAN interface
1229           with a persistent device name form `udev` is not created.
1230           A WLAN interface with the persistent name assigned from `udev`
1231           would have to be created explicitly.
1232         '';
1234       type = with types; attrsOf (submodule {
1236         options = {
1238           device = mkOption {
1239             type = types.str;
1240             example = "wlp6s0";
1241             description = "The name of the underlying hardware WLAN device as assigned by `udev`.";
1242           };
1244           type = mkOption {
1245             type = types.enum [ "managed" "ibss" "monitor" "mesh" "wds" ];
1246             default = "managed";
1247             example = "ibss";
1248             description = ''
1249               The type of the WLAN interface.
1250               The type has to be supported by the underlying hardware of the device.
1251             '';
1252           };
1254           meshID = mkOption {
1255             type = types.nullOr types.str;
1256             default = null;
1257             description = "MeshID of interface with type `mesh`.";
1258           };
1260           flags = mkOption {
1261             type = with types; nullOr (enum [ "none" "fcsfail" "control" "otherbss" "cook" "active" ]);
1262             default = null;
1263             example = "control";
1264             description = ''
1265               Flags for interface of type `monitor`.
1266             '';
1267           };
1269           fourAddr = mkOption {
1270             type = types.nullOr types.bool;
1271             default = null;
1272             description = "Whether to enable `4-address mode` with type `managed`.";
1273           };
1275           mac = mkOption {
1276             type = types.nullOr types.str;
1277             default = null;
1278             example = "02:00:00:00:00:01";
1279             description = ''
1280               MAC address to use for the device. If `null`, then the MAC of the
1281               underlying hardware WLAN device is used.
1283               INFO: Locally administered MAC addresses are of the form:
1284               - x2:xx:xx:xx:xx:xx
1285               - x6:xx:xx:xx:xx:xx
1286               - xA:xx:xx:xx:xx:xx
1287               - xE:xx:xx:xx:xx:xx
1288             '';
1289           };
1291         };
1293       });
1295     };
1297     networking.useDHCP = mkOption {
1298       type = types.bool;
1299       default = true;
1300       description = ''
1301         Whether to use DHCP to obtain an IP address and other
1302         configuration for all network interfaces that do not have any manually
1303         configured IPv4 addresses.
1304       '';
1305     };
1307     networking.useNetworkd = mkOption {
1308       default = false;
1309       type = types.bool;
1310       description = ''
1311         Whether we should use networkd as the network configuration backend or
1312         the legacy script based system. Note that this option is experimental,
1313         enable at your own risk.
1314       '';
1315     };
1317     networking.tempAddresses = mkOption {
1318       default = if cfg.enableIPv6 then "default" else "disabled";
1319       defaultText = literalExpression ''
1320         if ''${config.${opt.enableIPv6}} then "default" else "disabled"
1321       '';
1322       type = types.enum (lib.attrNames tempaddrValues);
1323       description = ''
1324         Whether to enable IPv6 Privacy Extensions for interfaces not
1325         configured explicitly in
1326         [](#opt-networking.interfaces._name_.tempAddress).
1328         This sets the ipv6.conf.*.use_tempaddr sysctl for all
1329         interfaces. Possible values are:
1331         ${tempaddrDoc}
1332       '';
1333     };
1335   };
1338   ###### implementation
1340   config = {
1342     warnings = (concatMap (i: i.warnings) interfaces) ++ (lib.optional
1343       (config.systemd.network.enable && cfg.useDHCP && !cfg.useNetworkd) ''
1344         The combination of `systemd.network.enable = true`, `networking.useDHCP = true` and `networking.useNetworkd = false` can cause both networkd and dhcpcd to manage the same interfaces. This can lead to loss of networking. It is recommended you choose only one of networkd (by also enabling `networking.useNetworkd`) or scripting (by disabling `systemd.network.enable`)
1345       '');
1347     assertions =
1348       (forEach interfaces (i: {
1349         # With the linux kernel, interface name length is limited by IFNAMSIZ
1350         # to 16 bytes, including the trailing null byte.
1351         # See include/linux/if.h in the kernel sources
1352         assertion = stringLength i.name < 16;
1353         message = ''
1354           The name of networking.interfaces."${i.name}" is too long, it needs to be less than 16 characters.
1355         '';
1356       })) ++ (forEach slaveIfs (i: {
1357         assertion = i.ipv4.addresses == [ ] && i.ipv6.addresses == [ ];
1358         message = ''
1359           The networking.interfaces."${i.name}" must not have any defined ips when it is a slave.
1360         '';
1361       })) ++ (forEach interfaces (i: {
1362         assertion = i.tempAddress != "disabled" -> cfg.enableIPv6;
1363         message = ''
1364           Temporary addresses are only needed when IPv6 is enabled.
1365         '';
1366       })) ++ (forEach interfaces (i: {
1367         assertion = (i.virtual && i.virtualType == "tun") -> i.macAddress == null;
1368         message = ''
1369           Setting a MAC Address for tun device ${i.name} isn't supported.
1370         '';
1371       })) ++ [
1372         {
1373           assertion = cfg.hostId == null || (stringLength cfg.hostId == 8 && isHexString cfg.hostId);
1374           message = "Invalid value given to the networking.hostId option.";
1375         }
1376       ];
1378     boot.kernelModules = [ ]
1379       ++ optional hasVirtuals "tun"
1380       ++ optional hasSits "sit"
1381       ++ optional hasGres "gre"
1382       ++ optional hasBonds "bonding"
1383       ++ optional hasFous "fou";
1385     boot.extraModprobeConfig =
1386       # This setting is intentional as it prevents default bond devices
1387       # from being created.
1388       optionalString hasBonds "options bonding max_bonds=0";
1390     boot.kernel.sysctl = {
1391       "net.ipv4.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
1392       "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6);
1393       "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
1394       # allow all users to do ICMP echo requests (ping)
1395       "net.ipv4.ping_group_range" = mkDefault "0 2147483647";
1396       # networkmanager falls back to "/proc/sys/net/ipv6/conf/default/use_tempaddr"
1397       "net.ipv6.conf.default.use_tempaddr" = tempaddrValues.${cfg.tempAddresses}.sysctl;
1398     } // listToAttrs (forEach interfaces
1399         (i: nameValuePair "net.ipv4.conf.${replaceStrings ["."] ["/"] i.name}.proxy_arp" i.proxyARP))
1400       // listToAttrs (forEach interfaces
1401         (i: let
1402           opt = i.tempAddress;
1403           val = tempaddrValues.${opt}.sysctl;
1404          in nameValuePair "net.ipv6.conf.${replaceStrings ["."] ["/"] i.name}.use_tempaddr" val));
1406     systemd.services.domainname = lib.mkIf (cfg.domain != null) {
1407       wantedBy = [ "sysinit.target" ];
1408       before = [ "sysinit.target" "shutdown.target" ];
1409       conflicts = [ "shutdown.target" ];
1410       unitConfig.DefaultDependencies = false;
1411       serviceConfig.ExecStart = ''${pkgs.nettools}/bin/domainname "${cfg.domain}"'';
1412       serviceConfig.Type = "oneshot";
1413     };
1415     environment.etc.hostid = mkIf (cfg.hostId != null) { source = hostidFile; };
1416     boot.initrd.systemd.contents."/etc/hostid" = mkIf (cfg.hostId != null) { source = hostidFile; };
1418     # static hostname configuration needed for hostnamectl and the
1419     # org.freedesktop.hostname1 dbus service (both provided by systemd)
1420     environment.etc.hostname = mkIf (cfg.hostName != "")
1421       {
1422         text = cfg.hostName + "\n";
1423       };
1425     environment.systemPackages =
1426       [ pkgs.host
1427         pkgs.iproute2
1428         pkgs.iputils
1429         pkgs.nettools
1430       ]
1431       ++ optionals config.networking.wireless.enable [
1432         pkgs.wirelesstools # FIXME: obsolete?
1433         pkgs.iw
1434       ]
1435       ++ bridgeStp;
1437     # Wake-on-LAN configuration is shared by the scripted and networkd backends.
1438     systemd.network.links = pipe interfaces [
1439       (filter (i: i.wakeOnLan.enable))
1440       (map (i: nameValuePair "40-${i.name}" {
1441         matchConfig.OriginalName = i.name;
1442         linkConfig.WakeOnLan = concatStringsSep " " i.wakeOnLan.policy;
1443       }))
1444       listToAttrs
1445     ];
1447     systemd.services = {
1448       network-local-commands = {
1449         description = "Extra networking commands.";
1450         before = [ "network.target" ];
1451         wantedBy = [ "network.target" ];
1452         after = [ "network-pre.target" ];
1453         unitConfig.ConditionCapability = "CAP_NET_ADMIN";
1454         path = [ pkgs.iproute2 ];
1455         serviceConfig.Type = "oneshot";
1456         serviceConfig.RemainAfterExit = true;
1457         script = ''
1458           # Run any user-specified commands.
1459           ${cfg.localCommands}
1460         '';
1461       };
1462     };
1463     services.mstpd = mkIf needsMstpd { enable = true; };
1465     virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; };
1467     services.udev.packages =  [
1468       (pkgs.writeTextFile rec {
1469         name = "ipv6-privacy-extensions.rules";
1470         destination = "/etc/udev/rules.d/98-${name}";
1471         text = let
1472           sysctl-value = tempaddrValues.${cfg.tempAddresses}.sysctl;
1473         in ''
1474           # enable and prefer IPv6 privacy addresses by default
1475           ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.bash}/bin/sh -c 'echo ${sysctl-value} > /proc/sys/net/ipv6/conf/$name/use_tempaddr'"
1476         '';
1477       })
1478       (pkgs.writeTextFile rec {
1479         name = "ipv6-privacy-extensions.rules";
1480         destination = "/etc/udev/rules.d/99-${name}";
1481         text = concatMapStrings (i:
1482           let
1483             opt = i.tempAddress;
1484             val = tempaddrValues.${opt}.sysctl;
1485             msg = tempaddrValues.${opt}.description;
1486           in
1487           ''
1488             # override to ${msg} for ${i.name}
1489             ACTION=="add", SUBSYSTEM=="net", NAME=="${i.name}", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceStrings ["."] ["/"] i.name}.use_tempaddr=${val}"
1490           '') (filter (i: i.tempAddress != cfg.tempAddresses) interfaces);
1491       })
1492     ] ++ lib.optional (cfg.wlanInterfaces != {})
1493       (pkgs.writeTextFile {
1494         name = "99-zzz-40-wlanInterfaces.rules";
1495         destination = "/etc/udev/rules.d/99-zzz-40-wlanInterfaces.rules";
1496         text =
1497           let
1498             # Collect all interfaces that are defined for a device
1499             # as device:interface key:value pairs.
1500             wlanDeviceInterfaces =
1501               let
1502                 allDevices = unique (mapAttrsToList (_: v: v.device) cfg.wlanInterfaces);
1503                 interfacesOfDevice = d: filterAttrs (_: v: v.device == d) cfg.wlanInterfaces;
1504               in
1505                 genAttrs allDevices (d: interfacesOfDevice d);
1507             # Convert device:interface key:value pairs into a list, and if it exists,
1508             # place the interface which is named after the device at the beginning.
1509             wlanListDeviceFirst = device: interfaces:
1510               if hasAttr device interfaces
1511               then mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n==device) interfaces) ++ mapAttrsToList (n: v: v//{_iName=n;}) (filterAttrs (n: _: n!=device) interfaces)
1512               else mapAttrsToList (n: v: v // {_iName = n;}) interfaces;
1514             # Udev script to execute for the default WLAN interface with the persistend udev name.
1515             # The script creates the required, new WLAN interfaces interfaces and configures the
1516             # existing, default interface.
1517             curInterfaceScript = device: current: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${device}.sh" ''
1518               #!${pkgs.runtimeShell}
1519               # Change the wireless phy device to a predictable name.
1520               ${pkgs.iw}/bin/iw phy `${pkgs.coreutils}/bin/cat /sys/class/net/$INTERFACE/phy80211/name` set name ${device}
1522               # Add new WLAN interfaces
1523               ${flip concatMapStrings new (i: ''
1524               ${pkgs.iw}/bin/iw phy ${device} interface add ${i._iName} type managed
1525               '')}
1527               # Configure the current interface
1528               ${pkgs.iw}/bin/iw dev ${device} set type ${current.type}
1529               ${optionalString (current.type == "mesh" && current.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${current.meshID}"}
1530               ${optionalString (current.type == "monitor" && current.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${current.flags}"}
1531               ${optionalString (current.type == "managed" && current.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if current.fourAddr then "on" else "off"}"}
1532               ${optionalString (current.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${device} address ${current.mac}"}
1533             '';
1535             # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface.
1536             newInterfaceScript = new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" ''
1537               #!${pkgs.runtimeShell}
1538               # Configure the new interface
1539               ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type}
1540               ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set meshid ${new.meshID}"}
1541               ${optionalString (new.type == "monitor" && new.flags!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set monitor ${new.flags}"}
1542               ${optionalString (new.type == "managed" && new.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set 4addr ${if new.fourAddr then "on" else "off"}"}
1543               ${optionalString (new.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${new._iName} address ${new.mac}"}
1544             '';
1546             # Udev attributes for systemd to name the device and to create a .device target.
1547             systemdAttrs = n: ''NAME:="${n}", ENV{INTERFACE}="${n}", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/${n}", TAG+="systemd"'';
1548           in
1549           flip (concatMapStringsSep "\n") (attrNames wlanDeviceInterfaces) (device:
1550             let
1551               interfaces = wlanListDeviceFirst device wlanDeviceInterfaces.${device};
1552               curInterface = elemAt interfaces 0;
1553               newInterfaces = drop 1 interfaces;
1554             in ''
1555             # It is important to have that rule first as overwriting the NAME attribute also prevents the
1556             # next rules from matching.
1557             ${flip (concatMapStringsSep "\n") (wlanListDeviceFirst device wlanDeviceInterfaces.${device}) (interface:
1558             ''ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", ENV{INTERFACE}=="${interface._iName}", ${systemdAttrs interface._iName}, RUN+="${newInterfaceScript interface}"'')}
1560             # Add the required, new WLAN interfaces to the default WLAN interface with the
1561             # persistent, default name as assigned by udev.
1562             ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName}, RUN+="${curInterfaceScript device curInterface newInterfaces}"
1563             # Generate the same systemd events for both 'add' and 'move' udev events.
1564             ACTION=="move", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", NAME=="${device}", ${systemdAttrs curInterface._iName}
1565           '');
1566       });
1567   };