mirror of
https://github.com/openwrt/packages.git
synced 2026-04-15 19:02:09 +00:00
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>
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
|