diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking/dhcpcd.nix')
-rw-r--r-- | nixpkgs/nixos/modules/services/networking/dhcpcd.nix | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix new file mode 100644 index 000000000000..7b2786034552 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix @@ -0,0 +1,202 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + dhcpcd = if !config.boot.isContainer then pkgs.dhcpcd else pkgs.dhcpcd.override { udev = null; }; + + cfg = config.networking.dhcpcd; + + interfaces = attrValues config.networking.interfaces; + + enableDHCP = config.networking.dhcpcd.enable && + (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces); + + # Don't start dhcpcd on explicitly configured interfaces or on + # interfaces that are part of a bridge, bond or sit device. + ignoredInterfaces = + map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces) + ++ mapAttrsToList (i: _: i) config.networking.sits + ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges)) + ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.vswitches)) + ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds)) + ++ config.networking.dhcpcd.denyInterfaces; + + arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null + else if a1 == null then a2 else if a2 == null then a1 + else a1 ++ a2; + + # If dhcp is disabled but explicit interfaces are enabled, + # we need to provide dhcp just for those interfaces. + allowInterfaces = arrayAppendOrNull cfg.allowInterfaces + (if !config.networking.useDHCP && enableDHCP then + map (i: i.name) (filter (i: i.useDHCP == true) interfaces) else null); + + # Config file adapted from the one that ships with dhcpcd. + dhcpcdConf = pkgs.writeText "dhcpcd.conf" + '' + # Inform the DHCP server of our hostname for DDNS. + hostname + + # A list of options to request from the DHCP server. + option domain_name_servers, domain_name, domain_search, host_name + option classless_static_routes, ntp_servers, interface_mtu + + # A ServerID is required by RFC2131. + # Commented out because of many non-compliant DHCP servers in the wild :( + #require dhcp_server_identifier + + # A hook script is provided to lookup the hostname if not set by + # the DHCP server, but it should not be run by default. + nohook lookup-hostname + + # Ignore peth* devices; on Xen, they're renamed physical + # Ethernet cards used for bridging. Likewise for vif* and tap* + # (Xen) and virbr* and vnet* (libvirt). + denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit* + + # Use the list of allowed interfaces if specified + ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} + + ${cfg.extraConfig} + ''; + + exitHook = pkgs.writeText "dhcpcd.exit-hook" + '' + if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then + # Restart ntpd. We need to restart it to make sure that it + # will actually do something: if ntpd cannot resolve the + # server hostnames in its config file, then it will never do + # anything ever again ("couldn't resolve ..., giving up on + # it"), so we silently lose time synchronisation. This also + # applies to openntpd. + ${config.systemd.package}/bin/systemctl try-reload-or-restart ntpd.service openntpd.service chronyd.service || true + fi + + ${cfg.runHook} + ''; + +in + +{ + + ###### interface + + options = { + + networking.dhcpcd.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable dhcpcd for device configuration. This is mainly to + explicitly disable dhcpcd (for example when using networkd). + ''; + }; + + networking.dhcpcd.persistent = mkOption { + type = types.bool; + default = false; + description = '' + Whenever to leave interfaces configured on dhcpcd daemon + shutdown. Set to true if you have your root or store mounted + over the network or this machine accepts SSH connections + through DHCP interfaces and clients should be notified when + it shuts down. + ''; + }; + + networking.dhcpcd.denyInterfaces = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Disable the DHCP client for any interface whose name matches + any of the shell glob patterns in this list. The purpose of + this option is to blacklist virtual interfaces such as those + created by Xen, libvirt, LXC, etc. + ''; + }; + + networking.dhcpcd.allowInterfaces = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + description = '' + Enable the DHCP client for any interface whose name matches + any of the shell glob patterns in this list. Any interface not + explicitly matched by this pattern will be denied. This pattern only + applies when non-null. + ''; + }; + + networking.dhcpcd.extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Literal string to append to the config file generated for dhcpcd. + ''; + }; + + networking.dhcpcd.runHook = mkOption { + type = types.lines; + default = ""; + example = "if [[ $reason =~ BOUND ]]; then echo $interface: Routers are $new_routers - were $old_routers; fi"; + description = '' + Shell code that will be run after all other hooks. See + `man dhcpcd-run-hooks` for details on what is possible. + ''; + }; + + }; + + + ###### implementation + + config = mkIf enableDHCP { + + systemd.services.dhcpcd = let + cfgN = config.networking; + hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "") + && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != "")); + in + { description = "DHCP Client"; + + wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target"; + wants = [ "network.target" "systemd-udev-settle.service" ]; + before = [ "network-online.target" ]; + after = [ "systemd-udev-settle.service" ]; + + # Stopping dhcpcd during a reconfiguration is undesirable + # because it brings down the network interfaces configured by + # dhcpcd. So do a "systemctl restart" instead. + stopIfChanged = false; + + path = [ dhcpcd pkgs.nettools pkgs.openresolv ]; + + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; + + serviceConfig = + { Type = "forking"; + PIDFile = "/run/dhcpcd.pid"; + ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd -w --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; + ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind"; + Restart = "always"; + }; + }; + + environment.systemPackages = [ dhcpcd ]; + + environment.etc = + [ { source = exitHook; + target = "dhcpcd.exit-hook"; + } + ]; + + powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable + '' + # Tell dhcpcd to rebind its interfaces if it's running. + ${config.systemd.package}/bin/systemctl reload dhcpcd.service + ''; + + }; + +} |