{
  config,
  lib,
  pkgs,
  ...
}:

let
  names = mylist: (lib.strings.concatMapStringsSep "," (x: ''"${x}"'') mylist);
  # Pass the names of the wan/lan ports
  nftConfig =
    {
      wan,
      lan,
      limitedLan ? [ ],
      openPorts ? [
        "ssh"
        "67"
        "53"
      ],
      # ssh, dhcpd, dns
      openUDPPorts ? [
        "67"
        "53"
      ], # dhcpd, dns
    }:
    let
      lanList = names lan;
      allLan = names (lan ++ limitedLan);
      wanName = names wan;
      portsString = lib.strings.concatMapStringsSep "\n" (
        x: "iifname { ${lanList}, \"tailscale0\" } tcp dport ${toString x} accept"
      ) openPorts;
      udpPortsString = lib.strings.concatMapStringsSep "\n" (
        x: "iifname { ${lanList}, \"tailscale0\" } udp dport ${toString x} accept"
      ) openUDPPorts;
    in
    lib.strings.concatStringsSep "\n" [
      "table ip filter {"
      "	chain output {"
      "		type filter hook output priority 100; policy accept;"
      "	}"

      "	chain input {"
      "		type filter hook input priority 0; policy drop;"

      "		iifname lo accept"
      portsString
      udpPortsString
      "		iifname { ${lanList} } accept comment \"Allows LAN traffic and outgoing\""
      "		iifname { ${wanName} } ct state { established, related } accept comment \"Allows existing connections\""
      "		iifname { ${wanName} } icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment \"Allow some ICMP traffic\""
      "		iifname { ${wanName} } counter drop comment \"Drop other incoming traffic, and count how much\""
      "	}"
      "	chain forward {"
      "		type filter hook forward priority 0; policy drop;"
      "		iifname { ${allLan} } oifname { ${wanName} } accept comment \"Forward LAN to WAN\""
      "		iifname { ${wanName} } oifname { ${allLan} } ct state established, related accept comment \"Allow incoming established traffic\""
      "	}"
      "}"

      "table ip nat {"
      "	chain postrouting {"
      "		type nat hook postrouting priority 100; policy accept;"
      "		oifname { ${wanName} } masquerade"
      "	}"
      "}"

      "table ip6 filter {"
      "	chain input {"
      "		type filter hook input priority 0; policy drop;"
      "	}"
      "	chain forward {"
      "		type filter hook forward priority 0; policy drop;"
      "	}"
      "}"
    ];
  cfg = config.greg.router;

in
with lib;
{
  options.greg.router = {
    enable = mkEnableOption "Enable NFTables and routing";
    wan = mkOption {
      type = (types.listOf types.str);
      description = "The name of the network interface that is the WAN connection";
    };
    lan = mkOption {
      type = (types.listOf types.str);
      description = "A list of all network interfaces that are considered LAN connections";
    };
    limited = mkOption {
      type = (types.listOf types.str);
      description = "A list of limited access LAN connections - such as IOT connections and similar.";
      default = [ ];
    };
  };

  config = mkIf cfg.enable {
    networking.nftables = {
      enable = true;
      ruleset = (
        nftConfig {
          inherit (cfg) lan wan;
          openPorts = config.networking.firewall.allowedTCPPorts;
          openUDPPorts = config.networking.firewall.allowedUDPPorts;
        }
      );
    };

    environment.systemPackages = [
      pkgs.pciutils
      pkgs.tcpdump
    ];
  };
}
