fw4: fix emitting device jump rules for family restricted zones
[project/firewall4.git] / root / usr / share / ucode / fw4.uc
index 8e38a5cc15fe26ca35ba24d93010eaaa22958c39..f7a88a6469936a354851ceec10e1812528949513 100644 (file)
@@ -1432,7 +1432,7 @@ return {
                        case 'ipv4_addr':
                                ip = filter(this.parse_subnet(values[i]), a => (a.family == 4));
 
-                               switch (length(ip)) {
+                               switch (length(ip) ?? 0) {
                                case 0: return null;
                                case 1: break;
                                default: this.warn("Set entry '%s' resolves to multiple addresses, using first one", values[i]);
@@ -1616,6 +1616,22 @@ return {
                }
        },
 
+       l4proto: function(family, proto) {
+               switch (proto.name) {
+               case 'icmp':
+                       switch (family ?? 0) {
+                       case 0:
+                               return this.set(['icmp', 'ipv6-icmp']);
+
+                       case 6:
+                               return 'ipv6-icmp';
+                       }
+
+               default:
+                       return proto.name;
+               }
+       },
+
        datetime: function(stamp) {
                return sprintf('"%04d-%02d-%02d %02d:%02d:%02d"',
                               stamp.year, stamp.month, stamp.day,
@@ -2007,11 +2023,11 @@ return {
 
                                // we need to emit one or two AF specific rules
                                else {
-                                       if (family_is_ipv4(zone) && length(match_subnets[0]))
+                                       if (!family || family == 4)
                                                for (let subnets in subnets_group_by_masking(match_subnets[0]))
                                                        add_rule(4, devgroup, subnets, zone);
 
-                                       if (family_is_ipv6(zone) && length(match_subnets[1]))
+                                       if (!family || family == 6)
                                                for (let subnets in subnets_group_by_masking(match_subnets[1]))
                                                        add_rule(6, devgroup, subnets, zone);
                                }
@@ -2355,7 +2371,7 @@ return {
                                }
                                else if (need_src_action_chain(r)) {
                                        r.jump_chain = sprintf("%s_from_%s", r.target, r.src.zone.name);
-                                       r.src.zone.dflags[r.target] = true;
+                                       r.src.zone.sflags[r.target] = true;
                                }
                                else if (r.target == "reject")
                                        r.jump_chain = "handle_reject";
@@ -2830,8 +2846,12 @@ return {
                        if (length(rip[0]) > 1 || length(rip[1]) > 1)
                                this.warn_section(data, "specifies multiple rewrite addresses, using only first one");
 
+                       let has_ip4_addr = length(sip[0]) || length(dip[0]) || length(rip[0]),
+                           has_ip6_addr = length(sip[1]) || length(dip[1]) || length(rip[1]),
+                           has_any_addr = has_ip4_addr || has_ip6_addr;
+
                        /* check if there's no AF specific bits, in this case we can do an AF agnostic rule */
-                       if (!family && !length(sip[0]) && !length(sip[1]) && !length(dip[0]) && !length(dip[1]) && !length(rip[0]) && !length(rip[1])) {
+                       if (!family && !has_any_addr) {
                                /* for backwards compatibility, treat unspecified family as IPv4 unless user explicitly requested any (0) */
                                if (family == null)
                                        family = 4;
@@ -2841,13 +2861,13 @@ return {
 
                        /* we need to emit one or two AF specific rules */
                        else {
-                               if ((!family || family == 4) && (length(sip[0]) || length(dip[0]) || length(rip[0]))) {
+                               if ((!family || family == 4) && (!has_any_addr || has_ip4_addr)) {
                                        for (let saddrs in subnets_group_by_masking(sip[0]))
                                                for (let daddrs in subnets_group_by_masking(dip[0]))
                                                        add_rule(4, proto, saddrs, daddrs, rip[0], sport, dport, rport, ipset, redir);
                                }
 
-                               if ((!family || family == 6) && (length(sip[1]) || length(dip[1]) || length(rip[1]))) {
+                               if ((!family || family == 6) && (!has_any_addr || has_ip6_addr)) {
                                        for (let saddrs in subnets_group_by_masking(sip[1]))
                                                for (let daddrs in subnets_group_by_masking(dip[1]))
                                                        add_rule(6, proto, saddrs, daddrs, rip[1], sport, dport, rport, ipset, redir);
@@ -2861,7 +2881,7 @@ return {
                        enabled: [ "bool", "1" ],
 
                        name: [ "string", this.section_id(data[".name"]) ],
-                       family: [ "family", "4" ],
+                       family: [ "family" ],
 
                        src: [ "zone_ref" ],
                        device: [ "string" ],
@@ -2936,9 +2956,6 @@ return {
                        return;
                }
 
-               if (snat.src && snat.src.zone)
-                       snat.src.zone.dflags.snat = true;
-
                let add_rule = (family, proto, saddrs, daddrs, raddrs, sport, dport, rport, snat) => {
                        let n = {
                                ...snat,
@@ -2988,19 +3005,21 @@ return {
                        if (length(rip[0]) > 1 || length(rip[1]) > 1)
                                this.warn_section(data, "specifies multiple rewrite addresses, using only first one");
 
-                       /* inherit family restrictions from related zones */
-                       if (family === 0 || family === null) {
-                               let f = (rule.src && rule.src.zone) ? rule.src.zone.family : 0;
-
-                               if (f) {
-                                       this.warn_section(r,
-                                               sprintf("inheriting %s restriction from src %s",
-                                                       this.nfproto(f1, true), rule.src.zone.name));
+                       family = infer_family(family, [
+                               sip, "source IP",
+                               dip, "destination IP",
+                               rip, "rewrite IP",
+                               snat.src?.zone, "source zone"
+                       ]);
 
-                                       family = f;
-                               }
+                       if (type(family) == "string") {
+                               this.warn_section(data, family + ", skipping");
+                               continue;
                        }
 
+                       if (snat.src?.zone)
+                               snat.src.zone.dflags.snat = true;
+
                        /* if no family was configured, infer target family from IP addresses */
                        if (family === null) {
                                if ((length(sip[0]) || length(dip[0]) || length(rip[0])) && !length(sip[1]) && !length(dip[1]) && !length(rip[1]))
@@ -3008,7 +3027,7 @@ return {
                                else if ((length(sip[1]) || length(dip[1]) || length(rip[1])) && !length(sip[0]) && !length(dip[0]) && !length(rip[0]))
                                        family = 6;
                                else
-                                       family = 0;
+                                       family = 4; /* default to IPv4 only for backwards compatibility, unless an explict family any was configured */
                        }
 
                        /* check if there's no AF specific bits, in this case we can do an AF agnostic rule */