1{ config, lib, pkgs, ... }:
2
3with lib;
4with import ./systemd-unit-options.nix { inherit config lib; };
5with import ./systemd-lib.nix { inherit config lib pkgs; };
6
7let
8
9 cfg = config.systemd.network;
10
11 checkLink = checkUnitConfig "Link" [
12 (assertOnlyFields [
13 "Description" "Alias" "MACAddressPolicy" "MACAddress" "NamePolicy" "Name"
14 "MTUBytes" "BitsPerSecond" "Duplex" "WakeOnLan"
15 ])
16 (assertValueOneOf "MACAddressPolicy" ["persistent" "random"])
17 (assertMacAddress "MACAddress")
18 (assertValueOneOf "NamePolicy" [
19 "kernel" "database" "onboard" "slot" "path" "mac"
20 ])
21 (assertByteFormat "MTUBytes")
22 (assertByteFormat "BitsPerSecond")
23 (assertValueOneOf "Duplex" ["half" "full"])
24 (assertValueOneOf "WakeOnLan" ["phy" "magic" "off"])
25 ];
26
27 checkNetdev = checkUnitConfig "Netdev" [
28 (assertOnlyFields [
29 "Description" "Name" "Kind" "MTUBytes" "MACAddress"
30 ])
31 (assertHasField "Name")
32 (assertHasField "Kind")
33 (assertValueOneOf "Kind" [
34 "bridge" "bond" "vlan" "macvlan" "vxlan" "ipip"
35 "gre" "sit" "vti" "veth" "tun" "tap" "dummy"
36 ])
37 (assertByteFormat "MTUBytes")
38 (assertMacAddress "MACAddress")
39 ];
40
41 checkVlan = checkUnitConfig "VLAN" [
42 (assertOnlyFields ["Id"])
43 (assertRange "Id" 0 4094)
44 ];
45
46 checkMacvlan = checkUnitConfig "MACVLAN" [
47 (assertOnlyFields ["Mode"])
48 (assertValueOneOf "Mode" ["private" "vepa" "bridge" "passthru"])
49 ];
50
51 checkVxlan = checkUnitConfig "VXLAN" [
52 (assertOnlyFields ["Id" "Group" "TOS" "TTL" "MacLearning"])
53 (assertRange "TTL" 0 255)
54 (assertValueOneOf "MacLearning" boolValues)
55 ];
56
57 checkTunnel = checkUnitConfig "Tunnel" [
58 (assertOnlyFields ["Local" "Remote" "TOS" "TTL" "DiscoverPathMTU"])
59 (assertRange "TTL" 0 255)
60 (assertValueOneOf "DiscoverPathMTU" boolValues)
61 ];
62
63 checkPeer = checkUnitConfig "Peer" [
64 (assertOnlyFields ["Name" "MACAddress"])
65 (assertMacAddress "MACAddress")
66 ];
67
68 tunTapChecks = [
69 (assertOnlyFields ["OneQueue" "MultiQueue" "PacketInfo" "User" "Group"])
70 (assertValueOneOf "OneQueue" boolValues)
71 (assertValueOneOf "MultiQueue" boolValues)
72 (assertValueOneOf "PacketInfo" boolValues)
73 ];
74
75 checkTun = checkUnitConfig "Tun" tunTapChecks;
76
77 checkTap = checkUnitConfig "Tap" tunTapChecks;
78
79 checkBond = checkUnitConfig "Bond" [
80 (assertOnlyFields [
81 "Mode" "TransmitHashPolicy" "LACPTransmitRate" "MIIMonitorSec"
82 "UpDelaySec" "DownDelaySec"
83 ])
84 (assertValueOneOf "Mode" [
85 "balance-rr" "active-backup" "balance-xor"
86 "broadcast" "802.3ad" "balance-tlb" "balance-alb"
87 ])
88 (assertValueOneOf "TransmitHashPolicy" [
89 "layer2" "layer3+4" "layer2+3" "encap2+3" "802.3ad" "encap3+4"
90 ])
91 (assertValueOneOf "LACPTransmitRate" ["slow" "fast"])
92 ];
93
94 checkNetwork = checkUnitConfig "Network" [
95 (assertOnlyFields [
96 "Description" "DHCP" "DHCPServer" "IPv4LL" "IPv4LLRoute"
97 "LLMNR" "Domains" "Bridge" "Bond"
98 ])
99 (assertValueOneOf "DHCP" ["both" "none" "v4" "v6"])
100 (assertValueOneOf "DHCPServer" boolValues)
101 (assertValueOneOf "IPv4LL" boolValues)
102 (assertValueOneOf "IPv4LLRoute" boolValues)
103 (assertValueOneOf "LLMNR" boolValues)
104 ];
105
106 checkAddress = checkUnitConfig "Address" [
107 (assertOnlyFields ["Address" "Peer" "Broadcast" "Label"])
108 (assertHasField "Address")
109 ];
110
111 checkRoute = checkUnitConfig "Route" [
112 (assertOnlyFields ["Gateway" "Destination" "Metric"])
113 (assertHasField "Gateway")
114 ];
115
116 checkDhcp = checkUnitConfig "DHCP" [
117 (assertOnlyFields [
118 "UseDNS" "UseMTU" "SendHostname" "UseHostname" "UseDomains" "UseRoutes"
119 "CriticalConnections" "VendorClassIdentifier" "RequestBroadcast"
120 "RouteMetric"
121 ])
122 (assertValueOneOf "UseDNS" boolValues)
123 (assertValueOneOf "UseMTU" boolValues)
124 (assertValueOneOf "SendHostname" boolValues)
125 (assertValueOneOf "UseHostname" boolValues)
126 (assertValueOneOf "UseDomains" boolValues)
127 (assertValueOneOf "UseRoutes" boolValues)
128 (assertValueOneOf "CriticalConnections" boolValues)
129 (assertValueOneOf "RequestBroadcast" boolValues)
130 ];
131
132 commonNetworkOptions = {
133
134 enable = mkOption {
135 default = true;
136 type = types.bool;
137 description = ''
138 Whether to manage network configuration using <command>systemd-network</command>.
139 '';
140 };
141
142 matchConfig = mkOption {
143 default = {};
144 example = { Name = "eth0"; };
145 type = types.attrsOf unitOption;
146 description = ''
147 Each attribute in this set specifies an option in the
148 <literal>[Match]</literal> section of the unit. See
149 <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
150 <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>
151 <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
152 for details.
153 '';
154 };
155
156 };
157
158 linkOptions = commonNetworkOptions // {
159
160 linkConfig = mkOption {
161 default = {};
162 example = { MACAddress = "00:ff:ee:aa:cc:dd"; };
163 type = types.addCheck (types.attrsOf unitOption) checkLink;
164 description = ''
165 Each attribute in this set specifies an option in the
166 <literal>[Link]</literal> section of the unit. See
167 <citerefentry><refentrytitle>systemd.link</refentrytitle>
168 <manvolnum>5</manvolnum></citerefentry> for details.
169 '';
170 };
171
172 };
173
174 netdevOptions = commonNetworkOptions // {
175
176 netdevConfig = mkOption {
177 default = {};
178 example = { Name = "mybridge"; Kind = "bridge"; };
179 type = types.addCheck (types.attrsOf unitOption) checkNetdev;
180 description = ''
181 Each attribute in this set specifies an option in the
182 <literal>[Netdev]</literal> section of the unit. See
183 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
184 <manvolnum>5</manvolnum></citerefentry> for details.
185 '';
186 };
187
188 vlanConfig = mkOption {
189 default = {};
190 example = { Id = "4"; };
191 type = types.addCheck (types.attrsOf unitOption) checkVlan;
192 description = ''
193 Each attribute in this set specifies an option in the
194 <literal>[VLAN]</literal> section of the unit. See
195 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
196 <manvolnum>5</manvolnum></citerefentry> for details.
197 '';
198 };
199
200 macvlanConfig = mkOption {
201 default = {};
202 example = { Mode = "private"; };
203 type = types.addCheck (types.attrsOf unitOption) checkMacvlan;
204 description = ''
205 Each attribute in this set specifies an option in the
206 <literal>[MACVLAN]</literal> section of the unit. See
207 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
208 <manvolnum>5</manvolnum></citerefentry> for details.
209 '';
210 };
211
212 vxlanConfig = mkOption {
213 default = {};
214 example = { Id = "4"; };
215 type = types.addCheck (types.attrsOf unitOption) checkVxlan;
216 description = ''
217 Each attribute in this set specifies an option in the
218 <literal>[VXLAN]</literal> section of the unit. See
219 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
220 <manvolnum>5</manvolnum></citerefentry> for details.
221 '';
222 };
223
224 tunnelConfig = mkOption {
225 default = {};
226 example = { Remote = "192.168.1.1"; };
227 type = types.addCheck (types.attrsOf unitOption) checkTunnel;
228 description = ''
229 Each attribute in this set specifies an option in the
230 <literal>[Tunnel]</literal> section of the unit. See
231 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
232 <manvolnum>5</manvolnum></citerefentry> for details.
233 '';
234 };
235
236 peerConfig = mkOption {
237 default = {};
238 example = { Name = "veth2"; };
239 type = types.addCheck (types.attrsOf unitOption) checkPeer;
240 description = ''
241 Each attribute in this set specifies an option in the
242 <literal>[Peer]</literal> section of the unit. See
243 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
244 <manvolnum>5</manvolnum></citerefentry> for details.
245 '';
246 };
247
248 tunConfig = mkOption {
249 default = {};
250 example = { User = "openvpn"; };
251 type = types.addCheck (types.attrsOf unitOption) checkTun;
252 description = ''
253 Each attribute in this set specifies an option in the
254 <literal>[Tun]</literal> section of the unit. See
255 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
256 <manvolnum>5</manvolnum></citerefentry> for details.
257 '';
258 };
259
260 tapConfig = mkOption {
261 default = {};
262 example = { User = "openvpn"; };
263 type = types.addCheck (types.attrsOf unitOption) checkTap;
264 description = ''
265 Each attribute in this set specifies an option in the
266 <literal>[Tap]</literal> section of the unit. See
267 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
268 <manvolnum>5</manvolnum></citerefentry> for details.
269 '';
270 };
271
272 bondConfig = mkOption {
273 default = {};
274 example = { Mode = "802.3ad"; };
275 type = types.addCheck (types.attrsOf unitOption) checkBond;
276 description = ''
277 Each attribute in this set specifies an option in the
278 <literal>[Bond]</literal> section of the unit. See
279 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
280 <manvolnum>5</manvolnum></citerefentry> for details.
281 '';
282 };
283
284 };
285
286 addressOptions = {
287
288 addressConfig = mkOption {
289 default = {};
290 example = { Address = "192.168.0.100/24"; };
291 type = types.addCheck (types.attrsOf unitOption) checkAddress;
292 description = ''
293 Each attribute in this set specifies an option in the
294 <literal>[Address]</literal> section of the unit. See
295 <citerefentry><refentrytitle>systemd.network</refentrytitle>
296 <manvolnum>5</manvolnum></citerefentry> for details.
297 '';
298 };
299
300 };
301
302 routeOptions = {
303
304 routeConfig = mkOption {
305 default = {};
306 example = { Gateway = "192.168.0.1"; };
307 type = types.addCheck (types.attrsOf unitOption) checkRoute;
308 description = ''
309 Each attribute in this set specifies an option in the
310 <literal>[Route]</literal> section of the unit. See
311 <citerefentry><refentrytitle>systemd.network</refentrytitle>
312 <manvolnum>5</manvolnum></citerefentry> for details.
313 '';
314 };
315
316 };
317
318 networkOptions = commonNetworkOptions // {
319
320 networkConfig = mkOption {
321 default = {};
322 example = { Description = "My Network"; };
323 type = types.addCheck (types.attrsOf unitOption) checkNetwork;
324 description = ''
325 Each attribute in this set specifies an option in the
326 <literal>[Network]</literal> section of the unit. See
327 <citerefentry><refentrytitle>systemd.network</refentrytitle>
328 <manvolnum>5</manvolnum></citerefentry> for details.
329 '';
330 };
331
332 dhcpConfig = mkOption {
333 default = {};
334 example = { UseDNS = true; UseRoutes = true; };
335 type = types.addCheck (types.attrsOf unitOption) checkDhcp;
336 description = ''
337 Each attribute in this set specifies an option in the
338 <literal>[DHCP]</literal> section of the unit. See
339 <citerefentry><refentrytitle>systemd.network</refentrytitle>
340 <manvolnum>5</manvolnum></citerefentry> for details.
341 '';
342 };
343
344 name = mkOption {
345 type = types.nullOr types.str;
346 default = null;
347 description = ''
348 The name of the network interface to match against.
349 '';
350 };
351
352 DHCP = mkOption {
353 type = types.nullOr types.str;
354 default = null;
355 description = ''
356 Whether to enable DHCP on the interfaces matched.
357 '';
358 };
359
360 domains = mkOption {
361 type = types.nullOr (types.listOf types.str);
362 default = null;
363 description = ''
364 A list of domains to pass to the network config.
365 '';
366 };
367
368 address = mkOption {
369 default = [ ];
370 type = types.listOf types.str;
371 description = ''
372 A list of addresses to be added to the network section of the
373 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
374 <manvolnum>5</manvolnum></citerefentry> for details.
375 '';
376 };
377
378 gateway = mkOption {
379 default = [ ];
380 type = types.listOf types.str;
381 description = ''
382 A list of gateways to be added to the network section of the
383 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
384 <manvolnum>5</manvolnum></citerefentry> for details.
385 '';
386 };
387
388 dns = mkOption {
389 default = [ ];
390 type = types.listOf types.str;
391 description = ''
392 A list of dns servers to be added to the network section of the
393 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
394 <manvolnum>5</manvolnum></citerefentry> for details.
395 '';
396 };
397
398 ntp = mkOption {
399 default = [ ];
400 type = types.listOf types.str;
401 description = ''
402 A list of ntp servers to be added to the network section of the
403 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
404 <manvolnum>5</manvolnum></citerefentry> for details.
405 '';
406 };
407
408 vlan = mkOption {
409 default = [ ];
410 type = types.listOf types.str;
411 description = ''
412 A list of vlan interfaces to be added to the network section of the
413 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
414 <manvolnum>5</manvolnum></citerefentry> for details.
415 '';
416 };
417
418 macvlan = mkOption {
419 default = [ ];
420 type = types.listOf types.str;
421 description = ''
422 A list of macvlan interfaces to be added to the network section of the
423 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
424 <manvolnum>5</manvolnum></citerefentry> for details.
425 '';
426 };
427
428 vxlan = mkOption {
429 default = [ ];
430 type = types.listOf types.str;
431 description = ''
432 A list of vxlan interfaces to be added to the network section of the
433 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
434 <manvolnum>5</manvolnum></citerefentry> for details.
435 '';
436 };
437
438 tunnel = mkOption {
439 default = [ ];
440 type = types.listOf types.str;
441 description = ''
442 A list of tunnel interfaces to be added to the network section of the
443 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
444 <manvolnum>5</manvolnum></citerefentry> for details.
445 '';
446 };
447
448 addresses = mkOption {
449 default = [ ];
450 type = types.listOf types.optionSet;
451 options = [ addressOptions ];
452 description = ''
453 A list of address sections to be added to the unit. See
454 <citerefentry><refentrytitle>systemd.network</refentrytitle>
455 <manvolnum>5</manvolnum></citerefentry> for details.
456 '';
457 };
458
459 routes = mkOption {
460 default = [ ];
461 type = types.listOf types.optionSet;
462 options = [ routeOptions ];
463 description = ''
464 A list of route sections to be added to the unit. See
465 <citerefentry><refentrytitle>systemd.network</refentrytitle>
466 <manvolnum>5</manvolnum></citerefentry> for details.
467 '';
468 };
469
470 };
471
472 networkConfig = { name, config, ... }: {
473 config = {
474 matchConfig = optionalAttrs (config.name != null) {
475 Name = config.name;
476 };
477 networkConfig = optionalAttrs (config.DHCP != null) {
478 DHCP = config.DHCP;
479 } // optionalAttrs (config.domains != null) {
480 Domains = concatStringsSep " " config.domains;
481 };
482 };
483 };
484
485 commonMatchText = def: ''
486 [Match]
487 ${attrsToSection def.matchConfig}
488 '';
489
490 linkToUnit = name: def:
491 { inherit (def) enable;
492 text = commonMatchText def +
493 ''
494 [Link]
495 ${attrsToSection def.linkConfig}
496 '';
497 };
498
499 netdevToUnit = name: def:
500 { inherit (def) enable;
501 text = commonMatchText def +
502 ''
503 [NetDev]
504 ${attrsToSection def.netdevConfig}
505
506 ${optionalString (def.vlanConfig != { }) ''
507 [VLAN]
508 ${attrsToSection def.vlanConfig}
509
510 ''}
511 ${optionalString (def.macvlanConfig != { }) ''
512 [MACVLAN]
513 ${attrsToSection def.macvlanConfig}
514
515 ''}
516 ${optionalString (def.vxlanConfig != { }) ''
517 [VXLAN]
518 ${attrsToSection def.vxlanConfig}
519
520 ''}
521 ${optionalString (def.tunnelConfig != { }) ''
522 [Tunnel]
523 ${attrsToSection def.tunnelConfig}
524
525 ''}
526 ${optionalString (def.peerConfig != { }) ''
527 [Peer]
528 ${attrsToSection def.peerConfig}
529
530 ''}
531 ${optionalString (def.tunConfig != { }) ''
532 [Tun]
533 ${attrsToSection def.tunConfig}
534
535 ''}
536 ${optionalString (def.tapConfig != { }) ''
537 [Tap]
538 ${attrsToSection def.tapConfig}
539
540 ''}
541 ${optionalString (def.bondConfig != { }) ''
542 [Bond]
543 ${attrsToSection def.bondConfig}
544
545 ''}
546 '';
547 };
548
549 networkToUnit = name: def:
550 { inherit (def) enable;
551 text = commonMatchText def +
552 ''
553 [Network]
554 ${attrsToSection def.networkConfig}
555 ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)}
556 ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)}
557 ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)}
558 ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)}
559 ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)}
560 ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)}
561 ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)}
562 ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)}
563
564 ${optionalString (def.dhcpConfig != { }) ''
565 [DHCP]
566 ${attrsToSection def.dhcpConfig}
567
568 ''}
569 ${flip concatMapStrings def.addresses (x: ''
570 [Address]
571 ${attrsToSection x.addressConfig}
572
573 '')}
574 ${flip concatMapStrings def.routes (x: ''
575 [Route]
576 ${attrsToSection x.routeConfig}
577
578 '')}
579 '';
580 };
581
582in
583
584{
585
586 options = {
587
588 systemd.network.enable = mkOption {
589 default = false;
590 type = types.bool;
591 description = ''
592 Whether to enable networkd or not.
593 '';
594 };
595
596 systemd.network.links = mkOption {
597 default = {};
598 type = types.attrsOf types.optionSet;
599 options = [ linkOptions ];
600 description = "Definition of systemd network links.";
601 };
602
603 systemd.network.netdevs = mkOption {
604 default = {};
605 type = types.attrsOf types.optionSet;
606 options = [ netdevOptions ];
607 description = "Definition of systemd network devices.";
608 };
609
610 systemd.network.networks = mkOption {
611 default = {};
612 type = types.attrsOf types.optionSet;
613 options = [ networkOptions networkConfig ];
614 description = "Definition of systemd networks.";
615 };
616
617 systemd.network.units = mkOption {
618 description = "Definition of networkd units.";
619 default = {};
620 type = types.attrsOf types.optionSet;
621 options = { name, config, ... }:
622 { options = concreteUnitOptions;
623 config = {
624 unit = mkDefault (makeUnit name config);
625 };
626 };
627 };
628
629 };
630
631 config = mkIf config.systemd.network.enable {
632
633 systemd.additionalUpstreamSystemUnits =
634 [ "systemd-networkd.service" "systemd-networkd-wait-online.service" ];
635
636 systemd.network.units =
637 mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links
638 // mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
639 // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
640
641 environment.etc."systemd/network".source =
642 generateUnits "network" cfg.units [] [];
643
644 systemd.services.systemd-networkd = {
645 wantedBy = [ "multi-user.target" ];
646 before = [ "network-interfaces.target" ];
647 restartTriggers = [ config.environment.etc."systemd/network".source ];
648 };
649
650 systemd.services.systemd-networkd-wait-online = {
651 before = [ "network-online.target" "ip-up.target" ];
652 wantedBy = [ "network-online.target" "ip-up.target" ];
653 };
654
655 systemd.services."systemd-network-wait-online@" = {
656 description = "Wait for Network Interface %I to be Configured";
657 conflicts = [ "shutdown.target" ];
658 requisite = [ "systemd-networkd.service" ];
659 after = [ "systemd-networkd.service" ];
660 serviceConfig = {
661 Type = "oneshot";
662 RemainAfterExit = true;
663 ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I";
664 };
665 };
666
667 services.resolved.enable = mkDefault true;
668 services.timesyncd.enable = mkDefault config.services.ntp.enable;
669
670 };
671
672}