Skip to content

[25.12] pbr: update to 1.2.2-r6#28642

Merged
stangri merged 1 commit intoopenwrt:openwrt-25.12from
stangri:openwrt-25.12-pbr
Feb 27, 2026
Merged

[25.12] pbr: update to 1.2.2-r6#28642
stangri merged 1 commit intoopenwrt:openwrt-25.12from
stangri:openwrt-25.12-pbr

Conversation

@stangri
Copy link
Copy Markdown
Member

@stangri stangri commented Feb 26, 2026

Maintainer: me
Compile tested: x86_64, Dell EMC Edge620, OpenWrt 25.12.0-rc4
Run tested: x86_64, Dell EMC Edge620, OpenWrt 25.12.0-rc4

Description:
Update pbr from 1.2.1-r87 to 1.2.2-r6. This release adds mwan4 (Multi-WAN) integration, a diagnostic
support command, IPv6 lease-to-nftset handling,
improved split-uplink detection, stricter UCI
validation, shell variable quoting fixes across 30+ locations, and a comprehensive 126-case test suite with a full mock OpenWrt sysroot.


  • 31 files changed, +1,745 / -227 lines (net +1,518)
  • 1 commit: 61c8923pbr: update to 1.2.2-r6

  • Version bumped from 1.2.1-r87 to 1.2.2-r6
  • URL updated from github.com/stangri/pbr/ to github.com/mossdef-org/pbr/
  • No dependency changes

Three options changed from scalar to list type:

Option Old Type New Type
ignored_interface option list
lan_device option list
resolver_instance option list

Options reordered: scalars first, then lists,
matching UCI convention. No values changed.


The init script (/etc/init.d/pbr) received
significant additions and fixes across ~660 lines
(+443/-218).

Bumped from 24 to 25.

mwan4 (Multi-WAN) Integration (8 new functions):

  • mwan4_is_installed() — Detect mwan4 package
  • mwan4_is_running() — Check service status
  • mwan4_get_iface_list() — Get enabled interfaces
  • mwan4_get_strategy_list() — Get strategies
  • mwan4_get_iface_mark_chain() — Get nft mark chain for interface
  • mwan4_get_iface_nft_sets() — Get nftset names
  • mwan4_get_strategy_chain() — Get strategy chain
  • mwan4_get_mmx_mask() — Get Multi-WAN mark mask

Enables PBR to coordinate with mwan4 for combined
policy routing and multi-WAN failover.

Diagnostic support Command:

  • New support() function generates masked diagnostic output for troubleshooting
  • print_config_masked() redacts sensitive data (passwords, keys, tokens, PSKs, endpoints) while preserving IP addresses and structure

IPv6 Lease Handling:

  • New ipv6_leases_to_nftset() parses DHCPv6 leases from /tmp/hosts/odhcpd
  • Complements existing ipv4_leases_to_nftset()

Split Uplink Detection (3 new functions):

  • is_uplink4() — Check IPv4 uplink interface
  • is_uplink6() — Check IPv6 uplink interface
  • is_uplink() — Unified check (v4 or v6)
  • New ipv6_default_lookup variable for split IPv4/IPv6 uplink routing table assignment

ubus Integration:

  • New ubus_get_interface() queries PBR gateway data via ubus

Shell Variable Quoting (30+ locations):
Systematic conversion of bare variable references
to brace-quoted syntax throughout the script:

  • $2 to ${2} in string replacements
  • $_ret to ${_ret} in conditional expansions
  • $_mark to ${_mark} in nft rule generation
  • $nftset6 to ${nftset6} in dnsmasq rules
  • $nft_set_timeout to ${nft_set_timeout}
  • $xrayIfacePrefix to ${xrayIfacePrefix}
  • And many more across rule generation, output strings, and conditional expressions

Specific Fixes:

  • pbr_get_gateway6(): Changed is_wan to is_uplink4 for correct IPv4 uplink detection

  • is_netifd_interface(): Now checks both ip4table and ip6table (was IPv4 only)

  • load_environment(): Fixed inverted flag check (-z changed to -n for loadEnvironmentFlag)

  • Dnsmasq instance detection: Fixed UCI section lookup with proper variable handling

  • Help text URL: #WarningMessagesDetails changed to #warning-messages-details (kebab-case)

  • uplink_ip_rules_priority: Changed from uinteger to range(99,32765) to enforce valid Linux routing policy DB bounds

