pbr: update to 1.1.8-32

Makefile:
  - add SPDX-Identifier-License
  - update Copyright

README:
  - add basic README with the link to full documentation

Config:
  - add debug_dnsmasq
  - add procd_boot_trigger_delay

Init Script:
  - move extra_command calls high up for visibility
  - bump packageCompat to sync with luci app
  - implement support for debug_dnsmasq to dump dnsmasq debug into $packageDebugFile
  - create $runningStatusFile json-file allowing more verbose errors/warnings messages
  - replaced `state add` calls with json add calls to store errors/warnings messages
  - remove no longer needed errorSummary, warningSummary
  - ensure environment is only loaded once per run via $load_environment_flag
  - bugfix: update is_{host,hostname,domain,ipv4,mac_address} functions to properly sort policy entries
  - bugfix: change references to melmac.net to melmac.ca
  - add some new error/warning messages
  - add delay before service is started on boot via procd_boot_trigger_delay
  - bugfix: add logic to identify unknown policy entries instead of silently failing on them
  - store error/warning messages as json objects in ubus data for luci app
  - update load_validate_config with debug_dnsmasq and procd_boot_trigger_delay entries

Signed-off-by: Stan Grishin <stangri@melmac.ca>
(cherry picked from commit 4b831c6dbf)
This commit is contained in:
Stan Grishin
2025-08-01 01:04:35 +00:00
parent ff3c02aaf2
commit 208ccde35c
4 changed files with 251 additions and 174 deletions

View File

@@ -1,11 +1,11 @@
# Copyright 2017-2024 MOSSDeF, Stan Grishin (stangri@melmac.ca).
# This is free software, licensed under AGPL-3.0-or-later.
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright 2017-2025 MOSSDeF, Stan Grishin (stangri@melmac.ca).
include $(TOPDIR)/rules.mk
PKG_NAME:=pbr
PKG_VERSION:=1.1.8
PKG_RELEASE:=16
PKG_RELEASE:=32
PKG_LICENSE:=AGPL-3.0-or-later
PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca>

View File

