#!/usr/sbin/nft -f

{% import 'firewall/nftables-defines.j2' as group_tmpl %}
{% import 'firewall/nftables-bridge.j2' as bridge_tmpl %}

flush chain raw FW_CONNTRACK
flush chain ip6 raw FW_CONNTRACK

flush chain raw vyos_global_rpfilter
flush chain ip6 raw vyos_global_rpfilter

table raw {
    chain FW_CONNTRACK {
        {{ ipv4_conntrack_action }}
    }

    chain vyos_global_rpfilter {
{% if global_options.source_validation is vyos_defined('loose') %}
        fib saddr oif 0 counter drop
{% elif global_options.source_validation is vyos_defined('strict') %}
        fib saddr . iif oif 0 counter drop
{% endif %}
        return
    }
}

table ip6 raw {
    chain FW_CONNTRACK {
        {{ ipv6_conntrack_action }}
    }

    chain vyos_global_rpfilter {
{% if global_options.ipv6_source_validation is vyos_defined('loose') %}
        fib saddr oif 0 counter drop
{% elif global_options.ipv6_source_validation is vyos_defined('strict') %}
        fib saddr . iif oif 0 counter drop
{% endif %}
        return
    }
}

{% if first_install is not vyos_defined %}
delete table ip vyos_filter
{% endif %}
table ip vyos_filter {
{% if ipv4 is vyos_defined %}
{%     set ns = namespace(sets=[]) %}
{%     if ipv4.forward is vyos_defined %}
{%         for prior, conf in ipv4.forward.items() %}
{%             set def_action = conf.default_action %}
    chain VYOS_FORWARD_{{ prior }} {
        type filter hook forward priority {{ prior }}; policy {{ def_action }};
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('FWD', prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
    }
{%         endfor %}
{%     endif %}

{%     if ipv4.input is vyos_defined %}
{%         for prior, conf in ipv4.input.items() %}
{%             set def_action = conf.default_action %}
    chain VYOS_INPUT_{{ prior }} {
        type filter hook input priority {{ prior }}; policy {{ def_action }};
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('INP',prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
    }
{%         endfor %}
{%     endif %}

{%     if ipv4.output is vyos_defined %}
{%         for prior, conf in ipv4.output.items() %}
{%             set def_action = conf.default_action %}
    chain VYOS_OUTPUT_{{ prior }} {
        type filter hook output priority {{ prior }}; policy {{ def_action }};
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('OUT', prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['OUT_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
    }
{%         endfor %}
{%     endif %}
    chain VYOS_FRAG_MARK {
        type filter hook prerouting priority -450; policy accept;
        ip frag-off & 0x3fff != 0 meta mark set 0xffff1 return
    }
{%     if ipv4.prerouting is vyos_defined %}
{%         for prior, conf in ipv4.prerouting.items() %}
{%             set def_action = conf.default_action %}
    chain VYOS_PREROUTING_{{ prior }} {
        type filter hook prerouting priority {{ prior }}; policy {{ def_action }};
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('PRE', prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['PRE_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule(prior) }}
    }
{%         endfor %}
{%     endif %}

{%     if ipv4.name is vyos_defined %}
{%         for name_text, conf in ipv4.name.items() %}
    chain NAME_{{ name_text }} {
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('NAM', name_text, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule(name_text) }}
    }
{%         endfor %}
{%     endif %}

{%     for set_name in ns.sets %}
    set RECENT_{{ set_name }} {
        type ipv4_addr
        size 65535
        flags dynamic
    }
{%     endfor %}
{%     for set_name in ip_fqdn %}
    set FQDN_{{ set_name }} {
        type ipv4_addr
        flags interval
    }
{%     endfor %}
{%     if geoip_updated.name is vyos_defined %}
{%         for setname in geoip_updated.name %}
    set {{ setname }} {
        type ipv4_addr
        flags interval
    }
{%         endfor %}
{%     endif %}
{% endif %}
{{ group_tmpl.groups(group, False, True) }}
}

{% if first_install is not vyos_defined %}
delete table ip6 vyos_filter
{% endif %}
table ip6 vyos_filter {
{% if ipv6 is vyos_defined %}
{%     set ns = namespace(sets=[]) %}
{%     if ipv6.forward is vyos_defined %}
{%         for prior, conf in ipv6.forward.items() %}
{%             set def_action = conf.default_action %}
    chain VYOS_IPV6_FORWARD_{{ prior }} {
        type filter hook forward priority {{ prior }}; policy {{ def_action }};
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('FWD', prior, rule_id ,'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
    }
{%         endfor %}
{%     endif %}

{%     if ipv6.input is vyos_defined %}
{%         for prior, conf in ipv6.input.items() %}
{%             set def_action = conf.default_action %}
    chain VYOS_IPV6_INPUT_{{ prior }} {
        type filter hook input priority {{ prior }}; policy {{ def_action }};
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('INP', prior, rule_id ,'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
    }
{%         endfor %}
{%     endif %}

{%     if ipv6.output is vyos_defined %}
{%         for prior, conf in ipv6.output.items() %}
{%             set def_action = conf.default_action %}
    chain VYOS_IPV6_OUTPUT_{{ prior }} {
        type filter hook output priority {{ prior }}; policy {{ def_action }};
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('OUT', prior, rule_id ,'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['OUT_ ' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
    }
{%         endfor %}
{%     endif %}

    chain VYOS_FRAG6_MARK {
        type filter hook prerouting priority -450; policy accept;
        exthdr frag exists meta mark set 0xffff1 return
    }

{%     if ipv6.name is vyos_defined %}
{%         for name_text, conf in ipv6.name.items() %}
    chain NAME6_{{ name_text }} {
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('NAM', name_text, rule_id, 'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule(name_text, ipv6=True) }}
    }
{%         endfor %}
{%     endif %}

{%     for set_name in ns.sets %}
    set RECENT6_{{ set_name }} {
        type ipv6_addr
        size 65535
        flags dynamic
    }
{%     endfor %}
{%     for set_name in ip6_fqdn %}
    set FQDN_{{ set_name }} {
        type ipv6_addr
        flags interval
    }
{%     endfor %}
{%     if geoip_updated.ipv6_name is vyos_defined %}
{%         for setname in geoip_updated.ipv6_name %}
    set {{ setname }} {
        type ipv6_addr
        flags interval
    }
{%         endfor %}
{%     endif %}
{% endif %}
{{ group_tmpl.groups(group, True, True) }}
}

## Bridge Firewall
{% if first_install is not vyos_defined %}
delete table bridge vyos_filter
{% endif %}
{% if bridge is vyos_defined %}
table bridge vyos_filter {
{{ bridge_tmpl.bridge(bridge) }}
{{ group_tmpl.groups(group, False, False) }}
}
{% endif %}