1 From 980594ee7bcb0bcded95731bb12cf118d7f48951 Mon Sep 17 00:00:00 2001
2 From: Syrone Wong <wong.syrone@gmail.com>
3 Date: Sat, 9 Apr 2022 13:24:19 +0800
4 Subject: [PATCH] firewall4: add fullcone support
6 fullcone is drop-in replacement of masq for non-udp traffic
8 add runtime fullcone rule check, disable it globally if fullcone expr is
11 Renew: ZiMing Mo <msylgj@immortalwrt.org>
13 root/etc/config/firewall | 1 +
14 root/usr/share/firewall4/templates/ruleset.uc | 13 +++++-
15 .../firewall4/templates/zone-fullcone.uc | 4 ++
16 root/usr/share/ucode/fw4.uc | 38 ++++++++++++++++++-
17 4 files changed, 53 insertions(+), 3 deletions(-)
18 create mode 100644 root/usr/share/firewall4/templates/zone-fullcone.uc
20 --- a/root/etc/config/firewall
21 +++ b/root/etc/config/firewall
22 @@ -5,6 +5,7 @@ config defaults
24 # Uncomment this line to disable ipv6 rules
25 # option disable_ipv6 1
30 --- a/root/usr/share/firewall4/templates/ruleset.uc
31 +++ b/root/usr/share/firewall4/templates/ruleset.uc
33 +{# /usr/share/firewall4/templates/ruleset.uc #}
34 {% let flowtable_devices = fw4.resolve_offload_devices(); -%}
37 @@ -238,6 +239,10 @@ table inet fw4 {
38 {% for (let redirect in fw4.redirects(`dstnat_${zone.name}`)): %}
39 {%+ include("redirect.uc", { fw4, redirect }) %}
41 +{% if (fw4.default_option("fullcone")): %}
42 + {%+ include("zone-fullcone.uc", { fw4, zone, direction: "dstnat" }) %}
48 @@ -246,20 +251,24 @@ table inet fw4 {
49 {% for (let redirect in fw4.redirects(`srcnat_${zone.name}`)): %}
50 {%+ include("redirect.uc", { fw4, redirect }) %}
52 -{% if (zone.masq): %}
53 +{% if (zone.masq && !fw4.default_option("fullcone")): %}
54 {% for (let saddrs in zone.masq4_src_subnets): %}
55 {% for (let daddrs in zone.masq4_dest_subnets): %}
56 {%+ include("zone-masq.uc", { fw4, zone, family: 4, saddrs, daddrs }) %}
60 -{% if (zone.masq6): %}
61 +{% if (zone.masq6 && !fw4.default_option("fullcone")): %}
62 {% for (let saddrs in zone.masq6_src_subnets): %}
63 {% for (let daddrs in zone.masq6_dest_subnets): %}
64 {%+ include("zone-masq.uc", { fw4, zone, family: 6, saddrs, daddrs }) %}
68 +{% if (fw4.default_option("fullcone")): %}
69 + {%+ include("zone-fullcone.uc", { fw4, zone, direction: "srcnat" }) %}
76 +++ b/root/usr/share/firewall4/templates/zone-fullcone.uc
78 +{# /usr/share/firewall4/templates/zone-fullcone.uc #}
79 + fullcone comment "!fw4: Handle {{
81 +}} IPv4/IPv6 fullcone NAT traffic"
82 --- a/root/usr/share/ucode/fw4.uc
83 +++ b/root/usr/share/ucode/fw4.uc
85 +// /usr/share/ucode/fw4.uc
87 const fs = require("fs");
88 const uci = require("uci");
89 const ubus = require("ubus");
90 @@ -428,6 +430,25 @@ function nft_try_hw_offload(devices) {
94 +function nft_try_fullcone() {
96 + 'add table inet fw4-fullcone-test; ' +
97 + 'add chain inet fw4-fullcone-test dstnat { ' +
98 + 'type nat hook prerouting priority -100; policy accept; ' +
101 + 'add chain inet fw4-fullcone-test srcnat { ' +
102 + 'type nat hook postrouting priority -100; policy accept; ' +
105 + let cmd = sprintf("/usr/sbin/nft -c '%s' 2>/dev/null", replace(nft_test, "'", "'\\''"));
106 + let ok = system(cmd) == 0;
108 + warn("nft_try_fullcone: cmd "+ cmd + "\n");
115 read_kernel_version: function() {
116 @@ -1383,6 +1404,7 @@ return {
124 @@ -1802,6 +1824,7 @@ return {
127 let defs = this.parse_options(data, {
128 + fullcone: [ "bool", "0" ],
129 input: [ "policy", "drop" ],
130 output: [ "policy", "drop" ],
131 forward: [ "policy", "drop" ],
132 @@ -1834,6 +1857,14 @@ return {
134 delete defs.syn_flood;
136 + if (!nft_try_fullcone()) {
137 + delete defs.fullcone;
138 + warn("nft_try_fullcone failed, disable fullcone globally\n");
140 + if (this.state.defaults && !this.state.defaults.fullcone) {
141 + this.warn_section(data, "fullcone enabled");
144 this.state.defaults = defs;
147 @@ -2058,10 +2089,15 @@ return {
148 zone.related_subnets = related_subnets;
149 zone.related_physdevs = related_physdevs;
151 + if (this.state.defaults.fullcone) {
152 + zone.dflags.snat = true;
153 + zone.dflags.dnat = true;
156 if (zone.masq || zone.masq6)
157 zone.dflags.snat = true;
159 - if ((zone.auto_helper && !(zone.masq || zone.masq6)) || length(zone.helper)) {
160 + if ((zone.auto_helper && !(zone.masq || zone.masq6 || this.state.defaults.fullcone)) || length(zone.helper)) {
161 zone.dflags.helper = true;
163 for (let helper in (length(zone.helper) ? zone.helper : this.state.helpers)) {