summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hosts/sif/default.nix58
-rw-r--r--modules/networkd.nix297
-rw-r--r--modules/networkd/default.nix1686
-rw-r--r--modules/networkd/systemd-lib.nix237
-rw-r--r--modules/networkd/systemd-unit-options.nix536
-rw-r--r--modules/yggdrasil-wg/default.nix234
6 files changed, 2656 insertions, 392 deletions
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix
index 1658865a..43bd4485 100644
--- a/hosts/sif/default.nix
+++ b/hosts/sif/default.nix
@@ -70,32 +70,32 @@
70 ''; 70 '';
71 }; 71 };
72 72
73 wlanInterfaces = { 73 # wlanInterfaces = {
74 wlan0 = { 74 # wlan0 = {
75 device = "wlp82s0"; 75 # device = "wlp82s0";
76 }; 76 # };
77 }; 77 # };
78 78
79 bonds = { 79 # bonds = {
80 "lan" = { 80 # "lan" = {
81 interfaces = [ "wlan0" "enp0s31f6" "dock0" ]; 81 # interfaces = [ "wlan0" "enp0s31f6" "dock0" ];
82 driverOptions = { 82 # driverOptions = {
83 miimon = "1000"; 83 # miimon = "1000";
84 mode = "active-backup"; 84 # mode = "active-backup";
85 primary_reselect = "always"; 85 # primary_reselect = "always";
86 }; 86 # };
87 }; 87 # };
88 }; 88 # };
89 89
90 dhcpcd.enable = false; 90 dhcpcd.enable = false;
91 useDHCP = false; 91 useDHCP = false;
92 useNetworkd = true; 92 useNetworkd = true;
93 93
94 interfaces."tinc.yggdrasil" = { 94 # interfaces."tinc.yggdrasil" = {
95 virtual = true; 95 # virtual = true;
96 virtualType = config.services.tinc.networks.yggdrasil.interfaceType; 96 # virtualType = config.services.tinc.networks.yggdrasil.interfaceType;
97 macAddress = "5c:93:21:c3:61:39"; 97 # macAddress = "5c:93:21:c3:61:39";
98 }; 98 # };
99 }; 99 };
100 100
101 systemd.services."NetworkManager-wait-online".enable = false; 101 systemd.services."NetworkManager-wait-online".enable = false;
@@ -122,7 +122,7 @@
122 services = { 122 services = {
123 udev.packages = with pkgs; [ uhk-agent ]; 123 udev.packages = with pkgs; [ uhk-agent ];
124 124
125 tinc.yggdrasil.enable = true; 125 # tinc.yggdrasil.enable = true;
126 126
127 uucp = { 127 uucp = {
128 enable = true; 128 enable = true;
@@ -274,13 +274,13 @@
274 daemonNiceLevel = 10; 274 daemonNiceLevel = 10;
275 daemonIONiceLevel = 3; 275 daemonIONiceLevel = 3;
276 276
277 buildServers.vidhar = { 277 # buildServers.vidhar = {
278 address = "vidhar.yggdrasil"; 278 # address = "vidhar.yggdrasil";
279 systems = ["x86_64-linux" "i686-linux"]; 279 # systems = ["x86_64-linux" "i686-linux"];
280 maxJobs = 12; 280 # maxJobs = 12;
281 speedFactor = 4; 281 # speedFactor = 4;
282 supportedFeatures = ["nixos-test" "benchmark" "big-parallel" "kvm"]; 282 # supportedFeatures = ["nixos-test" "benchmark" "big-parallel" "kvm"];
283 }; 283 # };
284 }; 284 };
285 285
286 environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; 286 environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf;
diff --git a/modules/networkd.nix b/modules/networkd.nix
deleted file mode 100644
index d0a48f98..00000000
--- a/modules/networkd.nix
+++ /dev/null
@@ -1,297 +0,0 @@
1{ config, lib, utils, pkgs, ... }:
2
3with utils;
4with lib;
5
6let
7
8 cfg = config.networking;
9 interfaces = attrValues cfg.interfaces;
10
11 interfaceIps = i:
12 i.ipv4.addresses
13 ++ optionals cfg.enableIPv6 i.ipv6.addresses;
14
15 dhcpStr = useDHCP: if useDHCP == true || useDHCP == null then "yes" else "no";
16
17 slaves =
18 concatLists (map (bond: bond.interfaces) (attrValues cfg.bonds))
19 ++ concatLists (map (bridge: bridge.interfaces) (attrValues cfg.bridges))
20 ++ map (sit: sit.dev) (attrValues cfg.sits)
21 ++ map (vlan: vlan.interface) (attrValues cfg.vlans)
22 # add dependency to physical or independently created vswitch member interface
23 # TODO: warn the user that any address configured on those interfaces will be useless
24 ++ concatMap (i: attrNames (filterAttrs (_: config: config.type != "internal") i.interfaces)) (attrValues cfg.vswitches);
25
26in
27
28{
29 disabledModules = [ "tasks/network-interfaces-systemd.nix" ];
30
31 config = mkIf cfg.useNetworkd {
32
33 assertions = [ {
34 assertion = cfg.defaultGatewayWindowSize == null;
35 message = "networking.defaultGatewayWindowSize is not supported by networkd.";
36 } {
37 assertion = cfg.vswitches == {};
38 message = "networking.vswitches are not supported by networkd.";
39 } {
40 assertion = cfg.defaultGateway == null || cfg.defaultGateway.interface == null;
41 message = "networking.defaultGateway.interface is not supported by networkd.";
42 } {
43 assertion = cfg.defaultGateway6 == null || cfg.defaultGateway6.interface == null;
44 message = "networking.defaultGateway6.interface is not supported by networkd.";
45 } {
46 assertion = cfg.useDHCP == false;
47 message = ''
48 networking.useDHCP is not supported by networkd.
49 Please use per interface configuration and set the global option to false.
50 '';
51 } ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: {
52 assertion = !rstp;
53 message = "networking.bridges.${n}.rstp is not supported by networkd.";
54 });
55
56 networking.dhcpcd.enable = mkDefault false;
57
58 systemd.network =
59 let
60 domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain);
61 genericNetwork = override:
62 let gateways = optional (cfg.defaultGateway != null && (cfg.defaultGateway.address or "") != "") cfg.defaultGateway.address
63 ++ optional (cfg.defaultGateway6 != null && (cfg.defaultGateway6.address or "") != "") cfg.defaultGateway6.address;
64 in optionalAttrs (gateways != [ ]) {
65 routes = override (map (gateway: {
66 routeConfig = {
67 Gateway = gateway;
68 GatewayOnLink = false;
69 };
70 }) gateways);
71 } // optionalAttrs (domains != [ ]) {
72 domains = override domains;
73 };
74 in mkMerge [ {
75 enable = true;
76 }
77 (mkMerge (forEach interfaces (i: {
78 netdevs = mkIf i.virtual ({
79 "40-${i.name}" = {
80 netdevConfig = {
81 Name = i.name;
82 Kind = i.virtualType;
83 };
84 "${i.virtualType}Config" = optionalAttrs (i.virtualOwner != null) {
85 User = i.virtualOwner;
86 };
87 };
88 });
89 networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) {
90 name = mkDefault i.name;
91 DHCP = mkForce (dhcpStr
92 (if i.useDHCP != null then i.useDHCP else false));
93 address = forEach (interfaceIps i)
94 (ip: "${ip.address}/${toString ip.prefixLength}");
95 networkConfig.IPv6PrivacyExtensions = "kernel";
96 linkConfig = optionalAttrs (i.macAddress != null) {
97 MACAddress = i.macAddress;
98 } // optionalAttrs (i.mtu != null) {
99 MTUBytes = toString i.mtu;
100 };
101 }];
102 })))
103 (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: {
104 netdevs."40-${name}" = {
105 netdevConfig = {
106 Name = name;
107 Kind = "bridge";
108 };
109 };
110 networks = listToAttrs (forEach bridge.interfaces (bi:
111 nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
112 DHCP = mkOverride 0 (dhcpStr false);
113 networkConfig.Bridge = name;
114 } ])));
115 })))
116 (mkMerge (flip mapAttrsToList cfg.bonds (name: bond: {
117 netdevs."40-${name}" = {
118 netdevConfig = {
119 Name = name;
120 Kind = "bond";
121 };
122 bondConfig = let
123 # manual mapping as of 2017-02-03
124 # man 5 systemd.netdev [BOND]
125 # to https://www.kernel.org/doc/Documentation/networking/bonding.txt
126 # driver options.
127 driverOptionMapping = let
128 trans = f: optName: { valTransform = f; optNames = [optName]; };
129 simp = trans id;
130 ms = trans (v: v + "ms");
131 in {
132 Mode = simp "mode";
133 TransmitHashPolicy = simp "xmit_hash_policy";
134 LACPTransmitRate = simp "lacp_rate";
135 MIIMonitorSec = ms "miimon";
136 UpDelaySec = ms "updelay";
137 DownDelaySec = ms "downdelay";
138 LearnPacketIntervalSec = simp "lp_interval";
139 AdSelect = simp "ad_select";
140 FailOverMACPolicy = simp "fail_over_mac";
141 ARPValidate = simp "arp_validate";
142 # apparently in ms for this value?! Upstream bug?
143 ARPIntervalSec = simp "arp_interval";
144 ARPIPTargets = simp "arp_ip_target";
145 ARPAllTargets = simp "arp_all_targets";
146 PrimaryReselectPolicy = simp "primary_reselect";
147 ResendIGMP = simp "resend_igmp";
148 PacketsPerSlave = simp "packets_per_slave";
149 GratuitousARP = { valTransform = id;
150 optNames = [ "num_grat_arp" "num_unsol_na" ]; };
151 AllSlavesActive = simp "all_slaves_active";
152 MinLinks = simp "min_links";
153 };
154
155 do = bond.driverOptions;
156 assertNoUnknownOption = let
157 knownOptions = flatten (mapAttrsToList (_: kOpts: kOpts.optNames)
158 driverOptionMapping);
159 # options that apparently don’t exist in the networkd config
160 unknownOptions = [ "primary" ];
161 assertTrace = bool: msg: if bool then true else builtins.trace msg false;
162 in assert all (driverOpt: assertTrace
163 (elem driverOpt (knownOptions ++ unknownOptions))
164 "The bond.driverOption `${driverOpt}` cannot be mapped to the list of known networkd bond options. Please add it to the mapping above the assert or to `unknownOptions` should it not exist in networkd.")
165 (mapAttrsToList (k: _: k) do); "";
166 # get those driverOptions that have been set
167 filterSystemdOptions = filterAttrs (sysDOpt: kOpts:
168 any (kOpt: do ? ${kOpt}) kOpts.optNames);
169 # build final set of systemd options to bond values
170 buildOptionSet = mapAttrs (_: kOpts: with kOpts;
171 # we simply take the first set kernel bond option
172 # (one option has multiple names, which is silly)
173 head (map (optN: valTransform (do.${optN}))
174 # only map those that exist
175 (filter (o: do ? ${o}) optNames)));
176 in seq assertNoUnknownOption
177 (buildOptionSet (filterSystemdOptions driverOptionMapping));
178
179 };
180
181 networks = listToAttrs (forEach bond.interfaces (bi:
182 nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
183 DHCP = mkOverride 0 (dhcpStr false);
184 networkConfig.Bond = name;
185 } ])));
186 })))
187 (mkMerge (flip mapAttrsToList cfg.macvlans (name: macvlan: {
188 netdevs."40-${name}" = {
189 netdevConfig = {
190 Name = name;
191 Kind = "macvlan";
192 };
193 macvlanConfig = optionalAttrs (macvlan.mode != null) { Mode = macvlan.mode; };
194 };
195 networks."40-${macvlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
196 macvlan = [ name ];
197 } ]);
198 })))
199 (mkMerge (flip mapAttrsToList cfg.sits (name: sit: {
200 netdevs."40-${name}" = {
201 netdevConfig = {
202 Name = name;
203 Kind = "sit";
204 };
205 tunnelConfig =
206 (optionalAttrs (sit.remote != null) {
207 Remote = sit.remote;
208 }) // (optionalAttrs (sit.local != null) {
209 Local = sit.local;
210 }) // (optionalAttrs (sit.ttl != null) {
211 TTL = sit.ttl;
212 });
213 };
214 networks = mkIf (sit.dev != null) {
215 "40-${sit.dev}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
216 tunnel = [ name ];
217 } ]);
218 };
219 })))
220 (mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: {
221 netdevs."40-${name}" = {
222 netdevConfig = {
223 Name = name;
224 Kind = "vlan";
225 };
226 vlanConfig.Id = vlan.id;
227 };
228 networks."40-${vlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
229 vlan = [ name ];
230 } ]);
231 })))
232 ];
233
234 # We need to prefill the slaved devices with networking options
235 # This forces the network interface creator to initialize slaves.
236 networking.interfaces = listToAttrs (map (i: nameValuePair i { }) slaves);
237
238 systemd.services = let
239 # We must escape interfaces due to the systemd interpretation
240 subsystemDevice = interface:
241 "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
242 # support for creating openvswitch switches
243 createVswitchDevice = n: v: nameValuePair "${n}-netdev"
244 (let
245 deps = map subsystemDevice (attrNames (filterAttrs (_: config: config.type != "internal") v.interfaces));
246 ofRules = pkgs.writeText "vswitch-${n}-openFlowRules" v.openFlowRules;
247 in
248 { description = "Open vSwitch Interface ${n}";
249 wantedBy = [ "network.target" (subsystemDevice n) ];
250 # and create bridge before systemd-networkd starts because it might create internal interfaces
251 before = [ "systemd-networkd.service" ];
252 # shutdown the bridge when network is shutdown
253 partOf = [ "network.target" ];
254 # requires ovs-vswitchd to be alive at all times
255 bindsTo = [ "ovs-vswitchd.service" ];
256 # start switch after physical interfaces and vswitch daemon
257 after = [ "network-pre.target" "ovs-vswitchd.service" ] ++ deps;
258 wants = deps; # if one or more interface fails, the switch should continue to run
259 serviceConfig.Type = "oneshot";
260 serviceConfig.RemainAfterExit = true;
261 path = [ pkgs.iproute2 config.virtualisation.vswitch.package ];
262 preStart = ''
263 echo "Resetting Open vSwitch ${n}..."
264 ovs-vsctl --if-exists del-br ${n} -- add-br ${n} \
265 -- set bridge ${n} protocols=${concatStringsSep "," v.supportedOpenFlowVersions}
266 '';
267 script = ''
268 echo "Configuring Open vSwitch ${n}..."
269 ovs-vsctl ${concatStrings (mapAttrsToList (name: config: " -- add-port ${n} ${name}" + optionalString (config.vlan != null) " tag=${toString config.vlan}") v.interfaces)} \
270 ${concatStrings (mapAttrsToList (name: config: optionalString (config.type != null) " -- set interface ${name} type=${config.type}") v.interfaces)} \
271 ${concatMapStrings (x: " -- set-controller ${n} " + x) v.controllers} \
272 ${concatMapStrings (x: " -- " + x) (splitString "\n" v.extraOvsctlCmds)}
273
274
275 echo "Adding OpenFlow rules for Open vSwitch ${n}..."
276 ovs-ofctl --protocols=${v.openFlowVersion} add-flows ${n} ${ofRules}
277 '';
278 postStop = ''
279 echo "Cleaning Open vSwitch ${n}"
280 echo "Shuting down internal ${n} interface"
281 ip link set ${n} down || true
282 echo "Deleting flows for ${n}"
283 ovs-ofctl --protocols=${v.openFlowVersion} del-flows ${n} || true
284 echo "Deleting Open vSwitch ${n}"
285 ovs-vsctl --if-exists del-br ${n} || true
286 '';
287 });
288 in mapAttrs' createVswitchDevice cfg.vswitches
289 // {
290 "network-local-commands" = {
291 after = [ "systemd-networkd.service" ];
292 bindsTo = [ "systemd-networkd.service" ];
293 };
294 };
295 };
296
297}
diff --git a/modules/networkd/default.nix b/modules/networkd/default.nix
new file mode 100644
index 00000000..007f14c6
--- /dev/null
+++ b/modules/networkd/default.nix
@@ -0,0 +1,1686 @@
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 check = {
12
13 link = {
14
15 sectionLink = checkUnitConfig "Link" [
16 (assertOnlyFields [
17 "Description"
18 "Alias"
19 "MACAddressPolicy"
20 "MACAddress"
21 "NamePolicy"
22 "Name"
23 "AlternativeNamesPolicy"
24 "AlternativeName"
25 "MTUBytes"
26 "BitsPerSecond"
27 "Duplex"
28 "AutoNegotiation"
29 "WakeOnLan"
30 "Port"
31 "Advertise"
32 "ReceiveChecksumOffload"
33 "TransmitChecksumOffload"
34 "TCPSegmentationOffload"
35 "TCP6SegmentationOffload"
36 "GenericSegmentationOffload"
37 "GenericReceiveOffload"
38 "LargeReceiveOffload"
39 "RxChannels"
40 "TxChannels"
41 "OtherChannels"
42 "CombinedChannels"
43 "RxBufferSize"
44 "TxBufferSize"
45 ])
46 (assertValueOneOf "MACAddressPolicy" ["persistent" "random" "none"])
47 (assertMacAddress "MACAddress")
48 (assertByteFormat "MTUBytes")
49 (assertByteFormat "BitsPerSecond")
50 (assertValueOneOf "Duplex" ["half" "full"])
51 (assertValueOneOf "AutoNegotiation" boolValues)
52 (assertValueOneOf "WakeOnLan" ["phy" "unicast" "multicast" "broadcast" "arp" "magic" "secureon" "off"])
53 (assertValueOneOf "Port" ["tp" "aui" "bnc" "mii" "fibre"])
54 (assertValueOneOf "ReceiveChecksumOffload" boolValues)
55 (assertValueOneOf "TransmitChecksumOffload" boolValues)
56 (assertValueOneOf "TCPSegmentationOffload" boolValues)
57 (assertValueOneOf "TCP6SegmentationOffload" boolValues)
58 (assertValueOneOf "GenericSegmentationOffload" boolValues)
59 (assertValueOneOf "GenericReceiveOffload" boolValues)
60 (assertValueOneOf "LargeReceiveOffload" boolValues)
61 (assertInt "RxChannels")
62 (assertRange "RxChannels" 1 4294967295)
63 (assertInt "TxChannels")
64 (assertRange "TxChannels" 1 4294967295)
65 (assertInt "OtherChannels")
66 (assertRange "OtherChannels" 1 4294967295)
67 (assertInt "CombinedChannels")
68 (assertRange "CombinedChannels" 1 4294967295)
69 (assertInt "RxBufferSize")
70 (assertInt "TxBufferSize")
71 ];
72 };
73
74 netdev = let
75
76 tunChecks = [
77 (assertOnlyFields [
78 "MultiQueue"
79 "PacketInfo"
80 "VNetHeader"
81 "User"
82 "Group"
83 ])
84 (assertValueOneOf "MultiQueue" boolValues)
85 (assertValueOneOf "PacketInfo" boolValues)
86 (assertValueOneOf "VNetHeader" boolValues)
87 ];
88 in {
89
90 sectionNetdev = checkUnitConfig "Netdev" [
91 (assertOnlyFields [
92 "Description"
93 "Name"
94 "Kind"
95 "MTUBytes"
96 "MACAddress"
97 ])
98 (assertHasField "Name")
99 (assertHasField "Kind")
100 (assertValueOneOf "Kind" [
101 "bond"
102 "bridge"
103 "dummy"
104 "gre"
105 "gretap"
106 "erspan"
107 "ip6gre"
108 "ip6tnl"
109 "ip6gretap"
110 "ipip"
111 "ipvlan"
112 "macvlan"
113 "macvtap"
114 "sit"
115 "tap"
116 "tun"
117 "veth"
118 "vlan"
119 "vti"
120 "vti6"
121 "vxlan"
122 "geneve"
123 "l2tp"
124 "macsec"
125 "vrf"
126 "vcan"
127 "vxcan"
128 "wireguard"
129 "netdevsim"
130 "nlmon"
131 "fou"
132 "xfrm"
133 "ifb"
134 "bareudp"
135 "batadv"
136 ])
137 (assertByteFormat "MTUBytes")
138 (assertMacAddress "MACAddress")
139 ];
140
141 sectionVLAN = checkUnitConfig "VLAN" [
142 (assertOnlyFields [
143 "Id"
144 "GVRP"
145 "MVRP"
146 "LooseBinding"
147 "ReorderHeader"
148 ])
149 (assertInt "Id")
150 (assertRange "Id" 0 4094)
151 (assertValueOneOf "GVRP" boolValues)
152 (assertValueOneOf "MVRP" boolValues)
153 (assertValueOneOf "LooseBinding" boolValues)
154 (assertValueOneOf "ReorderHeader" boolValues)
155 ];
156
157 sectionMACVLAN = checkUnitConfig "MACVLAN" [
158 (assertOnlyFields [
159 "Mode"
160 ])
161 (assertValueOneOf "Mode" ["private" "vepa" "bridge" "passthru"])
162 ];
163
164 sectionVXLAN = checkUnitConfig "VXLAN" [
165 (assertOnlyFields [
166 "VNI"
167 "Remote"
168 "Local"
169 "Group"
170 "TOS"
171 "TTL"
172 "MacLearning"
173 "FDBAgeingSec"
174 "MaximumFDBEntries"
175 "ReduceARPProxy"
176 "L2MissNotification"
177 "L3MissNotification"
178 "RouteShortCircuit"
179 "UDPChecksum"
180 "UDP6ZeroChecksumTx"
181 "UDP6ZeroChecksumRx"
182 "RemoteChecksumTx"
183 "RemoteChecksumRx"
184 "GroupPolicyExtension"
185 "GenericProtocolExtension"
186 "DestinationPort"
187 "PortRange"
188 "FlowLabel"
189 "IPDoNotFragment"
190 ])
191 (assertInt "VNI")
192 (assertRange "VNI" 1 16777215)
193 (assertValueOneOf "MacLearning" boolValues)
194 (assertInt "MaximumFDBEntries")
195 (assertValueOneOf "ReduceARPProxy" boolValues)
196 (assertValueOneOf "L2MissNotification" boolValues)
197 (assertValueOneOf "L3MissNotification" boolValues)
198 (assertValueOneOf "RouteShortCircuit" boolValues)
199 (assertValueOneOf "UDPChecksum" boolValues)
200 (assertValueOneOf "UDP6ZeroChecksumTx" boolValues)
201 (assertValueOneOf "UDP6ZeroChecksumRx" boolValues)
202 (assertValueOneOf "RemoteChecksumTx" boolValues)
203 (assertValueOneOf "RemoteChecksumRx" boolValues)
204 (assertValueOneOf "GroupPolicyExtension" boolValues)
205 (assertValueOneOf "GenericProtocolExtension" boolValues)
206 (assertInt "FlowLabel")
207 (assertRange "FlowLabel" 0 1048575)
208 (assertValueOneOf "IPDoNotFragment" (boolValues + ["inherit"]))
209 ];
210
211 sectionTunnel = checkUnitConfig "Tunnel" [
212 (assertOnlyFields [
213 "Local"
214 "Remote"
215 "TOS"
216 "TTL"
217 "DiscoverPathMTU"
218 "IPv6FlowLabel"
219 "CopyDSCP"
220 "EncapsulationLimit"
221 "Key"
222 "InputKey"
223 "OutputKey"
224 "Mode"
225 "Independent"
226 "AssignToLoopback"
227 "AllowLocalRemote"
228 "FooOverUDP"
229 "FOUDestinationPort"
230 "FOUSourcePort"
231 "Encapsulation"
232 "IPv6RapidDeploymentPrefix"
233 "ISATAP"
234 "SerializeTunneledPackets"
235 "ERSPANIndex"
236 ])
237 (assertInt "TTL")
238 (assertRange "TTL" 0 255)
239 (assertValueOneOf "DiscoverPathMTU" boolValues)
240 (assertValueOneOf "CopyDSCP" boolValues)
241 (assertValueOneOf "Mode" ["ip6ip6" "ipip6" "any"])
242 (assertValueOneOf "Independent" boolValues)
243 (assertValueOneOf "AssignToLoopback" boolValues)
244 (assertValueOneOf "AllowLocalRemote" boolValues)
245 (assertValueOneOf "FooOverUDP" boolValues)
246 (assertPort "FOUDestinationPort")
247 (assertPort "FOUSourcePort")
248 (assertValueOneOf "Encapsulation" ["FooOverUDP" "GenericUDPEncapsulation"])
249 (assertValueOneOf "ISATAP" boolValues)
250 (assertValueOneOf "SerializeTunneledPackets" boolValues)
251 (assertInt "ERSPANIndex")
252 (assertRange "ERSPANIndex" 1 1048575)
253 ];
254
255 sectionPeer = checkUnitConfig "Peer" [
256 (assertOnlyFields [
257 "Name"
258 "MACAddress"
259 ])
260 (assertMacAddress "MACAddress")
261 ];
262
263 sectionTun = checkUnitConfig "Tun" tunChecks;
264
265 sectionTap = checkUnitConfig "Tap" tunChecks;
266
267 # NOTE The PrivateKey directive is missing on purpose here, please
268 # do not add it to this list. The nix store is world-readable let's
269 # refrain ourselves from providing a footgun.
270 sectionWireGuard = checkUnitConfig "WireGuard" [
271 (assertOnlyFields [
272 "PrivateKeyFile"
273 "ListenPort"
274 "FirewallMark"
275 ])
276 (assertInt "FirewallMark")
277 (assertRange "FirewallMark" 1 4294967295)
278 ];
279
280 # NOTE The PresharedKey directive is missing on purpose here, please
281 # do not add it to this list. The nix store is world-readable,let's
282 # refrain ourselves from providing a footgun.
283 sectionWireGuardPeer = checkUnitConfig "WireGuardPeer" [
284 (assertOnlyFields [
285 "PublicKey"
286 "PresharedKeyFile"
287 "AllowedIPs"
288 "Endpoint"
289 "PersistentKeepalive"
290 ])
291 (assertInt "PersistentKeepalive")
292 (assertRange "PersistentKeepalive" 0 65535)
293 ];
294
295 sectionBond = checkUnitConfig "Bond" [
296 (assertOnlyFields [
297 "Mode"
298 "TransmitHashPolicy"
299 "LACPTransmitRate"
300 "MIIMonitorSec"
301 "UpDelaySec"
302 "DownDelaySec"
303 "LearnPacketIntervalSec"
304 "AdSelect"
305 "AdActorSystemPriority"
306 "AdUserPortKey"
307 "AdActorSystem"
308 "FailOverMACPolicy"
309 "ARPValidate"
310 "ARPIntervalSec"
311 "ARPIPTargets"
312 "ARPAllTargets"
313 "PrimaryReselectPolicy"
314 "ResendIGMP"
315 "PacketsPerSlave"
316 "GratuitousARP"
317 "AllSlavesActive"
318 "DynamicTransmitLoadBalancing"
319 "MinLinks"
320 ])
321 (assertValueOneOf "Mode" [
322 "balance-rr"
323 "active-backup"
324 "balance-xor"
325 "broadcast"
326 "802.3ad"
327 "balance-tlb"
328 "balance-alb"
329 ])
330 (assertValueOneOf "TransmitHashPolicy" [
331 "layer2"
332 "layer3+4"
333 "layer2+3"
334 "encap2+3"
335 "encap3+4"
336 ])
337 (assertValueOneOf "LACPTransmitRate" ["slow" "fast"])
338 (assertValueOneOf "AdSelect" ["stable" "bandwidth" "count"])
339 (assertInt "AdActorSystemPriority")
340 (assertRange "AdActorSystemPriority" 1 65535)
341 (assertInt "AdUserPortKey")
342 (assertRange "AdUserPortKey" 0 1023)
343 (assertValueOneOf "FailOverMACPolicy" ["none" "active" "follow"])
344 (assertValueOneOf "ARPValidate" ["none" "active" "backup" "all"])
345 (assertValueOneOf "ARPAllTargets" ["any" "all"])
346 (assertValueOneOf "PrimaryReselectPolicy" ["always" "better" "failure"])
347 (assertInt "ResendIGMP")
348 (assertRange "ResendIGMP" 0 255)
349 (assertInt "PacketsPerSlave")
350 (assertRange "PacketsPerSlave" 0 65535)
351 (assertInt "GratuitousARP")
352 (assertRange "GratuitousARP" 0 255)
353 (assertValueOneOf "AllSlavesActive" boolValues)
354 (assertValueOneOf "DynamicTransmitLoadBalancing" boolValues)
355 (assertInt "MinLinks")
356 (assertMinimum "MinLinks" 0)
357 ];
358
359 sectionXfrm = checkUnitConfig "Xfrm" [
360 (assertOnlyFields [
361 "InterfaceId"
362 "Independent"
363 ])
364 (assertInt "InterfaceId")
365 (assertRange "InterfaceId" 1 4294967295)
366 (assertValueOneOf "Independent" boolValues)
367 ];
368
369 sectionVRF = checkUnitConfig "VRF" [
370 (assertOnlyFields [
371 "Table"
372 ])
373 (assertInt "Table")
374 (assertMinimum "Table" 0)
375 ];
376 };
377
378 network = {
379
380 sectionLink = checkUnitConfig "Link" [
381 (assertOnlyFields [
382 "MACAddress"
383 "MTUBytes"
384 "ARP"
385 "Multicast"
386 "AllMulticast"
387 "Unmanaged"
388 "RequiredForOnline"
389 "ActivationPolicy"
390 ])
391 (assertMacAddress "MACAddress")
392 (assertByteFormat "MTUBytes")
393 (assertValueOneOf "ARP" boolValues)
394 (assertValueOneOf "Multicast" boolValues)
395 (assertValueOneOf "AllMulticast" boolValues)
396 (assertValueOneOf "Unmanaged" boolValues)
397 (assertValueOneOf "RequiredForOnline" (boolValues ++ [
398 "missing"
399 "off"
400 "no-carrier"
401 "dormant"
402 "degraded-carrier"
403 "carrier"
404 "degraded"
405 "enslaved"
406 "routable"
407 ]))
408 (assertValueOneOf "ActivationPolicy" ([
409 "up"
410 "always-up"
411 "manual"
412 "always-down"
413 "down"
414 "bound"
415 ]))
416 ];
417
418 sectionNetwork = checkUnitConfig "Network" [
419 (assertOnlyFields [
420 "Description"
421 "DHCP"
422 "DHCPServer"
423 "LinkLocalAddressing"
424 "IPv4LLRoute"
425 "DefaultRouteOnDevice"
426 "IPv6Token"
427 "LLMNR"
428 "MulticastDNS"
429 "DNSOverTLS"
430 "DNSSEC"
431 "DNSSECNegativeTrustAnchors"
432 "LLDP"
433 "EmitLLDP"
434 "BindCarrier"
435 "Address"
436 "Gateway"
437 "DNS"
438 "Domains"
439 "DNSDefaultRoute"
440 "NTP"
441 "IPForward"
442 "IPMasquerade"
443 "IPv6PrivacyExtensions"
444 "IPv6AcceptRA"
445 "IPv6DuplicateAddressDetection"
446 "IPv6HopLimit"
447 "IPv4ProxyARP"
448 "IPv6ProxyNDP"
449 "IPv6ProxyNDPAddress"
450 "IPv6SendRA"
451 "DHCPv6PrefixDelegation"
452 "IPv6MTUBytes"
453 "BatmanAdvanced"
454 "Bridge"
455 "Bond"
456 "VRF"
457 "VLAN"
458 "IPVLAN"
459 "MACVLAN"
460 "VXLAN"
461 "Tunnel"
462 "MACsec"
463 "ActiveSlave"
464 "PrimarySlave"
465 "ConfigureWithoutCarrier"
466 "IgnoreCarrierLoss"
467 "Xfrm"
468 "KeepConfiguration"
469 ])
470 # Note: For DHCP the values both, none, v4, v6 are deprecated
471 (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6"])
472 (assertValueOneOf "DHCPServer" boolValues)
473 (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6" "fallback" "ipv4-fallback"])
474 (assertValueOneOf "IPv4LLRoute" boolValues)
475 (assertValueOneOf "DefaultRouteOnDevice" boolValues)
476 (assertValueOneOf "LLMNR" (boolValues ++ ["resolve"]))
477 (assertValueOneOf "MulticastDNS" (boolValues ++ ["resolve"]))
478 (assertValueOneOf "DNSOverTLS" (boolValues ++ ["opportunistic"]))
479 (assertValueOneOf "DNSSEC" (boolValues ++ ["allow-downgrade"]))
480 (assertValueOneOf "LLDP" (boolValues ++ ["routers-only"]))
481 (assertValueOneOf "EmitLLDP" (boolValues ++ ["nearest-bridge" "non-tpmr-bridge" "customer-bridge"]))
482 (assertValueOneOf "DNSDefaultRoute" boolValues)
483 (assertValueOneOf "IPForward" (boolValues ++ ["ipv4" "ipv6"]))
484 (assertValueOneOf "IPMasquerade" boolValues)
485 (assertValueOneOf "IPv6PrivacyExtensions" (boolValues ++ ["prefer-public" "kernel"]))
486 (assertValueOneOf "IPv6AcceptRA" boolValues)
487 (assertInt "IPv6DuplicateAddressDetection")
488 (assertMinimum "IPv6DuplicateAddressDetection" 0)
489 (assertInt "IPv6HopLimit")
490 (assertMinimum "IPv6HopLimit" 0)
491 (assertValueOneOf "IPv4ProxyARP" boolValues)
492 (assertValueOneOf "IPv6ProxyNDP" boolValues)
493 (assertValueOneOf "IPv6SendRA" boolValues)
494 (assertValueOneOf "DHCPv6PrefixDelegation" boolValues)
495 (assertByteFormat "IPv6MTUBytes")
496 (assertValueOneOf "ActiveSlave" boolValues)
497 (assertValueOneOf "PrimarySlave" boolValues)
498 (assertValueOneOf "ConfigureWithoutCarrier" boolValues)
499 (assertValueOneOf "IgnoreCarrierLoss" boolValues)
500 (assertValueOneOf "KeepConfiguration" (boolValues ++ ["static" "dhcp-on-stop" "dhcp"]))
501 ];
502
503 sectionAddress = checkUnitConfig "Address" [
504 (assertOnlyFields [
505 "Address"
506 "Peer"
507 "Broadcast"
508 "Label"
509 "PreferredLifetime"
510 "Scope"
511 "HomeAddress"
512 "DuplicateAddressDetection"
513 "ManageTemporaryAddress"
514 "AddPrefixRoute"
515 "AutoJoin"
516 ])
517 (assertHasField "Address")
518 (assertValueOneOf "PreferredLifetime" ["forever" "infinity" "0" 0])
519 (assertValueOneOf "HomeAddress" boolValues)
520 (assertValueOneOf "DuplicateAddressDetection" ["ipv4" "ipv6" "both" "none"])
521 (assertValueOneOf "ManageTemporaryAddress" boolValues)
522 (assertValueOneOf "AddPrefixRoute" boolValues)
523 (assertValueOneOf "AutoJoin" boolValues)
524 ];
525
526 sectionRoutingPolicyRule = checkUnitConfig "RoutingPolicyRule" [
527 (assertOnlyFields [
528 "TypeOfService"
529 "From"
530 "To"
531 "FirewallMark"
532 "Table"
533 "Priority"
534 "IncomingInterface"
535 "OutgoingInterface"
536 "SourcePort"
537 "DestinationPort"
538 "IPProtocol"
539 "InvertRule"
540 "Family"
541 "User"
542 "SuppressPrefixLength"
543 ])
544 (assertInt "TypeOfService")
545 (assertRange "TypeOfService" 0 255)
546 (assertInt "FirewallMark")
547 (assertRange "FirewallMark" 1 4294967295)
548 (assertInt "Priority")
549 (assertPort "SourcePort")
550 (assertPort "DestinationPort")
551 (assertValueOneOf "InvertRule" boolValues)
552 (assertValueOneOf "Family" ["ipv4" "ipv6" "both"])
553 (assertInt "SuppressPrefixLength")
554 (assertRange "SuppressPrefixLength" 0 128)
555 ];
556
557 sectionRoute = checkUnitConfig "Route" [
558 (assertOnlyFields [
559 "Gateway"
560 "GatewayOnLink"
561 "Destination"
562 "Source"
563 "Metric"
564 "IPv6Preference"
565 "Scope"
566 "PreferredSource"
567 "Table"
568 "Protocol"
569 "Type"
570 "InitialCongestionWindow"
571 "InitialAdvertisedReceiveWindow"
572 "QuickAck"
573 "FastOpenNoCookie"
574 "TTLPropagate"
575 "MTUBytes"
576 "IPServiceType"
577 "MultiPathRoute"
578 ])
579 (assertValueOneOf "GatewayOnLink" boolValues)
580 (assertInt "Metric")
581 (assertValueOneOf "IPv6Preference" ["low" "medium" "high"])
582 (assertValueOneOf "Scope" ["global" "site" "link" "host" "nowhere"])
583 (assertValueOneOf "Type" [
584 "unicast"
585 "local"
586 "broadcast"
587 "anycast"
588 "multicast"
589 "blackhole"
590 "unreachable"
591 "prohibit"
592 "throw"
593 "nat"
594 "xresolve"
595 ])
596 (assertValueOneOf "QuickAck" boolValues)
597 (assertValueOneOf "FastOpenNoCookie" boolValues)
598 (assertValueOneOf "TTLPropagate" boolValues)
599 (assertByteFormat "MTUBytes")
600 (assertValueOneOf "IPServiceType" ["CS6" "CS4"])
601 ];
602
603 sectionDHCPv4 = checkUnitConfig "DHCPv4" [
604 (assertOnlyFields [
605 "UseDNS"
606 "RoutesToDNS"
607 "UseNTP"
608 "UseSIP"
609 "UseMTU"
610 "Anonymize"
611 "SendHostname"
612 "UseHostname"
613 "Hostname"
614 "UseDomains"
615 "UseRoutes"
616 "UseTimezone"
617 "ClientIdentifier"
618 "VendorClassIdentifier"
619 "UserClass"
620 "MaxAttempts"
621 "DUIDType"
622 "DUIDRawData"
623 "IAID"
624 "RequestBroadcast"
625 "RouteMetric"
626 "RouteTable"
627 "RouteMTUBytes"
628 "ListenPort"
629 "SendRelease"
630 "SendDecline"
631 "BlackList"
632 "RequestOptions"
633 "SendOption"
634 ])
635 (assertValueOneOf "UseDNS" boolValues)
636 (assertValueOneOf "RoutesToDNS" boolValues)
637 (assertValueOneOf "UseNTP" boolValues)
638 (assertValueOneOf "UseSIP" boolValues)
639 (assertValueOneOf "UseMTU" boolValues)
640 (assertValueOneOf "Anonymize" boolValues)
641 (assertValueOneOf "SendHostname" boolValues)
642 (assertValueOneOf "UseHostname" boolValues)
643 (assertValueOneOf "UseDomains" (boolValues ++ ["route"]))
644 (assertValueOneOf "UseRoutes" boolValues)
645 (assertValueOneOf "UseTimezone" boolValues)
646 (assertValueOneOf "ClientIdentifier" ["mac" "duid" "duid-only"])
647 (assertInt "IAID")
648 (assertValueOneOf "RequestBroadcast" boolValues)
649 (assertInt "RouteMetric")
650 (assertInt "RouteTable")
651 (assertRange "RouteTable" 0 4294967295)
652 (assertByteFormat "RouteMTUBytes")
653 (assertPort "ListenPort")
654 (assertValueOneOf "SendRelease" boolValues)
655 (assertValueOneOf "SendDecline" boolValues)
656 ];
657
658 sectionDHCPv6 = checkUnitConfig "DHCPv6" [
659 (assertOnlyFields [
660 "UseAddress"
661 "UseDNS"
662 "UseNTP"
663 "RouteMetric"
664 "RapidCommit"
665 "MUDURL"
666 "RequestOptions"
667 "SendVendorOption"
668 "ForceDHCPv6PDOtherInformation"
669 "PrefixDelegationHint"
670 "WithoutRA"
671 "SendOption"
672 "UserClass"
673 "VendorClass"
674 ])
675 (assertValueOneOf "UseAddress" boolValues)
676 (assertValueOneOf "UseDNS" boolValues)
677 (assertValueOneOf "UseNTP" boolValues)
678 (assertInt "RouteMetric")
679 (assertValueOneOf "RapidCommit" boolValues)
680 (assertValueOneOf "ForceDHCPv6PDOtherInformation" boolValues)
681 (assertValueOneOf "WithoutRA" ["solicit" "information-request"])
682 (assertRange "SendOption" 1 65536)
683 ];
684
685 sectionDHCPv6PrefixDelegation = checkUnitConfig "DHCPv6PrefixDelegation" [
686 (assertOnlyFields [
687 "SubnetId"
688 "Announce"
689 "Assign"
690 "Token"
691 ])
692 (assertValueOneOf "Announce" boolValues)
693 (assertValueOneOf "Assign" boolValues)
694 ];
695
696 sectionIPv6AcceptRA = checkUnitConfig "IPv6AcceptRA" [
697 (assertOnlyFields [
698 "UseDNS"
699 "UseDomains"
700 "RouteTable"
701 "UseAutonomousPrefix"
702 "UseOnLinkPrefix"
703 "RouterDenyList"
704 "RouterAllowList"
705 "PrefixDenyList"
706 "PrefixAllowList"
707 "RouteDenyList"
708 "RouteAllowList"
709 "DHCPv6Client"
710 ])
711 (assertValueOneOf "UseDNS" boolValues)
712 (assertValueOneOf "UseDomains" (boolValues ++ ["route"]))
713 (assertRange "RouteTable" 0 4294967295)
714 (assertValueOneOf "UseAutonomousPrefix" boolValues)
715 (assertValueOneOf "UseOnLinkPrefix" boolValues)
716 (assertValueOneOf "DHCPv6Client" (boolValues ++ ["always"]))
717 ];
718
719 sectionDHCPServer = checkUnitConfig "DHCPServer" [
720 (assertOnlyFields [
721 "PoolOffset"
722 "PoolSize"
723 "DefaultLeaseTimeSec"
724 "MaxLeaseTimeSec"
725 "EmitDNS"
726 "DNS"
727 "EmitNTP"
728 "NTP"
729 "EmitSIP"
730 "SIP"
731 "EmitPOP3"
732 "POP3"
733 "EmitSMTP"
734 "SMTP"
735 "EmitLPR"
736 "LPR"
737 "EmitRouter"
738 "EmitTimezone"
739 "Timezone"
740 "SendOption"
741 "SendVendorOption"
742 ])
743 (assertInt "PoolOffset")
744 (assertMinimum "PoolOffset" 0)
745 (assertInt "PoolSize")
746 (assertMinimum "PoolSize" 0)
747 (assertValueOneOf "EmitDNS" boolValues)
748 (assertValueOneOf "EmitNTP" boolValues)
749 (assertValueOneOf "EmitSIP" boolValues)
750 (assertValueOneOf "EmitPOP3" boolValues)
751 (assertValueOneOf "EmitSMTP" boolValues)
752 (assertValueOneOf "EmitLPR" boolValues)
753 (assertValueOneOf "EmitRouter" boolValues)
754 (assertValueOneOf "EmitTimezone" boolValues)
755 ];
756
757 sectionIPv6SendRA = checkUnitConfig "IPv6SendRA" [
758 (assertOnlyFields [
759 "Managed"
760 "OtherInformation"
761 "RouterLifetimeSec"
762 "RouterPreference"
763 "EmitDNS"
764 "DNS"
765 "EmitDomains"
766 "Domains"
767 "DNSLifetimeSec"
768 ])
769 (assertValueOneOf "Managed" boolValues)
770 (assertValueOneOf "OtherInformation" boolValues)
771 (assertValueOneOf "RouterPreference" ["high" "medium" "low" "normal" "default"])
772 (assertValueOneOf "EmitDNS" boolValues)
773 (assertValueOneOf "EmitDomains" boolValues)
774 ];
775
776 sectionIPv6Prefix = checkUnitConfig "IPv6Prefix" [
777 (assertOnlyFields [
778 "AddressAutoconfiguration"
779 "OnLink"
780 "Prefix"
781 "PreferredLifetimeSec"
782 "ValidLifetimeSec"
783 ])
784 (assertValueOneOf "AddressAutoconfiguration" boolValues)
785 (assertValueOneOf "OnLink" boolValues)
786 ];
787
788 };
789 };
790
791 commonNetworkOptions = {
792
793 enable = mkOption {
794 default = true;
795 type = types.bool;
796 description = ''
797 Whether to manage network configuration using <command>systemd-network</command>.
798 '';
799 };
800
801 matchConfig = mkOption {
802 default = {};
803 example = { Name = "eth0"; };
804 type = types.attrsOf unitOption;
805 description = ''
806 Each attribute in this set specifies an option in the
807 <literal>[Match]</literal> section of the unit. See
808 <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
809 <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>
810 <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
811 for details.
812 '';
813 };
814
815 extraConfig = mkOption {
816 default = "";
817 type = types.lines;
818 description = "Extra configuration append to unit";
819 };
820 };
821
822 linkOptions = commonNetworkOptions // {
823 # overwrite enable option from above
824 enable = mkOption {
825 default = true;
826 type = types.bool;
827 description = ''
828 Whether to enable this .link unit. It's handled by udev no matter if <command>systemd-networkd</command> is enabled or not
829 '';
830 };
831
832 linkConfig = mkOption {
833 default = {};
834 example = { MACAddress = "00:ff:ee:aa:cc:dd"; };
835 type = types.addCheck (types.attrsOf unitOption) check.link.sectionLink;
836 description = ''
837 Each attribute in this set specifies an option in the
838 <literal>[Link]</literal> section of the unit. See
839 <citerefentry><refentrytitle>systemd.link</refentrytitle>
840 <manvolnum>5</manvolnum></citerefentry> for details.
841 '';
842 };
843
844 };
845
846 wireguardPeerOptions = {
847 options = {
848 wireguardPeerConfig = mkOption {
849 default = {};
850 example = { };
851 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWireGuardPeer;
852 description = ''
853 Each attribute in this set specifies an option in the
854 <literal>[WireGuardPeer]</literal> section of the unit. See
855 <citerefentry><refentrytitle>systemd.network</refentrytitle>
856 <manvolnum>5</manvolnum></citerefentry> for details.
857 '';
858 };
859 };
860 };
861
862 netdevOptions = commonNetworkOptions // {
863
864 netdevConfig = mkOption {
865 default = {};
866 example = { Name = "mybridge"; Kind = "bridge"; };
867 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionNetdev;
868 description = ''
869 Each attribute in this set specifies an option in the
870 <literal>[Netdev]</literal> section of the unit. See
871 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
872 <manvolnum>5</manvolnum></citerefentry> for details.
873 '';
874 };
875
876 vlanConfig = mkOption {
877 default = {};
878 example = { Id = 4; };
879 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionVLAN;
880 description = ''
881 Each attribute in this set specifies an option in the
882 <literal>[VLAN]</literal> section of the unit. See
883 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
884 <manvolnum>5</manvolnum></citerefentry> for details.
885 '';
886 };
887
888 macvlanConfig = mkOption {
889 default = {};
890 example = { Mode = "private"; };
891 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionMACVLAN;
892 description = ''
893 Each attribute in this set specifies an option in the
894 <literal>[MACVLAN]</literal> section of the unit. See
895 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
896 <manvolnum>5</manvolnum></citerefentry> for details.
897 '';
898 };
899
900 vxlanConfig = mkOption {
901 default = {};
902 example = { Id = "4"; };
903 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionVXLAN;
904 description = ''
905 Each attribute in this set specifies an option in the
906 <literal>[VXLAN]</literal> section of the unit. See
907 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
908 <manvolnum>5</manvolnum></citerefentry> for details.
909 '';
910 };
911
912 tunnelConfig = mkOption {
913 default = {};
914 example = { Remote = "192.168.1.1"; };
915 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionTunnel;
916 description = ''
917 Each attribute in this set specifies an option in the
918 <literal>[Tunnel]</literal> section of the unit. See
919 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
920 <manvolnum>5</manvolnum></citerefentry> for details.
921 '';
922 };
923
924 peerConfig = mkOption {
925 default = {};
926 example = { Name = "veth2"; };
927 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionPeer;
928 description = ''
929 Each attribute in this set specifies an option in the
930 <literal>[Peer]</literal> section of the unit. See
931 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
932 <manvolnum>5</manvolnum></citerefentry> for details.
933 '';
934 };
935
936 tunConfig = mkOption {
937 default = {};
938 example = { User = "openvpn"; };
939 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionTun;
940 description = ''
941 Each attribute in this set specifies an option in the
942 <literal>[Tun]</literal> section of the unit. See
943 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
944 <manvolnum>5</manvolnum></citerefentry> for details.
945 '';
946 };
947
948 tapConfig = mkOption {
949 default = {};
950 example = { User = "openvpn"; };
951 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionTap;
952 description = ''
953 Each attribute in this set specifies an option in the
954 <literal>[Tap]</literal> section of the unit. See
955 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
956 <manvolnum>5</manvolnum></citerefentry> for details.
957 '';
958 };
959
960 wireguardConfig = mkOption {
961 default = {};
962 example = {
963 PrivateKeyFile = "/etc/wireguard/secret.key";
964 ListenPort = 51820;
965 FwMark = 42;
966 };
967 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWireGuard;
968 description = ''
969 Each attribute in this set specifies an option in the
970 <literal>[WireGuard]</literal> section of the unit. See
971 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
972 <manvolnum>5</manvolnum></citerefentry> for details.
973 Use <literal>PrivateKeyFile</literal> instead of
974 <literal>PrivateKey</literal>: the nix store is
975 world-readable.
976 '';
977 };
978
979 wireguardPeers = mkOption {
980 default = [];
981 example = [ { wireguardPeerConfig={
982 Endpoint = "192.168.1.1:51820";
983 PublicKey = "27s0OvaBBdHoJYkH9osZpjpgSOVNw+RaKfboT/Sfq0g=";
984 PresharedKeyFile = "/etc/wireguard/psk.key";
985 AllowedIPs = [ "10.0.0.1/32" ];
986 PersistentKeepalive = 15;
987 };}];
988 type = with types; listOf (submodule wireguardPeerOptions);
989 description = ''
990 Each item in this array specifies an option in the
991 <literal>[WireGuardPeer]</literal> section of the unit. See
992 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
993 <manvolnum>5</manvolnum></citerefentry> for details.
994 Use <literal>PresharedKeyFile</literal> instead of
995 <literal>PresharedKey</literal>: the nix store is
996 world-readable.
997 '';
998 };
999
1000 bondConfig = mkOption {
1001 default = {};
1002 example = { Mode = "802.3ad"; };
1003 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionBond;
1004 description = ''
1005 Each attribute in this set specifies an option in the
1006 <literal>[Bond]</literal> section of the unit. See
1007 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
1008 <manvolnum>5</manvolnum></citerefentry> for details.
1009 '';
1010 };
1011
1012 xfrmConfig = mkOption {
1013 default = {};
1014 example = { InterfaceId = 1; };
1015 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionXfrm;
1016 description = ''
1017 Each attribute in this set specifies an option in the
1018 <literal>[Xfrm]</literal> section of the unit. See
1019 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
1020 <manvolnum>5</manvolnum></citerefentry> for details.
1021 '';
1022 };
1023
1024 vrfConfig = mkOption {
1025 default = {};
1026 example = { Table = 2342; };
1027 type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionVRF;
1028 description = ''
1029 Each attribute in this set specifies an option in the
1030 <literal>[VRF]</literal> section of the unit. See
1031 <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
1032 <manvolnum>5</manvolnum></citerefentry> for details.
1033 A detailed explanation about how VRFs work can be found in the
1034 <link xlink:href="https://www.kernel.org/doc/Documentation/networking/vrf.txt">kernel
1035 docs</link>.
1036 '';
1037 };
1038
1039 };
1040
1041 addressOptions = {
1042 options = {
1043 addressConfig = mkOption {
1044 default = {};
1045 example = { Address = "192.168.0.100/24"; };
1046 type = types.addCheck (types.attrsOf unitOption) check.network.sectionAddress;
1047 description = ''
1048 Each attribute in this set specifies an option in the
1049 <literal>[Address]</literal> section of the unit. See
1050 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1051 <manvolnum>5</manvolnum></citerefentry> for details.
1052 '';
1053 };
1054 };
1055 };
1056
1057 routingPolicyRulesOptions = {
1058 options = {
1059 routingPolicyRuleConfig = mkOption {
1060 default = { };
1061 example = { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; } ;};
1062 type = types.addCheck (types.attrsOf unitOption) check.network.sectionRoutingPolicyRule;
1063 description = ''
1064 Each attribute in this set specifies an option in the
1065 <literal>[RoutingPolicyRule]</literal> section of the unit. See
1066 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1067 <manvolnum>5</manvolnum></citerefentry> for details.
1068 '';
1069 };
1070 };
1071 };
1072
1073 routeOptions = {
1074 options = {
1075 routeConfig = mkOption {
1076 default = {};
1077 example = { Gateway = "192.168.0.1"; };
1078 type = types.addCheck (types.attrsOf unitOption) check.network.sectionRoute;
1079 description = ''
1080 Each attribute in this set specifies an option in the
1081 <literal>[Route]</literal> section of the unit. See
1082 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1083 <manvolnum>5</manvolnum></citerefentry> for details.
1084 '';
1085 };
1086 };
1087 };
1088
1089 ipv6PrefixOptions = {
1090 options = {
1091 ipv6PrefixConfig = mkOption {
1092 default = {};
1093 example = { Prefix = "fd00::/64"; };
1094 type = types.addCheck (types.attrsOf unitOption) check.network.sectionIPv6Prefix;
1095 description = ''
1096 Each attribute in this set specifies an option in the
1097 <literal>[IPv6Prefix]</literal> section of the unit. See
1098 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1099 <manvolnum>5</manvolnum></citerefentry> for details.
1100 '';
1101 };
1102 };
1103 };
1104
1105 networkOptions = commonNetworkOptions // {
1106
1107 linkConfig = mkOption {
1108 default = {};
1109 example = { Unmanaged = true; };
1110 type = types.addCheck (types.attrsOf unitOption) check.network.sectionLink;
1111 description = ''
1112 Each attribute in this set specifies an option in the
1113 <literal>[Link]</literal> section of the unit. See
1114 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1115 <manvolnum>5</manvolnum></citerefentry> for details.
1116 '';
1117 };
1118
1119 networkConfig = mkOption {
1120 default = {};
1121 example = { Description = "My Network"; };
1122 type = types.addCheck (types.attrsOf unitOption) check.network.sectionNetwork;
1123 description = ''
1124 Each attribute in this set specifies an option in the
1125 <literal>[Network]</literal> section of the unit. See
1126 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1127 <manvolnum>5</manvolnum></citerefentry> for details.
1128 '';
1129 };
1130
1131 # systemd.network.networks.*.dhcpConfig has been deprecated in favor of ….dhcpV4Config
1132 # Produce a nice warning message so users know it is gone.
1133 dhcpConfig = mkOption {
1134 visible = false;
1135 apply = _: throw "The option `systemd.network.networks.*.dhcpConfig` can no longer be used since it's been removed. Please use `systemd.network.networks.*.dhcpV4Config` instead.";
1136 };
1137
1138 dhcpV4Config = mkOption {
1139 default = {};
1140 example = { UseDNS = true; UseRoutes = true; };
1141 type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPv4;
1142 description = ''
1143 Each attribute in this set specifies an option in the
1144 <literal>[DHCPv4]</literal> section of the unit. See
1145 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1146 <manvolnum>5</manvolnum></citerefentry> for details.
1147 '';
1148 };
1149
1150 dhcpV6Config = mkOption {
1151 default = {};
1152 example = { UseDNS = true; UseRoutes = true; };
1153 type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPv6;
1154 description = ''
1155 Each attribute in this set specifies an option in the
1156 <literal>[DHCPv6]</literal> section of the unit. See
1157 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1158 <manvolnum>5</manvolnum></citerefentry> for details.
1159 '';
1160 };
1161
1162 dhcpV6PrefixDelegationConfig = mkOption {
1163 default = {};
1164 example = { SubnetId = "auto"; Announce = true; };
1165 type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPv6PrefixDelegation;
1166 description = ''
1167 Each attribute in this set specifies an option in the
1168 <literal>[DHCPv6PrefixDelegation]</literal> section of the unit. See
1169 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1170 <manvolnum>5</manvolnum></citerefentry> for details.
1171 '';
1172 };
1173
1174 ipv6AcceptRAConfig = mkOption {
1175 default = {};
1176 example = { UseDNS = true; DHCPv6Client = "always"; };
1177 type = types.addCheck (types.attrsOf unitOption) check.network.sectionIPv6AcceptRA;
1178 description = ''
1179 Each attribute in this set specifies an option in the
1180 <literal>[IPv6AcceptRA]</literal> section of the unit. See
1181 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1182 <manvolnum>5</manvolnum></citerefentry> for details.
1183 '';
1184 };
1185
1186 dhcpServerConfig = mkOption {
1187 default = {};
1188 example = { PoolOffset = 50; EmitDNS = false; };
1189 type = types.addCheck (types.attrsOf unitOption) check.network.sectionDHCPServer;
1190 description = ''
1191 Each attribute in this set specifies an option in the
1192 <literal>[DHCPServer]</literal> section of the unit. See
1193 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1194 <manvolnum>5</manvolnum></citerefentry> for details.
1195 '';
1196 };
1197
1198 # systemd.network.networks.*.ipv6PrefixDelegationConfig has been deprecated
1199 # in 247 in favor of systemd.network.networks.*.ipv6SendRAConfig.
1200 ipv6PrefixDelegationConfig = mkOption {
1201 visible = false;
1202 apply = _: throw "The option `systemd.network.networks.*.ipv6PrefixDelegationConfig` has been replaced by `systemd.network.networks.*.ipv6SendRAConfig`.";
1203 };
1204
1205 ipv6SendRAConfig = mkOption {
1206 default = {};
1207 example = { EmitDNS = true; Managed = true; OtherInformation = true; };
1208 type = types.addCheck (types.attrsOf unitOption) check.network.sectionIPv6SendRA;
1209 description = ''
1210 Each attribute in this set specifies an option in the
1211 <literal>[IPv6SendRA]</literal> section of the unit. See
1212 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1213 <manvolnum>5</manvolnum></citerefentry> for details.
1214 '';
1215 };
1216
1217 ipv6Prefixes = mkOption {
1218 default = [];
1219 example = { AddressAutoconfiguration = true; OnLink = true; };
1220 type = with types; listOf (submodule ipv6PrefixOptions);
1221 description = ''
1222 A list of ipv6Prefix sections to be added to the unit. See
1223 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1224 <manvolnum>5</manvolnum></citerefentry> for details.
1225 '';
1226 };
1227
1228 name = mkOption {
1229 type = types.nullOr types.str;
1230 default = null;
1231 description = ''
1232 The name of the network interface to match against.
1233 '';
1234 };
1235
1236 DHCP = mkOption {
1237 type = types.nullOr types.str;
1238 default = null;
1239 description = ''
1240 Whether to enable DHCP on the interfaces matched.
1241 '';
1242 };
1243
1244 domains = mkOption {
1245 type = types.nullOr (types.listOf types.str);
1246 default = null;
1247 description = ''
1248 A list of domains to pass to the network config.
1249 '';
1250 };
1251
1252 address = mkOption {
1253 default = [ ];
1254 type = types.listOf types.str;
1255 description = ''
1256 A list of addresses to be added to the network section of the
1257 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1258 <manvolnum>5</manvolnum></citerefentry> for details.
1259 '';
1260 };
1261
1262 gateway = mkOption {
1263 default = [ ];
1264 type = types.listOf types.str;
1265 description = ''
1266 A list of gateways to be added to the network section of the
1267 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1268 <manvolnum>5</manvolnum></citerefentry> for details.
1269 '';
1270 };
1271
1272 dns = mkOption {
1273 default = [ ];
1274 type = types.listOf types.str;
1275 description = ''
1276 A list of dns servers to be added to the network section of the
1277 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1278 <manvolnum>5</manvolnum></citerefentry> for details.
1279 '';
1280 };
1281
1282 ntp = mkOption {
1283 default = [ ];
1284 type = types.listOf types.str;
1285 description = ''
1286 A list of ntp servers to be added to the network section of the
1287 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1288 <manvolnum>5</manvolnum></citerefentry> for details.
1289 '';
1290 };
1291
1292 bridge = mkOption {
1293 default = [ ];
1294 type = types.listOf types.str;
1295 description = ''
1296 A list of bridge interfaces to be added to the network section of the
1297 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1298 <manvolnum>5</manvolnum></citerefentry> for details.
1299 '';
1300 };
1301
1302 bond = mkOption {
1303 default = [ ];
1304 type = types.listOf types.str;
1305 description = ''
1306 A list of bond interfaces to be added to the network section of the
1307 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1308 <manvolnum>5</manvolnum></citerefentry> for details.
1309 '';
1310 };
1311
1312 vrf = mkOption {
1313 default = [ ];
1314 type = types.listOf types.str;
1315 description = ''
1316 A list of vrf interfaces to be added to the network section of the
1317 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1318 <manvolnum>5</manvolnum></citerefentry> for details.
1319 '';
1320 };
1321
1322 vlan = mkOption {
1323 default = [ ];
1324 type = types.listOf types.str;
1325 description = ''
1326 A list of vlan interfaces to be added to the network section of the
1327 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1328 <manvolnum>5</manvolnum></citerefentry> for details.
1329 '';
1330 };
1331
1332 macvlan = mkOption {
1333 default = [ ];
1334 type = types.listOf types.str;
1335 description = ''
1336 A list of macvlan interfaces to be added to the network section of the
1337 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1338 <manvolnum>5</manvolnum></citerefentry> for details.
1339 '';
1340 };
1341
1342 vxlan = mkOption {
1343 default = [ ];
1344 type = types.listOf types.str;
1345 description = ''
1346 A list of vxlan interfaces to be added to the network section of the
1347 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1348 <manvolnum>5</manvolnum></citerefentry> for details.
1349 '';
1350 };
1351
1352 tunnel = mkOption {
1353 default = [ ];
1354 type = types.listOf types.str;
1355 description = ''
1356 A list of tunnel interfaces to be added to the network section of the
1357 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1358 <manvolnum>5</manvolnum></citerefentry> for details.
1359 '';
1360 };
1361
1362 xfrm = mkOption {
1363 default = [ ];
1364 type = types.listOf types.str;
1365 description = ''
1366 A list of xfrm interfaces to be added to the network section of the
1367 unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle>
1368 <manvolnum>5</manvolnum></citerefentry> for details.
1369 '';
1370 };
1371
1372 addresses = mkOption {
1373 default = [ ];
1374 type = with types; listOf (submodule addressOptions);
1375 description = ''
1376 A list of address sections to be added to the unit. See
1377 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1378 <manvolnum>5</manvolnum></citerefentry> for details.
1379 '';
1380 };
1381
1382 routingPolicyRules = mkOption {
1383 default = [ ];
1384 type = with types; listOf (submodule routingPolicyRulesOptions);
1385 description = ''
1386 A list of routing policy rules sections to be added to the unit. See
1387 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1388 <manvolnum>5</manvolnum></citerefentry> for details.
1389 '';
1390 };
1391
1392 routes = mkOption {
1393 default = [ ];
1394 type = with types; listOf (submodule routeOptions);
1395 description = ''
1396 A list of route sections to be added to the unit. See
1397 <citerefentry><refentrytitle>systemd.network</refentrytitle>
1398 <manvolnum>5</manvolnum></citerefentry> for details.
1399 '';
1400 };
1401
1402 };
1403
1404 networkConfig = { config, ... }: {
1405 config = {
1406 matchConfig = optionalAttrs (config.name != null) {
1407 Name = config.name;
1408 };
1409 networkConfig = optionalAttrs (config.DHCP != null) {
1410 DHCP = config.DHCP;
1411 } // optionalAttrs (config.domains != null) {
1412 Domains = concatStringsSep " " config.domains;
1413 };
1414 };
1415 };
1416
1417 commonMatchText = def: optionalString (def.matchConfig != { }) ''
1418 [Match]
1419 ${attrsToSection def.matchConfig}
1420 '';
1421
1422 linkToUnit = name: def:
1423 { inherit (def) enable;
1424 text = commonMatchText def
1425 + ''
1426 [Link]
1427 ${attrsToSection def.linkConfig}
1428 ''
1429 + def.extraConfig;
1430 };
1431
1432 netdevToUnit = name: def:
1433 { inherit (def) enable;
1434 text = commonMatchText def
1435 + ''
1436 [NetDev]
1437 ${attrsToSection def.netdevConfig}
1438 ''
1439 + optionalString (def.vlanConfig != { }) ''
1440 [VLAN]
1441 ${attrsToSection def.vlanConfig}
1442 ''
1443 + optionalString (def.macvlanConfig != { }) ''
1444 [MACVLAN]
1445 ${attrsToSection def.macvlanConfig}
1446 ''
1447 + optionalString (def.vxlanConfig != { }) ''
1448 [VXLAN]
1449 ${attrsToSection def.vxlanConfig}
1450 ''
1451 + optionalString (def.tunnelConfig != { }) ''
1452 [Tunnel]
1453 ${attrsToSection def.tunnelConfig}
1454 ''
1455 + optionalString (def.peerConfig != { }) ''
1456 [Peer]
1457 ${attrsToSection def.peerConfig}
1458 ''
1459 + optionalString (def.tunConfig != { }) ''
1460 [Tun]
1461 ${attrsToSection def.tunConfig}
1462 ''
1463 + optionalString (def.tapConfig != { }) ''
1464 [Tap]
1465 ${attrsToSection def.tapConfig}
1466 ''
1467 + optionalString (def.wireguardConfig != { }) ''
1468 [WireGuard]
1469 ${attrsToSection def.wireguardConfig}
1470 ''
1471 + flip concatMapStrings def.wireguardPeers (x: ''
1472 [WireGuardPeer]
1473 ${attrsToSection x.wireguardPeerConfig}
1474 '')
1475 + optionalString (def.bondConfig != { }) ''
1476 [Bond]
1477 ${attrsToSection def.bondConfig}
1478 ''
1479 + optionalString (def.xfrmConfig != { }) ''
1480 [Xfrm]
1481 ${attrsToSection def.xfrmConfig}
1482 ''
1483 + optionalString (def.vrfConfig != { }) ''
1484 [VRF]
1485 ${attrsToSection def.vrfConfig}
1486 ''
1487 + def.extraConfig;
1488 };
1489
1490 networkToUnit = name: def:
1491 { inherit (def) enable;
1492 text = commonMatchText def
1493 + optionalString (def.linkConfig != { }) ''
1494 [Link]
1495 ${attrsToSection def.linkConfig}
1496 ''
1497 + ''
1498 [Network]
1499 ''
1500 + attrsToSection def.networkConfig
1501 + optionalString (def.address != [ ]) ''
1502 ${concatStringsSep "\n" (map (s: "Address=${s}") def.address)}
1503 ''
1504 + optionalString (def.gateway != [ ]) ''
1505 ${concatStringsSep "\n" (map (s: "Gateway=${s}") def.gateway)}
1506 ''
1507 + optionalString (def.dns != [ ]) ''
1508 ${concatStringsSep "\n" (map (s: "DNS=${s}") def.dns)}
1509 ''
1510 + optionalString (def.ntp != [ ]) ''
1511 ${concatStringsSep "\n" (map (s: "NTP=${s}") def.ntp)}
1512 ''
1513 + optionalString (def.bridge != [ ]) ''
1514 ${concatStringsSep "\n" (map (s: "Bridge=${s}") def.bridge)}
1515 ''
1516 + optionalString (def.bond != [ ]) ''
1517 ${concatStringsSep "\n" (map (s: "Bond=${s}") def.bond)}
1518 ''
1519 + optionalString (def.vrf != [ ]) ''
1520 ${concatStringsSep "\n" (map (s: "VRF=${s}") def.vrf)}
1521 ''
1522 + optionalString (def.vlan != [ ]) ''
1523 ${concatStringsSep "\n" (map (s: "VLAN=${s}") def.vlan)}
1524 ''
1525 + optionalString (def.macvlan != [ ]) ''
1526 ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)}
1527 ''
1528 + optionalString (def.vxlan != [ ]) ''
1529 ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)}
1530 ''
1531 + optionalString (def.tunnel != [ ]) ''
1532 ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)}
1533 ''
1534 + optionalString (def.xfrm != [ ]) ''
1535 ${concatStringsSep "\n" (map (s: "Xfrm=${s}") def.xfrm)}
1536 ''
1537 + ''
1538
1539 ''
1540 + flip concatMapStrings def.addresses (x: ''
1541 [Address]
1542 ${attrsToSection x.addressConfig}
1543 '')
1544 + flip concatMapStrings def.routingPolicyRules (x: ''
1545 [RoutingPolicyRule]
1546 ${attrsToSection x.routingPolicyRuleConfig}
1547 '')
1548 + flip concatMapStrings def.routes (x: ''
1549 [Route]
1550 ${attrsToSection x.routeConfig}
1551 '')
1552 + optionalString (def.dhcpV4Config != { }) ''
1553 [DHCPv4]
1554 ${attrsToSection def.dhcpV4Config}
1555 ''
1556 + optionalString (def.dhcpV6Config != { }) ''
1557 [DHCPv6]
1558 ${attrsToSection def.dhcpV6Config}
1559 ''
1560 + optionalString (def.dhcpV6PrefixDelegationConfig != { }) ''
1561 [DHCPv6PrefixDelegation]
1562 ${attrsToSection def.dhcpV6PrefixDelegationConfig}
1563 ''
1564 + optionalString (def.ipv6AcceptRAConfig != { }) ''
1565 [IPv6AcceptRA]
1566 ${attrsToSection def.ipv6AcceptRAConfig}
1567 ''
1568 + optionalString (def.dhcpServerConfig != { }) ''
1569 [DHCPServer]
1570 ${attrsToSection def.dhcpServerConfig}
1571 ''
1572 + optionalString (def.ipv6SendRAConfig != { }) ''
1573 [IPv6SendRA]
1574 ${attrsToSection def.ipv6SendRAConfig}
1575 ''
1576 + flip concatMapStrings def.ipv6Prefixes (x: ''
1577 [IPv6Prefix]
1578 ${attrsToSection x.ipv6PrefixConfig}
1579 '')
1580 + def.extraConfig;
1581 };
1582
1583 unitFiles = listToAttrs (map (name: {
1584 name = "systemd/network/${name}";
1585 value.source = "${cfg.units.${name}.unit}/${name}";
1586 }) (attrNames cfg.units));
1587in
1588
1589{
1590 disabledModules = [ "system/boot/networkd.nix" ];
1591
1592 options = {
1593
1594 systemd.network.enable = mkOption {
1595 default = false;
1596 type = types.bool;
1597 description = ''
1598 Whether to enable networkd or not.
1599 '';
1600 };
1601
1602 systemd.network.links = mkOption {
1603 default = {};
1604 type = with types; attrsOf (submodule [ { options = linkOptions; } ]);
1605 description = "Definition of systemd network links.";
1606 };
1607
1608 systemd.network.netdevs = mkOption {
1609 default = {};
1610 type = with types; attrsOf (submodule [ { options = netdevOptions; } ]);
1611 description = "Definition of systemd network devices.";
1612 };
1613
1614 systemd.network.networks = mkOption {
1615 default = {};
1616 type = with types; attrsOf (submodule [ { options = networkOptions; } networkConfig ]);
1617 description = "Definition of systemd networks.";
1618 };
1619
1620 systemd.network.units = mkOption {
1621 description = "Definition of networkd units.";
1622 default = {};
1623 internal = true;
1624 type = with types; attrsOf (submodule (
1625 { name, config, ... }:
1626 { options = mapAttrs (_: x: x // { internal = true; }) concreteUnitOptions;
1627 config = {
1628 unit = mkDefault (makeUnit name config);
1629 };
1630 }));
1631 };
1632
1633 };
1634
1635 config = mkMerge [
1636
1637 # .link units are honored by udev, no matter if systemd-networkd is enabled or not.
1638 {
1639 systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.link" (linkToUnit n v)) cfg.links;
1640 environment.etc = unitFiles;
1641 }
1642
1643 (mkIf config.systemd.network.enable {
1644
1645 users.users.systemd-network.group = "systemd-network";
1646
1647 systemd.additionalUpstreamSystemUnits = [
1648 "systemd-networkd-wait-online.service"
1649 "systemd-networkd.service"
1650 "systemd-networkd.socket"
1651 ];
1652
1653 systemd.network.units = mapAttrs' (n: v: nameValuePair "${n}.netdev" (netdevToUnit n v)) cfg.netdevs
1654 // mapAttrs' (n: v: nameValuePair "${n}.network" (networkToUnit n v)) cfg.networks;
1655
1656 # systemd-networkd is socket-activated by kernel netlink route change
1657 # messages. It is important to have systemd buffer those on behalf of
1658 # networkd.
1659 systemd.sockets.systemd-networkd.wantedBy = [ "sockets.target" ];
1660
1661 systemd.services.systemd-networkd = {
1662 wantedBy = [ "multi-user.target" ];
1663 aliases = [ "dbus-org.freedesktop.network1.service" ];
1664 restartTriggers = map (x: x.source) (attrValues unitFiles);
1665 };
1666
1667 systemd.services.systemd-networkd-wait-online = {
1668 wantedBy = [ "network-online.target" ];
1669 };
1670
1671 systemd.services."systemd-network-wait-online@" = {
1672 description = "Wait for Network Interface %I to be Configured";
1673 conflicts = [ "shutdown.target" ];
1674 requisite = [ "systemd-networkd.service" ];
1675 after = [ "systemd-networkd.service" ];
1676 serviceConfig = {
1677 Type = "oneshot";
1678 RemainAfterExit = true;
1679 ExecStart = "${config.systemd.package}/lib/systemd/systemd-networkd-wait-online -i %I";
1680 };
1681 };
1682
1683 services.resolved.enable = mkDefault true;
1684 })
1685 ];
1686}
diff --git a/modules/networkd/systemd-lib.nix b/modules/networkd/systemd-lib.nix
new file mode 100644
index 00000000..2dbf1503
--- /dev/null
+++ b/modules/networkd/systemd-lib.nix
@@ -0,0 +1,237 @@
1{ config, lib, pkgs }:
2
3with lib;
4
5let
6 cfg = config.systemd;
7 lndir = "${pkgs.xorg.lndir}/bin/lndir";
8in rec {
9
10 shellEscape = s: (replaceChars [ "\\" ] [ "\\\\" ] s);
11
12 mkPathSafeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
13
14 makeUnit = name: unit:
15 if unit.enable then
16 pkgs.runCommand "unit-${mkPathSafeName name}"
17 { preferLocalBuild = true;
18 allowSubstitutes = false;
19 inherit (unit) text;
20 }
21 ''
22 mkdir -p $out
23 echo -n "$text" > $out/${shellEscape name}
24 ''
25 else
26 pkgs.runCommand "unit-${mkPathSafeName name}-disabled"
27 { preferLocalBuild = true;
28 allowSubstitutes = false;
29 }
30 ''
31 mkdir -p $out
32 ln -s /dev/null $out/${shellEscape name}
33 '';
34
35 boolValues = [true false "yes" "no"];
36
37 digits = map toString (range 0 9);
38
39 isByteFormat = s:
40 let
41 l = reverseList (stringToCharacters s);
42 suffix = head l;
43 nums = tail l;
44 in elem suffix (["K" "M" "G" "T"] ++ digits)
45 && all (num: elem num digits) nums;
46
47 assertByteFormat = name: group: attr:
48 optional (attr ? ${name} && ! isByteFormat attr.${name})
49 "Systemd ${group} field `${name}' must be in byte format [0-9]+[KMGT].";
50
51 hexChars = stringToCharacters "0123456789abcdefABCDEF";
52
53 isMacAddress = s: stringLength s == 17
54 && flip all (splitString ":" s) (bytes:
55 all (byte: elem byte hexChars) (stringToCharacters bytes)
56 );
57
58 assertMacAddress = name: group: attr:
59 optional (attr ? ${name} && ! isMacAddress attr.${name})
60 "Systemd ${group} field `${name}' must be a valid mac address.";
61
62 isPort = i: i >= 0 && i <= 65535;
63
64 assertPort = name: group: attr:
65 optional (attr ? ${name} && ! isPort attr.${name})
66 "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number.";
67
68 assertValueOneOf = name: values: group: attr:
69 optional (attr ? ${name} && !elem attr.${name} values)
70 "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
71
72 assertHasField = name: group: attr:
73 optional (!(attr ? ${name}))
74 "Systemd ${group} field `${name}' must exist.";
75
76 assertRange = name: min: max: group: attr:
77 optional (attr ? ${name} && !(min <= attr.${name} && max >= attr.${name}))
78 "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
79
80 assertMinimum = name: min: group: attr:
81 optional (attr ? ${name} && attr.${name} < min)
82 "Systemd ${group} field `${name}' must be greater than or equal to ${toString min}";
83
84 assertOnlyFields = fields: group: attr:
85 let badFields = filter (name: ! elem name fields) (attrNames attr); in
86 optional (badFields != [ ])
87 "Systemd ${group} has extra fields [${concatStringsSep " " badFields}].";
88
89 assertInt = name: group: attr:
90 optional (attr ? ${name} && !isInt attr.${name})
91 "Systemd ${group} field `${name}' is not an integer";
92
93 checkUnitConfig = group: checks: attrs: let
94 # We're applied at the top-level type (attrsOf unitOption), so the actual
95 # unit options might contain attributes from mkOverride and mkIf that we need to
96 # convert into single values before checking them.
97 defs = mapAttrs (const (v:
98 if v._type or "" == "override" then v.content
99 else if v._type or "" == "if" then v.content
100 else v
101 )) attrs;
102 errors = concatMap (c: c group defs) checks;
103 in if errors == [] then true
104 else builtins.trace (concatStringsSep "\n" errors) false;
105
106 toOption = x:
107 if x == true then "true"
108 else if x == false then "false"
109 else toString x;
110
111 attrsToSection = as:
112 concatStrings (concatLists (mapAttrsToList (name: value:
113 map (x: ''
114 ${name}=${toOption x}
115 '')
116 (if isList value then value else [value]))
117 as));
118
119 generateUnits = generateUnits' true;
120
121 generateUnits' = allowCollisions: type: units: upstreamUnits: upstreamWants:
122 pkgs.runCommand "${type}-units"
123 { preferLocalBuild = true;
124 allowSubstitutes = false;
125 } ''
126 mkdir -p $out
127
128 # Copy the upstream systemd units we're interested in.
129 for i in ${toString upstreamUnits}; do
130 fn=${cfg.package}/example/systemd/${type}/$i
131 if ! [ -e $fn ]; then echo "missing $fn"; false; fi
132 if [ -L $fn ]; then
133 target="$(readlink "$fn")"
134 if [ ''${target:0:3} = ../ ]; then
135 ln -s "$(readlink -f "$fn")" $out/
136 else
137 cp -pd $fn $out/
138 fi
139 else
140 ln -s $fn $out/
141 fi
142 done
143
144 # Copy .wants links, but only those that point to units that
145 # we're interested in.
146 for i in ${toString upstreamWants}; do
147 fn=${cfg.package}/example/systemd/${type}/$i
148 if ! [ -e $fn ]; then echo "missing $fn"; false; fi
149 x=$out/$(basename $fn)
150 mkdir $x
151 for i in $fn/*; do
152 y=$x/$(basename $i)
153 cp -pd $i $y
154 if ! [ -e $y ]; then rm $y; fi
155 done
156 done
157
158 # Symlink all units provided listed in systemd.packages.
159 packages="${toString cfg.packages}"
160
161 # Filter duplicate directories
162 declare -A unique_packages
163 for k in $packages ; do unique_packages[$k]=1 ; done
164
165 for i in ''${!unique_packages[@]}; do
166 for fn in $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*; do
167 if ! [[ "$fn" =~ .wants$ ]]; then
168 if [[ -d "$fn" ]]; then
169 targetDir="$out/$(basename "$fn")"
170 mkdir -p "$targetDir"
171 ${lndir} "$fn" "$targetDir"
172 else
173 ln -s $fn $out/
174 fi
175 fi
176 done
177 done
178
179 # Symlink all units defined by systemd.units. If these are also
180 # provided by systemd or systemd.packages, then add them as
181 # <unit-name>.d/overrides.conf, which makes them extend the
182 # upstream unit.
183 for i in ${toString (mapAttrsToList (n: v: v.unit) units)}; do
184 fn=$(basename $i/*)
185 if [ -e $out/$fn ]; then
186 if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
187 ln -sfn /dev/null $out/$fn
188 else
189 ${if allowCollisions then ''
190 mkdir -p $out/$fn.d
191 ln -s $i/$fn $out/$fn.d/overrides.conf
192 '' else ''
193 echo "Found multiple derivations configuring $fn!"
194 exit 1
195 ''}
196 fi
197 else
198 ln -fs $i/$fn $out/
199 fi
200 done
201
202 # Create service aliases from aliases option.
203 ${concatStrings (mapAttrsToList (name: unit:
204 concatMapStrings (name2: ''
205 ln -sfn '${name}' $out/'${name2}'
206 '') unit.aliases) units)}
207
208 # Create .wants and .requires symlinks from the wantedBy and
209 # requiredBy options.
210 ${concatStrings (mapAttrsToList (name: unit:
211 concatMapStrings (name2: ''
212 mkdir -p $out/'${name2}.wants'
213 ln -sfn '../${name}' $out/'${name2}.wants'/
214 '') unit.wantedBy) units)}
215
216 ${concatStrings (mapAttrsToList (name: unit:
217 concatMapStrings (name2: ''
218 mkdir -p $out/'${name2}.requires'
219 ln -sfn '../${name}' $out/'${name2}.requires'/
220 '') unit.requiredBy) units)}
221
222 ${optionalString (type == "system") ''
223 # Stupid misc. symlinks.
224 ln -s ${cfg.defaultUnit} $out/default.target
225 ln -s ${cfg.ctrlAltDelUnit} $out/ctrl-alt-del.target
226 ln -s rescue.target $out/kbrequest.target
227
228 mkdir -p $out/getty.target.wants/
229 ln -s ../autovt@tty1.service $out/getty.target.wants/
230
231 ln -s ../local-fs.target ../remote-fs.target \
232 ../nss-lookup.target ../nss-user-lookup.target ../swap.target \
233 $out/multi-user.target.wants/
234 ''}
235 ''; # */
236
237}
diff --git a/modules/networkd/systemd-unit-options.nix b/modules/networkd/systemd-unit-options.nix
new file mode 100644
index 00000000..4154389b
--- /dev/null
+++ b/modules/networkd/systemd-unit-options.nix
@@ -0,0 +1,536 @@
1{ config, lib }:
2
3with lib;
4with import ./systemd-lib.nix { inherit config lib pkgs; };
5
6let
7 checkService = checkUnitConfig "Service" [
8 (assertValueOneOf "Type" [
9 "exec" "simple" "forking" "oneshot" "dbus" "notify" "idle"
10 ])
11 (assertValueOneOf "Restart" [
12 "no" "on-success" "on-failure" "on-abnormal" "on-abort" "always"
13 ])
14 ];
15
16in rec {
17
18 unitOption = mkOptionType {
19 name = "systemd option";
20 merge = loc: defs:
21 let
22 defs' = filterOverrides defs;
23 defs'' = getValues defs';
24 in
25 if isList (head defs'')
26 then concatLists defs''
27 else mergeEqualOption loc defs';
28 };
29
30 sharedOptions = {
31
32 enable = mkOption {
33 default = true;
34 type = types.bool;
35 description = ''
36 If set to false, this unit will be a symlink to
37 /dev/null. This is primarily useful to prevent specific
38 template instances
39 (e.g. <literal>serial-getty@ttyS0</literal>) from being
40 started. Note that <literal>enable=true</literal> does not
41 make a unit start by default at boot; if you want that, see
42 <literal>wantedBy</literal>.
43 '';
44 };
45
46 requiredBy = mkOption {
47 default = [];
48 type = types.listOf types.str;
49 description = ''
50 Units that require (i.e. depend on and need to go down with)
51 this unit. The discussion under <literal>wantedBy</literal>
52 applies here as well: inverse <literal>.requires</literal>
53 symlinks are established.
54 '';
55 };
56
57 wantedBy = mkOption {
58 default = [];
59 type = types.listOf types.str;
60 description = ''
61 Units that want (i.e. depend on) this unit. The standard way
62 to make a unit start by default at boot is to set this option
63 to <literal>[ "multi-user.target" ]</literal>. That's despite
64 the fact that the systemd.unit(5) manpage says this option
65 goes in the <literal>[Install]</literal> section that controls
66 the behaviour of <literal>systemctl enable</literal>. Since
67 such a process is stateful and thus contrary to the design of
68 NixOS, setting this option instead causes the equivalent
69 inverse <literal>.wants</literal> symlink to be present,
70 establishing the same desired relationship in a stateless way.
71 '';
72 };
73
74 aliases = mkOption {
75 default = [];
76 type = types.listOf types.str;
77 description = "Aliases of that unit.";
78 };
79
80 };
81
82 concreteUnitOptions = sharedOptions // {
83
84 text = mkOption {
85 type = types.nullOr types.str;
86 default = null;
87 description = "Text of this systemd unit.";
88 };
89
90 unit = mkOption {
91 internal = true;
92 description = "The generated unit.";
93 };
94
95 };
96
97 commonUnitOptions = sharedOptions // {
98
99 description = mkOption {
100 default = "";
101 type = types.str;
102 description = "Description of this unit used in systemd messages and progress indicators.";
103 };
104
105 documentation = mkOption {
106 default = [];
107 type = types.listOf types.str;
108 description = "A list of URIs referencing documentation for this unit or its configuration.";
109 };
110
111 requires = mkOption {
112 default = [];
113 type = types.listOf types.str;
114 description = ''
115 Start the specified units when this unit is started, and stop
116 this unit when the specified units are stopped or fail.
117 '';
118 };
119
120 wants = mkOption {
121 default = [];
122 type = types.listOf types.str;
123 description = ''
124 Start the specified units when this unit is started.
125 '';
126 };
127
128 after = mkOption {
129 default = [];
130 type = types.listOf types.str;
131 description = ''
132 If the specified units are started at the same time as
133 this unit, delay this unit until they have started.
134 '';
135 };
136
137 before = mkOption {
138 default = [];
139 type = types.listOf types.str;
140 description = ''
141 If the specified units are started at the same time as
142 this unit, delay them until this unit has started.
143 '';
144 };
145
146 bindsTo = mkOption {
147 default = [];
148 type = types.listOf types.str;
149 description = ''
150 Like ‘requires’, but in addition, if the specified units
151 unexpectedly disappear, this unit will be stopped as well.
152 '';
153 };
154
155 partOf = mkOption {
156 default = [];
157 type = types.listOf types.str;
158 description = ''
159 If the specified units are stopped or restarted, then this
160 unit is stopped or restarted as well.
161 '';
162 };
163
164 conflicts = mkOption {
165 default = [];
166 type = types.listOf types.str;
167 description = ''
168 If the specified units are started, then this unit is stopped
169 and vice versa.
170 '';
171 };
172
173 requisite = mkOption {
174 default = [];
175 type = types.listOf types.str;
176 description = ''
177 Similar to requires. However if the units listed are not started,
178 they will not be started and the transaction will fail.
179 '';
180 };
181
182 unitConfig = mkOption {
183 default = {};
184 example = { RequiresMountsFor = "/data"; };
185 type = types.attrsOf unitOption;
186 description = ''
187 Each attribute in this set specifies an option in the
188 <literal>[Unit]</literal> section of the unit. See
189 <citerefentry><refentrytitle>systemd.unit</refentrytitle>
190 <manvolnum>5</manvolnum></citerefentry> for details.
191 '';
192 };
193
194 restartTriggers = mkOption {
195 default = [];
196 type = types.listOf types.unspecified;
197 description = ''
198 An arbitrary list of items such as derivations. If any item
199 in the list changes between reconfigurations, the service will
200 be restarted.
201 '';
202 };
203
204 onFailure = mkOption {
205 default = [];
206 type = types.listOf types.str;
207 description = ''
208 A list of one or more units that are activated when
209 this unit enters the "failed" state.
210 '';
211 };
212
213 startLimitBurst = mkOption {
214 type = types.int;
215 description = ''
216 Configure unit start rate limiting. Units which are started
217 more than startLimitBurst times within an interval time
218 interval are not permitted to start any more.
219 '';
220 };
221
222 startLimitIntervalSec = mkOption {
223 type = types.int;
224 description = ''
225 Configure unit start rate limiting. Units which are started
226 more than startLimitBurst times within an interval time
227 interval are not permitted to start any more.
228 '';
229 };
230
231 };
232
233
234 serviceOptions = commonUnitOptions // {
235
236 environment = mkOption {
237 default = {};
238 type = with types; attrsOf (nullOr (oneOf [ str path package ]));
239 example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
240 description = "Environment variables passed to the service's processes.";
241 };
242
243 path = mkOption {
244 default = [];
245 type = with types; listOf (oneOf [ package str ]);
246 description = ''
247 Packages added to the service's <envar>PATH</envar>
248 environment variable. Both the <filename>bin</filename>
249 and <filename>sbin</filename> subdirectories of each
250 package are added.
251 '';
252 };
253
254 serviceConfig = mkOption {
255 default = {};
256 example =
257 { RestartSec = 5;
258 };
259 type = types.addCheck (types.attrsOf unitOption) checkService;
260 description = ''
261 Each attribute in this set specifies an option in the
262 <literal>[Service]</literal> section of the unit. See
263 <citerefentry><refentrytitle>systemd.service</refentrytitle>
264 <manvolnum>5</manvolnum></citerefentry> for details.
265 '';
266 };
267
268 script = mkOption {
269 type = types.lines;
270 default = "";
271 description = "Shell commands executed as the service's main process.";
272 };
273
274 scriptArgs = mkOption {
275 type = types.str;
276 default = "";
277 description = "Arguments passed to the main process script.";
278 };
279
280 preStart = mkOption {
281 type = types.lines;
282 default = "";
283 description = ''
284 Shell commands executed before the service's main process
285 is started.
286 '';
287 };
288
289 postStart = mkOption {
290 type = types.lines;
291 default = "";
292 description = ''
293 Shell commands executed after the service's main process
294 is started.
295 '';
296 };
297
298 reload = mkOption {
299 type = types.lines;
300 default = "";
301 description = ''
302 Shell commands executed when the service's main process
303 is reloaded.
304 '';
305 };
306
307 preStop = mkOption {
308 type = types.lines;
309 default = "";
310 description = ''
311 Shell commands executed to stop the service.
312 '';
313 };
314
315 postStop = mkOption {
316 type = types.lines;
317 default = "";
318 description = ''
319 Shell commands executed after the service's main process
320 has exited.
321 '';
322 };
323
324 restartIfChanged = mkOption {
325 type = types.bool;
326 default = true;
327 description = ''
328 Whether the service should be restarted during a NixOS
329 configuration switch if its definition has changed.
330 '';
331 };
332
333 reloadIfChanged = mkOption {
334 type = types.bool;
335 default = false;
336 description = ''
337 Whether the service should be reloaded during a NixOS
338 configuration switch if its definition has changed. If
339 enabled, the value of <option>restartIfChanged</option> is
340 ignored.
341 '';
342 };
343
344 stopIfChanged = mkOption {
345 type = types.bool;
346 default = true;
347 description = ''
348 If set, a changed unit is restarted by calling
349 <command>systemctl stop</command> in the old configuration,
350 then <command>systemctl start</command> in the new one.
351 Otherwise, it is restarted in a single step using
352 <command>systemctl restart</command> in the new configuration.
353 The latter is less correct because it runs the
354 <literal>ExecStop</literal> commands from the new
355 configuration.
356 '';
357 };
358
359 startAt = mkOption {
360 type = with types; either str (listOf str);
361 default = [];
362 example = "Sun 14:00:00";
363 description = ''
364 Automatically start this unit at the given date/time, which
365 must be in the format described in
366 <citerefentry><refentrytitle>systemd.time</refentrytitle>
367 <manvolnum>7</manvolnum></citerefentry>. This is equivalent
368 to adding a corresponding timer unit with
369 <option>OnCalendar</option> set to the value given here.
370 '';
371 apply = v: if isList v then v else [ v ];
372 };
373
374 };
375
376
377 socketOptions = commonUnitOptions // {
378
379 listenStreams = mkOption {
380 default = [];
381 type = types.listOf types.str;
382 example = [ "0.0.0.0:993" "/run/my-socket" ];
383 description = ''
384 For each item in this list, a <literal>ListenStream</literal>
385 option in the <literal>[Socket]</literal> section will be created.
386 '';
387 };
388
389 listenDatagrams = mkOption {
390 default = [];
391 type = types.listOf types.str;
392 example = [ "0.0.0.0:993" "/run/my-socket" ];
393 description = ''
394 For each item in this list, a <literal>ListenDatagram</literal>
395 option in the <literal>[Socket]</literal> section will be created.
396 '';
397 };
398
399 socketConfig = mkOption {
400 default = {};
401 example = { ListenStream = "/run/my-socket"; };
402 type = types.attrsOf unitOption;
403 description = ''
404 Each attribute in this set specifies an option in the
405 <literal>[Socket]</literal> section of the unit. See
406 <citerefentry><refentrytitle>systemd.socket</refentrytitle>
407 <manvolnum>5</manvolnum></citerefentry> for details.
408 '';
409 };
410
411 };
412
413
414 timerOptions = commonUnitOptions // {
415
416 timerConfig = mkOption {
417 default = {};
418 example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
419 type = types.attrsOf unitOption;
420 description = ''
421 Each attribute in this set specifies an option in the
422 <literal>[Timer]</literal> section of the unit. See
423 <citerefentry><refentrytitle>systemd.timer</refentrytitle>
424 <manvolnum>5</manvolnum></citerefentry> and
425 <citerefentry><refentrytitle>systemd.time</refentrytitle>
426 <manvolnum>7</manvolnum></citerefentry> for details.
427 '';
428 };
429
430 };
431
432
433 pathOptions = commonUnitOptions // {
434
435 pathConfig = mkOption {
436 default = {};
437 example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
438 type = types.attrsOf unitOption;
439 description = ''
440 Each attribute in this set specifies an option in the
441 <literal>[Path]</literal> section of the unit. See
442 <citerefentry><refentrytitle>systemd.path</refentrytitle>
443 <manvolnum>5</manvolnum></citerefentry> for details.
444 '';
445 };
446
447 };
448
449
450 mountOptions = commonUnitOptions // {
451
452 what = mkOption {
453 example = "/dev/sda1";
454 type = types.str;
455 description = "Absolute path of device node, file or other resource. (Mandatory)";
456 };
457
458 where = mkOption {
459 example = "/mnt";
460 type = types.str;
461 description = ''
462 Absolute path of a directory of the mount point.
463 Will be created if it doesn't exist. (Mandatory)
464 '';
465 };
466
467 type = mkOption {
468 default = "";
469 example = "ext4";
470 type = types.str;
471 description = "File system type.";
472 };
473
474 options = mkOption {
475 default = "";
476 example = "noatime";
477 type = types.commas;
478 description = "Options used to mount the file system.";
479 };
480
481 mountConfig = mkOption {
482 default = {};
483 example = { DirectoryMode = "0775"; };
484 type = types.attrsOf unitOption;
485 description = ''
486 Each attribute in this set specifies an option in the
487 <literal>[Mount]</literal> section of the unit. See
488 <citerefentry><refentrytitle>systemd.mount</refentrytitle>
489 <manvolnum>5</manvolnum></citerefentry> for details.
490 '';
491 };
492 };
493
494 automountOptions = commonUnitOptions // {
495
496 where = mkOption {
497 example = "/mnt";
498 type = types.str;
499 description = ''
500 Absolute path of a directory of the mount point.
501 Will be created if it doesn't exist. (Mandatory)
502 '';
503 };
504
505 automountConfig = mkOption {
506 default = {};
507 example = { DirectoryMode = "0775"; };
508 type = types.attrsOf unitOption;
509 description = ''
510 Each attribute in this set specifies an option in the
511 <literal>[Automount]</literal> section of the unit. See
512 <citerefentry><refentrytitle>systemd.automount</refentrytitle>
513 <manvolnum>5</manvolnum></citerefentry> for details.
514 '';
515 };
516 };
517
518 targetOptions = commonUnitOptions;
519
520 sliceOptions = commonUnitOptions // {
521
522 sliceConfig = mkOption {
523 default = {};
524 example = { MemoryMax = "2G"; };
525 type = types.attrsOf unitOption;
526 description = ''
527 Each attribute in this set specifies an option in the
528 <literal>[Slice]</literal> section of the unit. See
529 <citerefentry><refentrytitle>systemd.slice</refentrytitle>
530 <manvolnum>5</manvolnum></citerefentry> for details.
531 '';
532 };
533
534 };
535
536}
diff --git a/modules/yggdrasil-wg/default.nix b/modules/yggdrasil-wg/default.nix
index 8e2ba7a4..80443644 100644
--- a/modules/yggdrasil-wg/default.nix
+++ b/modules/yggdrasil-wg/default.nix
@@ -5,86 +5,183 @@ with lib;
5let 5let
6 listenPort = 51820; 6 listenPort = 51820;
7 udp2rawPort = 51821; 7 udp2rawPort = 51821;
8 subnet = "2a03:4000:52:ada:1"; 8 wgSubnet = "2a03:4000:52:ada:1";
9 subnetLength = 80; 9 wgSubnetLength = 80;
10 hostLength = subnetLength + 16; 10 wgHostLength = wgSubnetLength + 16;
11 batSubnet = "2a03:4000:52:ada:2";
12 batSubnetLength = 80;
13 batHostLength = batSubnetLength + 16;
11 14
12 links = [ 15 links = mkLinks [
13 { from = "vidhar"; 16 { from = "vidhar";
14 to = "surtr"; 17 to = "surtr";
15 endpointHost = "202.61.241.61"; 18 endpointHost = "202.61.241.61";
16 persistentKeepalive = 25; 19 udp2raw = true;
17 dynamicEndpointRefreshSeconds = 86400; 20 PersistentKeepalive = 25;
18 } 21 }
19 { from = "sif"; 22 { from = "sif";
20 to = "surtr"; 23 to = "surtr";
21 endpointHost = "202.61.241.61"; 24 endpointHost = "202.61.241.61";
22 persistentKeepalive = 25; 25 udp2raw = true;
23 dynamicEndpointRefreshSeconds = 86400; 26 PersistentKeepalive = 25;
24 } 27 }
25 ];
26 routes = [
27 { from = "sif"; 28 { from = "sif";
28 to = "vidhar"; 29 to = "vidhar";
29 via = "surtr"; 30 endpointHost = "192.168.2.168";
30 } 31 PersistentKeepalive = 25;
31 { from = "vidhar";
32 to = "sif";
33 via = "surtr";
34 } 32 }
35 ]; 33 ];
36 hostIPs = { 34 wgHostIPs = {
37 surtr = ["${subnet}::/${toString hostLength}"]; 35 surtr = "${wgSubnet}::/${toString wgHostLength}";
38 vidhar = ["${subnet}:1::/${toString hostLength}"]; 36 vidhar = "${wgSubnet}:1::/${toString wgHostLength}";
39 sif = ["${subnet}:2::/${toString hostLength}"]; 37 sif = "${wgSubnet}:2::/${toString wgHostLength}";
38 };
39 greHostMACPrefixes = {
40 surtr = "02:00:00:00:00";
41 vidhar = "02:00:00:00:01";
42 sif = "02:00:00:00:02";
43 };
44 batHostIPs = {
45 surtr = ["${batSubnet}::/${toString batHostLength}"];
46 vidhar = ["${batSubnet}:1::/${toString batHostLength}"];
47 sif = ["${batSubnet}:2::/${toString batHostLength}"];
40 }; 48 };
41 49
42 mkPublicKeyPath = host: ./hosts + "/${host}.pub"; 50 mkPublicKeyPath = host: ./hosts + "/${host}.pub";
43 mkPrivateKeyPath = host: ./hosts + "/${host}.priv"; 51 mkPrivateKeyPath = host: ./hosts + "/${host}.priv";
52
53 kernel = config.boot.kernelPackages;
44 54
45 publicKeyPath = mkPublicKeyPath hostName; 55 publicKeyPath = mkPublicKeyPath hostName;
46 privateKeyPath = mkPrivateKeyPath hostName; 56 privateKeyPath = mkPrivateKeyPath hostName;
47 inNetwork = pathExists privateKeyPath && pathExists publicKeyPath; 57 inNetwork = pathExists privateKeyPath && pathExists publicKeyPath;
48 hostLinks = filter ({ from, to, ... }: from == hostName || to == hostName) links; 58 hostLinks = filter ({ from, to, ... }: from == hostName || to == hostName) links;
49 hostRoutes = filter ({ from, to, ... }: from == hostName || to == hostName) routes; 59 # hostRoutes = filter ({ from, to, ... }: from == hostName || to == hostName) routes;
50 isRouter = inNetwork && any ({via, ...}: via == hostName) routes; 60 # isRouter = inNetwork && any ({via, ...}: via == hostName) routes;
51 linkToPeer = ix: opts@{from, to, ...}: 61 linkToPeer = opts@{from, to, ...}:
52 let 62 let
53 other = if from == hostName then to else from; 63 other = if from == hostName then to else from;
54 in { 64 in {
55 allowedIPs = hostIPs.${other} ++ concatMap (rArgs: if rArgs.from != hostName || rArgs.via != to then [] else hostIPs.${rArgs.to}) routes; 65 AllowedIPs = wgHostIPs.${other}; # ++ concatMap (rArgs: if rArgs.from != hostName || rArgs.via != to then [] else wgHostIPs.${rArgs.to}) routes;
56 publicKey = trim (readFile (mkPublicKeyPath other)); 66 PublicKey = trim (readFile (mkPublicKeyPath other));
57 } // (optionalAttrs (from == hostName) (filterAttrs (n: _v: !(elem n ["from" "to" "endpointHost"])) opts // optionalAttrs (opts ? "endpointHost") { endpoint = "127.0.0.1:${toString (udp2rawPort + ix)}"; })); 67 } // (optionalAttrs (from == hostName) (filterAttrs (n: _v: !(elem n ["from" "to" "endpointHost" "udp2raw"])) opts // optionalAttrs (opts ? "endpointHost") (if opts ? "udp2raw" then { Endpoint = "127.0.0.1:${toString (udp2rawPort + opts.udp2raw)}"; } else { Endpoint = "${opts.endpointHost}:${toString listenPort}"; })));
58 68 linkToGreDev = opts@{from, to, ...}:
69 let
70 other = if from == hostName then to else from;
71 in nameValuePair "yggdrasil-gre-${other}" {
72 netdevConfig = {
73 Name = "yggdrasil-gre-${other}";
74 Kind = "ip6gretap";
75 MTUBytes = toString 1280;
76 };
77 tunnelConfig = {
78 Local = stripSubnet wgHostIPs.${hostName};
79 Remote = stripSubnet wgHostIPs.${other};
80 };
81 };
82 linkToGreNetwork = ix: opts@{from, to, ...}:
83 let
84 other = if from == hostName then to else from;
85 hexIx = let
86 hexIx' = toHexString ix;
87 in if (stringLength hexIx' < 2) then "0${hexIx'}" else hexIx';
88 in nameValuePair "yggdrasil-gre-${other}" {
89 matchConfig = {
90 Name = "yggdrasil-gre-${other}";
91 };
92 linkConfig = {
93 MACAddress = "${greHostMACPrefixes.${hostName}}:${hexIx}";
94 };
95 networkConfig = {
96 Tunnel = "yggdrasil-gre-${other}";
97 BatmanAdvanced = "yggdrasil";
98 };
99 linkConfig = {
100 RequiredForOnline = false;
101 };
102 };
103
59 trim = str: if hasSuffix "\n" str then trim (removeSuffix "\n" str) else str; 104 trim = str: if hasSuffix "\n" str then trim (removeSuffix "\n" str) else str;
60 stripSubnet = addr: let matchRes = builtins.match "^(.*)/[0-9]+$" addr; in if matchRes == null then addr else elemAt matchRes 0; 105 stripSubnet = addr: let matchRes = builtins.match "^(.*)/[0-9]+$" addr; in if matchRes == null then addr else elemAt matchRes 0;
106 optIx = optName: xs: let
107 withOpts = listToAttrs (imap0 (ix: x: nameValuePair x.name (x.value // { ${optName} = ix; })) (filter (x: x.value.${optName} or false) (imap0 (ix: nameValuePair (toString ix)) xs)));
108 withoutOpts = listToAttrs (map (nv: nameValuePair nv.name (removeAttrs nv.value [optName])) (filter (x: !(x.value.${optName} or false)) (imap0 (ix: nameValuePair (toString ix)) xs)));
109 in genList (ix: withOpts.${toString ix} or withoutOpts.${toString ix}) (length xs);
110 mkLinks = optIx "udp2raw";
61in { 111in {
62 config = { 112 config = {
63 assertions = [ 113 assertions = [
64 { assertion = inNetwork || !(pathExists privateKeyPath || pathExists publicKeyPath); 114 { assertion = inNetwork || !(pathExists privateKeyPath || pathExists publicKeyPath);
65 message = "yggdrasil-wg: Either both public and private keys must exist or neither."; 115 message = "yggdrasil-wg: Either both public and private keys must exist or neither.";
66 } 116 }
67 { assertion = !inNetwork || (hostIPs ? "${hostName}"); 117 { assertion = !inNetwork || (wgHostIPs ? "${hostName}");
68 message = "yggdrasil-wg: Entry in hostIPs must exist."; 118 message = "yggdrasil-wg: Entry in wgHostIPs must exist.";
69 } 119 }
70 ] ++ map ({from, to, ...}: let other = if from == hostName then to else from; in { assertion = pathExists (mkPublicKeyPath other); message = "yggdrasil-wg: This host (${hostName}) has a link with ‘${other}’, but no public key is available for ‘${other}’."; }) hostLinks; 120 ] ++ map ({from, to, ...}: let other = if from == hostName then to else from; in { assertion = pathExists (mkPublicKeyPath other); message = "yggdrasil-wg: This host (${hostName}) has a link with ‘${other}’, but no public key is available for ‘${other}’."; }) hostLinks;
71 121
72 networking.wireguard.interfaces = mkIf inNetwork { 122 systemd.network = mkIf inNetwork {
73 yggdrasil = { 123 enable = true;
74 allowedIPsAsRoutes = false; 124 netdevs = {
75 inherit listenPort; 125 yggdrasil-wg = {
76 ips = hostIPs.${hostName}; 126 netdevConfig = {
77 peers = filter (value: value != null) (imap0 (ix: opts@{to, from, ...}: if from == hostName || to == hostName then linkToPeer ix opts else null) links); 127 Name = "yggdrasil-wg";
78 privateKeyFile = config.sops.secrets."yggdrasil-wg.priv".path; 128 Kind = "wireguard";
79 postSetup = '' 129 MTUBytes = toString (1280 + 70);
80 ip li set mtu 1280 dev yggdrasil 130 };
81 ${concatMapStringsSep "\n" (linkArgs: let other = if linkArgs.from == hostName then linkArgs.to else linkArgs.from; in concatMapStringsSep "\n" (otherIP: "ip route replace \"${otherIP}\" dev \"yggdrasil\" table \"main\"") hostIPs.${other}) hostLinks} 131 wireguardConfig = {
82 ${concatMapStringsSep "\n" (routeArgs: let other = if routeArgs.from == hostName then routeArgs.to else routeArgs.from; in concatMapStringsSep "\n" (otherIP: concatMapStringsSep "\n" (viaIP: "ip route replace \"${otherIP}\" via \"${viaIP}\" dev \"yggdrasil\" table \"main\"") (map stripSubnet hostIPs.${routeArgs.via})) hostIPs.${other}) hostRoutes} 132 PrivateKeyFile = config.sops.secrets."yggdrasil-wg.priv".path;
83 ''; 133 ListenPort = listenPort;
84 }; 134 };
135 wireguardPeers = map (opts@{to, from, ...}: { wireguardPeerConfig = linkToPeer opts; }) hostLinks;
136 };
137 yggdrasil = {
138 netdevConfig = {
139 Name = "yggdrasil";
140 Kind = "batadv";
141 };
142 };
143 } // listToAttrs (map linkToGreDev hostLinks);
144
145 networks = {
146 yggdrasil-wg = {
147 name = "yggdrasil-wg";
148 matchConfig = {
149 Name = "yggdrasil-wg";
150 };
151 address = [wgHostIPs.${hostName}];
152 linkConfig = {
153 RequiredForOnline = false;
154 };
155 };
156 yggdrasil = {
157 name = "yggdrasil";
158 matchConfig = {
159 Name = "yggdrasil";
160 };
161 address = batHostIPs.${hostName};
162 linkConfig = {
163 RequiredForOnline = false;
164 };
165 };
166 } // listToAttrs (imap0 linkToGreNetwork hostLinks);
85 }; 167 };
86 168
87 systemd.services = listToAttrs (filter ({ value, ...}: value != null) (imap0 (ix: opts@{to, from, ...}: let other = if from == hostName then to else from; in nameValuePair "yggdrasil-udp2raw@${other}" (if opts ? "endpointHost" && (from == hostName || to == hostName) then { 169 # networking.wireguard.interfaces = mkIf inNetwork {
170 # yggdrasil = {
171 # allowedIPsAsRoutes = false;
172 # inherit listenPort;
173 # ips = wgHostIPs.${hostName};
174 # peers = filter (value: value != null) (map (opts@{to, from, ...}: if from == hostName || to == hostName then linkToPeer opts else null) links);
175 # privateKeyFile = config.sops.secrets."yggdrasil-wg.priv".path;
176 # postSetup = ''
177 # ip li set mtu 1280 dev yggdrasil
178 # ${concatMapStringsSep "\n" (linkArgs: let other = if linkArgs.from == hostName then linkArgs.to else linkArgs.from; in concatMapStringsSep "\n" (otherIP: "ip route replace \"${otherIP}\" dev \"yggdrasil\" table \"main\"") wgHostIPs.${other}) hostLinks}
179 # ${concatMapStringsSep "\n" (routeArgs: let other = if routeArgs.from == hostName then routeArgs.to else routeArgs.from; in concatMapStringsSep "\n" (otherIP: concatMapStringsSep "\n" (viaIP: "ip route replace \"${otherIP}\" via \"${viaIP}\" dev \"yggdrasil\" table \"main\"") (map stripSubnet wgHostIPs.${routeArgs.via})) wgHostIPs.${other}) hostRoutes}
180 # '';
181 # };
182 # };
183
184 systemd.services = listToAttrs (filter ({ value, ...}: value != null) (map (opts@{to, from, ...}: let other = if from == hostName then to else from; in nameValuePair "yggdrasil-udp2raw@${other}" (if opts ? "endpointHost" && opts ? "udp2raw" then {
88 path = with pkgs; [iptables]; 185 path = with pkgs; [iptables];
89 serviceConfig = { 186 serviceConfig = {
90 RuntimeDirectory = ["udp2raw-config-${other}"]; 187 RuntimeDirectory = ["udp2raw-config-${other}"];
@@ -95,16 +192,17 @@ in {
95 cat >''${RUNTIME_DIRECTORY}/udp2raw.conf <<EOF 192 cat >''${RUNTIME_DIRECTORY}/udp2raw.conf <<EOF
96 ${if from == hostName then '' 193 ${if from == hostName then ''
97 -c 194 -c
98 -l 127.0.0.1:${toString (udp2rawPort + ix)} 195 -l 127.0.0.1:${toString (udp2rawPort + opts.udp2raw)}
99 -r ${opts.endpointHost}:${toString (udp2rawPort + ix)} 196 -r ${opts.endpointHost}:${toString (udp2rawPort + opts.udp2raw)}
100 '' else '' 197 '' else ''
101 -s 198 -s
102 -l 0.0.0.0:${toString (udp2rawPort + ix)} 199 -l 0.0.0.0:${toString (udp2rawPort + opts.udp2raw)}
103 -r 127.0.0.1:${toString listenPort} 200 -r 127.0.0.1:${toString listenPort}
104 ''} 201 ''}
105 -k $secret 202 -k $secret
106 --auth-mode hmac_sha1 203 --auth-mode hmac_sha1
107 --raw-mode faketcp 204 --raw-mode faketcp
205 --seq-mode 4
108 -a 206 -a
109 --retry-on-error 207 --retry-on-error
110 EOF 208 EOF
@@ -112,38 +210,42 @@ in {
112 ExecStart = "${pkgs.udp2raw}/bin/udp2raw --conf-file \${RUNTIME_DIRECTORY}/udp2raw.conf"; 210 ExecStart = "${pkgs.udp2raw}/bin/udp2raw --conf-file \${RUNTIME_DIRECTORY}/udp2raw.conf";
113 Restart = "always"; 211 Restart = "always";
114 }; 212 };
115 } else null)) links)) // { 213 } else null)) hostLinks));
116 "wireguard-yggdrasil" = { 214 # // {
117 bindsTo = filter (value: value != null) (map (opts@{to, from, ...}: let other = if from == hostName then to else from; in if opts ? "endpointHost" then "yggdrasil-udp2raw@${other}.service" else null) hostLinks); 215 # "wireguard-yggdrasil" = {
118 after = filter (value: value != null) (map (opts@{to, from, ...}: let other = if from == hostName then to else from; in if opts ? "endpointHost" then "yggdrasil-udp2raw@${other}.service" else null) hostLinks); 216 # bindsTo = filter (value: value != null) (map (opts@{to, from, ...}: let other = if from == hostName then to else from; in if opts ? "endpointHost" && opts ? "udp2raw" then "yggdrasil-udp2raw@${other}.service" else null) hostLinks);
119 }; 217 # after = filter (value: value != null) (map (opts@{to, from, ...}: let other = if from == hostName then to else from; in if opts ? "endpointHost" && opts ? "udp2raw" then "yggdrasil-udp2raw@${other}.service" else null) hostLinks);
120 firewall.path = optionals isRouter [pkgs.procps]; 218 # };
121 }; 219 # firewall.path = optionals isRouter [pkgs.procps];
220 # };
122 221
123 sops.secrets = { 222 sops.secrets = {
124 "yggdrasil-wg.priv" = mkIf (pathExists privateKeyPath) { 223 "yggdrasil-wg.priv" = mkIf (pathExists privateKeyPath) {
125 format = "binary"; 224 format = "binary";
126 sopsFile = privateKeyPath; 225 sopsFile = privateKeyPath;
127 }; 226 };
128 "yggdrasil-udp2raw-secret" = mkIf (any (opts@{to, from, ...}: (to == hostName || from == hostName) && opts ? "endpointHost") links) { 227 "yggdrasil-udp2raw-secret" = mkIf (any (opts@{to, from, ...}: opts ? "endpointHost" && opts ? "udp2raw") hostLinks) {
129 format = "binary"; 228 format = "binary";
130 sopsFile = ./udp2raw-secret; 229 sopsFile = ./udp2raw-secret;
131 }; 230 };
132 }; 231 };
133 232
134 networking.hosts = mkIf inNetwork (listToAttrs (concatMap ({name, value}: map (ip: nameValuePair (stripSubnet ip) ["${name}.yggdrasil"]) value) (mapAttrsToList nameValuePair hostIPs))); 233 networking.hosts = mkIf inNetwork (listToAttrs (concatMap ({name, value}: map (ip: nameValuePair (stripSubnet ip) ["${name}.yggdrasil"]) value) (mapAttrsToList nameValuePair batHostIPs)));
135 234
136 networking.firewall = mkIf isRouter { 235 # networking.firewall = mkIf isRouter {
137 extraCommands = '' 236 # extraCommands = ''
138 ip6tables -A FORWARD -i yggdrasil -o yggdrasil -j nixos-fw-accept 237 # ip6tables -A FORWARD -i yggdrasil -o yggdrasil -j nixos-fw-accept
139 ip46tables -A FORWARD -j nixos-fw-log-refuse 238 # ip46tables -A FORWARD -j nixos-fw-log-refuse
140 sysctl net.ipv6.conf.all.forwarding=1 239 # sysctl net.ipv6.conf.all.forwarding=1
141 ''; 240 # '';
142 extraStopCommands = '' 241 # extraStopCommands = ''
143 sysctl net.ipv6.conf.all.forwarding=0 242 # sysctl net.ipv6.conf.all.forwarding=0
144 ip46tables -D FORWARD -j nixos-fw-log-refuse || true 243 # ip46tables -D FORWARD -j nixos-fw-log-refuse || true
145 ip6tables -D FORWARD -i yggdrasil -o yggdrasil -j nixos-fw-accept || true 244 # ip6tables -D FORWARD -i yggdrasil -o yggdrasil -j nixos-fw-accept || true
146 ''; 245 # '';
147 }; 246 # };
247
248 boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard ++ [kernel.batman_adv];
249 environment.systemPackages = with pkgs; [ wireguard-tools batctl ];
148 }; 250 };
149} 251}