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