{ config, lib, pkgs, ... }: let cfg = config.router; dhcpLans = lib.filter (l: l.dhcp.enable) (lib.attrValues cfg.lans); ipv4 = (import ./lib.nix { inherit lib; }).ipv4; in { config = lib.mkIf (cfg.enable && dhcpLans != [ ]) { # Kea skips interfaces without carrier when it opens its sockets and only # looks again on a reload: a LAN cable plugged in after boot would get no # DHCP until a restart. So restart it when a DHCP LAN gets carrier. services.networkd-dispatcher = { enable = true; rules.kea-carrier = { onState = [ "carrier" "degraded" "routable" ]; # The same for every configuration (the interfaces are in /etc): the # module builds it with writeShellApplication, whose shellcheck run # could not be rebuilt offline on the device. # Not a reload: a SIGHUP while Kea is still starting ends it (cleanly, # so systemd does not restart it). script = '' if ${pkgs.gnugrep}/bin/grep -qxF "$IFACE" /etc/router/dhcp-interfaces; then ${config.systemd.package}/bin/systemctl try-restart --no-block kea-dhcp4-server.service fi ''; }; }; environment.etc."router/dhcp-interfaces".text = lib.concatMapStrings (l: "${l.device}\n") dhcpLans; services.kea.dhcp4 = { enable = true; settings = { interfaces-config = { interfaces = map (l: l.device) dhcpLans; # Interfaces are configured asynchronously by networkd. re-detect = true; }; lease-database = { type = "memfile"; persist = true; name = "/var/lib/kea/dhcp4.leases"; }; valid-lifetime = 3600; # The ID keys persistent leases, so derive it from the network address: # stable when LANs are added or removed (an index would shift). subnet4 = map (l: { id = ipv4.networkOf (ipv4.parse l.ipv4.address) l.ipv4.prefixLength; inherit (l.ipv4) subnet; interface = l.device; pools = [ { pool = "${l.dhcp.pool.start} - ${l.dhcp.pool.end}"; } ]; reservations = lib.mapAttrsToList (hostname: r: { hw-address = lib.toLower r.mac; ip-address = r.address; inherit hostname; }) l.dhcp.reservations; option-data = [ { name = "routers"; data = l.ipv4.address; } { name = "domain-name-servers"; data = l.ipv4.address; } ]; }) dhcpLans; }; }; }; }