mirror of
https://github.com/openwrt/packages.git
synced 2026-05-31 15:02:01 +08:00
793274a78e
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 cf1d2770ed)
Signed-off-by: Stan Grishin <stangri@melmac.ca>
72 lines
2.8 KiB
Bash
Executable File
72 lines
2.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Test: Miscellaneous validators (MAC, integer, URL, negation, version comparison)
|
|
. "$(dirname "$0")/../lib/setup.sh"
|
|
|
|
oneTimeTearDown() { rm -rf "${MOCK_ROOT:-}"; }
|
|
|
|
testMacAddress() {
|
|
assertTrue "Uppercase MAC" "is_mac_address 'AA:BB:CC:DD:EE:FF'"
|
|
assertTrue "Lowercase MAC" "is_mac_address 'aa:bb:cc:dd:ee:ff'"
|
|
assertTrue "Numeric MAC" "is_mac_address '00:11:22:33:44:55'"
|
|
assertFalse "Too short" "is_mac_address 'AA:BB:CC:DD:EE'"
|
|
assertFalse "Too long" "is_mac_address 'AA:BB:CC:DD:EE:FF:00'"
|
|
assertFalse "Dash notation" "is_mac_address 'AA-BB-CC-DD-EE-FF'"
|
|
assertFalse "Not a MAC" "is_mac_address 'not_a_mac'"
|
|
assertFalse "Empty string" "is_mac_address ''"
|
|
}
|
|
|
|
testMacAddressBadNotation() {
|
|
assertTrue "Dash notation" "is_mac_address_bad_notation 'AA-BB-CC-DD-EE-FF'"
|
|
assertFalse "Colon notation" "is_mac_address_bad_notation 'AA:BB:CC:DD:EE:FF'"
|
|
}
|
|
|
|
testIsInteger() {
|
|
assertTrue "Zero" "is_integer '0'"
|
|
assertTrue "Positive" "is_integer '123'"
|
|
assertTrue "Large number" "is_integer '999999'"
|
|
assertFalse "Empty string" "is_integer ''"
|
|
assertFalse "Letters" "is_integer 'abc'"
|
|
assertFalse "Decimal" "is_integer '12.34'"
|
|
assertFalse "Negative" "is_integer '-1'"
|
|
}
|
|
|
|
testIsNegated() {
|
|
assertTrue "Negated IP" "is_negated '!192.168.1.1'"
|
|
assertTrue "Negated domain" "is_negated '!example.com'"
|
|
assertFalse "Not negated" "is_negated '192.168.1.1'"
|
|
assertFalse "Empty string" "is_negated ''"
|
|
}
|
|
|
|
testUrlValidators() {
|
|
assertTrue "HTTP URL" "is_url_http 'http://example.com'"
|
|
assertTrue "HTTPS URL" "is_url_https 'https://example.com'"
|
|
assertTrue "FTP URL" "is_url_ftp 'ftp://files.example.com'"
|
|
assertTrue "File URL" "is_url_file 'file:///tmp/list.txt'"
|
|
assertFalse "HTTPS is not HTTP" "is_url_http 'https://example.com'"
|
|
assertFalse "HTTP is not HTTPS" "is_url_https 'http://example.com'"
|
|
assertTrue "HTTP is URL" "is_url 'http://example.com'"
|
|
assertTrue "HTTPS is URL" "is_url 'https://example.com'"
|
|
assertTrue "FTP is URL" "is_url 'ftp://example.com'"
|
|
assertTrue "File is URL" "is_url 'file:///tmp/x'"
|
|
assertFalse "Plain domain not URL" "is_url 'example.com'"
|
|
}
|
|
|
|
testVersionComparison() {
|
|
assertTrue "2.0 > 1.0" "is_greater '2.0' '1.0'"
|
|
assertTrue "1.10 > 1.9" "is_greater '1.10' '1.9'"
|
|
assertFalse "1.0 not > 2.0" "is_greater '1.0' '2.0'"
|
|
assertFalse "Equal not greater" "is_greater '1.0' '1.0'"
|
|
assertTrue "Equal is >=" "is_greater_or_equal '1.0' '1.0'"
|
|
assertTrue "Greater is >=" "is_greater_or_equal '2.0' '1.0'"
|
|
assertFalse "Lesser not >=" "is_greater_or_equal '1.0' '2.0'"
|
|
}
|
|
|
|
testFamilyMismatch() {
|
|
assertTrue "IPv4 src IPv6 dst" "is_family_mismatch '192.168.1.1' '::1'"
|
|
assertTrue "IPv6 src IPv4 dst" "is_family_mismatch '::1' '10.0.0.1'"
|
|
assertFalse "Both IPv4" "is_family_mismatch '10.0.0.1' '10.0.0.2'"
|
|
assertFalse "Both IPv6" "is_family_mismatch '::1' '::2'"
|
|
}
|
|
|
|
. shunit2
|