mirror of
https://github.com/Ysurac/openmptcprouter.git
synced 2025-02-13 11:51:54 +00:00
248 lines
7.6 KiB
Diff
248 lines
7.6 KiB
Diff
From d4081c498ddca184578903fe5199d390bbc0707b Mon Sep 17 00:00:00 2001
|
|
From: Syrone Wong <wong.syrone@gmail.com>
|
|
Date: Sat, 9 Apr 2022 13:24:19 +0800
|
|
Subject: [PATCH] firewall4: add fullcone support
|
|
|
|
fullcone is drop-in replacement of masq for non-udp traffic
|
|
|
|
add runtime fullcone rule check, disable it globally if fullcone expr is
|
|
invalid
|
|
|
|
defaults.fullcone is the global switch, while zone.fullcone4 and
|
|
zone.fullcone6 are switches for IPv4 and IPv6 respectively, most
|
|
IPv6 traffic do NOT need this FullCone NAT functionality.
|
|
---
|
|
root/etc/config/firewall | 3 +
|
|
root/usr/share/firewall4/templates/ruleset.uc | 16 +++-
|
|
.../firewall4/templates/zone-fullcone.uc | 4 +
|
|
root/usr/share/ucode/fw4.uc | 76 ++++++++++++++++++-
|
|
4 files changed, 96 insertions(+), 3 deletions(-)
|
|
create mode 100644 root/usr/share/firewall4/templates/zone-fullcone.uc
|
|
|
|
diff --git a/root/etc/config/firewall b/root/etc/config/firewall
|
|
index b9a4647..7187723 100644
|
|
--- a/root/etc/config/firewall
|
|
+++ b/root/etc/config/firewall
|
|
@@ -5,6 +5,7 @@ config defaults
|
|
option forward REJECT
|
|
# Uncomment this line to disable ipv6 rules
|
|
# option disable_ipv6 1
|
|
+ option fullcone '0'
|
|
|
|
config zone
|
|
option name lan
|
|
@@ -20,6 +21,8 @@ config zone
|
|
option input REJECT
|
|
option output ACCEPT
|
|
option forward REJECT
|
|
+ option fullcone4 '0'
|
|
+ option fullcone6 '0'
|
|
option masq 1
|
|
option mtu_fix 1
|
|
|
|
diff --git a/root/usr/share/firewall4/templates/ruleset.uc b/root/usr/share/firewall4/templates/ruleset.uc
|
|
index eaa1f04..e29eae6 100644
|
|
--- a/root/usr/share/firewall4/templates/ruleset.uc
|
|
+++ b/root/usr/share/firewall4/templates/ruleset.uc
|
|
@@ -310,6 +310,12 @@ table inet fw4 {
|
|
{% for (let redirect in fw4.redirects(`dstnat_${zone.name}`)): %}
|
|
{%+ include("redirect.uc", { fw4, redirect }) %}
|
|
{% endfor %}
|
|
+{% if (zone.fullcone4): %}
|
|
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 4, direction: "dstnat" }) %}
|
|
+{% endif %}
|
|
+{% if (zone.fullcone6): %}
|
|
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 6, direction: "dstnat" }) %}
|
|
+{% endif %}
|
|
{% fw4.includes('chain-append', `dstnat_${zone.name}`) %}
|
|
}
|
|
|
|
@@ -320,20 +326,26 @@ table inet fw4 {
|
|
{% for (let redirect in fw4.redirects(`srcnat_${zone.name}`)): %}
|
|
{%+ include("redirect.uc", { fw4, redirect }) %}
|
|
{% endfor %}
|
|
-{% if (zone.masq): %}
|
|
+{% if (zone.masq && !zone.fullcone4): %}
|
|
{% for (let saddrs in zone.masq4_src_subnets): %}
|
|
{% for (let daddrs in zone.masq4_dest_subnets): %}
|
|
{%+ include("zone-masq.uc", { fw4, zone, family: 4, saddrs, daddrs }) %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
{% endif %}
|
|
-{% if (zone.masq6): %}
|
|
+{% if (zone.masq6 && !zone.fullcone6): %}
|
|
{% for (let saddrs in zone.masq6_src_subnets): %}
|
|
{% for (let daddrs in zone.masq6_dest_subnets): %}
|
|
{%+ include("zone-masq.uc", { fw4, zone, family: 6, saddrs, daddrs }) %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
{% endif %}
|
|
+{% if (zone.fullcone4): %}
|
|
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 4, direction: "srcnat" }) %}
|
|
+{% endif %}
|
|
+{% if (zone.fullcone6): %}
|
|
+ {%+ include("zone-fullcone.uc", { fw4, zone, family: 6, direction: "srcnat" }) %}
|
|
+{% endif %}
|
|
{% fw4.includes('chain-append', `srcnat_${zone.name}`) %}
|
|
}
|
|
|
|
diff --git a/root/usr/share/firewall4/templates/zone-fullcone.uc b/root/usr/share/firewall4/templates/zone-fullcone.uc
|
|
new file mode 100644
|
|
index 0000000..77d9806
|
|
--- /dev/null
|
|
+++ b/root/usr/share/firewall4/templates/zone-fullcone.uc
|
|
@@ -0,0 +1,4 @@
|
|
+{# /usr/share/firewall4/templates/zone-fullcone.uc #}
|
|
+ meta nfproto {{ fw4.nfproto(family) }} fullcone comment "!fw4: Handle {{
|
|
+ zone.name
|
|
+}} {{ fw4.nfproto(family, true) }} fullcone NAT {{ direction }} traffic"
|
|
diff --git a/root/usr/share/ucode/fw4.uc b/root/usr/share/ucode/fw4.uc
|
|
index 1b4764c..c5716da 100644
|
|
--- a/root/usr/share/ucode/fw4.uc
|
|
+++ b/root/usr/share/ucode/fw4.uc
|
|
@@ -1,3 +1,5 @@
|
|
+// /usr/share/ucode/fw4.uc
|
|
+
|
|
const fs = require("fs");
|
|
const uci = require("uci");
|
|
const ubus = require("ubus");
|
|
@@ -428,6 +430,25 @@ function nft_try_hw_offload(devices) {
|
|
return (rc == 0);
|
|
}
|
|
|
|
+function nft_try_fullcone() {
|
|
+ let nft_test =
|
|
+ 'add table inet fw4-fullcone-test; ' +
|
|
+ 'add chain inet fw4-fullcone-test dstnat { ' +
|
|
+ 'type nat hook prerouting priority -100; policy accept; ' +
|
|
+ 'fullcone; ' +
|
|
+ '}; ' +
|
|
+ 'add chain inet fw4-fullcone-test srcnat { ' +
|
|
+ 'type nat hook postrouting priority -100; policy accept; ' +
|
|
+ 'fullcone; ' +
|
|
+ '}; ';
|
|
+ let cmd = sprintf("/usr/sbin/nft -c '%s' 2>/dev/null", replace(nft_test, "'", "'\\''"));
|
|
+ let ok = system(cmd) == 0;
|
|
+ //if (!ok) {
|
|
+ // warn("nft_try_fullcone: cmd "+ cmd + "\n");
|
|
+ //}
|
|
+ return ok;
|
|
+}
|
|
+
|
|
|
|
return {
|
|
read_kernel_version: function() {
|
|
@@ -765,6 +786,18 @@ return {
|
|
warn(`[!] ${msg}\n`);
|
|
},
|
|
|
|
+ myinfo: function(fmt, ...args) {
|
|
+ if (getenv("QUIET"))
|
|
+ return;
|
|
+
|
|
+ let msg = sprintf(fmt, ...args);
|
|
+
|
|
+ if (getenv("TTY"))
|
|
+ warn(`\033[32m${msg}\033[m\n`);
|
|
+ else
|
|
+ warn(`[I] ${msg}\n`);
|
|
+ },
|
|
+
|
|
get: function(sid, opt) {
|
|
return this.cursor.get("firewall", sid, opt);
|
|
},
|
|
@@ -946,6 +979,21 @@ return {
|
|
}
|
|
},
|
|
|
|
+ myinfo_section: function(s, msg) {
|
|
+ if (s[".name"]) {
|
|
+ if (s.name)
|
|
+ this.myinfo("Section %s (%s) %s", this.section_id(s[".name"]), s.name, msg);
|
|
+ else
|
|
+ this.myinfo("Section %s %s", this.section_id(s[".name"]), msg);
|
|
+ }
|
|
+ else {
|
|
+ if (s.name)
|
|
+ this.myinfo("ubus %s (%s) %s", s.type || "rule", s.name, msg);
|
|
+ else
|
|
+ this.myinfo("ubus %s %s", s.type || "rule", msg);
|
|
+ }
|
|
+ },
|
|
+
|
|
parse_policy: function(val) {
|
|
return this.parse_enum(val, [
|
|
"accept",
|
|
@@ -1385,6 +1433,7 @@ return {
|
|
"dnat",
|
|
"snat",
|
|
"masquerade",
|
|
+ "fullcone",
|
|
"accept",
|
|
"reject",
|
|
"drop"
|
|
@@ -1852,6 +1901,7 @@ return {
|
|
}
|
|
|
|
let defs = this.parse_options(data, {
|
|
+ fullcone: [ "bool", "0" ],
|
|
input: [ "policy", "drop" ],
|
|
output: [ "policy", "drop" ],
|
|
forward: [ "policy", "drop" ],
|
|
@@ -1884,6 +1934,11 @@ return {
|
|
|
|
delete defs.syn_flood;
|
|
|
|
+ if (!nft_try_fullcone()) {
|
|
+ delete defs.fullcone;
|
|
+ //warn("nft_try_fullcone failed, disable fullcone globally\n");
|
|
+ }
|
|
+
|
|
this.state.defaults = defs;
|
|
},
|
|
|
|
@@ -1908,6 +1963,8 @@ return {
|
|
masq_dest: [ "network", null, PARSE_LIST ],
|
|
|
|
masq6: [ "bool" ],
|
|
+ fullcone4: [ "bool", "0" ],
|
|
+ fullcone6: [ "bool", "0" ],
|
|
|
|
extra: [ "string", null, UNSUPPORTED ],
|
|
extra_src: [ "string", null, UNSUPPORTED ],
|
|
@@ -1940,6 +1997,18 @@ return {
|
|
}
|
|
}
|
|
|
|
+ if (this.state.defaults && !this.state.defaults.fullcone) {
|
|
+ this.warn_section(data, "fullcone in defaults not enabled, ignore zone fullcone settings");
|
|
+ zone.fullcone4 = false;
|
|
+ zone.fullcone6 = false;
|
|
+ }
|
|
+ if (zone.fullcone4) {
|
|
+ this.myinfo_section(data, "IPv4 fullcone enabled for zone '" + zone.name + "'");
|
|
+ }
|
|
+ if (zone.fullcone6) {
|
|
+ this.myinfo_section(data, "IPv6 fullcone enabled for zone '" + zone.name + "'");
|
|
+ }
|
|
+
|
|
if (zone.mtu_fix && this.kernel < 0x040a0000) {
|
|
this.warn_section(data, "option 'mtu_fix' requires kernel 4.10 or later");
|
|
return;
|
|
@@ -2110,10 +2179,15 @@ return {
|
|
zone.related_subnets = related_subnets;
|
|
zone.related_physdevs = related_physdevs;
|
|
|
|
+ if (zone.fullcone4 || zone.fullcone6) {
|
|
+ zone.dflags.snat = true;
|
|
+ zone.dflags.dnat = true;
|
|
+ }
|
|
+
|
|
if (zone.masq || zone.masq6)
|
|
zone.dflags.snat = true;
|
|
|
|
- if ((zone.auto_helper && !(zone.masq || zone.masq6)) || length(zone.helper)) {
|
|
+ if ((zone.auto_helper && !(zone.masq || zone.masq6 || zone.fullcone4 || zone.fullcone6)) || length(zone.helper)) {
|
|
zone.dflags.helper = true;
|
|
|
|
for (let helper in (length(zone.helper) ? zone.helper : this.state.helpers)) {
|