mirror of
https://github.com/openwrt/packages.git
synced 2026-05-31 15:02:01 +08:00
dde3a694f0
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>
62 lines
2.5 KiB
Bash
Executable File
62 lines
2.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Test: String utility functions
|
|
. "$(dirname "$0")/../lib/setup.sh"
|
|
|
|
oneTimeTearDown() { rm -rf "${MOCK_ROOT:-}"; }
|
|
|
|
testStrContains() {
|
|
assertTrue "Contains word" "str_contains 'hello world' 'world'"
|
|
assertTrue "Contains substring" "str_contains 'hello world' 'lo wo'"
|
|
assertTrue "Contains middle" "str_contains 'abcdef' 'bcd'"
|
|
assertFalse "Does not contain" "str_contains 'hello' 'xyz'"
|
|
assertFalse "Empty haystack" "str_contains '' 'test'"
|
|
# In bash, ${1//} with empty pattern doesn't remove anything, so returns false
|
|
assertFalse "Empty needle returns false" "str_contains 'hello' ''"
|
|
}
|
|
|
|
testStrContainsWord() {
|
|
assertTrue "Contains exact word" "str_contains_word 'one two three' 'two'"
|
|
assertFalse "Partial not word match" "str_contains_word 'one twothree' 'two'"
|
|
assertTrue "Single word" "str_contains_word 'one' 'one'"
|
|
assertFalse "Word not present" "str_contains_word 'one two three' 'four'"
|
|
}
|
|
|
|
testStrToLower() {
|
|
assertEquals "All caps to lower" "hello" "$(str_to_lower 'HELLO')"
|
|
assertEquals "Mixed case" "hello" "$(str_to_lower 'Hello')"
|
|
assertEquals "Already lowercase" "hello" "$(str_to_lower 'hello')"
|
|
assertEquals "With numbers" "123abc" "$(str_to_lower '123ABC')"
|
|
}
|
|
|
|
testStrToUpper() {
|
|
assertEquals "All lower to upper" "HELLO" "$(str_to_upper 'hello')"
|
|
assertEquals "Mixed case" "HELLO" "$(str_to_upper 'Hello')"
|
|
assertEquals "With numbers" "123ABC" "$(str_to_upper '123abc')"
|
|
}
|
|
|
|
testStrFirstWord() {
|
|
assertEquals "First of two" "hello" "$(str_first_word 'hello world')"
|
|
assertEquals "First of three" "one" "$(str_first_word 'one two three')"
|
|
assertEquals "Single word" "single" "$(str_first_word 'single')"
|
|
}
|
|
|
|
testStrReplace() {
|
|
assertEquals "Replace word" "hello universe" "$(str_replace 'hello world' 'world' 'universe')"
|
|
assertEquals "Replace dots" "aXbXc" "$(str_replace 'a.b.c' '.' 'X')"
|
|
assertEquals "No match unchanged" "hello world" "$(str_replace 'hello world' 'xyz' 'abc')"
|
|
}
|
|
|
|
testStrExtrasToUnderscore() {
|
|
assertEquals "Dot to underscore" "hello_world" "$(str_extras_to_underscore 'hello.world')"
|
|
assertEquals "Spaces to underscores" "a_b_c" "$(str_extras_to_underscore 'a b c')"
|
|
assertEquals "Slash to underscore" "test_path" "$(str_extras_to_underscore 'test/path')"
|
|
assertEquals "Multiple dots collapsed" "no_dups" "$(str_extras_to_underscore 'no..dups')"
|
|
}
|
|
|
|
testStrExtrasToSpace() {
|
|
assertEquals "Delimiters to spaces" "a b c d" "$(str_extras_to_space 'a,b;c{d')"
|
|
assertEquals "Closing brace to space" "a b" "$(str_extras_to_space 'a}b')"
|
|
}
|
|
|
|
. shunit2
|