{ config, lib, ... }:

with import ./network.nix;

let
  # TCP services
  ssh_tcp = 22;
  http_tcp = 80;
  https_tcp = 443;
  synapse_tcp = 8448;
  gitea_tcp = 10022;
  prosody_tcp = 5222; 
  minecraft_tcp = 25565;

  # UDP services
  dns_udp = 53;
  wireguard_udp = 1194;
  
  # TCP/UDP services
  torrent_a = 51413;
  torrent_b = 51414;

  # grouping
  open_tcp_ports = lib.concatMapStringsSep "," (x: toString x) [
    ssh_tcp
    http_tcp
    https_tcp
    synapse_tcp
    gitea_tcp
    torrent_a
    torrent_b
  ];
  open_udp_ports = lib.concatMapStringsSep "," (x: toString x) [
    wireguard_udp
    torrent_a
    torrent_b
  ];
  open_tcp_ports_vpn = lib.concatMapStringsSep "," (x: toString x) [
    ssh_tcp
    http_tcp
    https_tcp
    prosody_tcp
    minecraft_tcp
  ];
  open_udp_ports_vpn = lib.concatMapStringsSep "," (x: toString x) [
    dns_udp
    wireguard_udp
  ];

in {
  networking = {
    # needed to use nftables
    firewall.enable = false;
    nat.enable = false;

    nftables = {
      enable = true;
      ruleset = ''
                table ip raw {
                  chain PREROUTING {
                    type filter hook prerouting priority raw; policy accept;
                  }

                  chain OUTPUT {
                    type filter hook output priority raw; policy accept;
                  }
                }

                table ip nat {
                  chain PREROUTING {
                    type nat hook prerouting priority dstnat; policy accept;
                  }

                  chain INPUT {
                    type nat hook input priority 100; policy accept;
                  }

                  chain OUTPUT {
                    type nat hook output priority -100; policy accept;
                  }

                  chain POSTROUTING {
                    type nat hook postrouting priority srcnat; policy accept;
                    oifname ${wan-if} ip saddr {${
                      lib.concatStringsSep "," towan-wg
                    }} masquerade
                    oifname ${wan-if} ip saddr ${docker-net} masquerade
                  }
                }

                table ip mangle {
                  chain PREROUTING {
                    type filter hook prerouting priority mangle; policy drop;
                    ct state invalid,untracked drop comment "drop invalid"
                    ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
                    ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
                    iifname ${wan-if} ip saddr ${vpn-net} drop comment "bind any ip to intf ${wan-if}"
                    iifname ${wan-if} ip saddr 127.0.0.0/8 drop comment "bind any ip to intf ${wan-if}"
                    iifname ${wan-if} accept comment "bind any ip to intf ${wan-if}"
                    iifname ${proxy-if} ip saddr ${proxy-net} accept comment "bind ip ${proxy-net} to intf ${proxy-if}"
                    iifname ${vpn-if} ip saddr ${vpn-net} accept comment "bind ip ${vpn-net} to intf ${vpn-if}"
                    iifname ${docker-if} ip saddr ${docker-net} accept comment "bind ip ${docker-net} to intf ${docker-if}"
                    iifname "lo" accept comment "bind any ip to intf lo"
                    jump mangle_drop
                  }

                  chain INPUT {
                    type filter hook input priority mangle; policy accept;
                  }

                  chain FORWARD {
                    type filter hook forward priority mangle; policy accept;
                  }

                  chain OUTPUT {
                    type route hook output priority mangle; policy accept;
                  }

                  chain POSTROUTING {
                    type filter hook postrouting priority mangle; policy accept;
                  }

                  chain mangle_drop {
                    ip protocol icmp jump mangle_drop_icmp
                    ip protocol udp jump mangle_drop_udp
                    ip protocol tcp jump mangle_drop_tcp
                    log prefix "MANGLE-DROP-UNK "
                    drop
                  }

                  chain mangle_drop_icmp {
                    log prefix "MANGLE-DROP-ICMP "
                    drop
                  }

                  chain mangle_drop_tcp {
                    log prefix "MANGLE-DROP-TCP "
                    drop
                  }

                  chain mangle_drop_udp {
                  log prefix "MANGLE-DROP-UDP "
                  drop
                  }
                }

                table ip filter {
                  chain INPUT {
                    type filter hook input priority filter; policy drop;

                    ct state established,related accept
                    iifname "lo" accept comment "loopback"
                    ip daddr 255.255.255.255 accept comment "allow broadcast traffic"
                    ip daddr 224.0.0.0/4 accept comment "allow multicast traffic"
                    ip saddr ${lan-net} accept comment "lan > local"
        	          ip saddr ${proxy-wg} accept comment "proxy > local"
                    ip saddr {${lib.concatStringsSep "," gdevices-wg}} accept comment "vpn > local"

                    iifname ${wan-if} tcp dport {${open_tcp_ports}} accept
                    iifname ${wan-if} udp dport {${open_udp_ports}} accept
                    iifname ${vpn-if} tcp dport {${open_tcp_ports_vpn}} accept
                    iifname ${vpn-if} udp dport {${open_udp_ports_vpn}} accept
                    iifname ${vpn-if} icmp type echo-request accept
                    iifname ${docker-if} udp dport 53 accept
                    jump filter_drop
                  }

                  chain FORWARD {
                    type filter hook forward priority filter; policy drop;
                    ct state established,related accept

                    # client to client
                    ip saddr {${lib.concatStringsSep "," c2c-wg}} ip daddr {${
                      lib.concatStringsSep "," c2c-wg
                    }} accept
                    
                    # gdevices talking to everyone in VPN
                    ip saddr {${
                      lib.concatStringsSep "," gdevices-wg
                    }} ip daddr ${vpn-net} accept
                    ip saddr {${
                      lib.concatStringsSep "," gamenet-wg
                    }} ip daddr {${lib.concatStringsSep "," gamenet-wg}} accept
                    
                    # nat to wan
                    oifname ${wan-if} ip saddr {${
                      lib.concatStringsSep "," towan-wg
                    }} accept
                    
                    oifname ${wan-if} ip saddr ${docker-net} accept
                    
                    jump filter_drop
                  }

                  chain OUTPUT {
                    type filter hook output priority filter; policy drop;
                    ct state established,related accept
                    accept comment "local > *"
                    jump filter_drop
                  }

                  chain filter_drop {
                    ip protocol icmp jump filter_drop_icmp
                    ip protocol udp jump filter_drop_udp
                    ip protocol tcp jump filter_drop_tcp
                    log prefix "DROP-UNK "
                    drop
                  }

                  chain filter_drop_icmp {
                    log prefix "DROP-icmp "
                    drop
                  }

                  chain filter_drop_tcp {
                    log prefix "DROP-tcp "
                    drop
                  }

                  chain filter_drop_udp {
                    log prefix "DROP-udp "
                    drop
                  }
                } 
              '';
    };
  };
}