Three options now use config_get_list instead of config_get to support multiple values:

  • ignored_interface
  • lan_device
  • resolver_instance

Rule Cleanup Refactored:

  • Replaced complex awk-based rule parsing with priority-range approach
  • Calculates prio_min = priority - max_ifaces and prio_max = priority, iterates and deletes rules within range
  • Skips netifd-managed fwmark rules
  • Added legacy rule cleanup for suppress_prefixlength entries

Firewall Sync:

  • Added fw4 -q reload after successful nft file installation to ensure fw4 state synchronizes with PBR's nftables changes

Resolver Instance Handling:

  • Added robustness checks in _dnsmasq_instance_config(): file existence check and instance validity check

  • Better section name resolution with UCI query

  • Added missing setup parameter in resolver instance setup calls

  • uci_get_device() — Replaced with inline call

  • uci_get_protocol() — Replaced with inline call


In 70-pbr, fixed shell variable quoting:

${DEVICE:+ ($DEVICE)}
${DEVICE:+ (${DEVICE})}

In pbr.user.netflix, fixed two instances of
bare variable expansion in parameter substitution:

params="${params:+$params, }${p}"
params="${params:+${params}, }${p}"

A full test suite is added in net/pbr/tests/
(21 new files, ~1,300 lines) using the shunit2
framework with a complete mock OpenWrt sysroot.

Runner (run_tests.sh):

  • Discovers test files via glob pattern
  • Supports pattern-based filtering via CLI arg
  • Executes each test in isolated bash subprocess
  • Captures output, reports pass/fail with color
  • Accumulates stats and lists failures at end
  • Requires shunit2 package

Setup (lib/setup.sh):

  • Creates temporary mock sysroot ($MOCK_ROOT)
  • Sets IPKG_INSTROOT for OpenWrt path resolution
  • Installs mock libraries, configs, and binaries
  • Stubs rc.common, procd, logger, resolveip, jsonfilter, pidof, sync
  • Sources pbr init script with readonly keyword stripped (allows test overrides)
  • Redirects all file paths to temp directories

UCI Config API (lib/mocks/functions.sh):

  • Full config_load parser for UCI syntax
  • config_get, config_get_bool, config_get_list, config_foreach, config_list_foreach
  • uci_set, uci_get, uci_add_list, uci_remove, uci_remove_list, uci_commit
  • Stores state in associative arrays

Network API (lib/mocks/network.sh):

  • network_get_device, network_get_physdev, network_get_gateway, network_get_gateway6, network_get_protocol, network_get_ipaddr, network_get_ip6addr, network_get_dnsserver, network_flush_cache
  • Backed by MOCK_NET_* variables that tests override to simulate different network states
  • Pre-configured: wan (eth0/dhcp/192.168.1.1), wan6 (eth0/dhcpv6/fd00::1), wg0 (wireguard), lan (br-lan/static), loopback (lo/static)

JSON Shell (lib/mocks/jshn.sh):

  • Minimal JSON-in-shell implementation
  • json_init, json_add_string/boolean/int, json_add_object/array, json_close_*, json_select, json_get_var, json_get_keys, json_dump, json_load
  • Associative array backend with path tracking

Mock Binaries:

  • nft — Returns fw4 table structure with standard chains (input, forward, output, dstnat, mangle_*); passes syntax checks
  • dnsmasq — Reports version with nftset support
  • readlink — Returns /usr/libexec/ip-full for */sbin/ip (simulates ip-full installed)

Mock UCI Configs:

  • pbr — Full config: enabled, policies (vpn_all, vpn_gaming, disabled_policy), dns_policy, nft settings, interface lists
  • network — Interfaces: loopback, lan, wan, wan6, wg0 (wireguard)
  • firewall — Zones: lan (accept all), wan (reject input/forward)
  • dhcp — DHCP server stub
  • system — Hostname and timezone

01_validation — Input Validation (67 cases):

