[ -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"
readonly rtTablesFile='/etc/iproute2/rt_tables'
# package config options
+debug_dnsmasq=
enabled=
fw_mask=
icmp_interface=
ipv6_enabled=
nft_user_set_policy=
nft_user_set_counter=
+procd_boot_trigger_delay=
procd_reload_delay=
procd_lan_device=
procd_wan_interface=
nft_set_timeout=
# run-time
+load_environment_flag=
aghConfigFile='/etc/AdGuardHome/AdGuardHome.yaml'
gatewaySummary=
-errorSummary=
-warningSummary=
wanIface4=
wanIface6=
dnsmasqFileList=
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;;
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"; }
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"; }
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}" ]; }
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!";;
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";;
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'";;
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"
}
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"
}
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'
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
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
_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
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
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; }
load_network "$param"
;;
esac
+ load_environment_flag=1
}
# shellcheck disable=SC2317
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
if [ -n "$wanGW" ]; then
return 0
else
- state add 'errorSummary' 'errorNoWanGateway'
+ json add error 'errorNoWanGateway'
return 1
fi
}
cp -f "$nftTempFile" "$nftPermFile"; then
output_okn
else
- state add 'errorSummary' 'errorNftFileInstall' "$nftTempFile"
+ json add error 'errorNftFileInstall' "$nftTempFile"
output_failn
fi
;;
[ -x "$nft" ] || return 1
if [ "${#nftset4}" -gt '255' ]; then
- state add 'errorSummary' 'errorNftsetNameTooLong' "$nftset4"
+ json add error 'errorNftsetNameTooLong' "$nftset4"
return 1
fi
[ -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
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
- json_close_object
- json_dump
+ return
;;
- 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
+ '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_select ..
;;
- 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() {
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
;;
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
;;
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
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"
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
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
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
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
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"
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"
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
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
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
[ "$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
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")"
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
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
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
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
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}"
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
[ "$((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"
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
;;
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
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
}
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'
case $serviceStartTrigger in
on_interface_reload)
+ output_okn
output 1 "Reloading Interface: $reloadedIface "
json_add_array 'gateways'
process_interface 'all' 'prepare'
cleanup_marking_chains
cleanup_rt_tables
nft_file 'create'
+ output_okn
output 1 'Processing interfaces '
json_add_array 'gateways'
process_interface 'all' 'prepare'
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
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
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'
# 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' \
'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' \