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.useDHCP || any (i: i.useDHCP == true) interfaces; 14 15 # Don't start dhcpcd on explicitly configured interfaces or on 16 # interfaces that are part of a bridge, bond or sit device. 17 ignoredInterfaces = 18 map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ip4 != [ ] || i.ipAddress != null) interfaces) 19 ++ mapAttrsToList (i: _: i) config.networking.sits 20 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges)) 21 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.vswitches)) 22 ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds)) 23 ++ config.networking.dhcpcd.denyInterfaces; 24 25 arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null 26 else if a1 == null then a2 else if a2 == null then a1 27 else a1 ++ a2; 28 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) (filter (i: i.useDHCP == true) interfaces) else null); 34 35 # Config file adapted from the one that ships with dhcpcd. 36 dhcpcdConf = pkgs.writeText "dhcpcd.conf" 37 '' 38 # Inform the DHCP server of our hostname for DDNS. 39 hostname 40 41 # A list of options to request from the DHCP server. 42 option domain_name_servers, domain_name, domain_search, host_name 43 option classless_static_routes, ntp_servers, interface_mtu 44 45 # A ServerID is required by RFC2131. 46 # Commented out because of many non-compliant DHCP servers in the wild :( 47 #require dhcp_server_identifier 48 49 # A hook script is provided to lookup the hostname if not set by 50 # the DHCP server, but it should not be run by default. 51 nohook lookup-hostname 52 53 # Ignore peth* devices; on Xen, they're renamed physical 54 # Ethernet cards used for bridging. Likewise for vif* and tap* 55 # (Xen) and virbr* and vnet* (libvirt). 56 denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit* 57 58 # Use the list of allowed interfaces if specified 59 ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} 60 61 ${cfg.extraConfig} 62 ''; 63 64 # Hook for emitting ip-up/ip-down events. 65 exitHook = pkgs.writeText "dhcpcd.exit-hook" 66 '' 67 if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then 68 # Restart ntpd. We need to restart it to make sure that it 69 # will actually do something: if ntpd cannot resolve the 70 # server hostnames in its config file, then it will never do 71 # anything ever again ("couldn't resolve ..., giving up on 72 # it"), so we silently lose time synchronisation. This also 73 # applies to openntpd. 74 ${config.systemd.package}/bin/systemctl try-restart ntpd.service 75 ${config.systemd.package}/bin/systemctl try-restart openntpd.service 76 77 ${config.systemd.package}/bin/systemctl start ip-up.target 78 fi 79 80 #if [ "$reason" = EXPIRE -o "$reason" = RELEASE -o "$reason" = NOCARRIER ] ; then 81 # ${config.systemd.package}/bin/systemctl start ip-down.target 82 #fi 83 84 ${cfg.runHook} 85 ''; 86 87in 88 89{ 90 91 ###### interface 92 93 options = { 94 95 networking.dhcpcd.persistent = mkOption { 96 type = types.bool; 97 default = false; 98 description = '' 99 Whenever to leave interfaces configured on dhcpcd daemon 100 shutdown. Set to true if you have your root or store mounted 101 over the network or this machine accepts SSH connections 102 through DHCP interfaces and clients should be notified when 103 it shuts down. 104 ''; 105 }; 106 107 networking.dhcpcd.denyInterfaces = mkOption { 108 type = types.listOf types.str; 109 default = []; 110 description = '' 111 Disable the DHCP client for any interface whose name matches 112 any of the shell glob patterns in this list. The purpose of 113 this option is to blacklist virtual interfaces such as those 114 created by Xen, libvirt, LXC, etc. 115 ''; 116 }; 117 118 networking.dhcpcd.allowInterfaces = mkOption { 119 type = types.nullOr (types.listOf types.str); 120 default = null; 121 description = '' 122 Enable the DHCP client for any interface whose name matches 123 any of the shell glob patterns in this list. Any interface not 124 explicitly matched by this pattern will be denied. This pattern only 125 applies when non-null. 126 ''; 127 }; 128 129 networking.dhcpcd.extraConfig = mkOption { 130 type = types.lines; 131 default = ""; 132 description = '' 133 Literal string to append to the config file generated for dhcpcd. 134 ''; 135 }; 136 137 networking.dhcpcd.runHook = mkOption { 138 type = types.lines; 139 default = ""; 140 example = "if [[ $reason =~ BOUND ]]; then echo $interface: Routers are $new_routers - were $old_routers; fi"; 141 description = '' 142 Shell code that will be run after all other hooks. See 143 `man dhcpcd-run-hooks` for details on what is possible. 144 ''; 145 }; 146 147 }; 148 149 150 ###### implementation 151 152 config = mkIf enableDHCP { 153 154 systemd.services.dhcpcd = 155 { description = "DHCP Client"; 156 157 wantedBy = [ "network.target" ]; 158 # Work-around to deal with problems where the kernel would remove & 159 # re-create Wifi interfaces early during boot. 160 after = [ "network-interfaces.target" ]; 161 162 # Stopping dhcpcd during a reconfiguration is undesirable 163 # because it brings down the network interfaces configured by 164 # dhcpcd. So do a "systemctl restart" instead. 165 stopIfChanged = false; 166 167 path = [ dhcpcd pkgs.nettools pkgs.openresolv ]; 168 169 unitConfig.ConditionCapability = "CAP_NET_ADMIN"; 170 171 serviceConfig = 172 { Type = "forking"; 173 PIDFile = "/run/dhcpcd.pid"; 174 ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; 175 ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind"; 176 Restart = "always"; 177 }; 178 }; 179 180 environment.systemPackages = [ dhcpcd ]; 181 182 environment.etc = 183 [ { source = exitHook; 184 target = "dhcpcd.exit-hook"; 185 } 186 ]; 187 188 powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable 189 '' 190 # Tell dhcpcd to rebind its interfaces if it's running. 191 ${config.systemd.package}/bin/systemctl reload dhcpcd.service 192 ''; 193 194 }; 195 196}