01_ipv4_validation (13 cases):

  • Valid IPs: 192.168.1.1, 10.0.0.1, 172.16.0.1
  • Valid CIDR: /8, /24, /32, /0
  • Invalid: octets >255, wrong octet count, CIDR >32, IPv6 addresses, domain names

02_ipv6_validation (21 cases):

  • Valid: ::1, fe80::1, 2001:db8::1, fd00::1, full addresses, ::/0
  • Invalid: IPv4 addrs, plain strings, MACs
  • Scope detection: global (2001:db8::/32), link-local (fe80::/10), ULA (fd00::/8)

03_domain_validation (8 cases):

  • Host: single labels (router, host123)
  • Hostname: multi-label (example.com, sub.example.com, deep.sub.example.com)
  • Domain: FQDN or single-label
  • Invalid: IPs, empty strings, MAC notation

04_misc_validators (25 cases):

  • MAC addresses (colon notation, case variants)
  • Integer validation (positive, not negative)
  • Negation marker (! prefix detection)
  • URL schemes (http, https, ftp, file://)
  • Version comparison (is_greater, is_greater_or_equal)
  • Family mismatch (IPv4/IPv6 mixing detection)

02_string_utils — String Functions (8 cases):

01_str_functions:

  • str_contains — Substring search
  • str_contains_word — Word-boundary search
  • str_to_lower / str_to_upper — Case convert
  • str_first_word — Token extraction
  • str_replace — String substitution
  • str_extras_to_underscore — Normalize delims
  • str_extras_to_space — Expand delimiters

03_wan_detection — Interface Detection
(13 cases):

01_wan_types:

  • is_wan4 — Detects wan/wanX, not wan6/lan/wg0
  • is_wan6 — Detects wan6/mwan6 (IPv6-aware)
  • is_wan6_disabled — Disabled when ipv6 off
  • is_wan — Unified v4+v6 detection
  • is_uplink4 / is_uplink6 — Uplink detection
  • is_tor — Case-insensitive tor detection
  • is_ignore_target — Ignore target detection
  • is_list — Comma/space list vs single value

04_config — Configuration Loading (13 cases):

01_load_config (7 cases):

  • Default values from UCI config
  • Hex value parsing (fw_mask, uplink_mark)
  • XOR calculation (fw_maskXor = ~fw_mask)
  • List parsing (ignored_interface, resolver)
  • nft parameters (auto-merge, flags)
  • Config-loaded flag tracking

02_disabled_service (2 cases):

  • Disabled: enabled option becomes unset
  • Enabled: enabled option is set

03_config_ipv6 (4 cases):

  • IPv6 enabled: config and uplink interface set
  • IPv6 disabled: both unset
  • Reload behavior verification

05_nft — nftables Integration (14 cases):

01_nft_file_operations (8 cases):

  • File creation with nft shebang
  • Chain creation (dstnat, forward, output, prerouting)
  • Jump rules and guard rules
  • File append, content search, file deletion

02_nft_check_element (6 cases):

  • fw4 table existence
  • Chain existence (input, forward, output, dstnat, mangle_*)
  • Non-existent chain detection

06_network — Network Functions (11 cases):

01_gateway_discovery (4 cases):

  • IPv4 gateway from mock (192.168.1.1)
  • IPv4 gateway fallback (ip addr parsing)
  • IPv6 gateway from mock (fd00::1)
  • Interface finding for uplinks

02_supported_interfaces (7 cases):

  • Ignored: loopback in ignored list
  • LAN detection vs non-LAN
  • Uplink support (wan is supported)
  • LAN/loopback not supported
  • Wireguard supported (wg0)
  • Explicit custom interface support

cd net/pbr/tests && sh run_tests.sh

Requires: bash, shunit2.
Optional filter: sh run_tests.sh 01_validation

(cherry picked from commit cf1d277)

Update pbr from 1.2.1-r87 to 1.2.2-r6. This release
adds mwan4 (Multi-WAN) integration, a diagnostic
`support` command, IPv6 lease-to-nftset handling,
improved split-uplink detection, stricter UCI
validation, shell variable quoting fixes across 30+
locations, and a comprehensive 126-case test suite
with a full mock OpenWrt sysroot.

Signed-off-by: Stan Grishin <stangri@melmac.ca>

---

- **31 files changed**, +1,745 / -227 lines
  (net +1,518)
- **1 commit**: `61c8923` —
  `pbr: update to 1.2.2-r6`

---

- Version bumped from `1.2.1-r87` to `1.2.2-r6`
- URL updated from `github.com/stangri/pbr/` to
  `github.com/mossdef-org/pbr/`
- No dependency changes

---

Three options changed from scalar to list type:

| Option              | Old Type | New Type |
|---------------------|----------|----------|
| `ignored_interface` | `option` | `list`   |
| `lan_device`        | `option` | `list`   |
| `resolver_instance` | `option` | `list`   |

Options reordered: scalars first, then lists,
matching UCI convention. No values changed.

---

The init script (`/etc/init.d/pbr`) received
significant additions and fixes across ~660 lines
(+443/-218).

Bumped from `24` to `25`.

**mwan4 (Multi-WAN) Integration (8 new functions):**
- `mwan4_is_installed()` — Detect mwan4 package
- `mwan4_is_running()` — Check service status
- `mwan4_get_iface_list()` — Get enabled interfaces
- `mwan4_get_strategy_list()` — Get strategies
- `mwan4_get_iface_mark_chain()` — Get nft mark
  chain for interface
- `mwan4_get_iface_nft_sets()` — Get nftset names
- `mwan4_get_strategy_chain()` — Get strategy chain
- `mwan4_get_mmx_mask()` — Get Multi-WAN mark mask

Enables PBR to coordinate with mwan4 for combined
policy routing and multi-WAN failover.

**Diagnostic `support` Command:**
- New `support()` function generates masked
  diagnostic output for troubleshooting
- `print_config_masked()` redacts sensitive data
  (passwords, keys, tokens, PSKs, endpoints)
  while preserving IP addresses and structure

**IPv6 Lease Handling:**
- New `ipv6_leases_to_nftset()` parses DHCPv6
  leases from `/tmp/hosts/odhcpd`
- Complements existing `ipv4_leases_to_nftset()`

**Split Uplink Detection (3 new functions):**
- `is_uplink4()` — Check IPv4 uplink interface
- `is_uplink6()` — Check IPv6 uplink interface
- `is_uplink()` — Unified check (v4 or v6)
- New `ipv6_default_lookup` variable for split
  IPv4/IPv6 uplink routing table assignment

**ubus Integration:**
- New `ubus_get_interface()` queries PBR gateway
  data via ubus

**Shell Variable Quoting (30+ locations):**
Systematic conversion of bare variable references
to brace-quoted syntax throughout the script:
- `$2` to `${2}` in string replacements
- `$_ret` to `${_ret}` in conditional expansions
- `$_mark` to `${_mark}` in nft rule generation
- `$nftset6` to `${nftset6}` in dnsmasq rules
- `$nft_set_timeout` to `${nft_set_timeout}`
- `$xrayIfacePrefix` to `${xrayIfacePrefix}`
- And many more across rule generation, output
  strings, and conditional expressions

**Specific Fixes:**
- `pbr_get_gateway6()`: Changed `is_wan` to
  `is_uplink4` for correct IPv4 uplink detection
- `is_netifd_interface()`: Now checks both
  `ip4table` and `ip6table` (was IPv4 only)
- `load_environment()`: Fixed inverted flag check
  (`-z` changed to `-n` for `loadEnvironmentFlag`)
- Dnsmasq instance detection: Fixed UCI section
  lookup with proper variable handling
- Help text URL: `#WarningMessagesDetails` changed
  to `#warning-messages-details` (kebab-case)

- `uplink_ip_rules_priority`: Changed from
  `uinteger` to `range(99,32765)` to enforce
  valid Linux routing policy DB bounds

Three options now use `config_get_list` instead of
`config_get` to support multiple values:
- `ignored_interface`
- `lan_device`
- `resolver_instance`

**Rule Cleanup Refactored:**
- Replaced complex awk-based rule parsing with
  priority-range approach
- Calculates `prio_min = priority - max_ifaces`
  and `prio_max = priority`, iterates and deletes
  rules within range
- Skips netifd-managed fwmark rules
- Added legacy rule cleanup for
  `suppress_prefixlength` entries

**Firewall Sync:**
- Added `fw4 -q reload` after successful nft file
  installation to ensure fw4 state synchronizes
  with PBR's nftables changes

**Resolver Instance Handling:**
- Added robustness checks in
  `_dnsmasq_instance_config()`: file existence
  check and instance validity check
- Better section name resolution with UCI query
- Added missing `setup` parameter in resolver
  instance setup calls

- `uci_get_device()` — Replaced with inline call
- `uci_get_protocol()` — Replaced with inline call

---

In `70-pbr`, fixed shell variable quoting:
```sh
${DEVICE:+ ($DEVICE)}
${DEVICE:+ (${DEVICE})}
```

---

In `pbr.user.netflix`, fixed two instances of
bare variable expansion in parameter substitution:
```sh
params="${params:+$params, }${p}"
params="${params:+${params}, }${p}"
```

---

A full test suite is added in `net/pbr/tests/`
(21 new files, ~1,300 lines) using the shunit2
framework with a complete mock OpenWrt sysroot.

**Runner (`run_tests.sh`):**
- Discovers test files via glob pattern
- Supports pattern-based filtering via CLI arg
- Executes each test in isolated bash subprocess
- Captures output, reports pass/fail with color
- Accumulates stats and lists failures at end
- Requires `shunit2` package

**Setup (`lib/setup.sh`):**
- Creates temporary mock sysroot (`$MOCK_ROOT`)
- Sets `IPKG_INSTROOT` for OpenWrt path resolution
- Installs mock libraries, configs, and binaries
- Stubs `rc.common`, procd, logger, resolveip,
  jsonfilter, pidof, sync
- Sources pbr init script with `readonly` keyword
  stripped (allows test overrides)
- Redirects all file paths to temp directories

**UCI Config API (`lib/mocks/functions.sh`):**
- Full `config_load` parser for UCI syntax
- `config_get`, `config_get_bool`,
  `config_get_list`, `config_foreach`,
  `config_list_foreach`
- `uci_set`, `uci_get`, `uci_add_list`,
  `uci_remove`, `uci_remove_list`, `uci_commit`
- Stores state in associative arrays

**Network API (`lib/mocks/network.sh`):**
- `network_get_device`, `network_get_physdev`,
  `network_get_gateway`, `network_get_gateway6`,
  `network_get_protocol`, `network_get_ipaddr`,
  `network_get_ip6addr`, `network_get_dnsserver`,
  `network_flush_cache`
- Backed by `MOCK_NET_*` variables that tests
  override to simulate different network states
- Pre-configured: wan (eth0/dhcp/192.168.1.1),
  wan6 (eth0/dhcpv6/fd00::1), wg0 (wireguard),
  lan (br-lan/static), loopback (lo/static)

**JSON Shell (`lib/mocks/jshn.sh`):**
- Minimal JSON-in-shell implementation
- `json_init`, `json_add_string/boolean/int`,
  `json_add_object/array`, `json_close_*`,
  `json_select`, `json_get_var`, `json_get_keys`,
  `json_dump`, `json_load`
- Associative array backend with path tracking

**Mock Binaries:**
- `nft` — Returns fw4 table structure with
  standard chains (input, forward, output,
  dstnat, mangle_*); passes syntax checks
- `dnsmasq` — Reports version with nftset support
- `readlink` — Returns `/usr/libexec/ip-full`
  for `*/sbin/ip` (simulates ip-full installed)

**Mock UCI Configs:**
- `pbr` — Full config: enabled, policies
  (vpn_all, vpn_gaming, disabled_policy),
  dns_policy, nft settings, interface lists
- `network` — Interfaces: loopback, lan, wan,
  wan6, wg0 (wireguard)
- `firewall` — Zones: lan (accept all),
  wan (reject input/forward)
- `dhcp` — DHCP server stub
- `system` — Hostname and timezone

**01_validation — Input Validation (67 cases):**

`01_ipv4_validation` (13 cases):
- Valid IPs: 192.168.1.1, 10.0.0.1, 172.16.0.1
- Valid CIDR: /8, /24, /32, /0
- Invalid: octets >255, wrong octet count,
  CIDR >32, IPv6 addresses, domain names

`02_ipv6_validation` (21 cases):
- Valid: ::1, fe80::1, 2001:db8::1, fd00::1,
  full addresses, ::/0
- Invalid: IPv4 addrs, plain strings, MACs
- Scope detection: global (2001:db8::/32),
  link-local (fe80::/10), ULA (fd00::/8)

`03_domain_validation` (8 cases):
- Host: single labels (router, host123)
- Hostname: multi-label (example.com,
  sub.example.com, deep.sub.example.com)
- Domain: FQDN or single-label
- Invalid: IPs, empty strings, MAC notation

`04_misc_validators` (25 cases):
- MAC addresses (colon notation, case variants)
- Integer validation (positive, not negative)
- Negation marker (! prefix detection)
- URL schemes (http, https, ftp, file://)
- Version comparison (is_greater,
  is_greater_or_equal)
- Family mismatch (IPv4/IPv6 mixing detection)

**02_string_utils — String Functions (8 cases):**

`01_str_functions`:
- `str_contains` — Substring search
- `str_contains_word` — Word-boundary search
- `str_to_lower` / `str_to_upper` — Case convert
- `str_first_word` — Token extraction
- `str_replace` — String substitution
- `str_extras_to_underscore` — Normalize delims
- `str_extras_to_space` — Expand delimiters

**03_wan_detection — Interface Detection
  (13 cases):**

`01_wan_types`:
- `is_wan4` — Detects wan/wanX, not wan6/lan/wg0
- `is_wan6` — Detects wan6/mwan6 (IPv6-aware)
- `is_wan6_disabled` — Disabled when ipv6 off
- `is_wan` — Unified v4+v6 detection
- `is_uplink4` / `is_uplink6` — Uplink detection
- `is_tor` — Case-insensitive tor detection
- `is_ignore_target` — Ignore target detection
- `is_list` — Comma/space list vs single value

**04_config — Configuration Loading (13 cases):**

`01_load_config` (7 cases):
- Default values from UCI config
- Hex value parsing (fw_mask, uplink_mark)
- XOR calculation (fw_maskXor = ~fw_mask)
- List parsing (ignored_interface, resolver)
- nft parameters (auto-merge, flags)
- Config-loaded flag tracking

`02_disabled_service` (2 cases):
- Disabled: enabled option becomes unset
- Enabled: enabled option is set

`03_config_ipv6` (4 cases):
- IPv6 enabled: config and uplink interface set
- IPv6 disabled: both unset
- Reload behavior verification

**05_nft — nftables Integration (14 cases):**

`01_nft_file_operations` (8 cases):
- File creation with nft shebang
- Chain creation (dstnat, forward, output,
  prerouting)
- Jump rules and guard rules
- File append, content search, file deletion

`02_nft_check_element` (6 cases):
- fw4 table existence
- Chain existence (input, forward, output,
  dstnat, mangle_*)
- Non-existent chain detection

**06_network — Network Functions (11 cases):**

`01_gateway_discovery` (4 cases):
- IPv4 gateway from mock (192.168.1.1)
- IPv4 gateway fallback (ip addr parsing)
- IPv6 gateway from mock (fd00::1)
- Interface finding for uplinks

`02_supported_interfaces` (7 cases):
- Ignored: loopback in ignored list
- LAN detection vs non-LAN
- Uplink support (wan is supported)
- LAN/loopback not supported
- Wireguard supported (wg0)
- Explicit custom interface support

---

```sh
cd net/pbr/tests && sh run_tests.sh
```

Requires: `bash`, `shunit2`.
Optional filter: `sh run_tests.sh 01_validation`

Signed-off-by: Stan Grishin <stangri@melmac.ca>
(cherry picked from commit cf1d277)
Signed-off-by: Stan Grishin <stangri@melmac.ca>
@stangri stangri added the OpenWrt 25.12 Issues/PR on branch 25.12 label Feb 26, 2026
@stangri stangri merged commit 33e3f91 into openwrt:openwrt-25.12 Feb 27, 2026
12 checks passed
@stangri stangri deleted the openwrt-25.12-pbr branch February 28, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OpenWrt 25.12 Issues/PR on branch 25.12

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant