{ pkgs, lib, config, machines, ... }:
let
  inherit (builtins) hasAttr readFile;
  inherit (pkgs.lib) unlinesAttrs;
  inherit (config) networking;
  inherit (config.users) users groups;
in
{
networking.firewall.enable = false;
security.lockKernelModules = false;
systemd.services.disable-kernel-module-loading.after = [ "nftables.service" ];
systemd.services.nftables.serviceConfig.TimeoutStartSec = "20";
networking.nftables = {
  enable = true;
  ruleset = lib.mkBefore ''
    table inet filter {
      set lograte4 { type ipv4_addr; size 65535; flags dynamic; }
      set lograte6 { type ipv6_addr; size 65535; flags dynamic; }
      chain block {
        add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "block: "
        add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "block: "
        counter drop
      }
      chain ping-flood {
        add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "ping-flood: "
        add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "ping-flood: "
        counter drop
      }
      chain smurf {
        add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "smurf: "
        add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "smurf: "
        counter drop
      }
      chain bogus-tcp {
        add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "bogus-tcp: "
        add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "bogus-tcp: "
        counter drop
      }
      chain syn-flood {
        add @lograte4 { ip  saddr limit rate 1/minute } log level warn prefix "syn-flood: "
        add @lograte6 { ip6 saddr limit rate 1/minute } log level warn prefix "syn-flood: "
        counter drop
      }
      chain check-tcp {
        tcp flags syn tcp option maxseg size != 536-65535 counter goto bogus-tcp
        tcp flags & (ack|fin) == fin counter goto bogus-tcp
        tcp flags & (ack|psh) == psh counter goto bogus-tcp
        tcp flags & (ack|urg) == urg counter goto bogus-tcp
        tcp flags & (fin|ack) == fin counter goto bogus-tcp
        tcp flags & (fin|rst) == (fin|rst) counter goto bogus-tcp
        tcp flags & (fin|psh|ack) == (fin|psh) counter goto bogus-tcp
        tcp flags & (syn|fin) == (syn|fin) counter goto bogus-tcp comment "SYN-FIN scan"
        tcp flags & (syn|rst) == (syn|rst) counter goto bogus-tcp comment "SYN-RST scan"
        tcp flags == (fin|syn|rst|psh|ack|urg) counter goto bogus-tcp comment "XMAS scan"
        tcp flags == 0x0 counter goto bogus-tcp comment "NULL scan"
        tcp flags == (fin|urg|psh) counter goto bogus-tcp
        tcp flags == (fin|urg|psh|syn) counter goto bogus-tcp comment "NMAP-ID"
        tcp flags == (fin|urg|syn|rst|ack) counter goto bogus-tcp

        ct state new tcp flags != syn counter goto bogus-tcp
        tcp sport 0 tcp flags & (fin|syn|rst|ack) == syn counter goto bogus-tcp
        tcp flags & (fin|syn|rst|ack) == syn counter limit rate over 30/second burst 60 packets goto syn-flood
      }
      chain net2fw {
        #udp dport mdns ip6 daddr ff02::fb counter accept comment "Accept mDNS"
        #udp dport mdns ip daddr 224.0.0.251 counter accept comment "Accept mDNS"
        #jump non-internet

        #ct state new add @connlimit { ip saddr ct count over 20 } counter tcp reject with tcp reset

        # Some .nix append rules here with: add rule inet filter net2fw ...
      }
      chain fw2net {
        tcp dport { 80, 443 } counter accept comment "HTTP"
        udp dport 123 skuid ${users.systemd-timesync.name} counter accept comment "NTP"
        tcp dport 9418 counter accept comment "Git"

        # Some .nix append rules here with: add rule inet filter fw2net ...
      }
      chain intra2fw {
        # Some .nix append rules here with: add rule inet filter intra2fw ...
      }
      chain fw2intra {
        # Some .nix append rules here with: add rule inet filter fw2intra ...
      }
      chain fwd-intra {
        # Some .nix append rules here with: add rule inet filter fwd-intra ...
      }
      chain extra2fw {
        # Some .nix append rules here with: add rule inet filter extra2fw ...
      }
      chain accept-icmpv6 {
        # Traffic That Must Not Be Dropped
        # https://tools.ietf.org/html/rfc4890#section-4.4.1
        icmpv6 type destination-unreachable counter accept
        icmpv6 type packet-too-big counter accept
        icmpv6 type time-exceeded counter accept
        icmpv6 type parameter-problem counter accept

        # Address Configuration and Router Selection messages
        # (must be received with hop limit = 255)
        icmpv6 type nd-router-solicit ip6 hoplimit 255 counter accept
        ip6 nexthdr ipv6-icmp icmpv6 type nd-router-advert ip6 hoplimit 255 counter accept
        icmpv6 type nd-neighbor-solicit ip6 hoplimit 255 counter accept
        icmpv6 type nd-neighbor-advert ip6 hoplimit 255 counter accept
        icmpv6 type nd-redirect ip6 hoplimit 255 log level warn prefix "icmpv6: nd-redirect: " counter drop
        icmpv6 type ind-neighbor-solicit ip6 hoplimit 255 counter accept
        icmpv6 type ind-neighbor-advert ip6 hoplimit 255 counter accept

        # Link-local multicast receiver notification messages
        # (must have link-local source address)
        icmpv6 type mld-listener-query ip6 saddr fe80::/10 counter accept
        icmpv6 type mld-listener-report ip6 saddr fe80::/10 counter accept
        icmpv6 type mld-listener-done ip6 saddr fe80::/10 counter accept
        # https://tools.ietf.org/html/rfc3810 Multicast Listener Discovery Version 2 (MLDv2) for IPv6
        icmpv6 type mld2-listener-report ip6 saddr fe80::/10 counter accept

        # SEND Certificate Path notification messages
        # (must be received with hop limit = 255)
        icmpv6 type 148 ip6 hoplimit 255 counter accept comment "certificate-path-solicitation"
        icmpv6 type 149 ip6 hoplimit 255 counter accept comment "certificate-path-advertisement"

        # Multicast Router Discovery messages
        # (must have link-local source address and hop limit = 1)
        icmpv6 type 151 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-advertisement"
        icmpv6 type 152 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-solicitation"
        icmpv6 type 153 ip6 saddr fe80::/10 ip6 hoplimit 1 counter accept comment "multicast-router-termination"
      }

      chain input {
        type filter hook input priority filter
        policy drop

        iifname lo accept

        jump check-tcp

        ct state { established, related } accept

        # Connectivity checking messages
        # (multicast) ping
        ip protocol icmp icmp type echo-reply counter accept

        ${lib.optionalString networking.enableIPv6 ''
        # drop packets with rh0 headers
        rt type 0 jump block
        rt type 0 jump block
        rt type 0 jump block

        # (multicast) ping
        ip6 nexthdr ipv6-icmp icmpv6 type echo-reply counter accept

        #ip6 daddr fe80::/64 udp dport 546 counter accept comment "DHCPv6"
        ''}

        ct state invalid counter drop

        ip protocol icmp icmp type destination-unreachable counter accept
        ip protocol icmp icmp type time-exceeded counter accept
        ip protocol icmp icmp type parameter-problem counter accept
        ip protocol icmp icmp type echo-request limit rate over 10/second burst 20 packets goto ping-flood
        ip protocol icmp icmp type echo-request counter accept
        # echo-reply is handled before invalid packets to allow multicast ping
        # which do not have an associated connection.

        #ip daddr 224.0.0.251 udp dport 5353 counter accept comment "mDNS"
        #ip daddr 239.255.255.250 udp dport 1900 counter accept comment "UPnP"
        #ip saddr 0.0.0.0/32 counter accept comment "DHCP"
        #ip udp sport 67 udp dport 68 counter accept comment "DHCP"

        ${lib.optionalString networking.enableIPv6 ''
        ip6 nexthdr ipv6-icmp jump accept-icmpv6

        # Connectivity checking messages
        icmpv6 type echo-request counter accept
        # echo-reply is handled before invalid because of multicast

        ip6 nexthdr ipv6-icmp log level err prefix "net2fw: icmpv6: catch all: " counter reject

        ip6 daddr ff02::fb udp dport 5353 counter accept comment "mDNS"
        ip6 daddr ff02::f udp dport 1900 counter accept comment "UPnP"
        ''}

        ip saddr 224.0.0.0/4 counter goto smurf
        fib saddr type broadcast counter goto smurf

        # admin services
        tcp dport 22 counter accept comment "SSH"
        udp dport 60000-61000 counter accept comment "Mosh"

        # Some .nix append gotos here with: add rule inet filter input iffname ... goto ...
      }
      chain forward {
        type filter hook forward priority filter
        policy drop

        ct state { related, established } accept

        ip protocol icmp icmp type destination-unreachable counter accept
        ip protocol icmp icmp type time-exceeded counter accept
        ip protocol icmp icmp type parameter-problem counter accept
        ip protocol icmp icmp type echo-request counter accept

        ${lib.optionalString networking.enableIPv6 ''
        # Traffic That Must Not Be Dropped
        # https://tools.ietf.org/html/rfc4890#section-4.3.1
        ip6 nexthdr ipv6-icmp icmpv6 type destination-unreachable counter accept
        ip6 nexthdr ipv6-icmp icmpv6 type packet-too-big counter accept
        ip6 nexthdr ipv6-icmp icmpv6 type time-exceeded counter accept
        ip6 nexthdr ipv6-icmp icmpv6 type parameter-problem counter accept

        # Connectivity checking messages
        ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter accept
        ip6 nexthdr ipv6-icmp icmpv6 type echo-reply counter accept

        # Traffic That Normally Should Not Be Dropped
        # https://tools.ietf.org/html/rfc4890#section-4.3.2
        ip6 nexthdr ipv6-icmp icmpv6 type 144 counter accept comment "home-agent-address-discovery-request"
        ip6 nexthdr ipv6-icmp icmpv6 type 145 counter accept comment "home-agent-address-discovery-reply"
        ip6 nexthdr ipv6-icmp icmpv6 type 146 counter accept comment "mobile-prefix-solicitation"
        ip6 nexthdr ipv6-icmp icmpv6 type 147 counter accept comment "mobile-prefix-advertisement"
        ''}
      }
      chain output {
        type filter hook output priority filter
        policy drop

        oifname lo accept

        ct state { related, established } accept

        ip protocol icmp counter accept
        ip daddr 224.0.0.0/4 udp dport 1900 counter accept comment "UPnP"
        meta skuid 0 udp dport 33434-33523 counter accept comment "traceroute"

        ${lib.optionalString networking.enableIPv6 ''
        ip6 nexthdr ipv6-icmp jump accept-icmpv6

        # Connectivity checking messages
        ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter accept
        ip6 nexthdr ipv6-icmp icmpv6 type echo-reply counter accept
        ip6 nexthdr ipv6-icmp log level err prefix "fw2net: icmpv6: catch all: " counter reject

        ip6 daddr ff02::1:2/64 udp dport 547 counter accept comment "DHCPv6"
        ''}

        ct state invalid log level warn prefix "fw2net: invalid: " counter drop

        tcp dport 22 counter accept comment "SSH"

        # Some .nix append gotos here with: add rule inet filter output oifname ... goto ...
      }
    }
    table inet nat {
      chain prerouting {
        type nat hook prerouting priority filter
        policy accept
      }
      chain postrouting {
        type nat hook postrouting priority srcnat
        policy accept
      }
    }
  '';
};
}