return fields;
}
+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 nft_json_command(...args) {
+ let cmd = [ "/usr/sbin/nft", "--terse", "--json", ...args ];
+ let nft = fs.popen(join(" ", cmd), "r");
+ let info;
+
+ if (nft) {
+ try {
+ info = filter(json(nft.read("all"))?.nftables,
+ item => (type(item) == "object" && !item.metainfo));
+ }
+ catch (e) {
+ warn(sprintf("Unable to parse nftables JSON output: %s\n", e));
+ }
+
+ nft.close();
+ }
+ else {
+ warn(sprintf("Unable to popen() %s: %s\n", cmd, fs.error()));
+ }
+
+ return info || [];
+}
+
+function nft_try_hw_offload(devices) {
+ let nft_test =
+ 'add table inet fw4-hw-offload-test; ' +
+ 'add flowtable inet fw4-hw-offload-test ft { ' +
+ 'hook ingress priority 0; ' +
+ 'devices = { "' + join('", "', devices) + '" }; ' +
+ 'flags offload; ' +
+ '}';
+
+ let rc = system(sprintf("/usr/sbin/nft -c '%s' 2>/dev/null", replace(nft_test, "'", "'\\''")));
+
+ return (rc == 0);
+}
+
return {
read_kernel_version: function() {
return v;
},
+ resolve_offload_devices: function() {
+ if (!this.default_option("flow_offloading"))
+ return [];
+
+ let devstatus = null;
+ let devices = [];
+
+ if (this.default_option("flow_offloading_hw")) {
+ let bus = ubus.connect();
+
+ if (bus) {
+ devstatus = bus.call("network.device", "status") || {};
+ bus.disconnect();
+ }
+
+ for (let zone in this.zones())
+ for (let device in zone.match_devices)
+ push(devices, ...resolve_lower_devices(devstatus, device));
+
+ devices = uniq(devices);
+
+ if (nft_try_hw_offload(devices))
+ return devices;
+
+ this.warn('Hardware flow offloading unavailable, falling back to software offloading');
+ this.state.defaults.flow_offloading_hw = false;
+ }
+
+ devices = [];
+
+ for (let zone in this.zones())
+ for (let device in zone.match_devices)
+ push(devices, ...resolve_lower_devices(null, device));
+
+ return uniq(devices);
+ },
+
+ check_set_types: function() {
+ let sets = {};
+
+ for (let item in nft_json_command("list", "sets", "inet"))
+ if (item.set?.table == "fw4")
+ sets[item.set.name] = (type(item.set.type) == "array") ? item.set.type : [ item.set.type ];
+
+ return sets;
+ },
+
+ check_flowtable: function() {
+ for (let item in nft_json_command("list", "flowtables", "inet"))
+ if (item.flowtable?.table == "fw4" && item.flowtable?.name == "ft")
+ return true;
+
+ return false;
+ },
+
read_state: function() {
let fd = fs.open(STATEFILE, "r");
let state = null;
//
- // Build list of forwardings
+ // Build list of rules
//
- this.cursor.foreach("firewall", "forwarding", f => self.parse_forwarding(f));
+ map(filter(this.state.ubus_rules, r => (r.type == "rule")), r => self.parse_rule(r));
+ this.cursor.foreach("firewall", "rule", r => self.parse_rule(r));
//
- // Build list of rules
+ // Build list of forwardings
//
- map(filter(this.state.ubus_rules, r => (r.type == "rule")), r => self.parse_rule(r));
- this.cursor.foreach("firewall", "rule", r => self.parse_rule(r));
+ this.cursor.foreach("firewall", "forwarding", f => self.parse_forwarding(f));
//
let f1 = fwd.src.zone ? fwd.src.zone.family : 0;
let f2 = fwd.dest.zone ? fwd.dest.zone.family : 0;
- if (f1 != 0 && f2 != 0 && f1 != f2) {
+ if (f1 && f2 && f1 != f2) {
this.warn_section(data,
- sprintf("references src %s restricted to %s and dest restricted to %s, ignoring forwarding",
+ sprintf("references src %s restricted to %s and dest %s restricted to %s, ignoring forwarding",
fwd.src.zone.name, this.nfproto(f1, true),
fwd.dest.zone.name, this.nfproto(f2, true)));
}
/* build reflection rules */
- if (redir.reflection && (length(rip[0]) || length(rip[1])) &&
- redir.src && redir.src.zone && redir.src.zone[family == 4 ? "masq" : "masq6"] &&
- redir.dest && redir.dest.zone) {
-
+ if (redir.reflection && (length(rip[0]) || length(rip[1])) && redir.src?.zone && redir.dest?.zone) {
let refredir = {
name: redir.name + " (reflection)",
let refaddrs = (redir.reflection_src == "internal") ? iaddrs : eaddrs;
for (let i = 0; i <= 1; i++) {
- if (length(rip[i])) {
+ if (redir.src.zone[i ? "masq6" : "masq"] && length(rip[i])) {
let snat_addr = refaddrs[i]?.[0];
/* For internal reflection sources try to find a suitable candiate IP