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