OSDN Git Service

firewall4: add fullcone support
authorSyrone Wong <wong.syrone@gmail.com>
Thu, 21 Apr 2022 09:42:30 +0000 (17:42 +0800)
committerZiMing Mo <msylgj@immortalwrt.org>
Fri, 22 Apr 2022 07:25:20 +0000 (15:25 +0800)
Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
Signed-off-by: ZiMing Mo <msylgj@immortalwrt.org>
package/network/config/firewall4/Makefile
package/network/config/firewall4/patches/001-firewall4-add-support-for-fullcone-nat.patch [new file with mode: 0644]

index 4a456e7..2cc51bd 100644 (file)
@@ -23,7 +23,7 @@ define Package/firewall4
   TITLE:=OpenWrt 4th gen firewall
   DEPENDS:= \
        +kmod-nft-core +kmod-nft-fib +kmod-nft-offload \
-       +kmod-nft-nat +kmod-nft-nat6 \
+       +kmod-nft-nat +kmod-nft-nat6 +kmod-nft-fullcone \
        +nftables-json \
        +ucode +ucode-mod-fs +ucode-mod-ubus +ucode-mod-uci
   EXTRA_DEPENDS:=ucode (>= 2022-03-22)
diff --git a/package/network/config/firewall4/patches/001-firewall4-add-support-for-fullcone-nat.patch b/package/network/config/firewall4/patches/001-firewall4-add-support-for-fullcone-nat.patch
new file mode 100644 (file)
index 0000000..012f5c8
--- /dev/null
@@ -0,0 +1,198 @@
+From 980594ee7bcb0bcded95731bb12cf118d7f48951 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
+---
+ root/etc/config/firewall                      |  2 +
+ root/usr/share/firewall4/templates/ruleset.uc | 13 +++++-
+ .../firewall4/templates/zone-fullcone.uc      |  4 ++
+ root/usr/share/ucode/fw4.uc                   | 44 ++++++++++++++++++-
+ 4 files changed, 60 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 f4a3322..2f17ad2 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 '1'
+ config zone
+       option name             lan
+@@ -20,6 +21,7 @@ config zone
+       option input            REJECT
+       option output           ACCEPT
+       option forward          REJECT
++      option fullcone '1'
+       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 b402315..aae698f 100644
+--- a/root/usr/share/firewall4/templates/ruleset.uc
++++ b/root/usr/share/firewall4/templates/ruleset.uc
+@@ -1,3 +1,4 @@
++{# /usr/share/firewall4/templates/ruleset.uc #}
+ {% let flowtable_devices = fw4.resolve_offload_devices(); -%}
+ table inet fw4
+@@ -237,6 +238,10 @@ table inet fw4 {
+ {%   for (let redirect in fw4.redirects("dstnat_"+zone.name)): %}
+               {%+ include("redirect.uc", { fw4, redirect }) %}
+ {%   endfor %}
++{%   if (zone.fullcone): %}
++              {%+ include("zone-fullcone.uc", { fw4, zone, direction: "dstnat" }) %}
++{%   endif %}
++
+       }
+ {%  endif %}
+@@ -245,20 +250,24 @@ 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.fullcone): %}
+ {%    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.fullcone): %}
+ {%    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.fullcone): %}
++              {%+ include("zone-fullcone.uc", { fw4, zone, direction: "srcnat" }) %}
++{%   endif %}
++
+       }
+ {%  endif %}
+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..f0647da
+--- /dev/null
++++ b/root/usr/share/firewall4/templates/zone-fullcone.uc
+@@ -0,0 +1,4 @@
++{# /usr/share/firewall4/templates/zone-fullcone.uc #}
++              fullcone comment "!fw4: Handle {{
++              zone.name
++}} IPv4/IPv6 fullcone NAT traffic"
+diff --git a/root/usr/share/ucode/fw4.uc b/root/usr/share/ucode/fw4.uc
+index b81f9ad..ec5958e 100644
+--- a/root/usr/share/ucode/fw4.uc
++++ b/root/usr/share/ucode/fw4.uc
+@@ -1,3 +1,5 @@
++// /usr/share/ucode/fw4.uc
++
+ let fs = require("fs");
+ let uci = require("uci");
+ let ubus = require("ubus");
+@@ -419,6 +421,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() {
+@@ -1382,6 +1403,7 @@ return {
+                       "dnat",
+                       "snat",
+                       "masquerade",
++                      "fullcone",
+                       "accept",
+                       "reject",
+                       "drop"
+@@ -1787,6 +1809,7 @@ return {
+               }
+               let defs = this.parse_options(data, {
++                      fullcone: [ "bool", "0" ],
+                       input: [ "policy", "drop" ],
+                       output: [ "policy", "drop" ],
+                       forward: [ "policy", "drop" ],
+@@ -1819,6 +1842,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;
+       },
+@@ -1843,6 +1871,7 @@ return {
+                       masq_dest: [ "network", null, PARSE_LIST ],
+                       masq6: [ "bool" ],
++                      fullcone: [ "bool", "0" ],
+                       extra: [ "string", null, UNSUPPORTED ],
+                       extra_src: [ "string", null, UNSUPPORTED ],
+@@ -1873,6 +1902,14 @@ return {
+                       return;
+               }
++              if (this.state.defaults && !this.state.defaults.fullcone) {
++                      this.warn_section(data, "fullcone in defaults not enabled, ignore zone fullcone setting");
++                      zone.fullcone = false;
++              }
++              if (zone.fullcone) {
++                      this.warn_section(data, "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;
+@@ -2041,10 +2078,15 @@ return {
+               zone.related_subnets = related_subnets;
+               zone.related_physdevs = related_physdevs;
++              if (zone.fullcone) {
++                      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.fullcone)) || length(zone.helper)) {
+                       zone.dflags.helper = true;
+                       for (let helper in (length(zone.helper) ? zone.helper : this.state.helpers)) {