1 { config, lib, pkgs, ... }:
4 dhcpcd = if !config.boot.isContainer then pkgs.dhcpcd else pkgs.dhcpcd.override { udev = null; };
6 cfg = config.networking.dhcpcd;
8 interfaces = lib.attrValues config.networking.interfaces;
10 enableDHCP = config.networking.dhcpcd.enable &&
11 (config.networking.useDHCP || lib.any (i: i.useDHCP == true) interfaces);
13 enableNTPService = (config.services.ntp.enable || config.services.ntpd-rs.enable || config.services.openntpd.enable || config.services.chrony.enable);
15 # Don't start dhcpcd on explicitly configured interfaces or on
16 # interfaces that are part of a bridge, bond or sit device.
18 map (i: i.name) (lib.filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces)
19 ++ lib.mapAttrsToList (i: _: i) config.networking.sits
20 ++ lib.concatLists (lib.attrValues (lib.mapAttrs (n: v: v.interfaces) config.networking.bridges))
21 ++ lib.flatten (lib.concatMap (i: lib.attrNames (lib.filterAttrs (_: config: config.type != "internal") i.interfaces)) (lib.attrValues config.networking.vswitches))
22 ++ lib.concatLists (lib.attrValues (lib.mapAttrs (n: v: v.interfaces) config.networking.bonds))
23 ++ config.networking.dhcpcd.denyInterfaces;
25 arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null
26 else if a1 == null then a2 else if a2 == null then a1
29 # If dhcp is disabled but explicit interfaces are enabled,
30 # we need to provide dhcp just for those interfaces.
31 allowInterfaces = arrayAppendOrNull cfg.allowInterfaces
32 (if !config.networking.useDHCP && enableDHCP then
33 map (i: i.name) (lib.filter (i: i.useDHCP == true) interfaces) else null);
35 staticIPv6Addresses = map (i: i.name) (lib.filter (i: i.ipv6.addresses != [ ]) interfaces);
37 noIPv6rs = lib.concatStringsSep "\n" (map (name: ''
40 '') staticIPv6Addresses);
42 # Config file adapted from the one that ships with dhcpcd.
43 dhcpcdConf = pkgs.writeText "dhcpcd.conf"
45 # Inform the DHCP server of our hostname for DDNS.
48 # A list of options to request from the DHCP server.
49 option domain_name_servers, domain_name, domain_search, host_name
50 option classless_static_routes, ntp_servers, interface_mtu
52 # A ServerID is required by RFC2131.
53 # Commented out because of many non-compliant DHCP servers in the wild :(
54 #require dhcp_server_identifier
56 # A hook script is provided to lookup the hostname if not set by
57 # the DHCP server, but it should not be run by default.
58 nohook lookup-hostname
60 # Ignore peth* devices; on Xen, they're renamed physical
61 # Ethernet cards used for bridging. Likewise for vif* and tap*
62 # (Xen) and virbr* and vnet* (libvirt).
63 denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit*
65 # Use the list of allowed interfaces if specified
66 ${lib.optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"}
68 # Immediately fork to background if specified, otherwise wait for IP address to be assigned
70 background = "background";
74 both = "waitip 4\nwaitip 6";
78 ${lib.optionalString (config.networking.enableIPv6 == false) ''
79 # Don't solicit or accept IPv6 Router Advertisements and DHCPv6 if disabled IPv6
83 ${lib.optionalString (config.networking.enableIPv6 && cfg.IPv6rs == null && staticIPv6Addresses != [ ]) noIPv6rs}
84 ${lib.optionalString (config.networking.enableIPv6 && cfg.IPv6rs == false) ''
91 exitHook = pkgs.writeText "dhcpcd.exit-hook" ''
92 ${lib.optionalString enableNTPService ''
93 if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then
94 # Restart ntpd. We need to restart it to make sure that it will actually do something:
95 # if ntpd cannot resolve the server hostnames in its config file, then it will never do
96 # anything ever again ("couldn't resolve ..., giving up on it"), so we silently lose
97 # time synchronisation. This also applies to openntpd.
98 ${lib.optionalString config.services.ntp.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service || true"}
99 ${lib.optionalString config.services.ntpd-rs.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd-rs.service || true"}
100 ${lib.optionalString config.services.openntpd.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart openntpd.service || true"}
101 ${lib.optionalString config.services.chrony.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart chronyd.service || true"}
116 networking.dhcpcd.enable = lib.mkOption {
117 type = lib.types.bool;
120 Whether to enable dhcpcd for device configuration. This is mainly to
121 explicitly disable dhcpcd (for example when using networkd).
125 networking.dhcpcd.persistent = lib.mkOption {
126 type = lib.types.bool;
129 Whenever to leave interfaces configured on dhcpcd daemon
130 shutdown. Set to true if you have your root or store mounted
131 over the network or this machine accepts SSH connections
132 through DHCP interfaces and clients should be notified when
137 networking.dhcpcd.denyInterfaces = lib.mkOption {
138 type = lib.types.listOf lib.types.str;
141 Disable the DHCP client for any interface whose name matches
142 any of the shell glob patterns in this list. The purpose of
143 this option is to blacklist virtual interfaces such as those
144 created by Xen, libvirt, LXC, etc.
148 networking.dhcpcd.allowInterfaces = lib.mkOption {
149 type = lib.types.nullOr (lib.types.listOf lib.types.str);
152 Enable the DHCP client for any interface whose name matches
153 any of the shell glob patterns in this list. Any interface not
154 explicitly matched by this pattern will be denied. This pattern only
155 applies when non-null.
159 networking.dhcpcd.extraConfig = lib.mkOption {
160 type = lib.types.lines;
163 Literal string to append to the config file generated for dhcpcd.
167 networking.dhcpcd.IPv6rs = lib.mkOption {
168 type = lib.types.nullOr lib.types.bool;
171 Force enable or disable solicitation and receipt of IPv6 Router Advertisements.
172 This is required, for example, when using a static unique local IPv6 address (ULA)
173 and global IPv6 address auto-configuration with SLAAC.
177 networking.dhcpcd.runHook = lib.mkOption {
178 type = lib.types.lines;
180 example = "if [[ $reason =~ BOUND ]]; then echo $interface: Routers are $new_routers - were $old_routers; fi";
182 Shell code that will be run after all other hooks. See
183 `man dhcpcd-run-hooks` for details on what is possible.
187 networking.dhcpcd.wait = lib.mkOption {
188 type = lib.types.enum [ "background" "any" "ipv4" "ipv6" "both" "if-carrier-up" ];
191 This option specifies when the dhcpcd service will fork to background.
192 If set to "background", dhcpcd will fork to background immediately.
193 If set to "ipv4" or "ipv6", dhcpcd will wait for the corresponding IP
194 address to be assigned. If set to "any", dhcpcd will wait for any type
195 (IPv4 or IPv6) to be assigned. If set to "both", dhcpcd will wait for
196 both an IPv4 and an IPv6 address before forking.
197 The option "if-carrier-up" is equivalent to "any" if either ethernet
198 is plugged nor WiFi is powered, and to "background" otherwise.
205 ###### implementation
207 config = lib.mkIf enableDHCP {
210 # dhcpcd doesn't start properly with malloc ∉ [ jemalloc libc mimalloc scudo ]
211 # see https://github.com/NixOS/nixpkgs/issues/151696
214 -> lib.elem config.environment.memoryAllocator.provider [ "jemalloc" "libc" "mimalloc" "scudo" ];
216 dhcpcd with privilege separation is incompatible with chosen system malloc.
217 Currently `graphene-hardened` allocator is known to be broken.
218 To disable dhcpcd's privilege separation, overlay Nixpkgs and override dhcpcd
219 to set `enablePrivSep = false`.
223 environment.etc."dhcpcd.conf".source = dhcpcdConf;
225 systemd.services.dhcpcd = let
226 cfgN = config.networking;
227 hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "")
228 && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != ""));
230 { description = "DHCP Client";
232 wantedBy = [ "multi-user.target" ] ++ lib.optional (!hasDefaultGatewaySet) "network-online.target";
233 wants = [ "network.target" ];
234 before = [ "network-online.target" ];
236 restartTriggers = lib.optional (enableNTPService || cfg.runHook != "") [ exitHook ];
238 # Stopping dhcpcd during a reconfiguration is undesirable
239 # because it brings down the network interfaces configured by
240 # dhcpcd. So do a "systemctl restart" instead.
241 stopIfChanged = false;
243 path = [ dhcpcd pkgs.nettools config.networking.resolvconf.package ];
245 unitConfig.ConditionCapability = "CAP_NET_ADMIN";
249 PIDFile = "/run/dhcpcd/pid";
250 RuntimeDirectory = "dhcpcd";
251 ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${lib.optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}";
252 ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind";
254 } // lib.optionalAttrs (cfg.runHook == "") {
257 ProtectProc = "invisible";
258 # Access write directories
261 CapabilityBoundingSet = [ "CAP_NET_ADMIN" "CAP_NET_BIND_SERVICE" "CAP_NET_RAW" "CAP_SETGID" "CAP_SETUID" "CAP_SYS_CHROOT" ];
263 NoNewPrivileges = true;
265 ProtectSystem = true;
268 PrivateDevices = true;
269 PrivateUsers = false;
270 ProtectHostname = true;
272 ProtectKernelTunables = false;
273 ProtectKernelModules = true;
274 ProtectKernelLogs = true;
275 ProtectControlGroups = true;
276 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" "AF_PACKET" ];
277 RestrictNamespaces = true;
278 LockPersonality = true;
279 MemoryDenyWriteExecute = true;
280 RestrictRealtime = true;
281 RestrictSUIDSGID = true;
283 PrivateMounts = true;
284 # System Call Filtering
285 SystemCallArchitectures = "native";
286 SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @resources" "chroot" "gettid" "setgroups" "setuid" ];
290 users.users.dhcpcd = {
294 users.groups.dhcpcd = {};
296 environment.systemPackages = [ dhcpcd ];
298 environment.etc."dhcpcd.exit-hook" = lib.mkIf (enableNTPService || cfg.runHook != "") {
302 powerManagement.resumeCommands = lib.mkIf config.systemd.services.dhcpcd.enable
304 # Tell dhcpcd to rebind its interfaces if it's running.
305 /run/current-system/systemd/bin/systemctl reload dhcpcd.service