@@ -1,4 +1,18 @@
# README
# Policy-Based Routing (pbr)
Documentation for this project is available at [https://docs.openwrt.melmac.net/pbr/](https://docs.openwrt.melmac.net/pbr/).
[![OpenWrt](https://img.shields.io/badge/OpenWrt-Compatible-blueviolet)](https://openwrt.org)
[![Web UI](https://img.shields.io/badge/Web_UI-Available-blue)](https://docs.openwrt.melmac.ca/pbr/)
[![License](https://img.shields.io/badge/License-GPL--3.0-lightgrey)](https://github.com/stangri/pbr/blob/master/LICENSE)
Flexible policy-based routing (PBR) framework for OpenWrt.
Allows routing specific traffic (by IP, MAC, port, protocol, or domain) through a specific WAN, VPN, or tunnel.
## Features
- Route by IP, MAC, port, or domain name
- Works with WAN, VPNs (WireGuard, OpenVPN), or tunnels
- Lightweight shell-based implementation
- Optional Web UI integration via LuCI
**Full documentation:**
[https://docs.openwrt.melmac.ca/pbr/](https://docs.openwrt.melmac.ca/pbr/)

View File

@@ -1,4 +1,5 @@
config pbr 'config'
option debug_dnsmasq '0'
option enabled '0'
option verbosity '2'
option strict_enforcement '1'
@@ -8,6 +9,7 @@ config pbr 'config'
list ignored_interface 'vpnserver'
option boot_timeout '30'
option rule_create_option 'add'
option procd_boot_trigger_delay '5000'
option procd_reload_delay '1'
option webui_show_ignore_target '0'
option nft_rule_counter '0'

View File

@@ -12,21 +12,44 @@ USE_PROCD=1
[ -n "${IPKG_INSTROOT}" ] && return 0
if type extra_command >/dev/null 2>&1; then
extra_command 'status' "Generates output required to troubleshoot routing issues
Use '-d' option for more detailed output
Use '-p' option to automatically upload data under PBR paste.ee account
WARNING: while paste.ee uploads are unlisted, they are still publicly available
List domain names after options to include their lookup in report"
extra_command 'version' 'Show version information'
extra_command 'on_firewall_reload' ' Run service on firewall reload'
extra_command 'on_interface_reload' ' Run service on indicated interface reload'
else
# shellcheck disable=SC2034
EXTRA_COMMANDS='on_firewall_reload on_interface_reload status version'
# shellcheck disable=SC2034
EXTRA_HELP=" status Generates output required to troubleshoot routing issues
Use '-d' option for more detailed output
Use '-p' option to automatically upload data under PBR paste.ee account
WARNING: while paste.ee uploads are unlisted, they are still publicly available
List domain names after options to include their lookup in report"
fi
readonly packageName='pbr'
readonly PKG_VERSION='dev-test'
readonly packageCompat='11'
readonly packageCompat='14'
readonly serviceName="$packageName $PKG_VERSION"
readonly packageConfigFile="/etc/config/${packageName}"
readonly packageDebugFile="/var/run/${packageName}.debug"
readonly packageLockFile="/var/run/${packageName}.lock"
readonly dnsmasqFileDefault="/var/run/${packageName}.dnsmasq"
readonly runningStatusFile="/dev/shm/${packageName}.status.json"
readonly runningStatusFileLock="/var/lock/${packageName}.lock"
readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m'
readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
readonly _OKB_='\033[1;34m\xe2\x9c\x93\033[0m'
readonly __OKB__='\033[1;34m[\xe2\x9c\x93]\033[0m'
readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m'
readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
readonly _ERROR_='\033[0;31mERROR\033[0m'
readonly _WARNING_='\033[0;33mWARNING\033[0m'
readonly _ERROR_='\033[0;31mERROR:\033[0m'
readonly _WARNING_='\033[0;33mWARNING:\033[0m'
readonly ip_full='/usr/libexec/ip-full'
# shellcheck disable=SC2155
readonly ipTablePrefix="$packageName"
@@ -47,6 +70,7 @@ readonly xrayIfacePrefix='xray_'
readonly rtTablesFile='/etc/iproute2/rt_tables'
# package config options
debug_dnsmasq=
enabled=
fw_mask=
icmp_interface=
@@ -54,6 +78,7 @@ ignored_interface=
ipv6_enabled=
nft_user_set_policy=
nft_user_set_counter=
procd_boot_trigger_delay=
procd_reload_delay=
procd_lan_device=
procd_wan_interface=
@@ -76,10 +101,9 @@ nft_set_policy=
nft_set_timeout=
# run-time
load_environment_flag=
aghConfigFile='/etc/AdGuardHome/AdGuardHome.yaml'
gatewaySummary=
errorSummary=
warningSummary=
wanIface4=
wanIface6=
dnsmasqFileList=
@@ -143,6 +167,8 @@ output_okb() { output 1 "$_OKB_"; output 2 "$__OKB__\n"; }
output_okbn() { output 1 "$_OKB_\n"; output 2 "$__OKB__\n"; }
output_fail() { output 1 "$_FAIL_"; output 2 "$__FAIL__\n"; }
output_failn() { output 1 "$_FAIL_\n"; output 2 "$__FAIL__\n"; }
output_error() { output "${_ERROR_} $*!\n"; }
output_warning() { output "${_WARNING_} $*!\n"; }
quiet_mode() {
case "$1" in
on) verbosity=0;;
@@ -215,7 +241,9 @@ uci_get_device() {
uci_get_protocol() { uci_get 'network' "$1" 'proto'; }
is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
is_disabled_interface() { [ "$(uci_get 'network' "$1" 'disabled')" = '1' ]; }
is_domain(){ echo "$1" | grep -qE '^([a-zA-Z0-9][a-zA-Z0-9-]{0,61}\.)*[a-zA-Z]{2,}$'; }
is_host() { echo "$1" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9]$|^[a-zA-Z0-9]$'; }
is_hostname() { echo "$1" | grep -qE '^([a-zA-Z0-9]([a-zA-Z0-9_-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'; }
is_domain() { ! is_ipv4 "$1" && ! is_mac_address_bad_notation "$1" && { is_host "$1" || is_hostname "$1"; }; }
is_dslite() { local p; network_get_protocol p "$1"; [ "${p:0:6}" = "dslite" ]; }
is_family_mismatch() { ( is_ipv4 "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_ipv4 "${2//!}" ); }
is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
@@ -223,7 +251,7 @@ is_greater_or_equal() { test "$(printf '%s\n' "$@" | sort -V | head -n '1')" = "
is_ignored_interface() { str_contains_word "$ignored_interface" "$1"; }
is_ignore_target() { [ "$(str_to_lower "$1")" = 'ignore' ]; }
is_integer() { case "$1" in ''|*[!0-9]*) return 1;; esac; }
is_ipv4() { expr "${1%/*}" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
is_ipv4() { echo "$1" | grep -qE '^((25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(/(3[0-2]|[12]?[0-9]))?$'; }
is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ':'; }
is_ipv6_global_scope() { [ "${1:0:4}" = '2001' ]; }
is_ipv6_local_scope() { is_ipv6_local_link "$1" || is_ipv6_local_unique "$1"; }
@@ -232,7 +260,8 @@ is_ipv6_local_unique() { [ "${1:0:2}" = 'fc' ] || [ "${1:0:2}" = 'fd' ]; }
is_list() { str_contains "$1" ',' || str_contains "$1" ' '; }
is_lan() { local d; network_get_device d "$1"; str_contains "$procd_lan_device" "$d"; }
is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp" ]; }
is_mac_address() { expr "$1" : '[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]$' >/dev/null; }
is_mac_address() { echo "$1" | grep -qE '^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$'; }
is_mac_address_bad_notation() { echo "$1" | grep -qE '^([0-9A-Fa-f]{2}-){5}([0-9A-Fa-f]{2})$'; }
is_negated() { [ "${1:0:1}" = '!' ]; }
is_netifd_table() { grep -q "ip.table.*$1" /etc/config/network; }
is_netifd_table_interface() { local iface="$1"; [ "$(uci_get 'network' "$iface" 'ip4table')" = "${packageName}_${iface%6}" ]; }
@@ -311,47 +340,33 @@ check_dnsmasq_nftset() {
check_nft || return 1
check_dnsmasq || return 1
o="$(dnsmasq -v 2>/dev/null)"
[ -n "$debug_dnsmasq" ] && {
echo " $(date) dnsmasq output dump:";
# shellcheck disable=SC3003
echo "${o%$'\n'$'\n'This*}";
echo '-------------------------';
} >> "$packageDebugFile"
! echo "$o" | grep -q 'no-nftset' && echo "$o" | grep -q 'nftset'
}
print_json_bool() { json_init; json_add_boolean "$1" "$2"; json_dump; json_cleanup; }
print_json_string() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
try() {
if ! "$@" >/dev/null 2>&1; then
state add 'errorSummary' 'errorTryFailed' "$*"
json add error 'errorTryFailed' "$*"
return 1
fi
}
if type extra_command >/dev/null 2>&1; then
extra_command 'status' "Generates output required to troubleshoot routing issues
Use '-d' option for more detailed output
Use '-p' option to automatically upload data under VPR paste.ee account
WARNING: while paste.ee uploads are unlisted, they are still publicly available
List domain names after options to include their lookup in report"
extra_command 'version' 'Show version information'
extra_command 'on_firewall_reload' ' Run service on firewall reload'
extra_command 'on_interface_reload' ' Run service on indicated interface reload'
else
# shellcheck disable=SC2034
EXTRA_COMMANDS='on_firewall_reload on_interface_reload status version'
# shellcheck disable=SC2034
EXTRA_HELP=" status Generates output required to troubleshoot routing issues
Use '-d' option for more detailed output
Use '-p' option to automatically upload data under VPR paste.ee account
WARNING: while paste.ee uploads are unlisted, they are still publicly available
List domain names after options to include their lookup in report"
fi
get_text() {
local r
case "$1" in
errorConfigValidation) r="Config ($packageConfigFile) validation failure!";;
errorNoNft) r="Resolver set support (${resolver_set}) requires nftables, but nft binary cannot be found!";;
errorResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system!";;
errorServiceDisabled) r="The ${packageName} service is currently disabled!";;
errorServiceDisabled) r="The ${packageName} service is currently disabled";;
errorNoWanGateway) r="The ${serviceName} service failed to discover WAN gateway!";;
errorNoWanInterface) r="The %s interface not found, you need to set the 'pbr.config.procd_wan_interface' option!";;
errorNoWanInterfaceHint) r="Refer to https://docs.openwrt.melmac.net/pbr/#procd_wan_interface.";;
errorNoWanInterfaceHint) r="Refer to https://docs.openwrt.melmac.ca/pbr/#procd_wan_interface.";;
errorNftsetNameTooLong) r="The nft set name '%s' is longer than allowed 255 characters!";;
errorUnexpectedExit) r="Unexpected exit or service termination: '%s'!";;
errorPolicyNoSrcDest) r="Policy '%s' has no source/destination parameters!";;
@@ -374,6 +389,7 @@ get_text() {
errorPolicyProcessUnknownProtocol) r="Unknown protocol in policy '%s'!";;
errorPolicyProcessInsertionFailed) r="Insertion failed for both IPv4 and IPv6 for policy '%s'!";;
errorPolicyProcessInsertionFailedIpv4) r="Insertion failed for IPv4 for policy '%s'!";;
errorPolicyProcessUnknownEntry) r="Unknown entry in policy '%s'!";;
errorInterfaceRoutingEmptyValues) r="Received empty tid/mark or interface name when setting up routing!";;
errorFailedToResolve) r="Failed to resolve '%s'!";;
errorTryFailed) r="Command failed: %s";;
@@ -387,6 +403,7 @@ get_text() {
errorDefaultFw4ChainMissing) r="Default fw4 chain '%s' is missing!";;
errorRequiredBinaryMissing) r="Required binary '%s' is missing!";;
errorInterfaceRoutingUnknownDevType) r="Unknown IPv6 Link type for device '%s'!";;
errorUplinkDown) r="Uplink/WAN interface is still down, increase value of 'procd_boot_trigger_delay' option";;
warningInvalidOVPNConfig) r="Invalid OpenVPN config for '%s' interface.";;
warningResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system.";;
warningPolicyProcessCMD) r="'%s'";;
@@ -397,6 +414,7 @@ get_text() {
warningBadNftCallsInUserFile) r="Incompatible nft calls detected in user include file, disabling fw4 nft file support.";;
warningDnsmasqInstanceNoConfdir) r="Dnsmasq instance '%s' targeted in settings, but it doesn't have its own confdir.";;
warningDhcpLanForce) r="Please set 'dhcp.%s.force=1' to speed up service start-up.";;
*) r="Unknown error '%s'!";;
esac
echo "$r"
}
@@ -428,13 +446,13 @@ process_url() {
dl_temp_file="$(mktemp -u -q -t "${packageName}_tmp.XXXXXXXX")"
done
if is_url_file "$url" && ! is_present 'curl'; then
state add 'errorSummary' 'errorFileSchemaRequiresCurl' "$url"
json add error 'errorFileSchemaRequiresCurl' "$url"
elif is_url_https "$url" && [ -z "$dl_https_supported" ]; then
state add 'errorSummary' 'errorDownloadUrlNoHttps' "$url"
json add error 'errorDownloadUrlNoHttps' "$url"
elif $dl_command "$url" "$dl_flag" "$dl_temp_file" 2>/dev/null; then
sed 'N;s/\n/ /;s/\s\+/ /g;' "$dl_temp_file"
else
state add 'errorSummary' 'errorDownloadUrl' "$url"
json add error 'errorDownloadUrl' "$url"
fi
rm -f "$dl_temp_file"
}
@@ -443,6 +461,7 @@ load_package_config() {
local param="$1"
local user_file_check_result i
config_load "$packageName"
config_get_bool debug_dnsmasq 'config' 'debug_dnsmasq' '0'
config_get_bool enabled 'config' 'enabled' '0'
config_get fw_mask 'config' 'fw_mask' 'ff0000'
config_get icmp_interface 'config' 'icmp_interface'
@@ -461,12 +480,14 @@ load_package_config() {
config_get_bool strict_enforcement 'config' 'strict_enforcement' '1'
config_get supported_interface 'config' 'supported_interface'
config_get verbosity 'config' 'verbosity' '2'
config_get procd_reload_delay 'config' 'procd_reload_delay' '0'
config_get procd_boot_trigger_delay 'config' 'procd_boot_trigger_delay' '5000'
config_get procd_lan_device 'config' 'procd_lan_device' 'br-lan'
config_get procd_reload_delay 'config' 'procd_reload_delay' '0'
config_get procd_wan_interface 'config' 'procd_wan_interface' 'wan'
config_get procd_wan6_interface 'config' 'procd_wan6_interface' 'wan6'
config_get wan_ip_rules_priority 'config' 'wan_ip_rules_priority' '30000'
config_get wan_mark 'config' 'wan_mark' '010000'
fw_mask="0x${fw_mask}"
wan_mark="0x${wan_mark}"
if [ -x "$agh" ] && [ ! -s "$aghConfigFile" ]; then
@@ -477,6 +498,10 @@ load_package_config() {
fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")"
fw_maskXor="${fw_maskXor:-0xff00ffff}"
is_integer "$procd_boot_trigger_delay" || procd_boot_trigger_delay='5000'
[ "$procd_boot_trigger_delay" -lt '1000' ] && procd_boot_trigger_delay='1000'
[ "$debug_dnsmasq" != '1' ] && unset debug_dnsmasq
[ "$nft_rule_counter" != '1' ] && unset nft_rule_counter
[ "$nft_set_auto_merge" != '1' ] && unset nft_set_auto_merge
[ "$nft_set_counter" != '1' ] && unset nft_set_counter
@@ -513,7 +538,7 @@ load_environment() {
_check_dhcp_force() {
is_lan "$1" || return 0
if [ "$(uci_get dhcp "$1" force 0)" = '0' ]; then
state add 'warningSummary' 'warningDhcpLanForce' "$1"
json add warning 'warningDhcpLanForce' "$1"
fi
}
local i _ret=0
@@ -522,22 +547,22 @@ load_environment() {
uci_commit firewall
fi
if [ "$(readlink /sbin/ip)" != "$ip_full" ]; then
state add 'errorSummary' 'errorRequiredBinaryMissing' 'ip-full'
json add error 'errorRequiredBinaryMissing' 'ip-full'
_ret='1'
fi
if ! nft_call list table inet fw4; then
state add 'errorSummary' 'errorDefaultFw4TableMissing' 'fw4'
json add error 'errorDefaultFw4TableMissing' 'fw4'
_ret='1'
fi
if is_config_enabled 'dns_policy' || is_tor_running; then
if ! nft_call list chain inet fw4 dstnat; then
state add 'errorSummary' 'errorDefaultFw4ChainMissing' 'dstnat'
json add error 'errorDefaultFw4ChainMissing' 'dstnat'
_ret='1'
fi
fi
for i in $chainsList; do
if ! nft_call list chain inet fw4 "mangle_${i}"; then
state add 'errorSummary' 'errorDefaultFw4ChainMissing' "mangle_${i}"
json add error 'errorDefaultFw4ChainMissing' "mangle_${i}"
_ret='1'
fi
done
@@ -546,20 +571,25 @@ load_environment() {
return "$_ret"
}
local param="$1" validation_result="$2"
[ -z "$load_environment_flag" ] || return 0
json init
case "$param" in
on_boot|on_start)
output 1 "Loading environment ($param) "
load_package_config "$param"
if [ "$enabled" -eq '0' ]; then
output 1 "$_FAIL_\n"
state add 'errorSummary' 'errorServiceDisabled'
json add error 'errorServiceDisabled'
output_error "$(get_text 'errorServiceDisabled')"
output "Run the following commands before starting service again:\n"
output "uci set ${packageName}.config.enabled='1'; uci commit $packageName;\n"
return 1
fi
if [ -n "$validation_result" ] && [ "$validation_result" != '0' ]; then
output 1 "$_FAIL_\n"
output "${_ERROR_}: The $packageName config validation failed!\n"
json add error 'errorConfigValidation'
output_error "$(get_text 'errorConfigValidation')"
output "Please check if the '$packageConfigFile' contains correct values for config options.\n"
state add 'errorSummary' 'errorConfigValidation'
return 1
fi
_system_health_check || { output 1 "$_FAIL_\n"; return 1; }
@@ -578,6 +608,7 @@ load_environment() {
load_network "$param"
;;
esac
load_environment_flag=1
}
# shellcheck disable=SC2317
@@ -620,8 +651,8 @@ load_network() {
is_wan_up() {
local sleepCount='1' param="$1"
if [ -z "$(uci_get network "$procd_wan_interface")" ]; then
state add 'errorSummary' 'errorNoWanInterface' "$procd_wan_interface"
state add 'errorSummary' 'errorNoWanInterfaceHint'
json add error 'errorNoWanInterface' "$procd_wan_interface"
json add error 'errorNoWanInterfaceHint'
return 1
fi
network_flush_cache
@@ -629,7 +660,7 @@ is_wan_up() {
if [ -n "$wanGW" ]; then
return 0
else
state add 'errorSummary' 'errorNoWanGateway'
json add error 'errorNoWanGateway'
return 1
fi
}
@@ -666,7 +697,7 @@ nft_file() {
cp -f "$nftTempFile" "$nftPermFile"; then
output_okn
else
state add 'errorSummary' 'errorNftFileInstall' "$nftTempFile"
json add error 'errorNftFileInstall' "$nftTempFile"
output_failn
fi
;;
@@ -685,7 +716,7 @@ nftset() {
[ -x "$nft" ] || return 1
if [ "${#nftset4}" -gt '255' ]; then
state add 'errorSummary' 'errorNftsetNameTooLong' "$nftset4"
json add error 'errorNftsetNameTooLong' "$nftset4"
return 1
fi
@@ -706,7 +737,7 @@ nftset() {
[ -z "$param4" ] && param4="$(resolveip_to_nftset4 "$param")"
[ -z "$param6" ] && param6="$(resolveip_to_nftset6 "$param")"
if [ -z "$param4" ] && [ -z "$param6" ]; then
state add 'errorSummary' 'errorFailedToResolve' "$param"
json add error 'errorFailedToResolve' "$param"
else
[ -n "$param4" ] && nft4 add element inet "$nftTable" "$nftset4" "{ $param4 }" && ipv4_error=0
[ -n "$param6" ] && nft6 add element inet "$nftTable" "$nftset6" "{ $param6 }" && ipv6_error=0
@@ -831,73 +862,49 @@ cleanup_sets() {
done
}
state() {
local action="$1" param="$2" value="${3//#/_}"
local array_name
shift 3
# shellcheck disable=SC2124
local extras="$@"
local line error_id error_extra label
json() {
local status message stats i
local action="$1" param="$2" value="$3"; shift 3; local info="$*";
[ "$param" = 'error' ] && param='errors'
[ "$param" = 'warning' ] && param='warnings'
{ json_load_file "$runningStatusFile" || json_init; } >/dev/null 2>&1
case "$action" in
add)
line="$(eval echo "\$$param")"
eval "$param"='${line:+$line#}${value}${extras:+ $extras}'
;;
json)
json_init
json_add_object "$packageName"
case "$param" in
errorSummary)
array_name='errors';;
warningSummary)
array_name='warnings';;
esac
json_add_array "$array_name"
if [ -n "$(eval echo "\$$param")" ]; then
while read -r line; do
if str_contains "$line" ' '; then
error_id="${line% *}"
error_extra="${line#* }"
else
error_id="$line"
'get')
if json_select "$param" >/dev/null 2>&1; then
if [ -n "$value" ]; then
{
if json_select "$value"; then
json_get_var 'i' "${info:-code}"
json_select ..
fi
json_add_object "$array_name"
json_add_string 'id' "$error_id"
json_add_string 'extra' "$error_extra"
json_close_object
done <<EOF
$(eval echo "\$$param" | tr \# \\n)
EOF
} >/dev/null 2>&1
else
json_get_keys i
fi
printf "%b" "$i"
json_select ..
fi
json_close_array
return
;;
'add')
{ json_select "$param" || json_add_array "$param"; } >/dev/null 2>&1
json_add_object ""
json_add_string 'code' "$value"
json_add_string 'info' "$info"
json_close_object
json_dump
json_select ..
;;
print)
[ -z "$(eval echo "\$$param")" ] && return 0
case "$param" in
errorSummary)
label="${_ERROR_}:";;
warningSummary)
label="${_WARNING_}:";;
esac
while read -r line; do
if str_contains "$line" ' '; then
error_id="${line% *}"
error_extra="${line#* }"
printf "%b $(get_text "$error_id")\n" "$label" "$error_extra"
else
error_id="$line"
printf "%b $(get_text "$error_id")\n" "$label"
fi
done <<EOF
$(eval echo "\$$param" | tr \# \\n)
EOF
;;
set)
eval "$param"='${value}${extras:+ $extras}'
'init')
json_init
json_add_array 'errors'
json_close_array
json_add_array 'warnings'
json_close_array
;;
esac
mkdir -p "${runningStatusFile%/*}"
json_dump > "$runningStatusFile"
sync
}
resolver() {
@@ -952,7 +959,6 @@ resolver() {
for d in $value; do
nftset 'add_dnsmasq_element' "$iface" "$target" "$type" "$uid" "$name" "$d"
done
# nftset 'add_dnsmasq_element' "$iface" "$target" "$type" "$uid" "$name" "$(str_to_dnsmsaq_nftset "$value")"
;;
create_resolver_set)
[ -n "$resolver_set_supported" ] || return 1
@@ -960,14 +966,14 @@ resolver() {
;;
check_support)
if [ ! -x "$nft" ]; then
state add 'errorSummary' 'errorNoNft'
json add error 'errorNoNft'
return 1
fi
if ! dnsmasq -v 2>/dev/null | grep -q 'no-nftset' && dnsmasq -v 2>/dev/null | grep -q 'nftset'; then
if check_dnsmasq_nftset; then
resolver_set_supported='true'
return 0
else
state add 'warningSummary' 'warningResolverNotSupported'
json add warning 'warningResolverNotSupported'
return 1
fi
;;
@@ -1074,20 +1080,20 @@ dns_policy_routing() {
if [ -z "${dest_dns_ipv4}${dest_dns_ipv6}" ]; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessNoInterfaceDns' "'$dest_dns'"
json add error 'errorPolicyProcessNoInterfaceDns' "'$dest_dns'"
return 1
fi
if [ -z "$ipv6_enabled" ] && is_ipv6 "$(str_first_word "$src_addr")"; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
json add error 'errorPolicyProcessNoIpv6' "$name"
return 1
fi
if { is_ipv4 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv4" ]; } || \
{ is_ipv6 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv6" ]; }; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns'"
json add error 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns'"
return 1
fi
@@ -1119,7 +1125,7 @@ dns_policy_routing() {
resolved_ipv4="$(resolveip_to_nftset4 "$d")"
resolved_ipv6="$(resolveip_to_nftset6 "$d")"
if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
state add 'errorSummary' 'errorFailedToResolve' "$d"
json add error 'errorFailedToResolve' "$d"
else
[ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
[ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
@@ -1154,15 +1160,15 @@ dns_policy_routing() {
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param6"
json add error 'errorPolicyProcessInsertionFailed' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4"
json add error 'errorPolicyProcessCMD' "nft $param6"
logger -t "$packageName" "ERROR: nft $param4"
logger -t "$packageName" "ERROR: nft $param6"
elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
json add error 'errorPolicyProcessInsertionFailedIpv4' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4"
logger -t "$packageName" "ERROR: nft $param4"
fi
done
@@ -1183,7 +1189,7 @@ policy_routing() {
if [ -z "$ipv6_enabled" ] && \
{ is_ipv6 "$(str_first_word "$src_addr")" || is_ipv6 "$(str_first_word "$dest_addr")"; }; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
json add error 'errorPolicyProcessNoIpv6' "$name"
return 1
fi
@@ -1203,14 +1209,14 @@ policy_routing() {
dest6="return"
else
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessUnknownFwmark' "$iface"
json add error 'errorPolicyProcessUnknownFwmark' "$iface"
return 1
fi
# TODO: implement actual family mismatch check on lists
# if is_family_mismatch "$src_addr" "$dest_addr"; then
# processPolicyError='true'
# state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_addr'"
# json add error 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_addr'"
# return 1
# fi
@@ -1229,7 +1235,7 @@ policy_routing() {
unset proto_i
elif ! is_supported_protocol "$proto_i"; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessUnknownProtocol' "${name}: '$proto_i'"
json add error 'errorPolicyProcessUnknownProtocol' "${name}: '$proto_i'"
return 1
fi
@@ -1255,7 +1261,7 @@ policy_routing() {
resolved_ipv4="$(resolveip_to_nftset4 "$d")"
resolved_ipv6="$(resolveip_to_nftset6 "$d")"
if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
state add 'errorSummary' 'errorFailedToResolve' "$d"
json add error 'errorFailedToResolve' "$d"
else
[ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
[ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
@@ -1296,7 +1302,7 @@ policy_routing() {
resolved_ipv4="$(resolveip_to_nftset4 "$d")"
resolved_ipv6="$(resolveip_to_nftset6 "$d")"
if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
state add 'errorSummary' 'errorFailedToResolve' "$d"
json add error 'errorFailedToResolve' "$d"
else
[ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
[ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
@@ -1352,15 +1358,15 @@ policy_routing() {
nft6 "$param6" "$dest6" || ipv6_error='1'
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4 $dest4"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param6 $dest6"
json add error 'errorPolicyProcessInsertionFailed' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4 $dest4"
json add error 'errorPolicyProcessCMD' "nft $param6 $dest6"
logger -t "$packageName" "ERROR: nft $param4 $dest4"
logger -t "$packageName" "ERROR: nft $param6 $dest6"
elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4 $dest4"
json add error 'errorPolicyProcessInsertionFailedIpv4' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4 $dest4"
logger -t "$packageName" "ERROR: nft $param4 $dest4"
fi
done
@@ -1384,15 +1390,15 @@ policy_routing() {
fi
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param6"
json add error 'errorPolicyProcessInsertionFailed' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4"
json add error 'errorPolicyProcessCMD' "nft $param6"
logger -t "$packageName" "ERROR: nft $param4"
logger -t "$packageName" "ERROR: nft $param6"
elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
json add error 'errorPolicyProcessInsertionFailedIpv4' "$name"
json add error 'errorPolicyProcessCMD' "nft $param4"
logger -t "$packageName" "ERROR: nft $param4"
fi
fi
@@ -1427,11 +1433,11 @@ dns_policy_process() {
unset processDnsPolicyError
output 2 "Routing '$name' DNS to $dest_dns "
if [ -z "$src_addr" ]; then
state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
json add error 'errorPolicyNoSrcDest' "$name"
output_fail; return 1;
fi
if [ -z "$dest_dns" ]; then
state add 'errorSummary' 'errorPolicyNoDns' "$name"
json add error 'errorPolicyNoDns' "$name"
output_fail; return 1;
fi
@@ -1474,15 +1480,15 @@ policy_process() {
[ "$proto" = 'all' ] && unset proto
output 2 "Routing '$name' via $interface "
if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then
state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
json add error 'errorPolicyNoSrcDest' "$name"
output_fail; return 1;
fi
if [ -z "$interface" ]; then
state add 'errorSummary' 'errorPolicyNoInterface' "$name"
json add error 'errorPolicyNoInterface' "$name"
output_fail; return 1;
fi
if ! is_supported_interface "$interface"; then
state add 'errorSummary' 'errorPolicyUnknownInterface' "$name"
json add error 'errorPolicyUnknownInterface' "$name"
output_fail; return 1;
fi
@@ -1509,6 +1515,7 @@ policy_process() {
local filter_list_src_addr='phys_dev phys_dev_negative mac_address mac_address_negative domain domain_negative ipv4 ipv4_negative ipv6 ipv6_negative'
local filter_list_dest_addr='domain domain_negative ipv4 ipv4_negative ipv6 ipv6_negative'
local filter_group_src_addr filtered_value_src_addr filter_group_dest_addr filtered_value_dest_addr
local processed_value_src_addr processed_value_dest_addr
[ -z "$src_addr" ] && filter_list_src_addr='none'
for filter_group_src_addr in $filter_list_src_addr; do
filtered_value_src_addr="$(filter_options "$filter_group_src_addr" "$src_addr")"
@@ -1524,11 +1531,27 @@ policy_process() {
continue
fi
policy_routing "$name" "$interface" "$filtered_value_src_addr" "$src_port" "$filtered_value_dest_addr" "$dest_port" "$proto" "$chain" "$uid"
processed_value_src_addr="${processed_value_src_addr:+$processed_value_src_addr }$filtered_value_src_addr"
processed_value_dest_addr="${processed_value_dest_addr:+$processed_value_dest_addr }$filtered_value_dest_addr"
fi
done
fi
done
for i in $src_addr; do
if ! str_contains "$processed_value_src_addr" "$i"; then
processPolicyError='true'
json add error 'errorPolicyProcessUnknownEntry' "$name: $i"
fi
done
for i in $dest_addr; do
if ! str_contains "$processed_value_dest_addr" "$i"; then
processPolicyError='true'
json add error 'errorPolicyProcessUnknownEntry' "$name: $i"
fi
done
if [ -n "$processPolicyError" ]; then
output_fail
else
@@ -1540,7 +1563,7 @@ interface_routing() {
local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev="$6" gw6="$7" dev6="$8" priority="$9"
local dscp s=0 i ipv4_error=1 ipv6_error=1
if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
state add 'errorSummary' 'errorInterfaceRoutingEmptyValues'
json add error 'errorInterfaceRoutingEmptyValues'
return 1
fi
case "$action" in
@@ -1602,7 +1625,7 @@ EOF
elif ip -6 address show dev "$dev6" | grep -q "POINTOPOINT"; then
try ip -6 route add default dev "$dev6" table "$tid" metric "$procd_wan6_metric" || ipv6_error=1
else
state add 'errorSummary' 'errorInterfaceRoutingUnknownDevType' "$dev6"
json add error 'errorInterfaceRoutingUnknownDevType' "$dev6"
fi
# if ! ip -6 route add default via "$gw6" dev "$dev6" table "$tid" >/dev/null 2>&1; then
# try ip -6 route add default dev "$dev6" table "$tid" metric "$procd_wan6_metric" || ipv6_error=1
@@ -1693,7 +1716,7 @@ EOF
elif ip -6 address show dev "$dev6" | grep -q "POINTOPOINT"; then
try ip -6 route add default dev "$dev6" table "$tid" metric "$procd_wan6_metric" || ipv6_error=1
else
state add 'errorSummary' 'errorInterfaceRoutingUnknownDevType' "$dev6"
json add error 'errorInterfaceRoutingUnknownDevType' "$dev6"
fi
while read -r i; do
# shellcheck disable=SC2086
@@ -1752,7 +1775,7 @@ process_interface() {
if [ "$iface" = 'tor' ]; then
case "$action" in
create|reload)
create|reload|reload_interface)
torDnsPort="$(get_tor_dns_port)"
torTrafficPort="$(get_tor_traffic_port)"
displayText="${iface}/53->${torDnsPort}/80,443->${torTrafficPort}"
@@ -1769,7 +1792,7 @@ process_interface() {
disabled="$(uci_get 'network' "$iface" 'disabled')"
listen_port="$(uci_get 'network' "$iface" 'listen_port')"
case "$action" in
create|reload)
create|reload|reload_interface)
if [ "$disabled" != '1' ] && [ -n "$listen_port" ]; then
if [ -n "$wanIface4" ]; then
ip rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
@@ -1796,7 +1819,7 @@ process_interface() {
[ "$((ifaceMark))" -gt "$((fw_mask))" ] && return 1
if is_ovpn "$iface" && ! is_ovpn_valid "$iface"; then
: || state add 'warningSummary' 'warningInvalidOVPNConfig' "$iface"
: || json add warning 'warningInvalidOVPNConfig' "$iface"
fi
network_get_device dev "$iface"
@@ -1841,7 +1864,7 @@ process_interface() {
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
if is_netifd_table_interface "$iface"; then output_okb; else output_ok; fi
else
state add 'errorSummary' 'errorFailedSetup' "$displayText"
json add error 'errorFailedSetup' "$displayText"
output_fail
fi
;;
@@ -1918,7 +1941,7 @@ process_interface() {
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
if is_netifd_table_interface "$iface"; then output_okb; else output_ok; fi
else
state add 'errorSummary' 'errorFailedReload' "$displayText"
json add error 'errorFailedReload' "$displayText"
output_fail
fi
else
@@ -1936,26 +1959,26 @@ user_file_process() {
local shellBin="${SHELL:-/bin/ash}"
[ "$enabled" -gt '0' ] || return 0
if [ ! -s "$path" ]; then
state add 'errorSummary' 'errorUserFileNotFound' "$path"
json add error 'errorUserFileNotFound' "$path"
output_fail
return 1
fi
if ! $shellBin -n "$path"; then
state add 'errorSummary' 'errorUserFileSyntax' "$path"
json add error 'errorUserFileSyntax' "$path"
output_fail
return 1
fi
if is_bad_user_file_nft_call "$path"; then
state add 'errorSummary' 'errorIncompatibleUserFile' "$path"
json add error 'errorIncompatibleUserFile' "$path"
output_fail
return 1
fi
output 2 "Running $path "
# shellcheck disable=SC1090
if ! . "$path"; then
state add 'errorSummary' 'errorUserFileRunning' "$path"
json add error 'errorUserFileRunning' "$path"
if grep -q -w 'curl' "$path" && ! is_present 'curl'; then
state add 'errorSummary' 'errorUserFileNoCurl' "$path"
json add error 'errorUserFileNoCurl' "$path"
fi
output_fail
return 1
@@ -1995,11 +2018,14 @@ on_interface_reload() {
}
start_service() {
local resolverStoredHash resolverNewHash i param="$1" reloadedIface
local resolverStoredHash resolverNewHash i param="$1" reloadedIface k
[ -n "$pbr_boot_flag" ] && return 0
[ "$param" = 'on_boot' ] && return 0
load_environment "${param:-on_start}" "$(load_validate_config)" || return 1
is_wan_up "$param" || return 1
output "Processing environment (${param:-on_start}) "
is_wan_up "$param" || { output_error "$(get_text 'errorUplinkDown')"; return 1; }
process_interface 'all' 'prepare'
config_foreach process_interface 'interface' 'pre_init'
@@ -2056,6 +2082,7 @@ start_service() {
case $serviceStartTrigger in
on_interface_reload)
output_okn
output 1 "Reloading Interface: $reloadedIface "
json_add_array 'gateways'
process_interface 'all' 'prepare'
@@ -2073,6 +2100,7 @@ start_service() {
cleanup_marking_chains
cleanup_rt_tables
nft_file 'create'
output_okn
output 1 'Processing interfaces '
json_add_array 'gateways'
process_interface 'all' 'prepare'
@@ -2116,21 +2144,38 @@ start_service() {
esac
if [ -z "$gatewaySummary" ]; then
state add 'errorSummary' 'errorNoGateways'
json add error 'errorNoGateways'
fi
json_add_int 'packageCompat' "$packageCompat"
json_add_object 'status'
[ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary"
[ -n "$errorSummary" ] && json_add_string 'errors' "$errorSummary"
[ -n "$warningSummary" ] && json_add_string 'warnings' "$warningSummary"
json_close_object
json_add_array 'errors'
for k in $(json get errors); do
json_add_object "$k"
json_add_string 'code' "$(json get error "$k" 'code')"
json_add_string 'info' "$(json get error "$k" 'info')"
json_close_object
done
json_close_array
json_add_array 'warnings'
for k in $(json get warnings); do
json_add_object "$k"
json_add_string 'code' "$(json get warning "$k" 'code')"
json_add_string 'info' "$(json get warning "$k" 'info')"
json_close_object
done
json_close_array
if [ "$strict_enforcement" -ne '0' ] && str_contains "$gatewaySummary" '0.0.0.0'; then
json_add_string 'mode' 'strict'
fi
json_close_object
procd_close_data
procd_close_instance
}
service_started() {
[ -n "$pbr_boot_flag" ] && return 0
local error warning c
if nft_file 'exists'; then
procd_set_config_changed firewall
if nft_file 'exists'; then
@@ -2142,12 +2187,26 @@ service_started() {
else
[ -n "$gatewaySummary" ] && output "$serviceName (nft mode) started with gateways:\n${gatewaySummary}"
fi
state print 'errorSummary'
state print 'warningSummary'
error="$(json get error)"
warning="$(json get warning)"
if [ -n "$error" ]; then
for c in $error; do
code="$(json get error "$c" 'code')"
info="$(json get error "$c" 'info')"
output_error "$(get_text "$code" "$info")"
done
fi
if [ -n "$warning" ]; then
for c in $warning; do
code="$(json get warning "$c" 'code')"
info="$(json get warning "$c" 'info')"
output_warning "$(get_text "$code" "$info")"
done
fi
touch "$packageLockFile"
if [ -n "$errorSummary" ]; then
if [ -n "$error" ]; then
return 2
elif [ -n "$warningSummary" ]; then
elif [ -n "$warning" ]; then
return 1
else
return 0
@@ -2165,9 +2224,9 @@ service_triggers() {
load_validate_policy
load_validate_include
procd_close_validate
if [ -n "$pbr_boot_flag" ]; then
if [ -n "$pbr_boot_flag" ] && is_integer "$procd_boot_trigger_delay"; then
output "Setting trigger (on_boot) "
procd_add_raw_trigger "interface.*.up" 5000 "/etc/init.d/${packageName}" start && output_okn || output_failn
procd_add_raw_trigger "interface.*.up" "$procd_boot_trigger_delay" "/etc/init.d/${packageName}" start && output_okn || output_failn
else
procd_open_trigger
procd_add_config_trigger "config.change" 'openvpn' "/etc/init.d/${packageName}" reload 'on_openvpn_change'
@@ -2353,6 +2412,7 @@ status_service() {
# shellcheck disable=SC2120
load_validate_config() {
uci_load_validate "$packageName" "$packageName" "$1" "${2}${3:+ $3}" \
'debug_dnsmasq:bool:0' \
'enabled:bool:0' \
'strict_enforcement:bool:1' \
'ipv6_enabled:bool:0' \
@@ -2364,8 +2424,9 @@ load_validate_config() {
'icmp_interface:or("", tor, uci("network", "@interface"))' \
'ignored_interface:list(or(tor, uci("network", "@interface")))' \
'supported_interface:list(or(ignore, tor, regex("xray_.*"), uci("network", "@interface")))' \
'procd_reload_delay:integer:0' \
'procd_boot_trigger_delay:range(1000,10000):5000' \
'procd_lan_device:list(or(network)):br-lan' \
'procd_reload_delay:uinteger:0' \
'procd_wan_interface:network:wan' \
'procd_wan6_interface:network:wan6' \
'wan_ip_rules_priority:uinteger:30000' \