at 24.11-pre 11 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 dhcpcd = if !config.boot.isContainer then pkgs.dhcpcd else pkgs.dhcpcd.override { udev = null; }; 8 9 cfg = config.networking.dhcpcd; 10 11 interfaces = attrValues config.networking.interfaces; 12 13 enableDHCP = config.networking.dhcpcd.enable && 14 (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces); 15 16 enableNTPService = (config.services.ntp.enable || config.services.ntpd-rs.enable || config.services.openntpd.enable || config.services.chrony.enable); 17 18 # Don't start dhcpcd on explicitly configured interfaces or on 19 # interfaces that are part of a bridge, bond or sit device. 20 ignoredInterfaces = 21 map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces) 22 ++ mapAttrsToList (i: _: i) config.networking.sits 23 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges)) 24 ++ flatten (concatMap (i: attrNames (filterAttrs (_: config: config.type != "internal") i.interfaces)) (attrValues config.networking.vswitches)) 25 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds)) 26 ++ config.networking.dhcpcd.denyInterfaces; 27 28 arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null 29 else if a1 == null then a2 else if a2 == null then a1 30 else a1 ++ a2; 31 32 # If dhcp is disabled but explicit interfaces are enabled, 33 # we need to provide dhcp just for those interfaces. 34 allowInterfaces = arrayAppendOrNull cfg.allowInterfaces 35 (if !config.networking.useDHCP && enableDHCP then 36 map (i: i.name) (filter (i: i.useDHCP == true) interfaces) else null); 37 38 staticIPv6Addresses = map (i: i.name) (filter (i: i.ipv6.addresses != [ ]) interfaces); 39 40 noIPv6rs = concatStringsSep "\n" (map (name: '' 41 interface ${name} 42 noipv6rs 43 '') staticIPv6Addresses); 44 45 # Config file adapted from the one that ships with dhcpcd. 46 dhcpcdConf = pkgs.writeText "dhcpcd.conf" 47 '' 48 # Inform the DHCP server of our hostname for DDNS. 49 hostname 50 51 # A list of options to request from the DHCP server. 52 option domain_name_servers, domain_name, domain_search, host_name 53 option classless_static_routes, ntp_servers, interface_mtu 54 55 # A ServerID is required by RFC2131. 56 # Commented out because of many non-compliant DHCP servers in the wild :( 57 #require dhcp_server_identifier 58 59 # A hook script is provided to lookup the hostname if not set by 60 # the DHCP server, but it should not be run by default. 61 nohook lookup-hostname 62 63 # Ignore peth* devices; on Xen, they're renamed physical 64 # Ethernet cards used for bridging. Likewise for vif* and tap* 65 # (Xen) and virbr* and vnet* (libvirt). 66 denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit* 67 68 # Use the list of allowed interfaces if specified 69 ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} 70 71 # Immediately fork to background if specified, otherwise wait for IP address to be assigned 72 ${{ 73 background = "background"; 74 any = "waitip"; 75 ipv4 = "waitip 4"; 76 ipv6 = "waitip 6"; 77 both = "waitip 4\nwaitip 6"; 78 if-carrier-up = ""; 79 }.${cfg.wait}} 80 81 ${optionalString (config.networking.enableIPv6 == false) '' 82 # Don't solicit or accept IPv6 Router Advertisements and DHCPv6 if disabled IPv6 83 noipv6 84 ''} 85 86 ${optionalString (config.networking.enableIPv6 && cfg.IPv6rs == null && staticIPv6Addresses != [ ]) noIPv6rs} 87 ${optionalString (config.networking.enableIPv6 && cfg.IPv6rs == false) '' 88 noipv6rs 89 ''} 90 91 ${cfg.extraConfig} 92 ''; 93 94 exitHook = pkgs.writeText "dhcpcd.exit-hook" '' 95 ${optionalString enableNTPService '' 96 if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then 97 # Restart ntpd. We need to restart it to make sure that it will actually do something: 98 # if ntpd cannot resolve the server hostnames in its config file, then it will never do 99 # anything ever again ("couldn't resolve ..., giving up on it"), so we silently lose 100 # time synchronisation. This also applies to openntpd. 101 ${optionalString config.services.ntp.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service || true"} 102 ${optionalString config.services.ntpd-rs.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd-rs.service || true"} 103 ${optionalString config.services.openntpd.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart openntpd.service || true"} 104 ${optionalString config.services.chrony.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart chronyd.service || true"} 105 fi 106 ''} 107 108 ${cfg.runHook} 109 ''; 110 111in 112 113{ 114 115 ###### interface 116 117 options = { 118 119 networking.dhcpcd.enable = mkOption { 120 type = types.bool; 121 default = true; 122 description = '' 123 Whether to enable dhcpcd for device configuration. This is mainly to 124 explicitly disable dhcpcd (for example when using networkd). 125 ''; 126 }; 127 128 networking.dhcpcd.persistent = mkOption { 129 type = types.bool; 130 default = false; 131 description = '' 132 Whenever to leave interfaces configured on dhcpcd daemon 133 shutdown. Set to true if you have your root or store mounted 134 over the network or this machine accepts SSH connections 135 through DHCP interfaces and clients should be notified when 136 it shuts down. 137 ''; 138 }; 139 140 networking.dhcpcd.denyInterfaces = mkOption { 141 type = types.listOf types.str; 142 default = []; 143 description = '' 144 Disable the DHCP client for any interface whose name matches 145 any of the shell glob patterns in this list. The purpose of 146 this option is to blacklist virtual interfaces such as those 147 created by Xen, libvirt, LXC, etc. 148 ''; 149 }; 150 151 networking.dhcpcd.allowInterfaces = mkOption { 152 type = types.nullOr (types.listOf types.str); 153 default = null; 154 description = '' 155 Enable the DHCP client for any interface whose name matches 156 any of the shell glob patterns in this list. Any interface not 157 explicitly matched by this pattern will be denied. This pattern only 158 applies when non-null. 159 ''; 160 }; 161 162 networking.dhcpcd.extraConfig = mkOption { 163 type = types.lines; 164 default = ""; 165 description = '' 166 Literal string to append to the config file generated for dhcpcd. 167 ''; 168 }; 169 170 networking.dhcpcd.IPv6rs = mkOption { 171 type = types.nullOr types.bool; 172 default = null; 173 description = '' 174 Force enable or disable solicitation and receipt of IPv6 Router Advertisements. 175 This is required, for example, when using a static unique local IPv6 address (ULA) 176 and global IPv6 address auto-configuration with SLAAC. 177 ''; 178 }; 179 180 networking.dhcpcd.runHook = mkOption { 181 type = types.lines; 182 default = ""; 183 example = "if [[ $reason =~ BOUND ]]; then echo $interface: Routers are $new_routers - were $old_routers; fi"; 184 description = '' 185 Shell code that will be run after all other hooks. See 186 `man dhcpcd-run-hooks` for details on what is possible. 187 ''; 188 }; 189 190 networking.dhcpcd.wait = mkOption { 191 type = types.enum [ "background" "any" "ipv4" "ipv6" "both" "if-carrier-up" ]; 192 default = "any"; 193 description = '' 194 This option specifies when the dhcpcd service will fork to background. 195 If set to "background", dhcpcd will fork to background immediately. 196 If set to "ipv4" or "ipv6", dhcpcd will wait for the corresponding IP 197 address to be assigned. If set to "any", dhcpcd will wait for any type 198 (IPv4 or IPv6) to be assigned. If set to "both", dhcpcd will wait for 199 both an IPv4 and an IPv6 address before forking. 200 The option "if-carrier-up" is equivalent to "any" if either ethernet 201 is plugged nor WiFi is powered, and to "background" otherwise. 202 ''; 203 }; 204 205 }; 206 207 208 ###### implementation 209 210 config = mkIf enableDHCP { 211 212 assertions = [ { 213 # dhcpcd doesn't start properly with malloc ∉ [ libc scudo ] 214 # see https://github.com/NixOS/nixpkgs/issues/151696 215 assertion = 216 dhcpcd.enablePrivSep 217 -> elem config.environment.memoryAllocator.provider [ "libc" "scudo" ]; 218 message = '' 219 dhcpcd with privilege separation is incompatible with chosen system malloc. 220 Currently only the `libc` and `scudo` allocators are known to work. 221 To disable dhcpcd's privilege separation, overlay Nixpkgs and override dhcpcd 222 to set `enablePrivSep = false`. 223 ''; 224 } ]; 225 226 environment.etc."dhcpcd.conf".source = dhcpcdConf; 227 228 systemd.services.dhcpcd = let 229 cfgN = config.networking; 230 hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "") 231 && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != "")); 232 in 233 { description = "DHCP Client"; 234 235 wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target"; 236 wants = [ "network.target" ]; 237 before = [ "network-online.target" ]; 238 239 restartTriggers = optional (enableNTPService || cfg.runHook != "") [ exitHook ]; 240 241 # Stopping dhcpcd during a reconfiguration is undesirable 242 # because it brings down the network interfaces configured by 243 # dhcpcd. So do a "systemctl restart" instead. 244 stopIfChanged = false; 245 246 path = [ dhcpcd pkgs.nettools config.networking.resolvconf.package ]; 247 248 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 249 250 serviceConfig = 251 { Type = "forking"; 252 PIDFile = "/run/dhcpcd/pid"; 253 RuntimeDirectory = "dhcpcd"; 254 ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; 255 ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind"; 256 Restart = "always"; 257 }; 258 }; 259 260 users.users.dhcpcd = { 261 isSystemUser = true; 262 group = "dhcpcd"; 263 }; 264 users.groups.dhcpcd = {}; 265 266 environment.systemPackages = [ dhcpcd ]; 267 268 environment.etc."dhcpcd.exit-hook" = mkIf (enableNTPService || cfg.runHook != "") { 269 source = exitHook; 270 }; 271 272 powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable 273 '' 274 # Tell dhcpcd to rebind its interfaces if it's running. 275 /run/current-system/systemd/bin/systemctl reload dhcpcd.service 276 ''; 277 278 }; 279 280}