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