{ config, lib, pkgs, ... }: with lib; let cfg = config.networking.dhcpcd-prefixdelegation; downstreamInterfaceConfig = name: opts: "${name}${ optionalString (opts.sla_id != null) "/${builtins.toString opts.sla_id}${ optionalString (opts.prefix_len != null) "/${builtins.toString opts.prefix_len}${ optionalString (opts.suffix != null) "/${opts.suffix}" }" }" }"; interfaceConfig = name: opts: '' interface ${name} ipv6rs ia_pd ${builtins.toString opts.iaid}${ optionalString (opts.prefix != null) "/${opts.prefix}${ optionalString (opts.prefix_len != null) "/${builtins.toString opts.prefix_len}" }" } ${concatMapStringsSep " " ({name, value}: downstreamInterfaceConfig name value) (attrsToList opts.interfaces)} ''; dhcpcdConf = pkgs.writeText "dhcpcd.conf" '' duid noipv6rs waitip 6 ipv6only allowinterfaces ${concatStringsSep " " (builtins.attrNames cfg.interfaces)} ${concatMapStringsSep " " ({name, value}: concatStringsSep "" (builtins.attrNames value.interfaces)) (attrsToList cfg.interfaces)} ${concatMapStringsSep "\n" ({name, value}: interfaceConfig name value) (attrsToList cfg.interfaces)} ''; downstreamInterfaceOpts = { ... }: { options = { sla_id = mkOption { type = with types; nullOr ints.unsigned; default = null; }; prefix_len = mkOption { type = with types; nullOr ints.unsigned; default = null; }; suffix = mkOption { type = with types; nullOr str; default = null; }; }; }; interfaceOpts = { ... }: { options = { iaid = mkOption { type = with types; ints.unsigned; description = '' Request a delegated prefix with this IAID on this interface ''; }; prefix = mkOption { type = with types; nullOr str; default = null; }; prefix_len = mkOption { type = with types; nullOr ints.unsigned; default = null; }; interfaces = mkOption { type = with types; attrsOf (submodule downstreamInterfaceOpts); default = {}; description ='' Interfaces to assign IPv6 prefixes to ''; }; }; }; in { options = { networking.dhcpcd-prefixdelegation = { enable = mkEnableOption "dhcpcd for prefixdelegation"; interfaces = mkOption { type = with types; attrsOf (submodule interfaceOpts); default = {}; description = '' Interfaces to request IPv6 prefixes from ''; }; }; }; config = mkIf cfg.enable { environment.etc."dhcpcd.conf".source = dhcpcdConf; systemd.services.dhcpcd-prefixdelegation = { description = "DHCP Client for IPv6 Prefix Delegation"; wantedBy = [ "multi-user.target" ]; wants = [ "network.target" ]; before = [ "network-online.target" ]; # 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 = [ pkgs.dhcpcd ]; unitConfig.ConditionCapability = "CAP_NET_ADMIN"; serviceConfig = { Type = "forking"; PIDFile = "/run/dhcpcd/pid"; RuntimeDirectory = "dhcpcd"; ExecStart = "@${pkgs.dhcpcd}/sbin/dhcpcd dhcpcd --quiet --config ${dhcpcdConf}"; ExecReload = "${pkgs.dhcpcd}/sbin/dhcpcd --rebind"; Restart = "always"; }; }; users.users.dhcpcd = { isSystemUser = true; group = "dhcpcd"; }; users.groups.dhcpcd = {}; }; }