treewide: use modern syntax
authorJo-Philipp Wich <jo@mein.io>
Thu, 28 Apr 2022 19:41:13 +0000 (21:41 +0200)
committerJo-Philipp Wich <jo@mein.io>
Fri, 29 Apr 2022 12:54:27 +0000 (14:54 +0200)
 - Use `||=` operator expressions instead of `expr = expr || value` patterns
 - Use optional chaining instead of `foo && foo.bar && foo.bar.baz` patterns
 - Use template strings instead of string concatenations or sprintf() calls
 - Use `const` for global default variables

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

index 692d9aa0347682a1645ffb8566bb35b072323b7a..9b28ea6f1391d74709ad0393f8d3efd455b02965 100644 (file)
@@ -22,27 +22,25 @@ function reload_sets() {
                        continue;
 
                if (!exists(sets, set.name)) {
-                       warn(sprintf("Named set '%s' does not exist - do you need to restart the firewall?\n",
-                                    set.name));
+                       warn(`Named set '${set.name}' does not exist - do you need to restart the firewall?\n`);
                        continue;
                }
                else if (fw4.concat(sets[set.name]) != fw4.concat(set.types)) {
-                       warn(sprintf("Named set '%s' has a different type - want '%s' but is '%s' - do you need to restart the firewall?\n",
-                                    set.name, fw4.concat(set.types), fw4.concat(sets[set.name])));
+                       warn(`Named set '${set.name}' has a different type - want '${fw4.concat(set.types)}' but is '${fw4.concat(sets[set.name])}' - do you need to restart the firewall?\n`);
                        continue;
                }
 
                let first = true;
                let printer = (entry) => {
                        if (first) {
-                               print("add element inet fw4 ", set.name, " {\n");
+                               print(`add element inet fw4 ${set.name} {\n`);
                                first = false;
                        }
 
-                       print("\t", join(" . ", entry), ",\n");
+                       print(` ${join(" . ", entry)},\n`);
                };
 
-               print("flush set inet fw4 ", set.name, "\n");
+               print(`flush set inet fw4 ${set.name}\n`);
 
                map(set.entries, printer);
                fw4.parse_setfile(set, printer);
index 5369430e0ea5b934ec6d5721537133f01b78d4a7..3d1c113bf7e105947bcc4e7ced8e11dc213536f9 100644 (file)
@@ -68,4 +68,4 @@
 {%- else -%}
        {{ redirect.target }} {{ redirect.raddr ? fw4.host(redirect.raddr, redirect.rport != null) : '' }}
        {%- if (redirect.rport): %}:{{ fw4.port(redirect.rport) }}{% endif %}
-{% endif %} comment {{ fw4.quote("!fw4: " + redirect.name, true) }}
+{% endif %} comment {{ fw4.quote(`!fw4: ${redirect.name}`, true) }}
index d90595d647b60d96c2801214ce52184751c60145..9b169efb539375ce53a66a8884bdb82981134681 100644 (file)
@@ -94,4 +94,4 @@
    elif (rule.target): -%}
        {{ rule.target }} {%+
    endif -%}
-comment {{ fw4.quote("!fw4: " + rule.name, true) }}
+comment {{ fw4.quote(`!fw4: ${rule.name}`, true) }}
index 9cf7ef6d406c9b3f77049a9a594952da4ca49daa..0142d5afa93368521bf865c2edb6a153e0dadc05 100644 (file)
@@ -134,12 +134,12 @@ table inet fw4 {
        chain handle_reject {
                meta l4proto tcp reject with {{
                        (fw4.default_option("tcp_reject_code") != "tcp-reset")
-                               ? "icmpx type " + fw4.default_option("tcp_reject_code")
+                               ? `icmpx type ${fw4.default_option("tcp_reject_code")}`
                                : "tcp reset"
                }} comment "!fw4: Reject TCP traffic"
                reject with {{
                        (fw4.default_option("any_reject_code") != "tcp-reset")
-                               ? "icmpx type " + fw4.default_option("any_reject_code")
+                               ? `icmpx type ${fw4.default_option("any_reject_code")}`
                                : "tcp reset"
                }} comment "!fw4: Reject any other traffic"
        }
@@ -157,7 +157,7 @@ table inet fw4 {
 {% endif %}
 {% for (let zone in fw4.zones()): %}
        chain input_{{ zone.name }} {
-{%  for (let rule in fw4.rules("input_"+zone.name)): %}
+{%  for (let rule in fw4.rules(`input_${zone.name}`)): %}
                {%+ include("rule.uc", { fw4, rule }) %}
 {%  endfor %}
 {%  if (zone.dflags.dnat): %}
@@ -167,14 +167,14 @@ table inet fw4 {
        }
 
        chain output_{{ zone.name }} {
-{%  for (let rule in fw4.rules("output_"+zone.name)): %}
+{%  for (let rule in fw4.rules(`output_${zone.name}`)): %}
                {%+ include("rule.uc", { fw4, rule }) %}
 {%  endfor %}
                jump {{ zone.output }}_to_{{ zone.name }}
        }
 
        chain forward_{{ zone.name }} {
-{%  for (let rule in fw4.rules("forward_"+zone.name)): %}
+{%  for (let rule in fw4.rules(`forward_${zone.name}`)): %}
                {%+ include("rule.uc", { fw4, rule }) %}
 {%  endfor %}
 {%  if (zone.dflags.dnat): %}
@@ -235,7 +235,7 @@ table inet fw4 {
 {% for (let zone in fw4.zones()): %}
 {%  if (zone.dflags.dnat): %}
        chain dstnat_{{ zone.name }} {
-{%   for (let redirect in fw4.redirects("dstnat_"+zone.name)): %}
+{%   for (let redirect in fw4.redirects(`dstnat_${zone.name}`)): %}
                {%+ include("redirect.uc", { fw4, redirect }) %}
 {%   endfor %}
        }
@@ -243,7 +243,7 @@ table inet fw4 {
 {%  endif %}
 {%  if (zone.dflags.snat): %}
        chain srcnat_{{ zone.name }} {
-{%   for (let redirect in fw4.redirects("srcnat_"+zone.name)): %}
+{%   for (let redirect in fw4.redirects(`srcnat_${zone.name}`)): %}
                {%+ include("redirect.uc", { fw4, redirect }) %}
 {%   endfor %}
 {%   if (zone.masq): %}
@@ -327,7 +327,7 @@ table inet fw4 {
 {%  for (let zone in fw4.zones()): %}
 {%   if (zone.dflags[target]): %}
        chain {{ target }}_{{ zone.name }} {
-{% for (let rule in fw4.rules(target+"_"+zone.name)): %}
+{% for (let rule in fw4.rules(`${target}_${zone.name}`)): %}
                {%+ include("rule.uc", { fw4, rule }) %}
 {% endfor %}
        }
index f7a88a6469936a354851ceec10e1812528949513..4ca1d79fa00a955e849e3261e409969ba65c47ce 100644 (file)
@@ -1,17 +1,17 @@
-let fs = require("fs");
-let uci = require("uci");
-let ubus = require("ubus");
+const fs = require("fs");
+const uci = require("uci");
+const ubus = require("ubus");
 
-let STATEFILE = "/var/run/fw4.state";
+const STATEFILE = "/var/run/fw4.state";
 
-let PARSE_LIST   = 0x01;
-let FLATTEN_LIST = 0x02;
-let NO_INVERT    = 0x04;
-let UNSUPPORTED  = 0x08;
-let REQUIRED     = 0x10;
-let DEPRECATED   = 0x20;
+const PARSE_LIST   = 0x01;
+const FLATTEN_LIST = 0x02;
+const NO_INVERT    = 0x04;
+const UNSUPPORTED  = 0x08;
+const REQUIRED     = 0x10;
+const DEPRECATED   = 0x20;
 
-let ipv4_icmptypes = {
+const ipv4_icmptypes = {
        "any": [ 0xFF, 0, 0xFF ],
        "echo-reply": [ 0, 0, 0xFF ],
        "pong": [ 0, 0, 0xFF ], /* Alias */
@@ -66,7 +66,7 @@ let ipv4_icmptypes = {
        "address-mask-reply": [ 18, 0, 0xFF ]
 };
 
-let ipv6_icmptypes = {
+const ipv6_icmptypes = {
        "destination-unreachable": [ 1, 0, 0xFF ],
        "no-route": [ 1, 0, 0 ],
        "communication-prohibited": [ 1, 1, 1 ],
@@ -104,7 +104,7 @@ let ipv6_icmptypes = {
        "redirect": [ 137, 0, 0xFF ]
 };
 
-let dscp_classes = {
+const dscp_classes = {
        "CS0": 0x00,
        "CS1": 0x08,
        "CS2": 0x10,
@@ -154,7 +154,7 @@ function to_bits(mask) {
        let bits = 0;
 
        for (let i = 0, z = false; i < length(a); i++) {
-               z = z || !a[i];
+               z ||= !a[i];
 
                while (!z && (a[i] & 0x80)) {
                        a[i] = (a[i] << 1) & 0xff;
@@ -227,15 +227,11 @@ function subnets_split_af(x) {
        let rv = {};
 
        for (let ag in to_array(x)) {
-               for (let a in filter(ag.addrs, a => (a.family == 4))) {
-                       rv[0] = rv[0] || [];
-                       push(rv[0], { ...a, invert: ag.invert });
-               }
+               for (let a in filter(ag.addrs, a => (a.family == 4)))
+                       push(rv[0] ||= [], { ...a, invert: ag.invert });
 
-               for (let a in filter(ag.addrs, a => (a.family == 6))) {
-                       rv[1] = rv[1] || [];
-                       push(rv[1], { ...a, invert: ag.invert });
-               }
+               for (let a in filter(ag.addrs, a => (a.family == 6)))
+                       push(rv[1] ||= [], { ...a, invert: ag.invert });
        }
 
        if (rv[0] || rv[1])
@@ -318,8 +314,8 @@ function infer_family(f, objects) {
                        }
 
                        return by
-                               ? sprintf('references IPv%d only %s but is restricted to IPv%d by %s', obj.family, desc, res, by)
-                               : sprintf('is restricted to IPv%d but referenced %s is IPv%d only', res, desc, obj.family);
+                               ? `references IPv${obj.family} only ${desc} but is restricted to IPv${res} by ${by}`
+                               : `is restricted to IPv${res} but referenced ${desc} is IPv${obj.family} only`;
                }
        }
 
@@ -333,15 +329,15 @@ function map_setmatch(set, match, proto) {
        let fields = [];
 
        for (let i, t in set.types) {
-               let dir = (((match.dir && match.dir[i]) || set.directions[i] || 'src') == 'src' ? 's' : 'd');
+               let dir = ((match.dir?.[i] || set.directions[i] || 'src') == 'src' ? 's' : 'd');
 
                switch (t) {
                case 'ipv4_addr':
-                       fields[i] = sprintf('ip %saddr', dir);
+                       fields[i] = `ip ${dir}saddr`;
                        break;
 
                case 'ipv6_addr':
-                       fields[i] = sprintf('ip6 %saddr', dir);
+                       fields[i] = `ip6 ${dir}saddr`;
                        break;
 
                case 'ether_addr':
@@ -352,7 +348,7 @@ function map_setmatch(set, match, proto) {
                        break;
 
                case 'inet_service':
-                       fields[i] = sprintf('%s %sport', proto, dir);
+                       fields[i] = `${proto} ${dir}port`;
                        break;
                }
        }
@@ -361,7 +357,7 @@ function map_setmatch(set, match, proto) {
 }
 
 function resolve_lower_devices(devstatus, devname) {
-       let dir = fs.opendir("/sys/class/net/" + devname);
+       let dir = fs.opendir(`/sys/class/net/${devname}`);
        let devs = [];
 
        if (dir) {
@@ -393,28 +389,29 @@ function nft_json_command(...args) {
                                item => (type(item) == "object" && !item.metainfo));
                }
                catch (e) {
-                       warn(sprintf("Unable to parse nftables JSON output: %s\n", e));
+                       warn(`Unable to parse nftables JSON output: ${e}\n`);
                }
 
                nft.close();
        }
        else {
-               warn(sprintf("Unable to popen() %s: %s\n", cmd, fs.error()));
+               warn(`Unable to popen() ${cmd}: ${fs.error()}\n`);
        }
 
        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 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, "'", "'\\''")));
+       let rc = system(`/usr/sbin/nft -c '${replace(nft_test, "'", "'\\''")}' 2>/dev/null`);
 
        return (rc == 0);
 }
@@ -499,7 +496,7 @@ return {
                                state = json(fd.read("all"));
                        }
                        catch (e) {
-                               warn(sprintf("Unable to parse '%s': %s\n", STATEFILE, e));
+                               warn(`Unable to parse '${STATEFILE}': ${e}\n`);
                        }
 
                        fd.close();
@@ -521,7 +518,7 @@ return {
                    bus.disconnect();
                }
                else {
-                       warn(sprintf("Unable to connect to ubus: %s\n", ubus.error()));
+                       warn(`Unable to connect to ubus: ${ubus.error()}\n`);
                }
 
 
@@ -529,7 +526,7 @@ return {
                // Gather logical network information from ubus
                //
 
-               if (type(ifaces) == "object" && type(ifaces.interface) == "array") {
+               if (type(ifaces?.interface) == "array") {
                        for (let ifc in ifaces.interface) {
                                let net = {
                                        up: ifc.up,
@@ -540,8 +537,7 @@ return {
 
                                if (type(ifc["ipv4-address"]) == "array") {
                                        for (let addr in ifc["ipv4-address"]) {
-                                               net.ipaddrs = net.ipaddrs || [];
-                                               push(net.ipaddrs, {
+                                               push(net.ipaddrs ||= [], {
                                                        family: 4,
                                                        addr: addr.address,
                                                        mask: to_mask(addr.mask, false),
@@ -552,8 +548,7 @@ return {
 
                                if (type(ifc["ipv6-address"]) == "array") {
                                        for (let addr in ifc["ipv6-address"]) {
-                                               net.ipaddrs = net.ipaddrs || [];
-                                               push(net.ipaddrs, {
+                                               push(net.ipaddrs ||= [], {
                                                        family: 6,
                                                        addr: addr.address,
                                                        mask: to_mask(addr.mask, true),
@@ -565,8 +560,7 @@ return {
                                if (type(ifc["ipv6-prefix-assignment"]) == "array") {
                                        for (let addr in ifc["ipv6-prefix-assignment"]) {
                                                if (addr["local-address"]) {
-                                                       net.ipaddrs = net.ipaddrs || [];
-                                                       push(net.ipaddrs, {
+                                                       push(net.ipaddrs ||= [], {
                                                                family: 6,
                                                                addr: addr["local-address"].address,
                                                                mask: to_mask(addr["local-address"].mask, true),
@@ -576,14 +570,14 @@ return {
                                        }
                                }
 
-                               if (type(ifc.data) == "object" && type(ifc.data.firewall) == "array") {
+                               if (type(ifc.data?.firewall) == "array") {
                                        let n = 0;
 
                                        for (let rulespec in ifc.data.firewall) {
                                                push(rules, {
                                                        ...rulespec,
 
-                                                       name: (rulespec.type != 'ipset') ? sprintf('ubus:%s[%s] %s %d', ifc.interface, ifc.proto, rulespec.type || 'rule', n) : rulespec.name,
+                                                       name: (rulespec.type != 'ipset') ? `ubus:${ifc.interface}[${ifc.proto}] ${rulespec.type || 'rule'} ${n}` : rulespec.name,
                                                        device: rulespec.device || ifc.l3_device
                                                });
 
@@ -602,14 +596,14 @@ return {
 
                if (type(services) == "object") {
                        for (let svcname, service in services) {
-                               if (type(service) == "object" && type(service.firewall) == "array") {
+                               if (type(service?.firewall) == "array") {
                                        let n = 0;
 
                                        for (let rulespec in services[svcname].firewall) {
                                                push(rules, {
                                                        ...rulespec,
 
-                                                       name: (rulespec.type != 'ipset') ? sprintf('ubus:%s %s %d', svcname, rulespec.type || 'rule', n) : rulespec.name
+                                                       name: (rulespec.type != 'ipset') ? `ubus:${svcname} ${rulespec.type || 'rule'} ${n}` : rulespec.name
                                                });
 
                                                n++;
@@ -617,14 +611,14 @@ return {
                                }
 
                                for (let svcinst, instance in service) {
-                                       if (type(instance) == "object" && type(instance.firewall) == "array") {
+                                       if (type(instance?.firewall) == "array") {
                                                let n = 0;
 
                                                for (let rulespec in instance.firewall) {
                                                        push(rules, {
                                                                ...rulespec,
 
-                                                               name: (rulespec.type != 'ipset') ? sprintf('ubus:%s[%s] %s %d', svcname, svcinst, rulespec.type || 'rule', n) : rulespec.name
+                                                               name: (rulespec.type != 'ipset') ? `ubus:${svcname}[${svcinst}] ${rulespec.type || 'rule'} ${n}` : rulespec.name
                                                        });
 
                                                        n++;
@@ -735,7 +729,7 @@ return {
                                fd.close();
                        }
                        else {
-                               warn("Unable to write '%s': %s\n", STATEFILE, fs.error());
+                               warn(`Unable to write '${STATEFILE}': ${fs.error()}\n`);
                        }
                }
        },
@@ -747,9 +741,9 @@ return {
                let msg = sprintf(fmt, ...args);
 
                if (getenv("TTY"))
-                       warn("\033[33m", msg, "\033[m\n");
+                       warn(`\033[33m${msg}\033[m\n`);
                else
-                       warn("[!] ", msg, "\n");
+                       warn(`[!] ${msg}\n`);
        },
 
        get: function(sid, opt) {
@@ -764,7 +758,7 @@ return {
                let rv = {};
 
                for (let key, val in spec) {
-                       let datatype = "parse_" + val[0],
+                       let datatype = `parse_${val[0]}`,
                            defval = val[1],
                            flags = val[2] || 0,
                            parsefn = (flags & PARSE_LIST) ? "parse_list" : "parse_opt";
@@ -775,15 +769,15 @@ return {
                                return false;
 
                        if (type(res) == "object" && res.invert && (flags & NO_INVERT)) {
-                               this.warn_section(s, "option '" + key + '" must not be negated');
+                               this.warn_section(s, `option '${key}' must not be negated`);
                                return false;
                        }
 
                        if (res != null) {
                                if (flags & DEPRECATED)
-                                       this.warn_section(s, "option '" + key + "' is deprecated by fw4");
+                                       this.warn_section(s, `option '${key}' is deprecated by fw4`);
                                else if (flags & UNSUPPORTED)
-                                       this.warn_section(s, "option '" + key + "' is not supported by fw4");
+                                       this.warn_section(s, `option '${key}' is not supported by fw4`);
                                else
                                        rv[key] = res;
                        }
@@ -791,7 +785,7 @@ return {
 
                for (let opt in s) {
                        if (index(opt, '.') != 0 && opt != 'type' && !exists(spec, opt)) {
-                               this.warn_section(s, "specifies unknown option '" + opt + "'");
+                               this.warn_section(s, `specifies unknown option '${opt}'`);
                        }
                }
 
@@ -911,7 +905,7 @@ return {
                                c++;
                        });
 
-                       return sprintf("@%s[%d]", s[".type"], c);
+                       return `@${s['.type']}[${c}]`;
                }
 
                return s[".name"];
@@ -1016,14 +1010,10 @@ return {
                let dir = split(rv.val, /[ \t,]/);
 
                for (let i = 0; i < 3 && i < length(dir); i++) {
-                       if (dir[i] == "dst" || dir[i] == "dest") {
-                               rv.dir = rv.dir || [];
-                               rv.dir[i] = "dst";
-                       }
-                       else if (dir[i] == "src") {
-                               rv.dir = rv.dir || [];
-                               rv.dir[i] = "src";
-                       }
+                       if (dir[i] == "dst" || dir[i] == "dest")
+                               (rv.dir ||= [])[i] = "dst";
+                       else if (dir[i] == "src")
+                               (rv.dir ||= [])[i] = "src";
                }
 
                return length(rv.name) ? rv : null;
@@ -1240,8 +1230,8 @@ return {
                let d = m ? match(m[1], /^([0-9]{1,4})(-([0-9]{1,2})(-([0-9]{1,2}))?)?$/) : null;
                let t = this.parse_time(m[2]);
 
-               d[3] = d[3] || 1;
-               d[5] = d[5] || 1;
+               d[3] ||= 1;
+               d[5] ||= 1;
 
                if (d == null || d[1] < 1970 || d[1] > 2038 || d[3] < 1 || d[3] > 12 || d[5] < 1 || d[5] > 31)
                        return null;
@@ -1292,8 +1282,7 @@ return {
                        if (!day)
                                return null;
 
-                       rv.days = rv.days || {};
-                       rv.days[day] = true;
+                       (rv.days ||= {})[day] = true;
                }
 
                rv.days = keys(rv.days);
@@ -1313,8 +1302,7 @@ return {
                        if (day < 1 || day > 31)
                                return null;
 
-                       rv.days = rv.days || [];
-                       rv.days[day] = true;
+                       (rv.days ||= [])[day] = true;
                }
 
                return rv.days ? rv : null;
@@ -1438,7 +1426,7 @@ return {
                                default: this.warn("Set entry '%s' resolves to multiple addresses, using first one", values[i]);
                                }
 
-                               rv[i] = ("net" in set.fw4types) ? ip[0].addr + "/" + ip[0].bits : ip[0].addr;
+                               rv[i] = ("net" in set.fw4types) ? `${ip[0].addr}/${ip[0].bits}` : ip[0].addr;
                                break;
 
                        case 'ipv6_addr':
@@ -1450,7 +1438,7 @@ return {
                                case 2: this.warn("Set entry '%s' resolves to multiple addresses, using first one", values[i]);
                                }
 
-                               rv[i] = ("net" in set.fw4types) ? ip[0].addr + "/" + ip[0].bits : ip[0].addr;
+                               rv[i] = ("net" in set.fw4types) ? `${ip[0].addr}/${ip[0].bits}` : ip[0].addr;
 
                                break;
 
@@ -1489,7 +1477,7 @@ return {
 
                if (val === null) {
                        if (flags & REQUIRED) {
-                               this.warn_section(s, "option '" + opt + "' is mandatory but not set");
+                               this.warn_section(s, `option '${opt}' is mandatory but not set`);
                                return NaN;
                        }
 
@@ -1497,7 +1485,7 @@ return {
                }
 
                if (type(val) == "array") {
-                       this.warn_section(s, "option '" + opt + "' must not be a list");
+                       this.warn_section(s, `option '${opt}' must not be a list`);
                        return NaN;
                }
                else if (val == null) {
@@ -1507,7 +1495,7 @@ return {
                let res = this[fn](val);
 
                if (res === null) {
-                       this.warn_section(s, "option '" + opt + "' specifies invalid value '" + val + "'");
+                       this.warn_section(s, `option '${opt}' specifies invalid value '${val}'`);
                        return NaN;
                }
 
@@ -1520,7 +1508,7 @@ return {
 
                if (val == null) {
                        if (flags & REQUIRED) {
-                               this.warn_section(s, "option '" + opt + "' is mandatory but not set");
+                               this.warn_section(s, `option '${opt}' is mandatory but not set`);
                                return NaN;
                        }
 
@@ -1531,7 +1519,7 @@ return {
                        let res = this[fn](val);
 
                        if (res === null) {
-                               this.warn_section(s, "option '" + opt + "' specifies invalid value '" + val + "'");
+                               this.warn_section(s, `option '${opt}' specifies invalid value '${val}'`);
                                return NaN;
                        }
 
@@ -1546,36 +1534,37 @@ return {
 
        quote: function(s, force) {
                if (force === true || !match(s, /^([0-9A-Fa-f:.\/-]+)( \. [0-9A-Fa-f:.\/-]+)*$/))
-                       return sprintf('"%s"', replace(s + "", /(["\\])/g, '\\$1'));
+                       return `"${replace(s + "", /(["\\])/g, '\\$1')}"`;
 
                return s;
        },
 
        cidr: function(a) {
                if (a.range)
-                       return sprintf("%s-%s", a.addr, a.addr2);
+                       return `${a.addr}-${a.addr2}`;
 
                if ((a.family == 4 && a.bits == 32) ||
                    (a.family == 6 && a.bits == 128))
                    return a.addr;
 
                if (a.bits >= 0)
-                       return sprintf("%s/%d", apply_mask(a.addr, a.bits), a.bits);
+                       return `${apply_mask(a.addr, a.bits)}/${a.bits}`;
 
-               return sprintf("%s/%s", a.addr, a.mask);
+               return `${a.addr}/${a.mask}`;
        },
 
        host: function(a, v6brackets) {
                return a.range
-                       ? sprintf("%s-%s", a.addr, a.addr2)
-                       : sprintf((a.family == 6 && v6brackets) ? "[%s]" : "%s", apply_mask(a.addr, a.bits));
+                       ? `${a.addr}-${a.addr2}`
+                       : (a.family == 6 && v6brackets)
+                               ? `[${apply_mask(a.addr, a.bits)}]` : apply_mask(a.addr, a.bits);
        },
 
        port: function(p) {
                if (p.min == p.max)
-                       return sprintf('%d', p.min);
+                       return `${p.min}`;
 
-               return sprintf('%d-%d', p.min, p.max);
+               return `${p.min}-${p.max}`;
        },
 
        set: function(v, force) {
@@ -1584,7 +1573,7 @@ return {
                v = filter(to_array(v), item => !seen[item]++);
 
                if (force || length(v) != 1)
-                       return sprintf('{ %s }', join(', ', map(v, this.quote)));
+                       return `{ ${join(', ', map(v, this.quote))} }`;
 
                return this.quote(v[0]);
        },
@@ -1651,7 +1640,7 @@ return {
        },
 
        is_loopback_dev: function(dev) {
-               let fd = fs.open(sprintf("/sys/class/net/%s/flags", dev), "r");
+               let fd = fs.open(`/sys/class/net/${dev}/flags`, "r");
 
                if (!fd)
                        return false;
@@ -1716,8 +1705,7 @@ return {
                let fd = fs.open(set.loadfile, "r");
 
                if (!fd) {
-                       warn(sprintf("Unable to load file '%s' for set '%s': %s\n",
-                                    set.loadfile, set.name, fs.error()));
+                       warn(`Unable to load file '${set.loadfile}' for set '${set.name}': ${fs.error()}\n`);
                        return;
                }
 
@@ -1732,8 +1720,7 @@ return {
                        let v = this.parse_ipsetentry(line, set);
 
                        if (!v) {
-                               this.warn("Skipping invalid entry '%s' in file '%s' for set '%s'",
-                                         line, set.loadfile, set.name);
+                               this.warn(`Skipping invalid entry '${line}' in file '${set.loadfile}' for set '${set.name}'`);
                                continue;
                        }
 
@@ -1790,10 +1777,9 @@ return {
                        return;
                }
 
-               helper.available = ((fs.stat("/sys/module/" + helper.module) || {}).type == "directory");
+               helper.available = (fs.stat(`/sys/module/${helper.module}`)?.type == "directory");
 
-               this.state.helpers = this.state.helpers || [];
-               push(this.state.helpers, helper);
+               push(this.state.helpers ||= [], helper);
        },
 
        parse_defaults: function(data) {
@@ -1885,7 +1871,7 @@ return {
                        return;
                }
                else if (zone.helper && !zone.helper.available) {
-                       this.warn_section(data, "uses unavailable ct helper '" + zone.helper.name + "', ignoring section");
+                       this.warn_section(data, `uses unavailable ct helper '${zone.helper.name}', ignoring section`);
                        return;
                }
 
@@ -1894,7 +1880,7 @@ return {
                        return;
                }
 
-               if (this.state.defaults && this.state.defaults.auto_helper === false)
+               if (this.state.defaults?.auto_helper === false)
                        zone.auto_helper = false;
 
                let match_devices = [];
@@ -1958,7 +1944,7 @@ return {
                ]);
 
                if (type(family) == "string") {
-                       this.warn_section(data, family + ", skipping");
+                       this.warn_section(data, `${family}, skipping`);
                        return;
                }
 
@@ -1969,7 +1955,7 @@ return {
                        let m = match(device.device, /^([^+]*)(\+)?$/);
 
                        if (!m) {
-                               this.warn_section(data, "skipping invalid wildcard pattern '" + device.device + '"');
+                               this.warn_section(data, `skipping invalid wildcard pattern '${device.device}'`);
                                continue;
                        }
 
@@ -2068,9 +2054,8 @@ return {
                                        continue;
 
                                for (let proto in helper.proto) {
-                                       this.state.rules = this.state.rules || [];
-                                       push(this.state.rules, {
-                                               chain: "helper_" + zone.name,
+                                       push(this.state.rules ||= [], {
+                                               chain: `helper_${zone.name}`,
                                                family: helper.family,
                                                name: helper.description || helper.name,
                                                proto: proto,
@@ -2083,8 +2068,7 @@ return {
                        }
                }
 
-               this.state.zones = this.state.zones || [];
-               push(this.state.zones, zone);
+               push(this.state.zones ||= [], zone);
        },
 
        parse_forwarding: function(data) {
@@ -2115,19 +2099,15 @@ return {
                                proto: { any: true }
                        };
 
-                       f.name = fwd.name || sprintf("Accept %s to %s forwarding",
-                               fwd.src.any ? "any" : fwd.src.zone.name,
-                               fwd.dest.any ? "any" : fwd.dest.zone.name);
-
-                       f.chain = fwd.src.any ? "forward" : sprintf("forward_%s", fwd.src.zone.name);
+                       f.name ||= `Accept ${fwd.src.any ? "any" : fwd.src.zone.name} to ${fwd.dest.any ? "any" : fwd.dest.zone.name} forwarding`;
+                       f.chain = fwd.src.any ? "forward" : `forward_${fwd.src.zone.name}`;
 
                        if (fwd.dest.any)
                                f.target = "accept";
                        else
-                               f.jump_chain = sprintf("accept_to_%s", fwd.dest.zone.name);
+                               f.jump_chain = `accept_to_${fwd.dest.zone.name}`;
 
-                       this.state.rules = this.state.rules || [];
-                       push(this.state.rules, f);
+                       push(this.state.rules ||= [], f);
                };
 
 
@@ -2140,23 +2120,19 @@ return {
 
                        if (f1 && f2 && f1 != f2) {
                                this.warn_section(data,
-                                       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)));
+                                       `references src ${fwd.src.zone.name} restricted to ${this.nfproto(f1, true)} and dest ${fwd.dest.zone.name} restricted to ${this.nfproto(f2, true)}, ignoring forwarding`);
 
                                return;
                        }
                        else if (f1) {
                                this.warn_section(data,
-                                       sprintf("inheriting %s restriction from src %s",
-                                               this.nfproto(f1, true), fwd.src.zone.name));
+                                       `inheriting ${this.nfproto(f1, true)} restriction from src ${fwd.src.zone.name}`);
 
                                family = f1;
                        }
                        else if (f2) {
                                this.warn_section(data,
-                                       sprintf("inheriting %s restriction from dest %s",
-                                               this.nfproto(f2, true), fwd.dest.zone.name));
+                                       `inheriting ${this.nfproto(f2, true)} restriction from dest ${fwd.dest.zone.name}`);
 
                                family = f2;
                        }
@@ -2231,7 +2207,7 @@ return {
                }
 
                if (rule.target in ["helper", "notrack"] && (!rule.src || !rule.src.zone)) {
-                       this.warn_section(data, "must specify a source zone for target '" + rule.target + "'");
+                       this.warn_section(data, `must specify a source zone for target '${rule.target}'`);
                        return;
                }
                else if (rule.target == "dscp" && !rule.set_dscp) {
@@ -2257,7 +2233,7 @@ return {
                        ipset = filter(this.state.ipsets, s => (s.name == rule.ipset.name))[0];
 
                        if (!ipset) {
-                               this.warn_section(data, "references unknown set '" + rule.ipset.name + "'");
+                               this.warn_section(data, `references unknown set '${rule.ipset.name}'`);
                                return;
                        }
 
@@ -2267,7 +2243,7 @@ return {
                        }
                }
 
-               let need_src_action_chain = (rule) => (rule.src && rule.src.zone && rule.src.zone.log && rule.target != "accept");
+               let need_src_action_chain = (rule) => (rule.src?.zone?.log && rule.target != "accept");
 
                let add_rule = (family, proto, saddrs, daddrs, sports, dports, icmptypes, icmpcodes, ipset, rule) => {
                        let r = {
@@ -2290,7 +2266,7 @@ return {
                                smacs_pos: map(filter_pos(rule.src_mac), m => m.mac),
                                smacs_neg: map(filter_neg(rule.src_mac), m => m.mac),
                                icmp_types: map(icmptypes, i => (family == 4 ? i.type : i.type6)),
-                               icmp_codes: map(icmpcodes, ic => sprintf('%d . %d', (family == 4) ? ic.type : ic.type6, (family == 4) ? ic.code_min : ic.code6_min))
+                               icmp_codes: map(icmpcodes, ic => `${(family == 4) ? ic.type : ic.type6} . ${(family == 4) ? ic.code_min : ic.code6_min}`)
                        };
 
                        if (!length(r.icmp_types))
@@ -2319,11 +2295,11 @@ return {
                        }
 
                        if (r.target == "notrack") {
-                               r.chain = sprintf("notrack_%s", r.src.zone.name);
+                               r.chain = `notrack_${r.src.zone.name}`;
                                r.src.zone.dflags.notrack = true;
                        }
                        else if (r.target == "helper") {
-                               r.chain = sprintf("helper_%s", r.src.zone.name);
+                               r.chain = `helper_${r.src.zone.name}`;
                                r.src.zone.dflags.helper = true;
                        }
                        else if (r.target == "mark" || r.target == "dscp") {
@@ -2353,7 +2329,7 @@ return {
 
                                if (r.src) {
                                        if (!r.src.any)
-                                               r.chain = sprintf("%s_%s", r.dest ? "forward" : "input", r.src.zone.name);
+                                               r.chain = `${r.dest ? "forward" : "input"}_${r.src.zone.name}`;
                                        else
                                                r.chain = r.dest ? "forward" : "input";
                                }
@@ -2366,11 +2342,11 @@ return {
                                }
 
                                if (r.dest && !r.dest.any) {
-                                       r.jump_chain = sprintf("%s_to_%s", r.target, r.dest.zone.name);
+                                       r.jump_chain = `${r.target}_to_${r.dest.zone.name}`;
                                        r.dest.zone.dflags[r.target] = true;
                                }
                                else if (need_src_action_chain(r)) {
-                                       r.jump_chain = sprintf("%s_from_%s", r.target, r.src.zone.name);
+                                       r.jump_chain = `${r.target}_from_${r.src.zone.name}`;
                                        r.src.zone.sflags[r.target] = true;
                                }
                                else if (r.target == "reject")
@@ -2380,8 +2356,7 @@ return {
                        if (r.device)
                                r[r.direction ? "oifnames" : "iifnames"] = [ r.device.device ];
 
-                       this.state.rules = this.state.rules || [];
-                       push(this.state.rules, r);
+                       push(this.state.rules ||= [], r);
                };
 
                for (let proto in rule.proto) {
@@ -2420,7 +2395,7 @@ return {
                        ]);
 
                        if (type(family) == "string") {
-                               this.warn_section(data, family + ", skipping");
+                               this.warn_section(data, `${family}, skipping`);
                                continue;
                        }
 
@@ -2548,7 +2523,7 @@ return {
                        ipset = filter(this.state.ipsets, s => (s.name == redir.ipset.name))[0];
 
                        if (!ipset) {
-                               this.warn_section(data, "references unknown set '" + redir.ipset.name + "'");
+                               this.warn_section(data, `references unknown set '${redir.ipset.name}'`);
                                return;
                        }
 
@@ -2595,12 +2570,12 @@ return {
                                return this.warn_section(data, "must not use non-contiguous masks in 'dest_ip'");
 
                        if (!redir.dest && redir.dest_ip && resolve_dest(redir))
-                               this.warn_section(data, "does not specify a destination, assuming '" + redir.dest.zone.name + "'");
+                               this.warn_section(data, `does not specify a destination, assuming '${redir.dest.zone.name}'`);
 
                        if (!redir.dest_port)
                                redir.dest_port = redir.src_dport;
 
-                       if (redir.reflection && redir.dest && redir.dest.zone && redir.src.zone.masq) {
+                       if (redir.reflection && redir.dest?.zone && redir.src.zone.masq) {
                                redir.dest.zone.dflags.accept = true;
                                redir.dest.zone.dflags.dnat = true;
                                redir.dest.zone.dflags.snat = true;
@@ -2667,7 +2642,7 @@ return {
 
                        switch (r.target) {
                        case "dnat":
-                               r.chain = sprintf("dstnat_%s", r.src.zone.name);
+                               r.chain = `dstnat_${r.src.zone.name}`;
                                r.src.zone.dflags.dnat = true;
 
                                if (!r.raddr)
@@ -2676,13 +2651,12 @@ return {
                                break;
 
                        case "snat":
-                               r.chain = sprintf("srcnat_%s", r.dest.zone.name);
+                               r.chain = `srcnat_${r.dest.zone.name}`;
                                r.dest.zone.dflags.snat = true;
                                break;
                        }
 
-                       this.state.redirects = this.state.redirects || [];
-                       push(this.state.redirects, r);
+                       push(this.state.redirects ||= [], r);
                };
 
                let to_hostaddr = (a) => {
@@ -2747,7 +2721,7 @@ return {
                        ]);
 
                        if (type(family) == "string") {
-                               this.warn_section(data, family + ", skipping");
+                               this.warn_section(data, `${family}, skipping`);
                                continue;
                        }
 
@@ -2755,7 +2729,7 @@ return {
                        if (redir.target == "dnat" && redir.reflection &&
                            (length(rip[0]) || length(rip[1])) && redir.src?.zone && redir.dest?.zone) {
                                let refredir = {
-                                       name: redir.name + " (reflection)",
+                                       name: `${redir.name} (reflection)`,
 
                                        helper: redir.helper,
 
@@ -2780,8 +2754,7 @@ return {
                                for (let rzone in rzones) {
                                        if (!is_family(rzone, family)) {
                                                this.warn_section(data,
-                                                       sprintf("is restricted to IPv%d but referenced reflection zone is IPv%d only, skipping",
-                                                               family, rzone.family));
+                                                       `is restricted to IPv${family} but referenced reflection zone is IPv${rzone.family} only, skipping`);
                                                continue;
                                        }
 
@@ -2813,7 +2786,7 @@ return {
                                                        }
 
                                                        if (!snat_addr) {
-                                                               this.warn_section(data, (redir.reflection_src || "external") + " rewrite IP cannot be determined, disabling reflection");
+                                                               this.warn_section(data, `${redir.reflection_src || "external"} rewrite IP cannot be determined, disabling reflection`);
                                                        }
                                                        else if (!length(iaddrs[i])) {
                                                                this.warn_section(data, "internal address range cannot be determined, disabling reflection");
@@ -2978,11 +2951,10 @@ return {
                                raddr: raddrs ? raddrs[0] : null,
                                rport: rport,
 
-                               chain: (snat.src && snat.src.zone) ? sprintf("srcnat_%s", snat.src.zone.name) : "srcnat"
+                               chain: snat.src?.zone ? `srcnat_${snat.src.zone.name}` : "srcnat"
                        };
 
-                       this.state.redirects = this.state.redirects || [];
-                       push(this.state.redirects, n);
+                       push(this.state.redirects ||= [], n);
                };
 
                for (let proto in snat.proto) {
@@ -3013,7 +2985,7 @@ return {
                        ]);
 
                        if (type(family) == "string") {
-                               this.warn_section(data, family + ", skipping");
+                               this.warn_section(data, `${family}, skipping`);
                                continue;
                        }
 
@@ -3136,17 +3108,15 @@ return {
                        interval: interval
                };
 
-               let self = this;
                s.entries = filter(map(ipset.entry, (e) => {
-                       let v = self.parse_ipsetentry(e, s);
+                       let v = this.parse_ipsetentry(e, s);
 
                        if (!v)
-                               self.warn_section(data, "ignoring invalid ipset entry '" + e + "'");
+                               this.warn_section(data, `ignoring invalid ipset entry '${e}'`);
 
                        return v;
                }), (e) => (e != null));
 
-               this.state.ipsets = this.state.ipsets || [];
-               push(this.state.ipsets, s);
+               push(this.state.ipsets ||= [], s);
        }
 };
index 913bfb3f308b57a1e862a45151b00adc5271dac2..65cddd174292f5e34d721f6dbcc306bf5695025f 100644 (file)
@@ -300,7 +300,14 @@ table inet fw4 {
 [call] fs.opendir path </sys/class/net/br-lan>
 [call] fs.opendir path </sys/class/net/eth0>
 [call] fs.opendir path </sys/class/net/eth1>
-[call] system command </usr/sbin/nft -c 'add table inet fw4-hw-offload-test; add flowtable inet fw4-hw-offload-test ft { hook ingress priority 0; devices = { "eth0", "eth1" }; flags offload; }' 2>/dev/null> timeout <null>
+[call] system command </usr/sbin/nft -c '
+               add table inet fw4-hw-offload-test;
+               add flowtable inet fw4-hw-offload-test ft {
+                       hook ingress priority 0;
+                       devices = { "eth0", "eth1" };
+                       flags offload;
+               }
+       ' 2>/dev/null> timeout <null>
 [call] fs.popen cmdline </usr/sbin/nft --terse --json list flowtables inet> mode <r>
 [call] fs.open path </sys/class/net/br-lan/flags> mode <r>
 [call] fs.open path </sys/class/net/br-lan/flags> mode <r>