summaryrefslogtreecommitdiff
path: root/data/templates/firewall
diff options
context:
space:
mode:
Diffstat (limited to 'data/templates/firewall')
-rw-r--r--data/templates/firewall/nftables-defines.j232
-rw-r--r--data/templates/firewall/nftables-nat.j2182
-rw-r--r--data/templates/firewall/nftables-nat.tmpl181
-rw-r--r--data/templates/firewall/nftables-nat66.j2102
-rw-r--r--data/templates/firewall/nftables-nat66.tmpl102
-rw-r--r--data/templates/firewall/nftables-policy.j255
-rw-r--r--data/templates/firewall/nftables-vrf-zones.j2 (renamed from data/templates/firewall/nftables-vrf-zones.tmpl)0
-rw-r--r--data/templates/firewall/nftables.j2274
-rw-r--r--data/templates/firewall/upnpd.conf.j2172
9 files changed, 817 insertions, 283 deletions
diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2
new file mode 100644
index 000000000..4fa92f2e3
--- /dev/null
+++ b/data/templates/firewall/nftables-defines.j2
@@ -0,0 +1,32 @@
+{% if group is vyos_defined %}
+{% if group.address_group is vyos_defined %}
+{% for group_name, group_conf in group.address_group.items() %}
+define A_{{ group_name }} = { {{ group_conf.address | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.ipv6_address_group is vyos_defined %}
+{% for group_name, group_conf in group.ipv6_address_group.items() %}
+define A6_{{ group_name }} = { {{ group_conf.address | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.mac_group is vyos_defined %}
+{% for group_name, group_conf in group.mac_group.items() %}
+define M_{{ group_name }} = { {{ group_conf.mac_address | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.network_group is vyos_defined %}
+{% for group_name, group_conf in group.network_group.items() %}
+define N_{{ group_name }} = { {{ group_conf.network | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.ipv6_network_group is vyos_defined %}
+{% for group_name, group_conf in group.ipv6_network_group.items() %}
+define N6_{{ group_name }} = { {{ group_conf.network | join(",") }} }
+{% endfor %}
+{% endif %}
+{% if group.port_group is vyos_defined %}
+{% for group_name, group_conf in group.port_group.items() %}
+define P_{{ group_name }} = { {{ group_conf.port | join(",") }} }
+{% endfor %}
+{% endif %}
+{% endif %} \ No newline at end of file
diff --git a/data/templates/firewall/nftables-nat.j2 b/data/templates/firewall/nftables-nat.j2
new file mode 100644
index 000000000..1481e9104
--- /dev/null
+++ b/data/templates/firewall/nftables-nat.j2
@@ -0,0 +1,182 @@
+#!/usr/sbin/nft -f
+
+{% macro nat_rule(rule, config, chain) %}
+{% set comment = '' %}
+{% set base_log = '' %}
+{% set src_addr = 'ip saddr ' ~ config.source.address.replace('!','!= ') if config.source.address is vyos_defined %}
+{% set dst_addr = 'ip daddr ' ~ config.destination.address.replace('!','!= ') if config.destination.address is vyos_defined %}
+{# negated port groups need special treatment, move != in front of { } group #}
+{% if config.source.port is vyos_defined and config.source.port.startswith('!') %}
+{% set src_port = 'sport != { ' ~ config.source.port.replace('!','') ~ ' }' %}
+{% else %}
+{% set src_port = 'sport { ' ~ config.source.port ~ ' }' if config.source.port is vyos_defined %}
+{% endif %}
+{# negated port groups need special treatment, move != in front of { } group #}
+{% if config.destination.port is vyos_defined and config.destination.port.startswith('!') %}
+{% set dst_port = 'dport != { ' ~ config.destination.port.replace('!','') ~ ' }' %}
+{% else %}
+{% set dst_port = 'dport { ' ~ config.destination.port ~ ' }' if config.destination.port is vyos_defined %}
+{% endif %}
+{% if chain is vyos_defined('PREROUTING') %}
+{% set comment = 'DST-NAT-' ~ rule %}
+{% set base_log = '[NAT-DST-' ~ rule %}
+{% set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
+{% if config.translation.address is vyos_defined %}
+{# support 1:1 network translation #}
+{% if config.translation.address | is_ip_network %}
+{% set trns_addr = 'dnat ip prefix to ip daddr map { ' ~ config.destination.address ~ ' : ' ~ config.translation.address ~ ' }' %}
+{# we can now clear out the dst_addr part as it's already covered in aboves map #}
+{% set dst_addr = '' %}
+{% else %}
+{% set trns_addr = 'dnat to ' ~ config.translation.address %}
+{% endif %}
+{% endif %}
+{% elif chain is vyos_defined('POSTROUTING') %}
+{% set comment = 'SRC-NAT-' ~ rule %}
+{% set base_log = '[NAT-SRC-' ~ rule %}
+{% set interface = ' oifname "' ~ config.outbound_interface ~ '"' if config.outbound_interface is vyos_defined and config.outbound_interface is not vyos_defined('any') else '' %}
+{% if config.translation.address is vyos_defined %}
+{% if config.translation.address is vyos_defined('masquerade') %}
+{% set trns_addr = config.translation.address %}
+{% if config.translation.port is vyos_defined %}
+{% set trns_addr = trns_addr ~ ' to ' %}
+{% endif %}
+{# support 1:1 network translation #}
+{% elif config.translation.address | is_ip_network %}
+{% set trns_addr = 'snat ip prefix to ip saddr map { ' ~ config.source.address ~ ' : ' ~ config.translation.address ~ ' }' %}
+{# we can now clear out the src_addr part as it's already covered in aboves map #}
+{% set src_addr = '' %}
+{% else %}
+{% set trns_addr = 'snat to ' ~ config.translation.address %}
+{% endif %}
+{% endif %}
+{% endif %}
+{% set trns_port = ':' ~ config.translation.port if config.translation.port is vyos_defined %}
+{# protocol has a default value thus it is always present #}
+{% if config.protocol is vyos_defined('tcp_udp') %}
+{% set protocol = 'tcp' %}
+{% set comment = comment ~ ' tcp_udp' %}
+{% else %}
+{% set protocol = config.protocol %}
+{% endif %}
+{% if config.log is vyos_defined %}
+{% if config.exclude is vyos_defined %}
+{% set log = base_log ~ '-EXCL]' %}
+{% elif config.translation.address is vyos_defined('masquerade') %}
+{% set log = base_log ~ '-MASQ]' %}
+{% else %}
+{% set log = base_log ~ ']' %}
+{% endif %}
+{% endif %}
+{% if config.exclude is vyos_defined %}
+{# rule has been marked as 'exclude' thus we simply return here #}
+{% set trns_addr = 'return' %}
+{% set trns_port = '' %}
+{% endif %}
+{# T1083: NAT address and port translation options #}
+{% if config.translation.options is vyos_defined %}
+{% if config.translation.options.address_mapping is vyos_defined('persistent') %}
+{% set trns_opts_addr = 'persistent' %}
+{% endif %}
+{% if config.translation.options.port_mapping is vyos_defined('random') %}
+{% set trns_opts_port = 'random' %}
+{% elif config.translation.options.port_mapping is vyos_defined('fully-random') %}
+{% set trns_opts_port = 'fully-random' %}
+{% endif %}
+{% endif %}
+{% if trns_opts_addr is vyos_defined and trns_opts_port is vyos_defined %}
+{% set trns_opts = trns_opts_addr ~ ',' ~ trns_opts_port %}
+{% elif trns_opts_addr is vyos_defined %}
+{% set trns_opts = trns_opts_addr %}
+{% elif trns_opts_port is vyos_defined %}
+{% set trns_opts = trns_opts_port %}
+{% endif %}
+{% set output = 'add rule ip nat ' ~ chain ~ interface %}
+{% if protocol is not vyos_defined('all') %}
+{% set output = output ~ ' ip protocol ' ~ protocol %}
+{% endif %}
+{% if src_addr is vyos_defined %}
+{% set output = output ~ ' ' ~ src_addr %}
+{% endif %}
+{% if src_port is vyos_defined %}
+{% set output = output ~ ' ' ~ protocol ~ ' ' ~ src_port %}
+{% endif %}
+{% if dst_addr is vyos_defined %}
+{% set output = output ~ ' ' ~ dst_addr %}
+{% endif %}
+{% if dst_port is vyos_defined %}
+{% set output = output ~ ' ' ~ protocol ~ ' ' ~ dst_port %}
+{% endif %}
+{# Count packets #}
+{% set output = output ~ ' counter' %}
+{# Special handling of log option, we must repeat the entire rule before the #}
+{# NAT translation options are added, this is essential #}
+{% if log is vyos_defined %}
+{% set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %}
+{% endif %}
+{% if trns_addr is vyos_defined %}
+{% set output = output ~ ' ' ~ trns_addr %}
+{% endif %}
+{% if trns_port is vyos_defined %}
+{# Do not add a whitespace here, translation port must be directly added after IP address #}
+{# e.g. 192.0.2.10:3389 #}
+{% set output = output ~ trns_port %}
+{% endif %}
+{% if trns_opts is vyos_defined %}
+{% set output = output ~ ' ' ~ trns_opts %}
+{% endif %}
+{% if comment is vyos_defined %}
+{% set output = output ~ ' comment "' ~ comment ~ '"' %}
+{% endif %}
+{{ log_output if log_output is vyos_defined }}
+{{ output }}
+{# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #}
+{% if config.protocol is vyos_defined('tcp_udp') %}
+{# Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #}
+{{ log_output | replace('tcp ', 'udp ') if log_output is vyos_defined }}
+{{ output | replace('tcp ', 'udp ') }}
+{% endif %}
+{% endmacro %}
+
+# Start with clean SNAT and DNAT chains
+flush chain ip nat PREROUTING
+flush chain ip nat POSTROUTING
+{% if helper_functions is vyos_defined('remove') %}
+{# NAT if going to be disabled - remove rules and targets from nftables #}
+{% set base_command = 'delete rule ip raw' %}
+{{ base_command }} PREROUTING handle {{ pre_ct_ignore }}
+{{ base_command }} OUTPUT handle {{ out_ct_ignore }}
+{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
+{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
+
+delete chain ip raw NAT_CONNTRACK
+
+{% elif helper_functions is vyos_defined('add') %}
+{# NAT if enabled - add targets to nftables #}
+add chain ip raw NAT_CONNTRACK
+add rule ip raw NAT_CONNTRACK counter accept
+{% set base_command = 'add rule ip raw' %}
+{{ base_command }} PREROUTING position {{ pre_ct_ignore }} counter jump VYOS_CT_HELPER
+{{ base_command }} OUTPUT position {{ out_ct_ignore }} counter jump VYOS_CT_HELPER
+{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
+{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
+{% endif %}
+
+#
+# Destination NAT rules build up here
+#
+add rule ip nat PREROUTING counter jump VYOS_PRE_DNAT_HOOK
+{% if destination.rule is vyos_defined %}
+{% for rule, config in destination.rule.items() if config.disable is not vyos_defined %}
+{{ nat_rule(rule, config, 'PREROUTING') }}
+{% endfor %}
+{% endif %}
+#
+# Source NAT rules build up here
+#
+add rule ip nat POSTROUTING counter jump VYOS_PRE_SNAT_HOOK
+{% if source.rule is vyos_defined %}
+{% for rule, config in source.rule.items() if config.disable is not vyos_defined %}
+{{ nat_rule(rule, config, 'POSTROUTING') }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl
deleted file mode 100644
index 40ed1b916..000000000
--- a/data/templates/firewall/nftables-nat.tmpl
+++ /dev/null
@@ -1,181 +0,0 @@
-#!/usr/sbin/nft -f
-
-{% macro nat_rule(rule, config, chain) %}
-{% set comment = '' %}
-{% set base_log = '' %}
-{% set src_addr = 'ip saddr ' + config.source.address.replace('!','!= ') if config.source is defined and config.source.address is defined and config.source.address is not none %}
-{% set dst_addr = 'ip daddr ' + config.destination.address.replace('!','!= ') if config.destination is defined and config.destination.address is defined and config.destination.address is not none %}
-{# negated port groups need special treatment, move != in front of { } group #}
-{% if config.source is defined and config.source.port is defined and config.source.port is not none and config.source.port.startswith('!=') %}
-{% set src_port = 'sport != { ' + config.source.port.replace('!=','') + ' }' %}
-{% else %}
-{% set src_port = 'sport { ' + config.source.port + ' }' if config.source is defined and config.source.port is defined and config.source.port is not none %}
-{% endif %}
-{# negated port groups need special treatment, move != in front of { } group #}
-{% if config.destination is defined and config.destination.port is defined and config.destination.port is not none and config.destination.port.startswith('!=') %}
-{% set dst_port = 'dport != { ' + config.destination.port.replace('!=','') + ' }' %}
-{% else %}
-{% set dst_port = 'dport { ' + config.destination.port + ' }' if config.destination is defined and config.destination.port is defined and config.destination.port is not none %}
-{% endif %}
-{% if chain == 'PREROUTING' %}
-{% set comment = 'DST-NAT-' + rule %}
-{% set base_log = '[NAT-DST-' + rule %}
-{% set interface = ' iifname "' + config.inbound_interface + '"' if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %}
-{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
-{# support 1:1 network translation #}
-{% if config.translation.address | is_ip_network %}
-{% set trns_addr = 'dnat ip prefix to ip daddr map { ' + config.destination.address + ' : ' + config.translation.address + ' }' %}
-{# we can now clear out the dst_addr part as it's already covered in aboves map #}
-{% set dst_addr = '' %}
-{% else %}
-{% set trns_addr = 'dnat to ' + config.translation.address %}
-{% endif %}
-{% endif %}
-{% elif chain == 'POSTROUTING' %}
-{% set comment = 'SRC-NAT-' + rule %}
-{% set base_log = '[NAT-SRC-' + rule %}
-{% set interface = ' oifname "' + config.outbound_interface + '"' if config.outbound_interface is defined and config.outbound_interface != 'any' else '' %}
-{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
-{% if config.translation.address == 'masquerade' %}
-{% set trns_addr = config.translation.address %}
-{% if config.translation.port is defined and config.translation.port is not none %}
-{% set trns_addr = trns_addr + ' to ' %}
-{% endif %}
-{# support 1:1 network translation #}
-{% elif config.translation.address | is_ip_network %}
-{% set trns_addr = 'snat ip prefix to ip saddr map { ' + config.source.address + ' : ' + config.translation.address + ' }' %}
-{# we can now clear out the src_addr part as it's already covered in aboves map #}
-{% set src_addr = '' %}
-{% else %}
-{% set trns_addr = 'snat to ' + config.translation.address %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% set trns_port = ':' + config.translation.port if config.translation is defined and config.translation.port is defined and config.translation.port is not none %}
-{# protocol has a default value thus it is always present #}
-{% if config.protocol == 'tcp_udp' %}
-{% set protocol = 'tcp' %}
-{% set comment = comment + ' tcp_udp' %}
-{% else %}
-{% set protocol = config.protocol %}
-{% endif %}
-{% if config.log is defined %}
-{% if config.exclude is defined %}
-{% set log = base_log + '-EXCL]' %}
-{% elif config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %}
-{% set log = base_log +'-MASQ]' %}
-{% else %}
-{% set log = base_log + ']' %}
-{% endif %}
-{% endif %}
-{% if config.exclude is defined %}
-{# rule has been marked as 'exclude' thus we simply return here #}
-{% set trns_addr = 'return' %}
-{% set trns_port = '' %}
-{% endif %}
-{# T1083: NAT address and port translation options #}
-{% if config.translation is defined and config.translation.options is defined and config.translation.options is not none %}
-{% if config.translation.options.address_mapping is defined and config.translation.options.address_mapping == "persistent" %}
-{% set trns_opts_addr = 'persistent' %}
-{% endif %}
-{% if config.translation.options.port_mapping is defined %}
-{% if config.translation.options.port_mapping == "random" %}
-{% set trns_opts_port = 'random' %}
-{% elif config.translation.options.port_mapping == "fully-random" %}
-{% set trns_opts_port = 'fully-random' %}
-{% endif %}
-{% endif %}
-{% endif %}
-{% if trns_opts_addr and trns_opts_port %}
-{% set trns_opts = trns_opts_addr + ',' + trns_opts_port %}
-{% elif trns_opts_addr %}
-{% set trns_opts = trns_opts_addr %}
-{% elif trns_opts_port %}
-{% set trns_opts = trns_opts_port %}
-{% endif %}
-{% set output = 'add rule ip nat ' + chain + interface %}
-{% if protocol != 'all' %}
-{% set output = output + ' ip protocol ' + protocol %}
-{% endif %}
-{% if src_addr %}
-{% set output = output + ' ' + src_addr %}
-{% endif %}
-{% if src_port %}
-{% set output = output + ' ' + protocol + ' ' + src_port %}
-{% endif %}
-{% if dst_addr %}
-{% set output = output + ' ' + dst_addr %}
-{% endif %}
-{% if dst_port %}
-{% set output = output + ' ' + protocol + ' ' + dst_port %}
-{% endif %}
-{# Count packets #}
-{% set output = output + ' counter' %}
-{# Special handling of log option, we must repeat the entire rule before the #}
-{# NAT translation options are added, this is essential #}
-{% if log %}
-{% set log_output = output + ' log prefix "' + log + '" comment "' + comment + '"' %}
-{% endif %}
-{% if trns_addr %}
-{% set output = output + ' ' + trns_addr %}
-{% endif %}
-{% if trns_port %}
-{# Do not add a whitespace here, translation port must be directly added after IP address #}
-{# e.g. 192.0.2.10:3389 #}
-{% set output = output + trns_port %}
-{% endif %}
-{% if trns_opts %}
-{% set output = output + ' ' + trns_opts %}
-{% endif %}
-{% if comment %}
-{% set output = output + ' comment "' + comment + '"' %}
-{% endif %}
-{{ log_output if log_output }}
-{{ output }}
-{# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #}
-{% if config.protocol == 'tcp_udp' %}
-{# Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #}
-{{ log_output | replace('tcp ', 'udp ') if log_output }}
-{{ output | replace('tcp ', 'udp ') }}
-{% endif %}
-{% endmacro %}
-
-# Start with clean NAT table
-flush table ip nat
-{% if helper_functions == 'remove' %}
-{# NAT if going to be disabled - remove rules and targets from nftables #}
-{% set base_command = 'delete rule ip raw' %}
-{{ base_command }} PREROUTING handle {{ pre_ct_ignore }}
-{{ base_command }} OUTPUT handle {{ out_ct_ignore }}
-{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
-{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
-
-delete chain ip raw NAT_CONNTRACK
-
-{% elif helper_functions == 'add' %}
-{# NAT if enabled - add targets to nftables #}
-add chain ip raw NAT_CONNTRACK
-add rule ip raw NAT_CONNTRACK counter accept
-{% set base_command = 'add rule ip raw' %}
-{{ base_command }} PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER
-{{ base_command }} OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER
-{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
-{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
-{% endif %}
-
-#
-# Destination NAT rules build up here
-#
-{% if destination is defined and destination.rule is defined and destination.rule is not none %}
-{% for rule, config in destination.rule.items() if config.disable is not defined %}
-{{ nat_rule(rule, config, 'PREROUTING') }}
-{% endfor %}
-{% endif %}
-#
-# Source NAT rules build up here
-#
-{% if source is defined and source.rule is defined and source.rule is not none %}
-{% for rule, config in source.rule.items() if config.disable is not defined %}
-{{ nat_rule(rule, config, 'POSTROUTING') }}
-{% endfor %}
-{% endif %}
diff --git a/data/templates/firewall/nftables-nat66.j2 b/data/templates/firewall/nftables-nat66.j2
new file mode 100644
index 000000000..003b138b2
--- /dev/null
+++ b/data/templates/firewall/nftables-nat66.j2
@@ -0,0 +1,102 @@
+#!/usr/sbin/nft -f
+
+{% macro nptv6_rule(rule,config, chain) %}
+{% set comment = '' %}
+{% set base_log = '' %}
+{% set src_prefix = 'ip6 saddr ' ~ config.source.prefix if config.source.prefix is vyos_defined %}
+{% set dest_address = 'ip6 daddr ' ~ config.destination.address if config.destination.address is vyos_defined %}
+{% if chain is vyos_defined('PREROUTING') %}
+{% set comment = 'DST-NAT66-' ~ rule %}
+{% set base_log = '[NAT66-DST-' ~ rule %}
+{% set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}
+{% if config.translation.address | is_ip_network %}
+{# support 1:1 network translation #}
+{% set dnat_type = 'dnat prefix to ' %}
+{% else %}
+{% set dnat_type = 'dnat to ' %}
+{% endif %}
+{% set trns_address = dnat_type ~ config.translation.address if config.translation.address is vyos_defined %}
+{% elif chain is vyos_defined('POSTROUTING') %}
+{% set comment = 'SRC-NAT66-' ~ rule %}
+{% set base_log = '[NAT66-SRC-' ~ rule %}
+{% if config.translation.address is vyos_defined %}
+{% if config.translation.address is vyos_defined('masquerade') %}
+{% set trns_address = config.translation.address %}
+{% else %}
+{% if config.translation.address | is_ip_network %}
+{# support 1:1 network translation #}
+{% set snat_type = 'snat prefix to ' %}
+{% else %}
+{% set snat_type = 'snat to ' %}
+{% endif %}
+{% set trns_address = snat_type ~ config.translation.address %}
+{% endif %}
+{% endif %}
+{% set interface = ' oifname "' ~ config.outbound_interface ~ '"' if config.outbound_interface is vyos_defined else '' %}
+{% endif %}
+{% if config.log is vyos_defined %}
+{% if config.translation.address is vyos_defined('masquerade') %}
+{% set log = base_log ~ '-MASQ]' %}
+{% else %}
+{% set log = base_log ~ ']' %}
+{% endif %}
+{% endif %}
+{% set output = 'add rule ip6 nat ' ~ chain ~ interface %}
+{# Count packets #}
+{% set output = output ~ ' counter' %}
+{# Special handling of log option, we must repeat the entire rule before the #}
+{# NAT translation options are added, this is essential #}
+{% if log is vyos_defined %}
+{% set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %}
+{% endif %}
+{% if src_prefix is vyos_defined %}
+{% set output = output ~ ' ' ~ src_prefix %}
+{% endif %}
+{% if dest_address is vyos_defined %}
+{% set output = output ~ ' ' ~ dest_address %}
+{% endif %}
+{% if trns_address is vyos_defined %}
+{% set output = output ~ ' ' ~ trns_address %}
+{% endif %}
+{% if comment is vyos_defined %}
+{% set output = output ~ ' comment "' ~ comment ~ '"' %}
+{% endif %}
+{{ log_output if log_output is vyos_defined }}
+{{ output }}
+{% endmacro %}
+
+# Start with clean NAT table
+flush table ip6 nat
+{% if helper_functions is vyos_defined('remove') %}
+{# NAT if going to be disabled - remove rules and targets from nftables #}
+{% set base_command = 'delete rule ip6 raw' %}
+{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
+{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
+
+delete chain ip6 raw NAT_CONNTRACK
+
+{% elif helper_functions is vyos_defined('add') %}
+{# NAT if enabled - add targets to nftables #}
+add chain ip6 raw NAT_CONNTRACK
+add rule ip6 raw NAT_CONNTRACK counter accept
+{% set base_command = 'add rule ip6 raw' %}
+{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
+{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
+{% endif %}
+
+#
+# Destination NAT66 rules build up here
+#
+{% if destination.rule is vyos_defined %}
+{% for rule, config in destination.rule.items() if config.disable is not vyos_defined %}
+{{ nptv6_rule(rule, config, 'PREROUTING') }}
+{% endfor %}
+{% endif %}
+#
+# Source NAT66 rules build up here
+#
+{% if source.rule is vyos_defined %}
+{% for rule, config in source.rule.items() if config.disable is not vyos_defined %}
+{{ nptv6_rule(rule, config, 'POSTROUTING') }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/firewall/nftables-nat66.tmpl b/data/templates/firewall/nftables-nat66.tmpl
deleted file mode 100644
index e5c1b1b8d..000000000
--- a/data/templates/firewall/nftables-nat66.tmpl
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/sbin/nft -f
-
-{% macro nptv6_rule(rule,config, chain) %}
-{% set comment = '' %}
-{% set base_log = '' %}
-{% set src_prefix = "ip6 saddr " + config.source.prefix if config.source is defined and config.source.prefix is defined and config.source.prefix is not none %}
-{% set dest_address = "ip6 daddr " + config.destination.address if config.destination is defined and config.destination.address is defined and config.destination.address is not none %}
-{% if chain == "PREROUTING" %}
-{% set comment = "DST-NAT66-" + rule %}
-{% set base_log = '[NAT66-DST-' + rule %}
-{% set interface = " iifname \"" + config.inbound_interface + "\"" if config.inbound_interface is defined and config.inbound_interface != 'any' else '' %}
-{% if config.translation.address | is_ip_network %}
-{# support 1:1 network translation #}
-{% set dnat_type = "dnat prefix to " %}
-{% else %}
-{% set dnat_type = "dnat to " %}
-{% endif %}
-{% set trns_address = dnat_type + config.translation.address if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
-{% elif chain == "POSTROUTING" %}
-{% set comment = 'SRC-NAT66-' + rule %}
-{% set base_log = '[NAT66-SRC-' + rule %}
-{% if config.translation is defined and config.translation.address is defined and config.translation.address is not none %}
-{% if config.translation.address == 'masquerade' %}
-{% set trns_address = config.translation.address %}
-{% else %}
-{% if config.translation.address | is_ip_network %}
-{# support 1:1 network translation #}
-{% set snat_type = "snat prefix to " %}
-{% else %}
-{% set snat_type = "snat to " %}
-{% endif %}
-{% set trns_address = snat_type + config.translation.address %}
-{% endif %}
-{% endif %}
-{% set interface = " oifname \"" + config.outbound_interface + "\"" if config.outbound_interface is defined else '' %}
-{% endif %}
-{% if config.log is defined %}
-{% if config.translation is defined and config.translation.address is defined and config.translation.address == 'masquerade' %}
-{% set log = base_log +'-MASQ]' %}
-{% else %}
-{% set log = base_log + "]" %}
-{% endif %}
-{% endif %}
-{% set output = "add rule ip6 nat " + chain + interface %}
-{# Count packets #}
-{% set output = output + " counter" %}
-{# Special handling of log option, we must repeat the entire rule before the #}
-{# NAT translation options are added, this is essential #}
-{% if log %}
-{% set log_output = output + " log prefix \"" + log + "\" comment \"" + comment + "\"" %}
-{% endif %}
-{% if src_prefix %}
-{% set output = output + " " + src_prefix %}
-{% endif %}
-{% if dest_address %}
-{% set output = output + " " + dest_address %}
-{% endif %}
-{% if trns_address %}
-{% set output = output + " " + trns_address %}
-{% endif %}
-{% if comment %}
-{% set output = output + " comment \"" + comment + "\"" %}
-{% endif %}
-{{ log_output if log_output }}
-{{ output }}
-{% endmacro %}
-
-# Start with clean NAT table
-flush table ip6 nat
-{% if helper_functions == 'remove' %}
-{# NAT if going to be disabled - remove rules and targets from nftables #}
-{% set base_command = "delete rule ip6 raw" %}
-{{base_command}} PREROUTING handle {{ pre_ct_conntrack }}
-{{base_command}} OUTPUT handle {{ out_ct_conntrack }}
-
-delete chain ip6 raw NAT_CONNTRACK
-
-{% elif helper_functions == 'add' %}
-{# NAT if enabled - add targets to nftables #}
-add chain ip6 raw NAT_CONNTRACK
-add rule ip6 raw NAT_CONNTRACK counter accept
-{% set base_command = "add rule ip6 raw" %}
-{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
-{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
-{% endif %}
-
-#
-# Destination NAT66 rules build up here
-#
-{% if destination is defined and destination.rule is defined and destination.rule is not none %}
-{% for rule, config in destination.rule.items() if config.disable is not defined %}
-{{ nptv6_rule(rule, config, 'PREROUTING') }}
-{% endfor %}
-{% endif %}
-#
-# Source NAT66 rules build up here
-#
-{% if source is defined and source.rule is defined and source.rule is not none %}
-{% for rule, config in source.rule.items() if config.disable is not defined %}
-{{ nptv6_rule(rule, config, 'POSTROUTING') }}
-{% endfor %}
-{% endif %}
diff --git a/data/templates/firewall/nftables-policy.j2 b/data/templates/firewall/nftables-policy.j2
new file mode 100644
index 000000000..0154c9f7e
--- /dev/null
+++ b/data/templates/firewall/nftables-policy.j2
@@ -0,0 +1,55 @@
+#!/usr/sbin/nft -f
+
+{% if cleanup_commands is vyos_defined %}
+{% for command in cleanup_commands %}
+{{ command }}
+{% endfor %}
+{% endif %}
+
+include "/run/nftables_defines.conf"
+
+table ip mangle {
+{% if first_install is vyos_defined %}
+ chain VYOS_PBR_PREROUTING {
+ type filter hook prerouting priority -150; policy accept;
+ }
+ chain VYOS_PBR_POSTROUTING {
+ type filter hook postrouting priority -150; policy accept;
+ }
+{% endif %}
+{% if route is vyos_defined %}
+{% for route_text, conf in route.items() %}
+ chain VYOS_PBR_{{ route_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(route_text, rule_id, 'ip') }}
+{% endfor %}
+{% endif %}
+ {{ conf | nft_default_rule(route_text) }}
+ }
+{% endfor %}
+{% endif %}
+}
+
+table ip6 mangle {
+{% if first_install is vyos_defined %}
+ chain VYOS_PBR6_PREROUTING {
+ type filter hook prerouting priority -150; policy accept;
+ }
+ chain VYOS_PBR6_POSTROUTING {
+ type filter hook postrouting priority -150; policy accept;
+ }
+{% endif %}
+{% if route6 is vyos_defined %}
+{% for route_text, conf in route6.items() %}
+ chain VYOS_PBR6_{{ route_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(route_text, rule_id, 'ip6') }}
+{% endfor %}
+{% endif %}
+ {{ conf | nft_default_rule(route_text) }}
+ }
+{% endfor %}
+{% endif %}
+}
diff --git a/data/templates/firewall/nftables-vrf-zones.tmpl b/data/templates/firewall/nftables-vrf-zones.j2
index eecf47b78..eecf47b78 100644
--- a/data/templates/firewall/nftables-vrf-zones.tmpl
+++ b/data/templates/firewall/nftables-vrf-zones.j2
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
new file mode 100644
index 000000000..fac3fad03
--- /dev/null
+++ b/data/templates/firewall/nftables.j2
@@ -0,0 +1,274 @@
+#!/usr/sbin/nft -f
+
+{% if cleanup_commands is vyos_defined %}
+{% for command in cleanup_commands %}
+{{ command }}
+{% endfor %}
+{% endif %}
+
+include "/run/nftables_defines.conf"
+
+table ip filter {
+{% if first_install is vyos_defined %}
+ chain VYOS_FW_FORWARD {
+ type filter hook forward priority 0; policy accept;
+ jump VYOS_POST_FW
+ }
+ chain VYOS_FW_LOCAL {
+ type filter hook input priority 0; policy accept;
+ jump VYOS_POST_FW
+ }
+ chain VYOS_FW_OUTPUT {
+ type filter hook output priority 0; policy accept;
+ jump VYOS_POST_FW
+ }
+ chain VYOS_POST_FW {
+ return
+ }
+ chain VYOS_FRAG_MARK {
+ type filter hook prerouting priority -450; policy accept;
+ ip frag-off & 0x3fff != 0 meta mark set 0xffff1 return
+ }
+{% endif %}
+{% if name is vyos_defined %}
+{% set ns = namespace(sets=[]) %}
+{% for name_text, conf in 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(name_text, rule_id) }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + [name_text + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ {{ conf | nft_default_rule(name_text) }}
+ }
+{% endfor %}
+{% for set_name in ns.sets %}
+ set RECENT_{{ set_name }} {
+ type ipv4_addr
+ size 65535
+ flags dynamic
+ }
+{% endfor %}
+{% endif %}
+{% if state_policy is vyos_defined %}
+ chain VYOS_STATE_POLICY {
+{% if state_policy.established is vyos_defined %}
+ {{ state_policy.established | nft_state_policy('established') }}
+{% endif %}
+{% if state_policy.invalid is vyos_defined %}
+ {{ state_policy.invalid | nft_state_policy('invalid') }}
+{% endif %}
+{% if state_policy.related is vyos_defined %}
+ {{ state_policy.related | nft_state_policy('related') }}
+{% endif %}
+ return
+ }
+{% endif %}
+}
+
+table ip6 filter {
+{% if first_install is vyos_defined %}
+ chain VYOS_FW6_FORWARD {
+ type filter hook forward priority 0; policy accept;
+ jump VYOS_POST_FW6
+ }
+ chain VYOS_FW6_LOCAL {
+ type filter hook input priority 0; policy accept;
+ jump VYOS_POST_FW6
+ }
+ chain VYOS_FW6_OUTPUT {
+ type filter hook output priority 0; policy accept;
+ jump VYOS_POST_FW6
+ }
+ chain VYOS_POST_FW6 {
+ return
+ }
+ chain VYOS_FRAG6_MARK {
+ type filter hook prerouting priority -450; policy accept;
+ exthdr frag exists meta mark set 0xffff1 return
+ }
+{% endif %}
+{% if ipv6_name is vyos_defined %}
+{% set ns = namespace(sets=[]) %}
+{% 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(name_text, rule_id, 'ip6') }}
+{% if rule_conf.recent is vyos_defined %}
+{% set ns.sets = ns.sets + [name_text + '_' + rule_id] %}
+{% endif %}
+{% endfor %}
+{% endif %}
+ {{ conf | nft_default_rule(name_text) }}
+ }
+{% endfor %}
+{% for set_name in ns.sets %}
+ set RECENT6_{{ set_name }} {
+ type ipv6_addr
+ size 65535
+ flags dynamic
+ }
+{% endfor %}
+{% endif %}
+{% if state_policy is vyos_defined %}
+ chain VYOS_STATE_POLICY6 {
+{% if state_policy.established is vyos_defined %}
+ {{ state_policy.established | nft_state_policy('established', ipv6=True) }}
+{% endif %}
+{% if state_policy.invalid is vyos_defined %}
+ {{ state_policy.invalid | nft_state_policy('invalid', ipv6=True) }}
+{% endif %}
+{% if state_policy.related is vyos_defined %}
+ {{ state_policy.related | nft_state_policy('related', ipv6=True) }}
+{% endif %}
+ return
+ }
+{% endif %}
+}
+
+{% if first_install is vyos_defined %}
+table ip nat {
+ chain PREROUTING {
+ type nat hook prerouting priority -100; policy accept;
+ counter jump VYOS_PRE_DNAT_HOOK
+ }
+
+ chain POSTROUTING {
+ type nat hook postrouting priority 100; policy accept;
+ counter jump VYOS_PRE_SNAT_HOOK
+ }
+
+ chain VYOS_PRE_DNAT_HOOK {
+ return
+ }
+
+ chain VYOS_PRE_SNAT_HOOK {
+ return
+ }
+}
+
+table ip6 nat {
+ chain PREROUTING {
+ type nat hook prerouting priority -100; policy accept;
+ counter jump VYOS_DNPT_HOOK
+ }
+
+ chain POSTROUTING {
+ type nat hook postrouting priority 100; policy accept;
+ counter jump VYOS_SNPT_HOOK
+ }
+
+ chain VYOS_DNPT_HOOK {
+ return
+ }
+
+ chain VYOS_SNPT_HOOK {
+ return
+ }
+}
+
+table inet mangle {
+ chain FORWARD {
+ type filter hook forward priority -150; policy accept;
+ }
+}
+
+table raw {
+ chain VYOS_TCP_MSS {
+ type filter hook forward priority -300; policy accept;
+ }
+
+ chain PREROUTING {
+ type filter hook prerouting priority -200; policy accept;
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump VYOS_CT_PREROUTING_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ chain OUTPUT {
+ type filter hook output priority -200; policy accept;
+ counter jump VYOS_CT_IGNORE
+ counter jump VYOS_CT_TIMEOUT
+ counter jump VYOS_CT_OUTPUT_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ ct helper rpc_tcp {
+ type "rpc" protocol tcp;
+ }
+
+ ct helper rpc_udp {
+ type "rpc" protocol udp;
+ }
+
+ ct helper tns_tcp {
+ type "tns" protocol tcp;
+ }
+
+ chain VYOS_CT_HELPER {
+ ct helper set "rpc_tcp" tcp dport {111} return
+ ct helper set "rpc_udp" udp dport {111} return
+ ct helper set "tns_tcp" tcp dport {1521,1525,1536} return
+ return
+ }
+
+ chain VYOS_CT_IGNORE {
+ return
+ }
+
+ chain VYOS_CT_TIMEOUT {
+ return
+ }
+
+ chain VYOS_CT_PREROUTING_HOOK {
+ return
+ }
+
+ chain VYOS_CT_OUTPUT_HOOK {
+ return
+ }
+
+ chain FW_CONNTRACK {
+ accept
+ }
+}
+
+table ip6 raw {
+ chain VYOS_TCP_MSS {
+ type filter hook forward priority -300; policy accept;
+ }
+
+ chain PREROUTING {
+ type filter hook prerouting priority -300; policy accept;
+ counter jump VYOS_CT_PREROUTING_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ chain OUTPUT {
+ type filter hook output priority -300; policy accept;
+ counter jump VYOS_CT_OUTPUT_HOOK
+ counter jump FW_CONNTRACK
+ notrack
+ }
+
+ chain VYOS_CT_PREROUTING_HOOK {
+ return
+ }
+
+ chain VYOS_CT_OUTPUT_HOOK {
+ return
+ }
+
+ chain FW_CONNTRACK {
+ accept
+ }
+}
+{% endif %}
diff --git a/data/templates/firewall/upnpd.conf.j2 b/data/templates/firewall/upnpd.conf.j2
new file mode 100644
index 000000000..27573cbf9
--- /dev/null
+++ b/data/templates/firewall/upnpd.conf.j2
@@ -0,0 +1,172 @@
+# This is the UPNP configuration file
+
+# WAN network interface
+ext_ifname={{ wan_interface }}
+{% if wan_ip is vyos_defined %}
+# If the WAN interface has several IP addresses, you
+# can specify the one to use below
+{% for addr in wan_ip %}
+ext_ip={{ addr }}
+{% endfor %}
+{% endif %}
+
+# LAN network interfaces IPs / networks
+{% if listen is vyos_defined %}
+# There can be multiple listening IPs for SSDP traffic, in that case
+# use multiple 'listening_ip=...' lines, one for each network interface.
+# It can be IP address or network interface name (ie. "eth0")
+# It is mandatory to use the network interface name in order to enable IPv6
+# HTTP is available on all interfaces.
+# When MULTIPLE_EXTERNAL_IP is enabled, the external IP
+# address associated with the subnet follows. For example:
+# listening_ip=192.168.0.1/24 88.22.44.13
+{% for addr in listen %}
+{% if addr | is_ipv4 %}
+listening_ip={{ addr }}
+{% elif addr | is_ipv6 %}
+ipv6_listening_ip={{ addr }}
+{% else %}
+listening_ip={{ addr }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+# CAUTION: mixing up WAN and LAN interfaces may introduce security risks!
+# Be sure to assign the correct interfaces to LAN and WAN and consider
+# implementing UPnP permission rules at the bottom of this configuration file
+
+# Port for HTTP (descriptions and SOAP) traffic. Set to 0 for autoselect.
+#http_port=0
+# Port for HTTPS. Set to 0 for autoselect (default)
+#https_port=0
+
+# Path to the UNIX socket used to communicate with MiniSSDPd
+# If running, MiniSSDPd will manage M-SEARCH answering.
+# default is /var/run/minissdpd.sock
+#minissdpdsocket=/var/run/minissdpd.sock
+
+{% if nat_pmp is vyos_defined %}
+# Enable NAT-PMP support (default is no)
+enable_natpmp=yes
+{% endif %}
+
+# Enable UPNP support (default is yes)
+enable_upnp=yes
+
+{% if pcp_lifetime is vyos_defined %}
+# PCP
+# Configure the minimum and maximum lifetime of a port mapping in seconds
+# 120s and 86400s (24h) are suggested values from PCP-base
+{% if pcp_lifetime.max is vyos_defined %}
+max_lifetime={{ pcp_lifetime.max }}
+{% endif %}
+{% if pcp_lifetime.min is vyos_defined %}
+min_lifetime={{ pcp_lifetime.min }}
+{% endif %}
+{% endif %}
+
+
+# To enable the next few runtime options, see compile time
+# ENABLE_MANUFACTURER_INFO_CONFIGURATION (config.h)
+
+{% if friendly_name is vyos_defined %}
+# Name of this service, default is "`uname -s` router"
+friendly_name= {{ friendly_name }}
+{% endif %}
+
+# Manufacturer name, default is "`uname -s`"
+manufacturer_name=VyOS
+
+# Manufacturer URL, default is URL of OS vendor
+manufacturer_url=https://vyos.io/
+
+# Model name, default is "`uname -s` router"
+model_name=VyOS Router Model
+
+# Model description, default is "`uname -s` router"
+model_description=Vyos open source enterprise router/firewall operating system
+
+# Model URL, default is URL of OS vendor
+model_url=https://vyos.io/
+
+{% if secure_mode is vyos_defined %}
+# Secure Mode, UPnP clients can only add mappings to their own IP
+secure_mode=yes
+{% else %}
+# Secure Mode, UPnP clients can only add mappings to their own IP
+secure_mode=no
+{% endif %}
+
+{% if presentation_url is vyos_defined %}
+# Default presentation URL is HTTP address on port 80
+# If set to an empty string, no presentationURL element will appear
+# in the XML description of the device, which prevents MS Windows
+# from displaying an icon in the "Network Connections" panel.
+#presentation_url= {{ presentation_url }}
+{% endif %}
+
+# Report system uptime instead of daemon uptime
+system_uptime=yes
+
+# Unused rules cleaning.
+# never remove any rule before this threshold for the number
+# of redirections is exceeded. default to 20
+clean_ruleset_threshold=10
+# Clean process work interval in seconds. default to 0 (disabled).
+# a 600 seconds (10 minutes) interval makes sense
+clean_ruleset_interval=600
+
+# Anchor name in pf (default is miniupnpd)
+anchor=VyOS
+
+uuid={{ uuid }}
+
+# Lease file location
+lease_file=/config/upnp.leases
+
+# Daemon's serial and model number when reporting to clients
+# (in XML description)
+#serial=12345678
+#model_number=1
+
+{% if rules is vyos_defined %}
+# UPnP permission rules
+# (allow|deny) (external port range) IP/mask (internal port range)
+# A port range is <min port>-<max port> or <port> if there is only
+# one port in the range.
+# IP/mask format must be nnn.nnn.nnn.nnn/nn
+# It is advised to only allow redirection of port >= 1024
+# and end the rule set with "deny 0-65535 0.0.0.0/0 0-65535"
+# The following default ruleset allows specific LAN side IP addresses
+# to request only ephemeral ports. It is recommended that users
+# modify the IP ranges to match their own internal networks, and
+# also consider implementing network-specific restrictions
+# CAUTION: failure to enforce any rules may permit insecure requests to be made!
+{% for rule, config in rules.items() %}
+{% if config.disable is vyos_defined %}
+{{ config.action }} {{ config.external_port_range }} {{ config.ip }} {{ config.internal_port_range }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% if stun is vyos_defined %}
+# WAN interface must have public IP address. Otherwise it is behind NAT
+# and port forwarding is impossible. In some cases WAN interface can be
+# behind unrestricted NAT 1:1 when all incoming traffic is NAT-ed and
+# routed to WAN interfaces without any filtering. In this cases miniupnpd
+# needs to know public IP address and it can be learnt by asking external
+# server via STUN protocol. Following option enable retrieving external
+# public IP address from STUN server and detection of NAT type. You need
+# to specify also external STUN server in stun_host option below.
+# This option is disabled by default.
+ext_perform_stun=yes
+# Specify STUN server, either hostname or IP address
+# Some public STUN servers:
+# stun.stunprotocol.org
+# stun.sipgate.net
+# stun.xten.com
+# stun.l.google.com (on non standard port 19302)
+ext_stun_host={{ stun.host }}
+# Specify STUN UDP port, by default it is standard port 3478.
+ext_stun_port={{ stun.port }}
+{% endif %}