fw4: improve flowtable handling
authorJo-Philipp Wich <jo@mein.io>
Fri, 4 Feb 2022 19:34:06 +0000 (20:34 +0100)
committerJo-Philipp Wich <jo@mein.io>
Fri, 4 Feb 2022 19:34:06 +0000 (20:34 +0100)
 - Delete the flowtable while loading the rulset in case it exists already
   since flowtable with offload flag canot overwrite ones without and vice
   versa

 - Resolve higher level devices such as 802.1q or bridge devices to lower,
   offload capable ones in case hardware offloading is requested

 - Revert disabling of "flow_offloading_hw" option

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
root/usr/share/firewall4/main.uc
root/usr/share/firewall4/templates/ruleset.uc
root/usr/share/ucode/fw4.uc

index d7dfdb0ca2446613e4f3b92547048674d65aef6e..839346b9b4d75bf89d30ea6805286ecca519b1f6 100644 (file)
@@ -1,6 +1,8 @@
 {%
 
 let fw4 = require("fw4");
+let ubus = require("ubus");
+let fs = require("fs");
 
 /* Find existing sets.
  *
@@ -94,14 +96,81 @@ function reload_sets() {
        }
 }
 
-function render_ruleset(use_statefile) {
+function resolve_lower_devices(devstatus, devname) {
+       let dir = fs.opendir("/sys/class/net/" + devname);
+       let devs = [];
+
+       if (dir) {
+               if (!devstatus || devstatus[devname]?.["hw-tc-offload"]) {
+                       push(devs, devname);
+               }
+               else {
+                       let e;
+
+                       while ((e = dir.read()) != null)
+                               if (index(e, "lower_") === 0)
+                                       push(devs, ...resolve_lower_devices(devstatus, substr(e, 6)));
+               }
+
+               dir.close();
+       }
+
+       return devs;
+}
+
+function resolve_offload_devices() {
+       if (!fw4.default_option("flow_offloading"))
+               return [];
+
+       let devstatus = null;
        let devices = [];
 
-       fw4.load(use_statefile);
+       if (fw4.default_option("flow_offloading_hw")) {
+               let bus = require("ubus").connect();
+
+               if (bus) {
+                       devstatus = bus.call("network.device", "status") || {};
+                       bus.disconnect();
+               }
+       }
 
-       map(fw4.zones(), zone => push(devices, ...zone.match_devices));
+       for (let zone in fw4.zones())
+               for (let device in zone.match_devices)
+                       push(devices, ...resolve_lower_devices(devstatus, device));
+
+       return uniq(devices);
+}
+
+function check_flowtable() {
+       let nft = fs.popen("nft --terse --json list flowtables inet");
+       let info;
+
+       if (nft) {
+               try {
+                       info = json(nft.read("all"));
+               }
+               catch (e) {
+                       info = {};
+               }
+
+               nft.close();
+       }
+
+       for (let item in info?.nftables)
+               if (item?.flowtable?.table == "fw4" && item?.flowtable?.name == "ft")
+                       return true;
+
+       return false;
+}
+
+function render_ruleset(use_statefile) {
+       fw4.load(use_statefile);
 
-       include("templates/ruleset.uc", { fw4, type, exists, length, include, devices: sort(devices) });
+       include("templates/ruleset.uc", {
+               fw4, type, exists, length, include,
+               devices: resolve_offload_devices(),
+               flowtable: check_flowtable()
+       });
 }
 
 function lookup_network(net) {
index 004cfca6bfb9bcfec926276ba412245237cdb943..8020bed69fb1b9c50b82289974544563f4866b59 100644 (file)
@@ -1,5 +1,8 @@
 table inet fw4
 flush table inet fw4
+{% if (flowtable): %}
+delete flowtable inet fw4 ft
+{% endif %}
 
 table inet fw4 {
 {% if (fw4.default_option("flow_offloading") && length(devices) > 0): %}
index 7a2cd7574cb5616a6f718f10e4db622039258af5..175883f85b254910dd409b03902afcd56a507411 100644 (file)
@@ -1695,7 +1695,7 @@ return {
                        custom_chains: [ "bool", null, UNSUPPORTED ],
                        disable_ipv6: [ "bool", null, UNSUPPORTED ],
                        flow_offloading: [ "bool", "0" ],
-                       flow_offloading_hw: [ "bool", "0", UNSUPPORTED ]
+                       flow_offloading_hw: [ "bool", "0" ]
                });
 
                if (defs.synflood_protect === null)