pbr: update to 1.2.2-r8
authorStan Grishin <redacted>
Mon, 2 Mar 2026 20:29:26 +0000 (20:29 +0000)
committerStan Grishin <redacted>
Wed, 4 Mar 2026 01:41:55 +0000 (17:41 -0800)
* bugfix: don't mask RFC1918 in the support output
* bugfix: proper processing of downed interfaces

Thanks to everyone who reported/tested and @egc112 for collecting feedback.

Signed-off-by: Stan Grishin <redacted>
net/pbr/Makefile
net/pbr/files/etc/init.d/pbr
net/pbr/tests/07_support/01_print_config_masked [new file with mode: 0644]

index 53ab64311dc8fe94d1af3e9c3576ff3897d36d95..f9668a5ce1df7055961bd49c8ac135e46076ebf8 100644 (file)
@@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=pbr
 PKG_VERSION:=1.2.2
-PKG_RELEASE:=6
+PKG_RELEASE:=8
 PKG_LICENSE:=AGPL-3.0-or-later
 PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca>
 
index f9a9c4230123f324ec01bc8414aeed877f1ecf7d..d110db0729b14a4a54e822dbaef559885c611b10 100755 (executable)
@@ -2218,12 +2218,6 @@ interface_routing() {
                                        try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
                                fi
 
-                               if ! nft_file 'match' 'temp' "${nftPrefix}_mark_${mark}"; then
-                                       nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}"
-                                       nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}"
-                                       nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return"
-                               fi
-
                                dscp="$(uci_get "$packageName" 'config' "${iface}_dscp" '0')"
                                if [ "$dscp" -ge '1' ] && [ "$dscp" -le '63' ]; then
                                        nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}"
@@ -2231,6 +2225,12 @@ interface_routing() {
                                if [ "$iface" = "$icmp_interface" ]; then
                                        nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}"
                                fi
+                       elif [ -n "$strict_enforcement" ] && ! { is_split_uplink && [ "$iface" = "$uplink_interface6" ]; }; then
+                               ipv4_error=0
+                               ip -4 rule flush table "$tid" >/dev/null 2>&1
+                               ip -4 route flush table "$tid" >/dev/null 2>&1
+                               try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
+                               try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
                        fi
 
                        if [ -n "$ipv6_enabled" ] && [ -n "$dev6" ]; then
@@ -2256,12 +2256,6 @@ interface_routing() {
                                        try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
                                fi
 
-                               if ! nft_file 'match' 'temp' "${nftPrefix}_mark_${mark}"; then
-                                       nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}"
-                                       nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}"
-                                       nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return"
-                               fi
-
                                dscp="$(uci_get "$packageName" 'config' "${iface}_dscp" '0')"
                                if [ "$dscp" -ge '1' ] && [ "$dscp" -le '63' ]; then
                                        nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}"
@@ -2269,6 +2263,19 @@ interface_routing() {
                                if [ "$iface" = "$icmp_interface" ]; then
                                        nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}"
                                fi
+                       elif [ -n "$ipv6_enabled" ] && [ -n "$strict_enforcement" ] && ! { is_split_uplink && [ "$iface" = "$uplink_interface4" ]; }; then
+                               ipv6_error=0
+                               ip -6 rule flush table "$tid" >/dev/null 2>&1
+                               ip -6 route flush table "$tid" >/dev/null 2>&1
+                               try ip -6 route replace unreachable default table "$tid" || ipv6_error=1
+                               try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
+                       fi
+
+                       # Always create the nft mark chain so policies can reference it
+                       if ! nft_file 'match' 'temp' "${nftPrefix}_mark_${mark}"; then
+                               nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}"
+                               nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}"
+                               nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return"
                        fi
 
                        if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
