# This test verifies DHCPv4 interaction between a client and a router.
# For successful DHCP allocations a dynamic update request is sent
# towards a nameserver to allocate a name in the lan.nixos.test zone.
# We then verify whether client and router can ping each other, and
# that the nameserver can resolve the clients fqdn to the correct IP
# address.

import ./make-test-python.nix ({ pkgs, lib, ...}: {
  meta.maintainers = with lib.maintainers; [ hexa ];

  name = "kea";

  nodes = {
    router = { config, pkgs, ... }: {
      virtualisation.vlans = [ 1 ];

      networking = {
        useDHCP = false;
        firewall.allowedUDPPorts = [ 67 ];
      };

      systemd.network = {
        enable = true;
        networks = {
          "01-eth1" = {
            name = "eth1";
            networkConfig = {
              Address = "10.0.0.1/29";
            };
          };
        };
      };

      services.kea.dhcp4 = {
        enable = true;
        settings = {
          valid-lifetime = 3600;
          renew-timer = 900;
          rebind-timer = 1800;

          lease-database = {
            type = "memfile";
            persist = true;
            name = "/var/lib/kea/dhcp4.leases";
          };

          control-socket = {
            socket-type = "unix";
            socket-name = "/run/kea/dhcp4.sock";
          };

          interfaces-config = {
            dhcp-socket-type = "raw";
            interfaces = [
              "eth1"
            ];
          };

          subnet4 = [ {
            id = 1;
            subnet = "10.0.0.0/29";
            pools = [ {
              pool = "10.0.0.3 - 10.0.0.3";
            } ];
          } ];

          # Enable communication between dhcp4 and a local dhcp-ddns
          # instance.
          # https://kea.readthedocs.io/en/kea-2.2.0/arm/dhcp4-srv.html#ddns-for-dhcpv4
          dhcp-ddns = {
            enable-updates = true;
          };

          ddns-send-updates = true;
          ddns-qualifying-suffix = "lan.nixos.test.";
        };
      };

      services.kea.dhcp-ddns = {
        enable = true;
        settings = {
          forward-ddns = {
            # Configure updates of a forward zone named `lan.nixos.test`
            # hosted at the nameserver at 10.0.0.2
            # https://kea.readthedocs.io/en/kea-2.2.0/arm/ddns.html#adding-forward-dns-servers
            ddns-domains = [ {
              name = "lan.nixos.test.";
              # Use a TSIG key in production!
              key-name = "";
              dns-servers = [ {
                ip-address = "10.0.0.2";
                port = 53;
              } ];
            } ];
          };
        };
      };

      services.kea.ctrl-agent = {
        enable = true;
        settings = {
          http-host = "127.0.0.1";
          http-port = 8000;
          control-sockets.dhcp4 = {
            socket-type = "unix";
            socket-name = "/run/kea/dhcp4.sock";
          };
        };
      };

      services.prometheus.exporters.kea = {
        enable = true;
        controlSocketPaths = [
          "http://127.0.0.1:8000"
        ];
      };
    };

    nameserver = { config, pkgs, ... }: {
      virtualisation.vlans = [ 1 ];

      networking = {
        useDHCP = false;
        firewall.allowedUDPPorts = [ 53 ];
      };

      systemd.network = {
        enable = true;
        networks = {
          "01-eth1" = {
            name = "eth1";
            networkConfig = {
              Address = "10.0.0.2/29";
            };
          };
        };
      };

      services.resolved.enable = false;

      # Set up an authoritative nameserver, serving the `lan.nixos.test`
      # zone and configure an ACL that allows dynamic updates from
      # the router's ip address.
      # This ACL is likely insufficient for production usage. Please
      # use TSIG keys.
      services.knot = let
        zone = pkgs.writeTextDir "lan.nixos.test.zone" ''
          @ SOA ns.nixos.test nox.nixos.test 0 86400 7200 3600000 172800
          @ NS nameserver
          nameserver A 10.0.0.3
          router A 10.0.0.1
        '';
        zonesDir = pkgs.buildEnv {
          name = "knot-zones";
          paths = [ zone ];
        };
      in {
        enable = true;
        extraArgs = [
          "-v"
        ];
        settings = {
          server.listen = [
            "0.0.0.0@53"
          ];

          log.syslog.any = "info";

          acl.dhcp_ddns = {
            address = "10.0.0.1";
            action = "update";
          };

          template.default = {
            storage = zonesDir;
            zonefile-sync = "-1";
            zonefile-load = "difference-no-serial";
            journal-content = "all";
          };

          zone."lan.nixos.test" = {
            file = "lan.nixos.test.zone";
            acl = [
              "dhcp_ddns"
            ];
          };
        };
      };

    };

    client = { config, pkgs, ... }: {
      virtualisation.vlans = [ 1 ];
      systemd.services.systemd-networkd.environment.SYSTEMD_LOG_LEVEL = "debug";
      networking = {
        useNetworkd = true;
        useDHCP = false;
        firewall.enable = false;
        interfaces.eth1.useDHCP = true;
      };
    };
  };
  testScript = { ... }: ''
    start_all()
    router.wait_for_unit("kea-dhcp4-server.service")
    client.wait_for_unit("systemd-networkd-wait-online.service")
    client.wait_until_succeeds("ping -c 5 10.0.0.1")
    router.wait_until_succeeds("ping -c 5 10.0.0.3")
    nameserver.wait_until_succeeds("kdig +short client.lan.nixos.test @10.0.0.2 | grep -q 10.0.0.3")
    router.log(router.execute("curl 127.0.0.1:9547")[1])
    router.succeed("curl --no-buffer 127.0.0.1:9547 | grep -qE '^kea_dhcp4_addresses_assigned_total.*1.0$'")
  '';
})
