#!/usr/sbin/nft -f

flush ruleset

table ip filter {
    # Filter traffic destined for the router itself:
    chain input {
        type filter hook input priority 0; policy drop;
        # Allow existing established and related connections
        ct state established,related accept
        
        {% if icmp_accept_lan != "" %}
        # Allow specific ICMP queries on lan interface
        iif "{{ interface_lan }}" ip saddr {{ subnet_lan }} icmp type { {{ icmp_accept_lan }} } accept
        {% endif %}

        {% if icmp_accept_wan != "" %}
        # Allow specific ICMP queries on wan interface
        iif "{{ interface_wan }}" ip icmp type { {{ icmp_accept_wan }} } accept
        {% endif %}

        {% if tcp_accept_lan != "" %}
        # Allow access to specific TCP ports from the lan interface
        ip saddr {{ subnet_lan }} iif "{{ interface_lan }}" tcp dport { {{ tcp_accept_lan }} } accept
        {% endif %}
        {% if udp_accept_lan != "" %}
        # Allow access to specific UDP ports from the lan interface
        ip saddr {{ subnet_lan }} iif "{{ interface_lan }}" udp dport { {{ udp_accept_lan }} } accept
        {% endif %}
        
        {% if tcp_accept_wan != "" %}
        # Allow access to specific TCP ports from the wan interface
        ip iif "{{ interface_wan }}" tcp dport { {{ tcp_accept_wan }} } accept
        {% endif %}
        
        {% if udp_accept_wan != "" %}
        # Allow access to specific UDP port from the wan interface
        ip iif "{{ interface_wan }}" udp dport { {{ udp_accept_lan }} } accept
        {% endif %}

        # Drop and log everything else
        log prefix "Dropped input packet: " drop
    }

    # Filter forwarded traffic (traffic passing through the router):
    # This filters traffic after the NAT prerouting chain modifies the destination route.
    # This filters traffic before the postrouting chain.
    chain forward {
        type filter hook forward priority 0; policy drop;
        # Allow existing established and related connections
        ct state established,related accept
        # Allow all egress from lan to wan
        ip saddr {{ subnet_lan }} iif "{{ interface_lan }}" oif "{{ interface_wan }}" accept

        {% if tcp_forward_lan.len() > 0 %}
        # DNAT forward TCP from lan
            {% for route in tcp_forward_lan.routes %}
        ip saddr {{ subnet_lan }} iif "{{ interface_lan }}" daddr {{ route.destination_ip }} tcp dport {{ route.destination_port }} accept
            {% endfor %}
        {% endif %}
        {% if udp_forward_lan.len() > 0 %}
        # DNAT forward UDP from lan
            {% for route in udp_forward_lan.routes %}
        ip saddr {{ subnet_lan }} iif "{{ interface_lan }}" daddr {{ route.destination_ip }} udp dport {{ route.destination_port }} accept
            {% endfor %}
        {% endif %}

        {% if tcp_forward_wan.len() > 0 %}
        # DNAT forward TCP from wan
            {% for route in tcp_forward_wan.routes %}
        iif "{{ interface_wan }}" daddr {{ route.destination_ip }} tcp dport {{ route.destination_port }} accept
            {% endfor %}
        {% endif %}
        {% if udp_forward_wan.len() > 0 %}
        # DNAT forward UDP from wan
            {% for route in udp_forward_wan.routes %}
        iif "{{ interface_wan }}" daddr {{ route.destination_ip }} udp dport {{ route.destination_port }} accept
            {% endfor %}
        {% endif %}
        
        # Log and drop all other packets
        log prefix "Dropped forward packet: " drop
    }

    # Filter traffic whose source is the router itself:
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

table ip nat {
    # DNAT for incoming traffic:
    # Modify destination address before routing decision in forwarding chain:
    chain prerouting {
        type nat hook prerouting priority 0; policy accept;
        {% if tcp_forward_lan.len() > 0 %}
        # DNAT from lan
            {% for route in tcp_forward_lan.routes %}
        iif "{{ interface_lan }}" ip saddr {{ subnet_lan }} tcp dport {{ route.incoming_port }} dnat to {{ route.destination_ip }}:{{ route.destination_port }}
            {% endfor %}
        {% endif %}
    }

    # SNAT for outgoing traffic:
    # Modify source address after the forwarding chain, before leaving the router:
    chain postrouting {
        type nat hook postrouting priority 100; policy accept;
        oif "{{ interface_wan }}" iif "{{ interface_lan }}" masquerade
    }
}