@@ -2318,6 +2325,12 @@ interface_routing() {
                                        fi
                                        try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
                                fi
+                       elif [ -n "$strict_enforcement" ] && ! { is_split_uplink && [ "$iface" = "$uplink_interface6" ]; }; then
+                               ipv4_error=0
+                               ip -4 rule flush table "$tid" >/dev/null 2>&1
+                               ip -4 route flush table "$tid" >/dev/null 2>&1
+                               try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
+                               try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
                        fi
 
                        if [ -n "$ipv6_enabled" ] && [ -n "$dev6" ]; then
@@ -2341,6 +2354,12 @@ interface_routing() {
                                        fi
                                        try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
                                fi
+                       elif [ -n "$ipv6_enabled" ] && [ -n "$strict_enforcement" ] && ! { is_split_uplink && [ "$iface" = "$uplink_interface4" ]; }; then
+                               ipv6_error=0
+                               ip -6 rule flush table "$tid" >/dev/null 2>&1
+                               ip -6 route flush table "$tid" >/dev/null 2>&1
+                               try ip -6 route replace unreachable default table "$tid" || ipv6_error=1
+                               try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
                        fi
 
                        if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
@@ -3140,16 +3159,25 @@ print_config_masked() {
 
                {
                        line = $0
+                       result = ""
 
                        # Mask digits inside IPv4-looking tokens, keep dots
+                       # RFC1918/loopback addresses are not sensitive — skip masking them
                        while (match(line, /([0-9]{1,3}\.){3}[0-9]{1,3}/)) {
                                ip = substr(line, RSTART, RLENGTH)
-                               masked = ip
-                               gsub(/[0-9]/, "*", masked)
-                               line = substr(line, 1, RSTART-1) masked substr(line, RSTART+RLENGTH)
+                               result = result substr(line, 1, RSTART-1)
+                               line = substr(line, RSTART+RLENGTH)
+
+                               if (ip ~ /^(10\.|127\.|192\.168\.)/ || ip ~ /^172\.(1[6-9]|2[0-9]|3[01])\./) {
+                                       result = result ip
+                               } else {
+                                       masked = ip
+                                       gsub(/[0-9]/, "*", masked)
+                                       result = result masked
+                               }
                        }
 
-                       print line
+                       print result line
                }
        ' \
        | sed -E 's/([a-fA-F0-9:]{2,}:){1,7}[a-fA-F0-9]{2,}/***/g'
diff --git a/net/pbr/tests/07_support/01_print_config_masked b/net/pbr/tests/07_support/01_print_config_masked
new file mode 100644 (file)
index 0000000..bf9f952
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/bash
+# Test: print_config_masked - IP masking logic
+. "$(dirname "$0")/../lib/setup.sh"
+
+oneTimeTearDown() { rm -rf "${MOCK_ROOT:-}"; }
+
+# Apply the IP-masking awk pass from print_config_masked to a single input line.
+# Mirrors the second awk block in print_config_masked so we can test it in isolation
+# without needing to write files to /etc/config/.
+_mask_ips() {
+       printf '%s\n' "$1" | awk '
+               /^[ \t]*(option|list)[ \t]+allowed_ips[ \t]+/ { print; next }
+               {
+                       line = $0; result = ""
+                       while (match(line, /([0-9]{1,3}\.){3}[0-9]{1,3}/)) {
+                               ip = substr(line, RSTART, RLENGTH)
+                               result = result substr(line, 1, RSTART-1)
+                               line = substr(line, RSTART+RLENGTH)
+                               if (ip ~ /^(10\.|127\.|192\.168\.)/ || ip ~ /^172\.(1[6-9]|2[0-9]|3[01])\./)
+                                       result = result ip
+                               else { masked = ip; gsub(/[0-9]/, "*", masked); result = result masked }
+                       }
+                       print result line
+               }
+       '
+}
+
+testPublicIPIsMasked() {
+       assertEquals "Public IP masked" \
+               "       option gateway '*.*.*.*'" \
+               "$(_mask_ips "  option gateway '1.2.3.4'")"
+}
+
+testRFC1918_10_preserved() {
+       assertEquals "10.x not masked" \
+               "       option gateway '10.0.0.1'" \
+               "$(_mask_ips "  option gateway '10.0.0.1'")"
+}
+
+testRFC1918_192_168_preserved() {
+       assertEquals "192.168.x not masked" \
+               "       option gateway '192.168.1.254'" \
+               "$(_mask_ips "  option gateway '192.168.1.254'")"
+}
+
+testRFC1918_172_16_preserved() {
+       assertEquals "172.16.x not masked" \
+               "       option gateway '172.16.0.1'" \
+               "$(_mask_ips "  option gateway '172.16.0.1'")"
+}
+
+testRFC1918_172_31_preserved() {
+       assertEquals "172.31.x not masked" \
+               "       option gateway '172.31.255.254'" \
+               "$(_mask_ips "  option gateway '172.31.255.254'")"
+}
+
+testBorderBelow_172_16_masked() {
+       assertEquals "172.15.x is not RFC1918 - masked" \
+               "       option gateway '***.**.*.*'" \
+               "$(_mask_ips "  option gateway '172.15.0.1'")"
+}
+
+testBorderAbove_172_31_masked() {
+       assertEquals "172.32.x is not RFC1918 - masked" \
+               "       option gateway '***.**.*.*'" \
+               "$(_mask_ips "  option gateway '172.32.0.1'")"
+}
+
+testLoopbackPreserved() {
+       assertEquals "127.x loopback not masked" \
+               "       option dns '127.0.0.1'" \
+               "$(_mask_ips "  option dns '127.0.0.1'")"
+}
+
+testAllowedIPsLineNotMasked() {
+       assertEquals "allowed_ips line bypasses IP masking" \
+               "       option allowed_ips '8.8.8.8/32'" \
+               "$(_mask_ips "  option allowed_ips '8.8.8.8/32'")"
+}
+
+testMixedLinePrivateAndPublic() {
+       assertEquals "Private preserved, public masked on same line" \
+               "       option foo '192.168.1.1 *.*.*.*'" \
+               "$(_mask_ips "  option foo '192.168.1.1 8.8.8.8'")"
+}
+
+. shunit2
git clone https://git.99rst.org/PROJECT