pbr: update to 1.2.0-r2
authorStan Grishin <[email protected]>
Tue, 7 Oct 2025 22:31:41 +0000 (22:31 +0000)
committerStan Grishin <[email protected]>
Fri, 10 Oct 2025 23:34:52 +0000 (16:34 -0700)
Makefile:
* stop shipping/dealing with the firewall hotplug (obsolete)
* install a third user-script (dnsprefetch) by @betonmischer

Config:
* remove obsolete options
* include the new user script

Init-script:
* start much earlier so that on boot, the procd_add_raw_trigger works on all systems
* create a ubus() helper function so that service delete does not produce "Command not found"
* rename options to better reflect their function:
  * procd_lan_device to lan_device
  * procd_wan_interface to uplink_interface
  * procd_wan6_interface to uplink_interface6
  * procd_wan6_metric to uplink_interface6_metric
  * wan_ip_rules_priority to uplink_ip_rules_priority
  * wan_mark to uplink_mark
* visually separate run-time variables from variables loaded from config options
* use ${IPKG_INSTROOT} when sourcing files
* fix typo in str_to_dnsmasq_nftset()
* use pidof to kill dnsmasq in dnsmasq_kill()
* add helper function uci_add_list_if_new()
* add helper function uci_changes()
* add helper function ubus() so that service delete does not produce "Command not found"
* implement the dnsmasq features check similar to dnsmasq init script
* add get_url() function similar to luci package
* add/modify error and warning messages
* change how mktemp is used for more reliable file creation
* unset non-true boolean package config options on load for easier checks later
* improve handling of nft/nft set options
* fewer calls to resolver() and resolver() optimization to speed up the service
* use softlinks instead of duplicating dnsmasq nftset files into each instance
* prevent duplication of dnsmasq nftset elements
* option to target a specific dest dns port in DNS policies
* bugfix: more reliable interface reloads
* display README links to errors/warnings sections if any errors/warnings discovered

Uci-defaults:
* transition from old options to new ones

Signed-off-by: Stan Grishin <[email protected]>
net/pbr/Makefile
net/pbr/files/etc/config/pbr
net/pbr/files/etc/init.d/pbr
net/pbr/files/etc/uci-defaults/90-pbr
net/pbr/files/etc/uci-defaults/99-pbr-version [new file with mode: 0644]
net/pbr/files/usr/share/pbr/pbr.user.dnsprefetch [new file with mode: 0644]

index 1cea31c7289f42ba3baa39c000a004862364bbe1..f66d797357997badd4f35c9bbb9a2ec7069d8764 100644 (file)
@@ -4,8 +4,8 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=pbr
-PKG_VERSION:=1.1.8
-PKG_RELEASE:=36
+PKG_VERSION:=1.2.0
+PKG_RELEASE:=2
 PKG_LICENSE:=AGPL-3.0-or-later
 PKG_MAINTAINER:=Stan Grishin <[email protected]>
 
@@ -75,7 +75,7 @@ define Package/pbr/default/install
        $(INSTALL_CONF) ./files/etc/config/pbr $(1)/etc/config/pbr
        $(INSTALL_DIR) $(1)/usr/share/pbr
        $(INSTALL_DATA) ./files/usr/share/pbr/.keep $(1)/usr/share/pbr/.keep
-       $(INSTALL_DATA) ./files/usr/share/pbr/firewall.include $(1)/usr/share/pbr/firewall.include
+       $(INSTALL_DATA) ./files/usr/share/pbr/pbr.user.dnsprefetch $(1)/usr/share/pbr/pbr.user.dnsprefetch
        $(INSTALL_DATA) ./files/usr/share/pbr/pbr.user.aws $(1)/usr/share/pbr/pbr.user.aws
        $(INSTALL_DATA) ./files/usr/share/pbr/pbr.user.netflix $(1)/usr/share/pbr/pbr.user.netflix
        $(INSTALL_DIR) $(1)/usr/share/nftables.d
@@ -113,7 +113,6 @@ define Package/pbr/prerm
 #!/bin/sh
 # check if we are on real system
 if [ -z "$${IPKG_INSTROOT}" ]; then
-       uci -q delete firewall.pbr || true
        echo -n "Stopping pbr service... "
        /etc/init.d/pbr stop quiet >/dev/null 2>&1 && echo "OK" || echo "FAIL"
        echo -n "Removing rc.d symlink for pbr... "
@@ -148,7 +147,6 @@ define Package/pbr-netifd/prerm
 #!/bin/sh
 # check if we are on real system
 if [ -z "$${IPKG_INSTROOT}" ]; then
-       uci -q delete firewall.pbr || true
        echo -n "Stopping pbr-netifd service... "
        /etc/init.d/pbr stop quiet >/dev/null 2>&1 && echo "OK" || echo "FAIL"
        echo -n "Removing rc.d symlink for pbr... "
index 8bf686f063555b0edc8c92b2b0849c8e4b67c174..de875c5ddcacb814b5f860158e88e28fff0eae7d 100644 (file)
@@ -1,5 +1,4 @@
 config pbr 'config'
-       option debug_dnsmasq '0'
        option enabled '0'
        option verbosity '2'
        option strict_enforcement '1'
@@ -7,7 +6,6 @@ config pbr 'config'
        list resolver_instance '*'
        option ipv6_enabled '0'
        list ignored_interface 'vpnserver'
-       option boot_timeout '30'
        option rule_create_option 'add'
        option procd_boot_trigger_delay '5000'
        option procd_reload_delay '1'
@@ -26,6 +24,10 @@ config pbr 'config'
        list webui_supported_protocol 'tcp udp'
        list webui_supported_protocol 'icmp'
 
+config include
+       option path '/usr/share/pbr/pbr.user.dnsprefetch'
+       option enabled '0'
+
 config include
        option path '/usr/share/pbr/pbr.user.aws'
        option enabled '0'
index 1f698ec4b2f304441db7acb694381e859ff50e9a..b87ef5dd45cf7afb4f2599d387a16f672b6cb209 100755 (executable)
@@ -6,12 +6,10 @@
 # sysctl net.ipv4.conf.all.rp_filter=1
 
 # shellcheck disable=SC2034
-START=94
+START=20
 # shellcheck disable=SC2034
 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
@@ -34,14 +32,13 @@ fi
 
 readonly packageName='pbr'
 readonly PKG_VERSION='dev-test'
-readonly packageCompat='14'
+readonly packageCompat='17'
 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 packageDnsmasqFile="/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'
@@ -69,8 +66,17 @@ readonly torConfigFile='/etc/tor/torrc'
 readonly xrayIfacePrefix='xray_'
 readonly rtTablesFile='/etc/iproute2/rt_tables'
 
+# Silence "Command failed: Not found" for redundant procd service delete calls
+__UBUS_BIN="$(command -v ubus || echo /bin/ubus)"
+ubus() {
+       if [ "$1" = "call" ] && [ "$2" = "service" ] && [ "$3" = "delete" ]; then
+               "$__UBUS_BIN" "$@" >/dev/null 2>&1 || true
+       else
+               "$__UBUS_BIN" "$@"
+       fi
+}
+
 # package config options
-debug_dnsmasq=
 enabled=
 fw_mask=
 icmp_interface=
@@ -80,17 +86,17 @@ nft_user_set_policy=
 nft_user_set_counter=
 procd_boot_trigger_delay=
 procd_reload_delay=
-procd_lan_device=
-procd_wan_interface=
-procd_wan6_interface=
-procd_wan6_metric='128'
+lan_device=
+uplink_interface=
+uplink_interface6=
+uplink_interface6_metric='128'
 resolver_set=
 resolver_instance=
 strict_enforcement=
 supported_interface=
 verbosity=
-wan_ip_rules_priority=
-wan_mark=
+uplink_ip_rules_priority=
+uplink_mark=
 nft_rule_counter=
 nft_set_auto_merge=
 nft_set_counter=
@@ -105,7 +111,6 @@ aghConfigFile='/etc/AdGuardHome/AdGuardHome.yaml'
 gatewaySummary=
 wanIface4=
 wanIface6=
-dnsmasqFileList=
 ifaceMark=
 ifaceTableID=
 ifacePriority=
@@ -114,37 +119,41 @@ ifacesSupported=
 firewallWanZone=
 wanGW4=
 wanGW6=
-pbr_boot_flag=
+pbrBootFlag=
 serviceStartTrigger=
 processDnsPolicyError=
 processPolicyError=
 processPolicyWarning=
-resolver_set_supported=
-policy_routing_nft_prev_param4=
-policy_routing_nft_prev_param6=
-nft_rule_params=
-nft_set_params=
+resolverSetSupported=
+pbrNftPrevParam4=
+pbrNftPrevParam6=
+nftRuleParams=
+nftSetParams=
 torDnsPort=
 torTrafficPort=
+dnsmasq_features=
+dnsmasq_ubus=
+loadEnvironmentFlag=
+loadPackageConfigFlag=
 
 # shellcheck disable=SC1091
-. /lib/functions.sh
+. "${IPKG_INSTROOT}/lib/functions.sh"
 # shellcheck disable=SC1091
-. /lib/functions/network.sh
+. "${IPKG_INSTROOT}/lib/functions/network.sh"
 # shellcheck disable=SC1091
-. /usr/share/libubox/jshn.sh
+. "${IPKG_INSTROOT}/usr/share/libubox/jshn.sh"
 
 debug() { local i j; for i in "$@"; do eval "j=\$$i"; logger "${packageName:+-t $packageName}" "${i}: ${j} "; done; }
-str_contains() { [ -n "$1" ] && [ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
-str_contains_word() { echo "$1" | grep -q -w "$2"; }
-str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
+str_contains() { [ "${1//$2}" != "$1" ]; }
+str_contains_word() { echo "$1" | grep -qw "$2"; }
+str_extras_to_underscore() { echo "$1" | sed -E 's/[\. ~`!@#$%^&*()+=,<>?;:\/\\-]/_/g; s/_+/_/g'; }
 str_extras_to_space() { echo "$1" | tr ',;{}' ' '; }
 str_first_value_interface() { local i; for i in $1; do is_supported_interface "$i" && { echo "$i"; break; }; done; }
 str_first_value_ipv4() { local i; for i in $1; do is_ipv4 "$i" && { echo "$i"; break; }; done; }
 str_first_value_ipv6() { local i; for i in $1; do is_ipv6 "$i" && { echo "$i"; break; }; done; }
 str_first_word() { echo "${1%% *}"; }
 str_replace() { echo "${1//$2/$3}"; }
-str_to_dnsmsaq_nftset() { echo "$1" | tr ' ' '/'; }
+str_to_dnsmasq_nftset() { echo "$1" | tr ' ' '/'; }
 str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
 str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
 # shellcheck disable=SC3060
@@ -177,8 +186,8 @@ quiet_mode() {
 pbr_find_iface() {
        local iface i param="$2"
        case "$param" in
-               wan6)  iface="$procd_wan6_interface";;
-               wan|*) iface="$procd_wan_interface";;
+               wan6)  iface="$uplink_interface6";;
+               wan|*) iface="$uplink_interface";;
        esac
        eval "$1"='${iface}'
 }
@@ -193,7 +202,7 @@ pbr_get_gateway4() {
 }
 pbr_get_gateway6() {
        local iface="$2" dev="$3" gw
-       [ "$iface" = "$procd_wan_interface" ] && iface="$procd_wan6_interface"  
+       [ "$iface" = "$uplink_interface" ] && iface="$uplink_interface6"  
        network_get_gateway6 gw "$iface" true
        if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
                gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | grep 'scope global' | awk '{print $2}')"
@@ -224,6 +233,7 @@ inline_set() {
 is_bad_user_file_nft_call() { grep -q '"\$nft" list' "$1" || grep '"\$nft" -f' "$1"; }
 # shellcheck disable=SC2317
 is_config_enabled() {
+# shellcheck disable=SC2329
        _check_config() { local en; config_get_bool en "$1" 'enabled' '1'; [ "$en" -gt '0' ] && _cfg_enabled=0; }
        local cfg="$1" _cfg_enabled=1
        [ -n "$1" ] || return 1
@@ -238,7 +248,7 @@ uci_get_device() {
        eval "$1=$__tmp"
 }
 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_default_dev() { [ "$1" = "$(ip -4 route show default | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1);exit}}')" ]; }
 is_disabled_interface() { [ "$(uci_get 'network' "$1" 'disabled')" = '1' ]; }
 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,}$'; }
@@ -257,7 +267,7 @@ is_ipv6_local_scope() { is_ipv6_local_link "$1" || is_ipv6_local_unique "$1"; }
 is_ipv6_local_link() { [ "${1:0:4}" = 'fe80' ]; }
 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_lan() { local d; network_get_device d "$1"; str_contains "$lan_device" "$d"; }
 is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp" ]; }
 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})$'; }
@@ -287,11 +297,11 @@ is_url_ftp() { [ "$1" != "${1#ftp://}" ]; }
 is_url_http() { [ "$1" != "${1#http://}" ]; }
 is_url_https() { [ "$1" != "${1#https://}" ]; }
 is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; }
-is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; }
+is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1##wan6}" != "$1" ] || [ "${1%%wan6}" != "$1" ]; }
 is_wg() { local p lp; network_get_protocol p "$1"; uci_get_listen_port lp "$1"; [ -z "$lp" ] && [ "${p:0:9}" = "wireguard" ]; }
 is_wg_server() { local p lp; network_get_protocol p "$1"; uci_get_listen_port lp "$1"; [ -n "$lp" ] && [ "${p:0:9}" = "wireguard" ]; }
 is_xray() { [ -n "$(get_xray_traffic_port "$1")" ]; }
-dnsmasq_kill() { killall -q -s HUP dnsmasq; }
+dnsmasq_kill() { pidof dnsmasq >/dev/null && kill -HUP $(pidof dnsmasq); }
 dnsmasq_restart() { output 3 'Restarting dnsmasq '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; }
 # shellcheck disable=SC2155
 get_ss_traffic_ports() { local i="$(jsonfilter -i "$ssConfigFile" -q -e "@.inbounds[*].port")"; echo "${i:-443}"; }
@@ -313,12 +323,31 @@ ipv4_leases_to_nftset(){ [ -s '/tmp/dhcp.leases' ] && awk -v arg="$1" 'BEGIN{fs=
 ipv6_leases_to_nftset(){ [ -s '/tmp/hosts/odhcpd' ] && awk -v arg="$1" 'BEGIN{fs=""};$0~arg{printf fs$1;fs=","}' /tmp/hosts/odhcpd;}
 # shellcheck disable=SC3037
 ports_to_nftset() { echo -en "$1"; }
-get_mark_nft_chains() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_" | awk '{ print $2 }'; }
-get_nft_sets() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep 'set' | grep "${nftPrefix}_" | awk '{ print $2 }'; }
+get_mark_nft_chains() { "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_" | awk '{ print $2 }'; }
+get_nft_sets() { "$nft" list table inet "$nftTable" 2>/dev/null | grep 'set' | grep "${nftPrefix}_" | awk '{ print $2 }'; }
 __ubus_get() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "$1"; }
 ubus_get_status() { __ubus_get "@.${packageName}.instances.main.data.status.${1}"; }
 ubus_get_interface() { __ubus_get "@.${packageName}.instances.main.data.gateways[@.name='${1}']${2:+.$2}"; }
 ubus_get_gateways() { __ubus_get "@.${packageName}.instances.main.data.gateways"; }
+uci_add_list_if_new() {
+       local PACKAGE="$1"
+       local CONFIG="$2"
+       local OPTION="$3"
+       local VALUE="$4"
+       local i
+       [ -n "$PACKAGE" ] && [ -n "$CONFIG" ] && [ -n "$OPTION" ] && [ -n "$VALUE" ] || return 1
+       for i in $(uci_get "$PACKAGE" "$CONFIG" "$OPTION"); do
+               [ "$i" = "$VALUE" ] && return 0
+       done
+       uci_add_list "$PACKAGE" "$CONFIG" "$OPTION" "$VALUE"
+}
+uci_changes() {
+       local PACKAGE="$1"
+       local CONFIG="$2"
+       local OPTION="$3"
+       [ -s "${UCI_CONFIG_DIR:-/etc/config/}${PACKAGE}" ] && \
+       [ -n "$(/sbin/uci ${UCI_CONFIG_DIR:+-c $UCI_CONFIG_DIR} changes "$PACKAGE${CONFIG:+.$CONFIG}${OPTION:+.$OPTION}")" ]
+}
 uci_get_listen_port() {
        local __tmp
        __tmp="$(uci_get 'network' "$2" 'listen_port')"
@@ -335,17 +364,8 @@ check_agh() { [ -x "$agh" ] && { [ -s "$aghConfigFile" ] || [ -s "${agh%/*}/AdGu
 check_dnsmasq() { command -v dnsmasq >/dev/null 2>&1; }
 check_unbound() { command -v unbound >/dev/null 2>&1; }
 check_dnsmasq_nftset() {
-       local o;
-       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'
+       [ -z "$dnsmasq_features" ] && dnsmasq_features="$(dnsmasq --version | grep -m1 'Compile time options:' | cut -d: -f2) "
+       [ "${dnsmasq_features#* nftset }" != "$dnsmasq_features" ]
 }
 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; }
@@ -356,63 +376,71 @@ try() {
        fi
 }
 
+get_url() {
+       printf "https://docs.openwrt.melmac.ca/%s/%s/%s" "$packageName" "${PKG_VERSION%%-*}" "$1"
+}
+
 get_text() {
        local r="$1"; shift;
        case "$r" in
-               errorConfigValidation) printf "Config (%s) validation failure" "$$packageConfigFile";;
+               errorConfigValidation) printf "Config (%s) validation failure" "$packageConfigFile";;
                errorNoNft) printf "Resolver set support (%s) requires nftables, but nft binary cannot be found" "$resolver_set";;
                errorResolverNotSupported) printf "Resolver set (%s) is not supported on this system" "$resolver_set";;
                errorServiceDisabled) printf "The %s service is currently disabled" "$packageName";;
                errorNoWanGateway) printf "The %s service failed to discover WAN gateway" "$serviceName";;
-               errorNoWanInterface) printf "The %s interface not found, you need to set the 'pbr.config.procd_wan_interface' option" "$@";;
-               errorNoWanInterfaceHint) printf "Refer to %s" 'https://docs.openwrt.melmac.ca/pbr/#procd_wan_interface';;
-               errorNftsetNameTooLong) printf "The nft set name '%s' is longer than allowed 255 characters" "$@";;
-               errorUnexpectedExit) printf "Unexpected exit or service termination: '%s'" "$@";;
-               errorPolicyNoSrcDest) printf "Policy '%s' has no source/destination parameters" "$@";;
-               errorPolicyNoInterface) printf "Policy '%s' has no assigned interface" "$@";;
-               errorPolicyNoDns) printf "Policy '%s' has no assigned DNS" "$@";;
-               errorPolicyProcessNoInterfaceDns) printf "Interface '%s' has no assigned DNS" "$@";;
-               errorPolicyUnknownInterface) printf "Policy '%s' has an unknown interface" "$@";;
-               errorPolicyProcessCMD) printf "'%s'" "$@";;
-               errorFailedSetup) printf "Failed to set up '%s'" "$@";;
-               errorFailedReload) printf "Failed to reload '%s'" "$@";;
-               errorUserFileNotFound) printf "Custom user file '%s' not found or empty" "$@";;
-               errorUserFileSyntax) printf "Syntax error in custom user file '%s'" "$@";;
-               errorUserFileRunning) printf "Error running custom user file '%s'" "$@";;
-               errorUserFileNoCurl) printf "Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed" "$@";;
+               errorNoUplinkInterface) printf "The %s interface not found, you need to set the 'pbr.config.uplink_interface' option" "$1";;
+               errorNoUplinkInterfaceHint) printf "Refer to %s" "$1";;
+               errorNftsetNameTooLong) printf "The nft set name '%s' is longer than allowed 255 characters" "$1";;
+               errorUnexpectedExit) printf "Unexpected exit or service termination: '%s'" "$1";;
+               errorPolicyNoSrcDest) printf "Policy '%s' has no source/destination parameters" "$1";;
+               errorPolicyNoInterface) printf "Policy '%s' has no assigned interface" "$1";;
+               errorPolicyNoDns) printf "Policy '%s' has no assigned DNS" "$1";;
+               errorPolicyProcessNoInterfaceDns) printf "Interface '%s' has no assigned DNS" "$1";;
+               errorPolicyUnknownInterface) printf "Policy '%s' has an unknown interface" "$1";;
+               errorPolicyProcessCMD) printf "'%s'" "$1";;
+               errorFailedSetup) printf "Failed to set up '%s'" "$1";;
+               errorFailedReload) printf "Failed to reload '%s'" "$1";;
+               errorUserFileNotFound) printf "Custom user file '%s' not found or empty" "$1";;
+               errorUserFileSyntax) printf "Syntax error in custom user file '%s'" "$1";;
+               errorUserFileRunning) printf "Error running custom user file '%s'" "$1";;
+               errorUserFileNoCurl) printf "Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed" "$1";;
                errorNoGateways) printf "Failed to set up any gateway";;
-               errorResolver) printf "Resolver '%s'" "$@";;
-               errorPolicyProcessNoIpv6) printf "Skipping IPv6 policy '%s' as IPv6 support is disabled" "$@";;
-               errorPolicyProcessUnknownFwmark) printf "Unknown packet mark for interface '%s'" "$@";;
-               errorPolicyProcessMismatchFamily) printf "Mismatched IP family between in policy '%s'" "$@";;
-               errorPolicyProcessUnknownProtocol) printf "Unknown protocol in policy '%s'" "$@";;
-               errorPolicyProcessInsertionFailed) printf "Insertion failed for both IPv4 and IPv6 for policy '%s'" "$@";;
-               errorPolicyProcessInsertionFailedIpv4) printf "Insertion failed for IPv4 for policy '%s'" "$@";;
-               errorPolicyProcessUnknownEntry) printf "Unknown entry in policy '%s'" "$@";;
+               errorResolver) printf "Resolver '%s'" "$1";;
+               errorPolicyProcessNoIpv6) printf "Skipping IPv6 policy '%s' as IPv6 support is disabled" "$1";;
+               errorPolicyProcessUnknownFwmark) printf "Unknown packet mark for interface '%s'" "$1";;
+               errorPolicyProcessMismatchFamily) printf "Mismatched IP family between in policy '%s'" "$1";;
+               errorPolicyProcessUnknownProtocol) printf "Unknown protocol in policy '%s'" "$1";;
+               errorPolicyProcessInsertionFailed) printf "Insertion failed for both IPv4 and IPv6 for policy '%s'" "$1";;
+               errorPolicyProcessInsertionFailedIpv4) printf "Insertion failed for IPv4 for policy '%s'" "$1";;
+               errorPolicyProcessUnknownEntry) printf "Unknown entry in policy '%s'" "$1";;
                errorInterfaceRoutingEmptyValues) printf "Received empty tid/mark or interface name when setting up routing";;
-               errorFailedToResolve) printf "Failed to resolve '%s'" "$@";;
-               errorTryFailed) printf "Command failed: %s" "$@";;
-               errorNftFileInstall) printf "Failed to install fw4 nft file '%s'" "$@";;
-               errorDownloadUrlNoHttps) printf "Failed to download '%s', HTTPS is not supported" "$@";;
-               errorDownloadUrl) printf "Failed to download '%s'" "$@";;
-               errorNoDownloadWithSecureReload) printf "Policy '%s' refers to URL which can't be downloaded in 'secure_reload' mode" "$@";;
+               errorFailedToResolve) printf "Failed to resolve '%s'" "$1";;
+               errorTryFailed) printf "Command failed: %s" "$1";;
+               errorNftFileInstall) printf "Failed to install fw4 nft file '%s'" "$1";;
+               errorDownloadUrlNoHttps) printf "Failed to download '%s', HTTPS is not supported" "$1";;
+               errorDownloadUrl) printf "Failed to download '%s'" "$1";;
+               errorNoDownloadWithSecureReload) printf "Policy '%s' refers to URL which can't be downloaded in 'secure_reload' mode" "$1";;
                errorFileSchemaRequiresCurl) printf "The file:// schema requires curl, but it's not detected on this system";;
-               errorIncompatibleUserFile) printf "Incompatible custom user file detected '%s'" "$@";;
-               errorDefaultFw4TableMissing) printf "Default fw4 table '%s' is missing" "$@";;
-               errorDefaultFw4ChainMissing) printf "Default fw4 chain '%s' is missing" "$@";;
-               errorRequiredBinaryMissing) printf "Required binary '%s' is missing" "$@";;
-               errorInterfaceRoutingUnknownDevType) printf "Unknown IPv6 Link type for device '%s'" "$@";;
+               errorIncompatibleUserFile) printf "Incompatible custom user file detected '%s'" "$1";;
+               errorDefaultFw4TableMissing) printf "Default fw4 table '%s' is missing" "$1";;
+               errorDefaultFw4ChainMissing) printf "Default fw4 chain '%s' is missing" "$1";;
+               errorRequiredBinaryMissing) printf "Required binary '%s' is missing" "$1";;
+               errorInterfaceRoutingUnknownDevType) printf "Unknown IPv6 Link type for device '%s'" "$1";;
                errorUplinkDown) printf "Uplink/WAN interface is still down, increase value of 'procd_boot_trigger_delay' option";;
-               warningInvalidOVPNConfig) printf "Invalid OpenVPN config for '%s' interface" "$@";;
+               errorMktempFileCreate) printf "Failed to create temporary file with mktemp mask: '%s'" "$1";;
+               errorSummary) printf "Errors encountered, please check %s" "$1";;
+               warningInvalidOVPNConfig) printf "Invalid OpenVPN config for '%s' interface" "$1";;
                warningResolverNotSupported) printf "Resolver set (%s) is not supported on this system" "$resolver_set";;
-               warningPolicyProcessCMD) printf "'%s'" "$@";;
-               warningTorUnsetParams) printf "Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'" "$@";;
-               warningTorUnsetProto) printf "Please unset 'proto' or set 'proto' to 'all' for policy '%s'" "$@";;
-               warningTorUnsetChainNft) printf "Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'" "$@";;
-               warningOutdatedWebUIApp) printf "The WebUI application is outdated (version %s), please update it" "$@";;
-               warningDnsmasqInstanceNoConfdir) printf "Dnsmasq instance '%s' targeted in settings, but it doesn't have its own confdir" "$@";;
-               warningDhcpLanForce) printf "Please set 'dhcp.%s.force=1' to speed up service start-up" "$@";;
-               *) printf "Unknown error/warning '%s'" "$@";;
+               warningPolicyProcessCMD) printf "'%s'" "$1";;
+               warningTorUnsetParams) printf "Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'" "$1";;
+               warningTorUnsetProto) printf "Please unset 'proto' or set 'proto' to 'all' for policy '%s'" "$1";;
+               warningTorUnsetChainNft) printf "Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'" "$1";;
+               warningOutdatedWebUIApp) printf "The WebUI application is outdated (version %s), please update it" "$1";;
+               warningDnsmasqInstanceNoConfdir) printf "Dnsmasq instance '%s' targeted in settings, but it doesn't have its own confdir" "$1";;
+               warningDhcpLanForce) printf "Please set 'dhcp.%s.force=1' to speed up service start-up" "$1";;
+               warningSummary) printf "Warnings encountered, please check %s" "$(get_url '#WarningMessagesDetails')";;
+               warningIncompatibleDHCPOption6) printf "Incompatible DHCP Option 6 for interface %s" "$1";;
+               *) printf "Unknown error/warning '%s'" "$1";;
        esac
 }
 
@@ -439,9 +467,11 @@ process_url() {
        else
                unset dl_https_supported
        fi
-       while [ -z "$dl_temp_file" ] || [ -e "$dl_temp_file" ]; do
-               dl_temp_file="$(mktemp -u -q -t "${packageName}_tmp.XXXXXXXX")"
-       done
+       dl_temp_file="$(mktemp -q -t "${packageName}_tmp.XXXXXXXX")"
+       if [ -z "$dl_temp_file" ] || [ ! -e "$dl_temp_file"  ]; then
+               json add error 'errorMktempFileCreate' "${packageName}_tmp.XXXXXXXX"
+               return 1
+       fi
        if is_url_file "$url" && ! is_present 'curl'; then
                json add error 'errorFileSchemaRequiresCurl' "$url"
        elif is_url_https "$url" && [ -z "$dl_https_supported" ]; then
@@ -456,70 +486,67 @@ process_url() {
 
 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_bool enabled                   'config' 'enabled'                  '0'
+       config_get      fw_mask                   'config' 'fw_mask'                  '00ff0000'
        config_get      icmp_interface            'config' 'icmp_interface'
        config_get      ignored_interface         'config' 'ignored_interface'
-       config_get_bool ipv6_enabled              'config' 'ipv6_enabled' '0'
-       config_get_bool nft_rule_counter          'config' 'nft_rule_counter' '0'
-       config_get_bool nft_set_auto_merge        'config' 'nft_set_auto_merge' '1'
-       config_get_bool nft_set_counter           'config' 'nft_set_counter' '0'
-       config_get_bool nft_set_flags_interval    'config' 'nft_set_flags_interval' '1'
-       config_get_bool nft_set_flags_timeout     'config' 'nft_set_flags_timeout' '0'
+       config_get_bool ipv6_enabled              'config' 'ipv6_enabled'             '0'
+       config_get_bool nft_rule_counter          'config' 'nft_rule_counter'         '0'
+       config_get_bool nft_set_auto_merge        'config' 'nft_set_auto_merge'       '1'
+       config_get_bool nft_set_counter           'config' 'nft_set_counter'          '0'
+       config_get_bool nft_set_flags_interval    'config' 'nft_set_flags_interval'   '1'
+       config_get_bool nft_set_flags_timeout     'config' 'nft_set_flags_timeout'    '0'
+       config_get_bool nft_user_set_counter      'config' 'nft_user_set_counter'     '0'
        config_get      nft_set_gc_interval       'config' 'nft_set_gc_interval'
-       config_get      nft_set_policy            'config' 'nft_set_policy' 'performance'
+       config_get      nft_set_policy            'config' 'nft_set_policy'          'performance'
        config_get      nft_set_timeout           'config' 'nft_set_timeout'
        config_get      resolver_set              'config' 'resolver_set'
-       config_get      resolver_instance         'config' 'resolver_instance' '*'
-       config_get_bool strict_enforcement        'config' 'strict_enforcement' '1'
+       config_get      resolver_instance         'config' 'resolver_instance'        '*'
+       config_get_bool strict_enforcement        'config' 'strict_enforcement'       '1'
        config_get      supported_interface       'config' 'supported_interface'
-       config_get      verbosity                 'config' 'verbosity' '2'
+       config_get      verbosity                 'config' 'verbosity'                '2'
        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'
+       config_get      lan_device                'config' 'lan_device'               'br-lan'
+       config_get      procd_reload_delay        'config' 'procd_reload_delay'       '0'
+       config_get      uplink_interface          'config' 'uplink_interface'         'wan'
+       config_get      uplink_interface6         'config' 'uplink_interface6'        'wan6'
+       config_get      uplink_ip_rules_priority  'config' 'uplink_ip_rules_priority' '30000'
+       config_get      uplink_mark               'config' 'uplink_mark'              '00010000'
 
        fw_mask="0x${fw_mask}"
-       wan_mark="0x${wan_mark}"
-       if [ -x "$agh" ] && [ ! -s "$aghConfigFile" ]; then
-               [ -s "${agh%/*}/AdGuardHome.yaml" ] && aghConfigFile="${agh%/*}/AdGuardHome.yaml"
-       fi
-       [ -n "$ipv6_enabled" ] && [ "$ipv6_enabled" -eq '0' ] && unset ipv6_enabled
-       [ -n "$nft_user_set_counter" ] && [ "$nft_user_set_counter" -eq '0' ] && unset nft_user_set_counter
+       uplink_mark="0x${uplink_mark}"
+
+       [ "$resolver_set" = 'none' ]      && unset resolver_set
+       [ "$enabled" = '1' ]              || unset enabled
+       [ "$ipv6_enabled" = '1' ]         || unset ipv6_enabled
+       [ "$strict_enforcement" = '1' ]   || unset strict_enforcement
+
        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
-       [ "$nft_set_flags_interval" != '1' ] && unset nft_set_flags_interval
-       [ "$nft_set_flags_timeout" != '1' ]  && unset nft_set_flags_timeout
-       [ -z "${nft_set_flags_timeout}${nft_set_timeout}" ] && unset nft_set_gc_interval
        local nft_set_flags
-       if [ -n "${nft_set_flags_interval}${nft_set_flags_timeout}" ]; then
-               [ -n "$nft_set_flags_interval" ] && nft_set_flags='flags interval'
-               if [ -n "$nft_set_flags_timeout" ]; then
-                       if [ -n "$nft_set_flags" ]; then
-                               nft_set_flags="${nft_set_flags}, timeout"
-                       else
-                               nft_set_flags='flags timeout'
-                       fi
-               fi
-       fi
+       case "${nft_set_flags_interval}:${nft_set_flags_timeout}" in
+               1:1) nft_set_flags="flags interval, timeout${nft_set_timeout:+; timeout $nft_set_timeout}";;
+               1:0) nft_set_flags='flags interval';;
+               0:1) nft_set_flags="flags timeout${nft_set_timeout:+; timeout $nft_set_timeout}";;
+               0:0) nft_set_flags='';;
+       esac
+
+       [ "$nft_user_set_counter" = '1' ]    || unset nft_user_set_counter
+       [ "$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
+       [ "$nft_set_flags_interval" = '1' ]  || unset nft_set_flags_interval
+       [ "$nft_set_flags_timeout" = '1' ]   || unset nft_set_flags_timeout
+       [ -n "${nft_set_flags_timeout}${nft_set_timeout}" ] || unset nft_set_gc_interval
 
-       nft_rule_params="${nft_rule_counter:+counter}"
+       nftRuleParams="${nft_rule_counter:+counter}"
 
-       nft_set_params=" \
+       nftSetParams=" \
                ${nft_set_auto_merge:+ auto-merge;} \
                ${nft_set_counter:+ counter;} \
                ${nft_set_flags:+ $nft_set_flags;} \
@@ -527,21 +554,42 @@ load_package_config() {
                ${nft_set_policy:+ policy "$nft_set_policy";} \
                ${nft_set_timeout:+ timeout "$nft_set_timeout";} \
                "
+
+       if [ -x "$agh" ] && [ ! -s "$aghConfigFile" ]; then
+               [ -s "${agh%/*}/AdGuardHome.yaml" ] && aghConfigFile="${agh%/*}/AdGuardHome.yaml"
+       fi
+       unset loadEnvironmentFlag
+       loadPackageConfigFlag='true'
 }
 
 # shellcheck disable=SC2317
 load_environment() {
        _system_health_check() {
-               _check_dhcp_force() {
+# shellcheck disable=SC2329
+               _check_lan_compatibility() {
                        is_lan "$1" || return 0
-                       if [ "$(uci_get dhcp "$1" force 0)" = '0' ]; then
+                       local force ipaddr dhcp_option i
+                       config_get force       "$1" force
+                       config_get ipaddr      "$1" ipaddr
+                       if [ "$force" = '0' ]; then
                                json add warning 'warningDhcpLanForce' "$1"
                        fi
+                       [ -n "$resolver_set" ] || return 0
+                       for i in $(uci_get 'dhcp' "$1" 'dhcp_option'); do
+                       local option="${i%%,*}" value="${i#*,}"
+                       if [ "$option" = '6' ] && [ "$value" != "${ipaddr%%/*}" ]; then
+                               json add warning 'warningIncompatibleDHCPOption6' "${1}: ${value}"
+                       fi
+                       done
                }
                local i _ret=0
+               if ! check_nft; then
+                       json add error 'errorNoNft'
+                       _ret='1'
+               fi
                if [ "$(uci_get 'firewall' 'defaults' 'auto_includes')" = '0' ]; then
                        uci_remove 'firewall' 'defaults' 'auto_includes'
-                       uci_commit firewall
+                       uci_commit 'firewall'
                fi
                if [ "$(readlink /sbin/ip)" != "$ip_full" ]; then
                        json add error 'errorRequiredBinaryMissing' 'ip-full'
@@ -564,16 +612,16 @@ load_environment() {
                        fi
                done
                config_load 'network'
-               config_foreach _check_dhcp_force 'interface'
+               config_foreach _check_lan_compatibility 'interface'
                return "$_ret"
        }
        local param="$1" validation_result="$2"
+       [ -z "$loadEnvironmentFlag" ] || return 0
+       [ -n "$loadPackageConfigFlag" ] || load_package_config "$param"
        case "$param" in
                on_boot|on_start)
-                       json init
                        output 1 "Loading environment ($param) "
-                       load_package_config "$param"
-                       if [ "$enabled" -eq '0' ]; then
+                       if [ -z "$enabled" ]; then
                                output 1 "$_FAIL_\n"
                                json add error 'errorServiceDisabled'
                                output_error "$(get_text 'errorServiceDisabled')"
@@ -589,27 +637,28 @@ load_environment() {
                                return 1
                        fi
                        _system_health_check || { output 1 "$_FAIL_\n"; return 1; }
-                       resolver 'check_support' && resolver 'configure_instances'
+                       resolver 'check_support'
                        load_network "$param"
                        output 1 "$_OK_\n"
                ;;
-               on_stop)
-                       json init
-                       output 1 "Loading environment ($param) "
-                       load_package_config "$param"
+               on_triggers)
                        load_network "$param"
-                       output 1 "$_OK_\n"
                ;;
-               on_triggers|*)
-                       load_package_config "$param"
+               on_interface_reload|on_reload|on_stop|*)
+                       output 1 "Loading environment ($param) "
                        load_network "$param"
+                       resolver 'check_support'
+                       output 1 "$_OK_\n"
                ;;
        esac
+       loadEnvironmentFlag='true'
 }
 
 # shellcheck disable=SC2317
 load_network() {
+# shellcheck disable=SC2329
        _build_ifaces_supported() { is_supported_interface "$1" && ! str_contains "$ifacesSupported" "$1" && ifacesSupported="${ifacesSupported}${1} "; }
+# shellcheck disable=SC2329
        _find_firewall_wan_zone() { [ "$(uci_get 'firewall' "$1" 'name')" = "wan" ] && firewallWanZone="$1"; }
        local i param="$1"
        local dev4 dev6
@@ -622,12 +671,12 @@ load_network() {
                config_load 'network'
                config_foreach _build_ifaces_supported 'interface'
        fi
-       wanIface4="$procd_wan_interface"
+       wanIface4="$uplink_interface"
        network_get_device dev4 "$wanIface4"
        [ -z "$dev4" ] && network_get_physdev dev4 "$wanIface4"
        [ -z "$wanGW4" ] && pbr_get_gateway4 wanGW4 "$wanIface4" "$dev4"
        if [ -n "$ipv6_enabled" ]; then
-               wanIface6="$procd_wan6_interface"
+               wanIface6="$uplink_interface6"
                network_get_device dev6 "$wanIface6"
                [ -z "$dev6" ] && network_get_physdev dev6 "$wanIface6"
                [ -z "$wanGW6" ] && pbr_get_gateway6 wanGW6 "$wanIface6" "$dev6"
@@ -646,9 +695,9 @@ load_network() {
 
 is_wan_up() {
        local param="$1"
-       if [ -z "$(uci_get network "$procd_wan_interface")" ]; then
-               json add error 'errorNoWanInterface' "$procd_wan_interface"
-               json add error 'errorNoWanInterfaceHint'
+       if [ -z "$(uci_get network "$uplink_interface")" ]; then
+               json add error 'errorNoUplinkInterface' "$uplink_interface"
+               json add error 'errorNoUplinkInterfaceHint' "$(get_url '#uplink_interface')"
                return 1
        fi
        network_flush_cache
@@ -661,10 +710,10 @@ is_wan_up() {
        fi
 }
 
-nft_call() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
+nft_call() { "$nft" "$@" >/dev/null 2>&1; }
 nft_file() {
        local i
-       [ -x "$nft" ] || return 1
+
        case "$1" in
                add|add_command)
                        shift
@@ -699,7 +748,7 @@ nft_file() {
                ;;
        esac
 }
-nft() { [ -x "$nft" ] && [ -n "$*" ] && nft_file 'add_command' "$@"; }
+nft() { [ -n "$*" ] && nft_file 'add_command' "$@"; }
 nft4() { nft "$@"; }
 nft6() { [ -n "$ipv6_enabled" ] || return 0; nft "$@"; }
 nftset() {
@@ -709,8 +758,6 @@ nftset() {
        nftset4="${nftPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
        nftset6="${nftPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
 
-       [ -x "$nft" ] || return 1
-
        if [ "${#nftset4}" -gt '255' ]; then 
                json add error 'errorNftsetNameTooLong' "$nftset4"
                return 1
@@ -742,46 +789,46 @@ nftset() {
                ;;
                add_dnsmasq_element)
                        [ -n "$ipv6_enabled" ] || unset nftset6
-                       # shellcheck disable=SC2086
-                       echo "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" | tee -a $dnsmasqFileList >/dev/null 2>&1 && ipv4_error=0
+                       grep -qxF "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" "$packageDnsmasqFile" && return 0
+                       echo "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" >> "$packageDnsmasqFile" && ipv4_error=0
                ;;
                create)
                        case "$type" in
                                ip|net)
-                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nft_set_params comment \"$comment\";}" && ipv4_error=0
-                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nft_set_params comment \"$comment\";}" && ipv6_error=0
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
                                ;;
                                mac)
-                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; $nft_set_params comment \"$comment\";}" && ipv4_error=0
-                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; $nft_set_params comment \"$comment\";}" && ipv6_error=0
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
                                ;;
                                esac
                ;;
                create_dnsmasq_set)
-                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nft_set_params comment \"$comment\";}" && ipv4_error=0
-                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nft_set_params comment \"$comment\";}" && ipv6_error=0
+                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
+                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
                ;;
                create_user_set)
                        case "$type" in
                                ip|net)
-                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nft_set_params comment \"$comment\";}" && ipv4_error=0
-                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nft_set_params comment \"$comment\";}" && ipv6_error=0
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
                                        case "$target" in
                                                dst)
-                                                       nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv4Flag}" daddr "@${nftset4}" "${nft_rule_params}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
-                                                       nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv6Flag}" daddr "@${nftset6}" "${nft_rule_params}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                                       nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv4Flag}" daddr "@${nftset4}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                                       nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv6Flag}" daddr "@${nftset6}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
                                                ;;
                                                src)
-                                                       nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv4Flag}" saddr "@${nftset4}" "${nft_rule_params}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
-                                                       nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv6Flag}" saddr "@${nftset6}" "${nft_rule_params}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                                       nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv4Flag}" saddr "@${nftset4}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                                       nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv6Flag}" saddr "@${nftset6}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
                                                ;;
                                        esac
                                        ;;
                                mac)
-                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; $nft_set_params comment \"$comment\"; }" && ipv4_error=0
-                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; $nft_set_params comment \"$comment\"; }" && ipv6_error=0
-                                       nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" "${nft_rule_params}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
-                                       nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" "${nft_rule_params}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+                                       nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; $nftSetParams comment \"$comment\"; }" && ipv4_error=0
+                                       nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; $nftSetParams comment \"$comment\"; }" && ipv6_error=0
+                                       nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+                                       nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
                                        ;;
                                esac
                ;;
@@ -862,9 +909,9 @@ json() {
        local status message stats i
        local action="$1" param="$2" value="$3"; shift 3; local info="$*";
        local _current_namespace="$_JSON_PREFIX"
+       json_set_namespace "${packageName//-/_}_"
        [ "$param" = 'error' ] && param='errors'
        [ "$param" = 'warning' ] && param='warnings'
-       json_set_namespace "${packageName}_"
        { json_load_file "$runningStatusFile" || json_init; } >/dev/null 2>&1
        case "$action" in
                'get')
@@ -893,6 +940,7 @@ json() {
                        json_select ..
                ;;
                'init')
+                       mkdir -p "${runningStatusFile%/*}"
                        json_init
                        json_add_array 'errors'
                        json_close_array
@@ -900,39 +948,44 @@ json() {
                        json_close_array
                ;;
        esac
-       mkdir -p "${runningStatusFile%/*}"
        json_dump > "$runningStatusFile"
        sync
        json_set_namespace "$_current_namespace"
 }
 
 resolver() {
-       _resolver_dnsmasq_confdir() {
-               local cfg="$1"
-               local confdir confdirFile
-# shellcheck disable=SC2016
-               if grep -q 'config_get dnsmasqconfdir "$cfg" confdir "/tmp/dnsmasq${cfg:+.$cfg}.d"' '/etc/init.d/dnsmasq'; then
-                       config_get confdir "$cfg" 'confdir' "/tmp/dnsmasq${cfg:+.$cfg}.d"
-               else
-                       config_get confdir "$cfg" 'confdir' '/tmp/dnsmasq.d'
-               fi
-               confdirFile="${confdir}/${packageName}"
-               if ! str_contains "$dnsmasqFileList" "$confdirFile"; then
-                       dnsmasqFileList="${dnsmasqFileList:+$dnsmasqFileList }${confdirFile}"
-               fi
+       _dnsmasq_instance_get_confdir() {
+               local cfg_file
+               [ -z "$dnsmasq_ubus" ] && dnsmasq_ubus="$(ubus call service list '{"name":"dnsmasq"}')"
+               cfg_file="$(echo "$dnsmasq_ubus" | jsonfilter -e "@.dnsmasq.instances.${1}.command" \
+                       | awk '{gsub(/\\\//,"/");gsub(/[][",]/,"");for(i=1;i<=NF;i++)if($i=="-C"){print $(i+1);exit}}')"
+               awk -F= '/^conf-dir=/{print $2; exit}' "$cfg_file"
+       }
+       _dnsmasq_instance_config() {
+               local cfg="$1" param="$2" confdir
+               case "$param" in
+                       cleanup)
+                               # clean up all dnsmasq configs
+                               confdir="$(_dnsmasq_instance_get_confdir "$cfg")"
+                               [ -n "$confdir" ] && rm -f "${confdir}/${packageName}"
+                               uci_remove_list 'dhcp' "$cfg" 'addnmount' "$packageDnsmasqFile"
+                       ;;
+                       setup)
+                               # add dnsmasq conf addnmounts to point to pbr file
+                               uci_add_list_if_new 'dhcp' "$cfg" 'addnmount' "$packageDnsmasqFile"
+                               # add softlink to pbr file
+                               confdir="$(_dnsmasq_instance_get_confdir "$cfg")"
+                               [ -n "$confdir" ] || return 1
+                               ln -sf "$packageDnsmasqFile" "${confdir}/${packageName}"
+                               chmod 660 "${confdir}/${packageName}"
+                               chown -h root:dnsmasq "${confdir}/${packageName}" >/dev/null 2>/dev/null
+                       ;;
+               esac
        }
        local agh_version
        local param="$1" iface="$2" target="$3" type="$4" uid="$5" name="$6" value="$7"
        shift
 
-       if [ "$param" = 'cleanup_all' ]; then
-               local dfl
-               for dfl in $dnsmasqFileList; do
-                       rm -f "$dfl"
-               done
-               return 0
-       fi
-
        case "$resolver_set" in
                ''|none)
                        case "$param" in
@@ -941,8 +994,6 @@ resolver() {
                                check_support) return 0;;
                                cleanup) return 0;;
                                configure) return 0;;
-                               init) return 0;;
-                               init_end) return 0;;
                                kill) return 0;;
                                reload) return 0;;
                                restart) return 0;;
@@ -953,23 +1004,19 @@ resolver() {
                dnsmasq.nftset)
                        case "$param" in
                                add_resolver_element)
-                                       [ -n "$resolver_set_supported" ] || return 1
+                                       [ -n "$resolverSetSupported" ] || return 1
                                        local d
                                        for d in $value; do
                                                nftset 'add_dnsmasq_element' "$iface" "$target" "$type" "$uid" "$name" "$d"
                                        done
                                ;;
                                create_resolver_set)
-                                       [ -n "$resolver_set_supported" ] || return 1
+                                       [ -n "$resolverSetSupported" ] || return 1
                                        nftset 'create_dnsmasq_set' "$iface" "$target" "$type" "$uid" "$name" "$value"
                                ;;
                                check_support)
-                                       if [ ! -x "$nft" ]; then
-                                               json add error 'errorNoNft'
-                                               return 1
-                                       fi
                                        if check_dnsmasq_nftset; then
-                                               resolver_set_supported='true'
+                                               resolverSetSupported='true'
                                                return 0
                                        else
                                                json add warning 'warningResolverNotSupported'
@@ -977,46 +1024,30 @@ resolver() {
                                        fi
                                ;;
                                cleanup)
-                                       if [ -n "$resolver_set_supported" ]; then
-                                               local dfl
-                                               for dfl in $dnsmasqFileList; do
-                                                       rm -f "$dfl"
-                                               done
-                                       fi
+                                       [ -n "$resolverSetSupported" ] || return 1
+                                       rm -f "$packageDnsmasqFile"
+                                       config_load 'dhcp'
+                                       config_foreach _dnsmasq_instance_config 'dnsmasq' 'cleanup'
                                ;;
                                configure)
-                                       if [ -n "$resolver_set_supported" ]; then
-                                               local dfl
-                                               for dfl in $dnsmasqFileList; do
-                                                       [ "${dfl%/*}" = '/var/run' ] && continue
-                                                       mkdir -p "${dfl%/*}"
-                                                       chmod -R 660 "${dfl%/*}"
-                                                       chown -R root:dnsmasq "${dfl%/*}"
-                                                       touch "$dfl"
-                                                       chmod 660 "$dfl"
-                                                       chown root:dnsmasq "$dfl"
-                                               done
-                                       fi
-                               ;;
-                               configure_instances)
+                                       [ -n "$resolverSetSupported" ] || return 1
+                                       rm -f "$packageDnsmasqFile"
+                                       touch "$packageDnsmasqFile"
                                        config_load 'dhcp'
                                        if [ "$resolver_instance" = "*" ]; then
-                                               config_foreach _resolver_dnsmasq_confdir 'dnsmasq'
+                                               config_foreach _dnsmasq_instance_config 'dnsmasq' 'setup'
                                        else
+                                               config_foreach _dnsmasq_instance_config 'dnsmasq' 'cleanup'
                                                for i in $resolver_instance; do
-                                                       _resolver_dnsmasq_confdir "@dnsmasq[$i]" \
-                                                       || _resolver_dnsmasq_confdir "$i"
+                                                       _dnsmasq_instance_config "@dnsmasq[$i]" \
+                                                       || _dnsmasq_instance_config "$i"
                                                done
                                        fi
-                                       str_contains "$dnsmasqFileList" "$dnsmasqFileDefault" || \
-                                               dnsmasqFileList="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFileDefault}"
                                ;;
-                               init) :;;
-                               init_end) :;;
                                kill)
-                                       [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
+                                       [ -n "$resolverSetSupported" ] && killall -q -s HUP dnsmasq;;
                                reload)
-                                       [ -z "$resolver_set_supported" ] && return 1
+                                       [ -z "$resolverSetSupported" ] && return 1
                                        output 3 'Reloading dnsmasq '
                                        if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
                                                output_okn
@@ -1027,7 +1058,7 @@ resolver() {
                                        fi
                                ;;
                                restart)
-                                       [ -z "$resolver_set_supported" ] && return 1
+                                       [ -z "$resolverSetSupported" ] && return 1
                                        output 3 'Restarting dnsmasq '
                                        if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
                                                output_okn
@@ -1038,15 +1069,16 @@ resolver() {
                                        fi
                                ;;
                                compare_hash)
-                                       [ -z "$resolver_set_supported" ] && return 1
+                                       [ -z "$resolverSetSupported" ] && return 1
+                                       uci_changes 'dhcp' && uci_commit 'dhcp' 
                                        local resolverNewHash
-                                       if [ -s "$dnsmasqFileDefault" ]; then
-                                               resolverNewHash="$(md5sum "$dnsmasqFileDefault" | awk '{ print $1; }')"
+                                       if [ -s "$packageDnsmasqFile" ]; then
+                                               resolverNewHash="$(md5sum "$packageDnsmasqFile" | awk '{ print $1; }')"
                                        fi
                                        [ "$resolverNewHash" != "$resolverStoredHash" ]
                                ;;
                                store_hash)
-                                       [ -s "$dnsmasqFileDefault" ] && resolverStoredHash="$(md5sum "$dnsmasqFileDefault" | awk '{ print $1; }')";;
+                                       [ -s "$packageDnsmasqFile" ] && resolverStoredHash="$(md5sum "$packageDnsmasqFile" | awk '{ print $1; }')";;
                        esac
                ;;
                unbound.nftset)
@@ -1056,8 +1088,6 @@ resolver() {
                                check_support) :;;
                                cleanup) :;;
                                configure) :;;
-                               init) :;;
-                               init_end) :;;
                                kill) :;;
                                reload) :;;
                                restart) :;;
@@ -1074,7 +1104,7 @@ dns_policy_routing() {
        local param4 param6
        local negation value dest4 dest6 first_value
        local inline_set_ipv4_empty_flag inline_set_ipv6_empty_flag
-       local name="$1" src_addr="$2" dest_dns="$3" uid="$4"
+       local name="$1" src_addr="$2" dest_dns="$3" uid="$4" dest_dns_port="$5"
        local chain='dstnat' iface='dns'
 
        if [ -z "${dest_dns_ipv4}${dest_dns_ipv6}" ]; then
@@ -1092,7 +1122,7 @@ dns_policy_routing() {
        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'
-               json add error 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns'"
+               json add error 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns':'$dest_dns_port'"
                return 1
        fi
 
@@ -1100,8 +1130,8 @@ dns_policy_routing() {
                unset param4
                unset param6
 
-               dest4="dport 53 dnat ip to ${dest_dns_ipv4}:53"
-               dest6="dport 53 dnat ip6 to ${dest_dns_ipv6}:53"
+               dest4="dport 53 dnat ip to ${dest_dns_ipv4}:${dest_dns_port}"
+               dest6="dport 53 dnat ip6 to ${dest_dns_ipv6}:${dest_dns_port}"
 
                if [ -n "$src_addr" ]; then
                        if [ "${src_addr:0:1}" = "!" ]; then
@@ -1140,21 +1170,21 @@ dns_policy_routing() {
                        fi
                fi
 
-               param4="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param4} ${nft_rule_params} meta nfproto ipv4 ${proto_i} ${dest4} comment \"$name\""
-               param6="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param6} ${nft_rule_params} meta nfproto ipv6 ${proto_i} ${dest6} comment \"$name\""
+               param4="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param4} ${nftRuleParams} meta nfproto ipv4 ${proto_i} ${dest4} comment \"$name\""
+               param6="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param6} ${nftRuleParams} meta nfproto ipv6 ${proto_i} ${dest6} comment \"$name\""
 
                local ipv4_error='0' ipv6_error='0'
-               if [ "$policy_routing_nft_prev_param4" != "$param4" ] && \
+               if [ "$pbrNftPrevParam4" != "$param4" ] && \
                        [ -n "$first_value" ] && ! is_ipv6 "$first_value" && \
                        [ -z "$inline_set_ipv4_empty_flag" ] && [ -n "$dest_dns_ipv4" ]; then
                                nft4 "$param4" || ipv4_error='1'
-                               policy_routing_nft_prev_param4="$param4"
+                               pbrNftPrevParam4="$param4"
                fi
-               if [ "$policy_routing_nft_prev_param6" != "$param6" ] && [ "$param4" != "$param6" ] && \
+               if [ "$pbrNftPrevParam6" != "$param6" ] && [ "$param4" != "$param6" ] && \
                        [ -n "$first_value" ] && ! is_ipv4 "$first_value" && \
                        [ -z "$inline_set_ipv6_empty_flag" ] && [ -n "$dest_dns_ipv6" ]; then
                                nft6 "$param6" || ipv6_error='1'
-                               policy_routing_nft_prev_param6="$param6"
+                               pbrNftPrevParam6="$param6"
                fi
 
                if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
@@ -1343,8 +1373,8 @@ policy_routing() {
                        local ipv4_error='0' ipv6_error='0'
                        local dest_i dest4 dest6
                        chain='dstnat'
-                       param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nft_rule_params} meta nfproto ipv4 $param4"
-                       param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nft_rule_params} meta nfproto ipv6 $param6"
+                       param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nftRuleParams} meta nfproto ipv4 $param4"
+                       param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nftRuleParams} meta nfproto ipv6 $param6"
                        dest_udp_53="udp dport 53 redirect to :${torDnsPort} comment \"Tor-DNS-UDP\""
                        dest_tcp_80="tcp dport 80 redirect to :${torTrafficPort} comment \"Tor-HTTP-TCP\""
                        dest_udp_80="udp dport 80 redirect to :${torTrafficPort} comment \"Tor-HTTP-UDP\""
@@ -1370,22 +1400,22 @@ policy_routing() {
                                fi
                        done
                else
-                       param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${param4} ${nft_rule_params} ${dest4} comment \"$name\""
-                       param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${param6} ${nft_rule_params} ${dest6} comment \"$name\""
+                       param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${param4} ${nftRuleParams} ${dest4} comment \"$name\""
+                       param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${param6} ${nftRuleParams} ${dest6} comment \"$name\""
                        local ipv4_error='0' ipv6_error='0'
-                       if [ "$policy_routing_nft_prev_param4" != "$param4" ] && \
+                       if [ "$pbrNftPrevParam4" != "$param4" ] && \
                                [ -z "$src_inline_set_ipv4_empty_flag" ] && [ -z "$dest_inline_set_ipv4_empty_flag" ] && \
                                [ "$filter_group_src_addr" != 'ipv6' ] && [ "$filter_group_src_addr" != 'ipv6_negative' ] && \
                                [ "$filter_group_dest_addr" != 'ipv6' ] && [ "$filter_group_dest_addr" != 'ipv6_negative' ]; then
                                        nft4 "$param4" || ipv4_error='1'
-                                       policy_routing_nft_prev_param4="$param4"
+                                       pbrNftPrevParam4="$param4"
                        fi
-                       if [ "$policy_routing_nft_prev_param6" != "$param6" ] && [ "$param4" != "$param6" ] && \
+                       if [ "$pbrNftPrevParam6" != "$param6" ] && [ "$param4" != "$param6" ] && \
                                [ -z "$src_inline_set_ipv6_empty_flag" ] && [ -z "$dest_inline_set_ipv6_empty_flag" ] && \
                                [ "$filter_group_src_addr" != 'ipv4' ] && [ "$filter_group_src_addr" != 'ipv4_negative' ] && \
                                [ "$filter_group_dest_addr" != 'ipv4' ] && [ "$filter_group_dest_addr" != 'ipv4_negative' ]; then
                                        nft6 "$param6" || ipv6_error='1'
-                                       policy_routing_nft_prev_param6="$param6"
+                                       pbrNftPrevParam6="$param6"
                        fi
                        if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
                                processPolicyError='true'
@@ -1407,7 +1437,7 @@ policy_routing() {
 dns_policy_process() {
        local i j uid="$1"
 
-       [ "$enabled" -gt '0' ] || return 0
+       [ "$enabled" = '1' ] || return 0
 
        src_addr="$(str_extras_to_space "$src_addr")"
        dest_dns="$(str_extras_to_space "$dest_dns")"
@@ -1430,7 +1460,7 @@ dns_policy_process() {
        fi
 
        unset processDnsPolicyError
-       output 2 "Routing '$name' DNS to $dest_dns "
+       output 2 "Routing '$name' DNS to $dest_dns:$dest_dns_port "
        if [ -z "$src_addr" ]; then
                json add error 'errorPolicyNoSrcDest' "$name"
                output_fail; return 1;
@@ -1452,7 +1482,7 @@ dns_policy_process() {
                        if str_contains "$filter_group_src_addr" 'ipv6' && [ -z "$dest_dns_ipv6" ] ; then
                                        continue
                        fi
-                       dns_policy_routing "$name" "$filtered_value_src_addr" "$dest_dns" "$uid"
+                       dns_policy_routing "$name" "$filtered_value_src_addr" "$dest_dns" "$uid" "$dest_dns_port"
                fi
        done
 
@@ -1466,7 +1496,7 @@ dns_policy_process() {
 policy_process() {
        local i j uid="$1"
 
-       [ "$enabled" -gt '0' ] || return 0
+       [ "$enabled" = '1' ] || return 0
 
        src_addr="$(str_extras_to_space "$src_addr")"
        src_port="$(str_extras_to_space "$src_port")"
@@ -1572,7 +1602,7 @@ interface_routing() {
                                ip -4 rule del table "$tid" prio "$priority" >/dev/null 2>&1
                                try ip -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
                                try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1 
-                               try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nft_rule_params} mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
+                               try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
                                try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
                                if [ -n "$ipv6_enabled" ]; then
                                        ipv6_error=0
@@ -1587,7 +1617,7 @@ interface_routing() {
                                fi
                                ip -4 rule flush table "$tid" >/dev/null 2>&1
                                ip -4 route flush table "$tid" >/dev/null 2>&1
-                               if [ -n "$gw4" ] || [ "$strict_enforcement" -ne '0' ]; then
+                               if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
                                        ipv4_error=0
                                        if [ -z "$gw4" ]; then
                                                try ip -4 route add unreachable default table "$tid" || ipv4_error=1
@@ -1609,29 +1639,30 @@ EOF
                                        try ip -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
                                fi
                                try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1 
-                               try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nft_rule_params} mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
+                               try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
                                try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
                                if [ -n "$ipv6_enabled" ]; then
                                        ipv6_error=0
                                        ip -6 rule flush table "$tid" >/dev/null 2>&1
                                        ip -6 route flush table "$tid" >/dev/null 2>&1
-                                       if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne '0' ]; then
+                                       if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ -n "$strict_enforcement" ]; then
                                                if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
                                                        try ip -6 route add unreachable default table "$tid" || ipv6_error=1
                                                elif ip -6 route list table main | grep -q " dev $dev6 "; then
                                                        if ip -6 address show dev "$dev6" | grep -q "BROADCAST"; then
-                                                               try ip -6 route add default via "$gw6" dev "$dev6" table "$tid" metric "$procd_wan6_metric" || ipv6_error=1
+                                                               try ip -6 route add default via "$gw6" dev "$dev6" table "$tid" metric "$uplink_interface6_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
+                                                               try ip -6 route add default dev "$dev6" table "$tid" metric "$uplink_interface6_metric" || ipv6_error=1
                                                        else
                                                                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
+#                                                              try ip -6 route add default dev "$dev6" table "$tid" metric "$uplink_interface6_metric" || ipv6_error=1
 #                                                      fi
                                                        while read -r i; do
                                                                i="$(echo "$i" | sed 's/ linkdown$//')"
                                                                i="$(echo "$i" | sed 's/ onlink$//')"
+                                                               i="$(echo "$i" | sed -E 's/ proto kernel//; s/ expires -?[0-9]+sec//')"
                                                                # shellcheck disable=SC2086
                                                                try ip -6 route add $i table "$tid" || ipv6_error=1
                                                        done << EOF
@@ -1648,15 +1679,15 @@ EOF
                        if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
                                dscp="$(uci_get "$packageName" 'config' "${iface}_dscp")"
                                if [ "${dscp:-0}" -ge '1' ] && [ "${dscp:-0}" -le '63' ]; then
-                                       try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} ${nft_rule_params} goto ${nftPrefix}_mark_${mark}" || s=1
+                                       try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
                                        if [ -n "$ipv6_enabled" ]; then
-                                               try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} ${nft_rule_params} goto ${nftPrefix}_mark_${mark}" || s=1
+                                               try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
                                        fi
                                fi
                                if [ "$iface" = "$icmp_interface" ]; then
-                                       try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp ${nft_rule_params} goto ${nftPrefix}_mark_${mark}" || s=1
+                                       try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
                                        if [ -n "$ipv6_enabled" ]; then
-                                               try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp ${nft_rule_params} goto ${nftPrefix}_mark_${mark}" || s=1
+                                               try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
                                        fi
                                fi
                        else
@@ -1688,32 +1719,30 @@ EOF
                        [ -n "$ipv6_enabled" ] && ip -6 rule del table "$tid" prio "$priority" >/dev/null 2>&1
                        is_netifd_table_interface "$iface" && return 0;
                        ipv4_error=0
-                       if ! is_netifd_table_interface "$iface"; then
-                               ip -4 rule flush table "$tid" >/dev/null 2>&1
-                               ip -4 route flush table "$tid" >/dev/null 2>&1
-                               if [ -n "$ipv6_enabled" ]; then
-                                       ip -6 rule flush table "$tid" >/dev/null 2>&1
-                                       ip -6 route flush table "$tid" >/dev/null 2>&1
-                               fi
+                       ip -4 rule flush table "$tid" >/dev/null 2>&1
+                       ip -4 route flush table "$tid" >/dev/null 2>&1
+                       if [ -n "$ipv6_enabled" ]; then
+                               ip -6 rule flush table "$tid" >/dev/null 2>&1
+                               ip -6 route flush table "$tid" >/dev/null 2>&1
                        fi
-                       if [ -n "$gw4" ] || [ "$strict_enforcement" -ne '0' ]; then
+                       if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
                                if [ -z "$gw4" ]; then
                                        try ip -4 route add unreachable default table "$tid" || ipv4_error=1
                                else
                                        try ip -4 route add default via "$gw4" dev "$dev" table "$tid" || ipv4_error=1
                                fi
-                               try ip rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
+                               try ip -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
                        fi
                        if [ -n "$ipv6_enabled" ]; then
                                ipv6_error=0
-                               if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne '0' ]; then
+                               if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ -n "$strict_enforcement" ]; then
                                        if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
                                                try ip -6 route add unreachable default table "$tid" || ipv6_error=1
                                        elif ip -6 route list table main | grep -q " dev $dev6 "; then
                                                if ip -6 address show dev "$dev6" | grep -q "BROADCAST"; then
-                                                       try ip -6 route add default via "$gw6" dev "$dev6" table "$tid" metric "$procd_wan6_metric" || ipv6_error=1
+                                                       try ip -6 route add default via "$gw6" dev "$dev6" table "$tid" metric "$uplink_interface6_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
+                                                       try ip -6 route add default dev "$dev6" table "$tid" metric "$uplink_interface6_metric" || ipv6_error=1
                                                else
                                                        json add error 'errorInterfaceRoutingUnknownDevType' "$dev6"
                                                fi
@@ -1766,8 +1795,8 @@ process_interface() {
 
        if [ "$iface" = 'all' ] && [ "$action" = 'prepare' ]; then
                config_load 'network'
-               ifaceMark="$(printf '0x%06x' "$wan_mark")"
-               ifacePriority="$wan_ip_rules_priority"
+               ifaceMark="$(printf '0x%06x' "$uplink_mark")"
+               ifacePriority="$uplink_ip_rules_priority"
                unset ifaceTableID
                return 0
        fi
@@ -1829,8 +1858,8 @@ process_interface() {
        fi
 
        [ -z "$dev6" ] && dev6="$dev"
-       [ -z "$ifaceMark" ] && ifaceMark="$(printf '0x%06x' "$wan_mark")"
-       [ -z "$ifacePriority" ] && ifacePriority="$wan_ip_rules_priority"
+       [ -z "$ifaceMark" ] && ifaceMark="$(printf '0x%06x' "$uplink_mark")"
+       [ -z "$ifacePriority" ] && ifacePriority="$uplink_ip_rules_priority"
 
        case "$action" in
                pre_init)
@@ -1838,7 +1867,7 @@ process_interface() {
                        eval "pre_init_mark_${iface//-/_}"='$ifaceMark'
                        eval "pre_init_priority_${iface//-/_}"='$ifacePriority'
                        eval "pre_init_tid_${iface//-/_}"='$ifaceTableID'
-                       ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))"
+                       ifaceMark="$(printf '0x%06x' $((ifaceMark + uplink_mark)))"
                        ifacePriority="$((ifacePriority - 1))"
                        ifaceTableID="$((ifaceTableID + 1))"
                        return 0
@@ -1944,19 +1973,19 @@ process_interface() {
                                        output_fail
                                fi
                        else
+                               json_add_gateway 'skip_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
                                gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
                        fi
                ;;
        esac
-#      ifaceTableID="$((ifaceTableID + 1))"
-       ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))"
+       ifaceMark="$(printf '0x%06x' $((ifaceMark + uplink_mark)))"
        ifacePriority="$((ifacePriority - 2))"
        return $s
 }
 
 user_file_process() {
        local shellBin="${SHELL:-/bin/ash}"
-       [ "$enabled" -gt '0' ] || return 0
+       [ "$enabled" = '1' ] || return 0
        if [ ! -s "$path" ]; then
                json add error 'errorUserFileNotFound' "$path"
                output_fail
@@ -1989,8 +2018,6 @@ user_file_process() {
 
 boot() {
        nft_file 'delete'
-#      ubus -t 30 wait_for network.interface 2>/dev/null
-       pbr_boot_flag=1
        rc_procd start_service 'on_boot' && service_started 'on_boot'
 }
 
@@ -2017,15 +2044,14 @@ on_interface_reload() {
 }
 
 start_service() {
-       local resolverStoredHash resolverNewHash i param="$1" reloadedIface k
+       local param="$1"
+       local resolverStoredHash resolverNewHash reloadedIface
+       local i k
 
-#      [ -n "$pbr_boot_flag" ] && return 0
-#      [ "$param" = 'on_boot' ] && return 0
+       load_package_config "$param"
+       [ "$param" = 'on_boot' ] && pbrBootFlag=1 && return 0
+       json init
        load_environment "${param:-on_start}" "$(load_validate_config)" || return 1
-       if [ -n "$pbr_boot_flag" ] || [ "$param" = 'on_boot' ]; then 
-               [ -n "$wanGW" ] || return 0
-               unset pbr_boot_flag
-       fi
 
        output "Processing environment (${param:-on_start}) "
        is_wan_up "$param" || { output_error "$(get_text 'errorUplinkDown')";  return 1; }
@@ -2085,7 +2111,7 @@ start_service() {
 
        case $serviceStartTrigger in
                on_interface_reload)
-       output_okn
+                       output_okn
                        output 1 "Reloading Interface: $reloadedIface "
                        json_add_array 'gateways'
                        process_interface 'all' 'prepare'
@@ -2095,15 +2121,13 @@ start_service() {
                ;;
                on_reload|on_start|*)
                        resolver 'store_hash'
-                       resolver 'cleanup_all'
                        resolver 'configure'
-                       resolver 'init'
                        cleanup_main_chains
                        cleanup_sets
                        cleanup_marking_chains
                        cleanup_rt_tables
                        nft_file 'create'
-       output_okn
+                       output_okn
                        output 1 'Processing interfaces '
                        json_add_array 'gateways'
                        process_interface 'all' 'prepare'
@@ -2141,17 +2165,13 @@ start_service() {
                                output 1 '\n'
                        fi
                        nft_file 'install'
-                       resolver 'init_end'
                        resolver 'compare_hash' && resolver 'restart'
                ;;
        esac
 
-       if [ -z "$gatewaySummary" ]; then
-               json add error 'errorNoGateways'
-       fi
        json_add_int 'packageCompat' "$packageCompat"
        json_add_object 'status'
-       [ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary"
+       [ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary" || json add error 'errorNoGateways'
        json_close_object
        json_add_array 'errors'
                for k in $(json get errors); do
@@ -2169,42 +2189,41 @@ start_service() {
                        json_close_object
                done
        json_close_array
-       if [ "$strict_enforcement" -ne '0' ] && str_contains "$gatewaySummary" '0.0.0.0'; then
+       if [ -n "$strict_enforcement" ] && str_contains "$gatewaySummary" '0.0.0.0'; then
                json_add_string 'mode' 'strict'
        fi
        procd_close_data
        procd_close_instance
 }
 
+service_running() { procd_set_config_changed firewall; }
 service_started() {
-       [ -n "$pbr_boot_flag" ] && return 0
+       [ -n "$pbrBootFlag" ] && return 0
        local error warning c
        if nft_file 'exists'; then
                procd_set_config_changed firewall
-               if nft_file 'exists'; then
-                       [ -n "$gatewaySummary" ] && output "$serviceName (fw4 nft file mode) started with gateways:\n${gatewaySummary}"
-               else
-                       output "$serviceName FAILED TO START in fw4 nft file mode!!!"
-                       output "Check the output of nft -c -f $nftTempFile"
-               fi
+               [ -n "$gatewaySummary" ] && output "$serviceName (fw4 nft file mode) started with gateways:\n${gatewaySummary}"
        else
-               [ -n "$gatewaySummary" ] && output "$serviceName (nft mode) started with gateways:\n${gatewaySummary}"
+               output "$serviceName FAILED TO START in fw4 nft file mode!!!"
+               output "Check the output of nft -c -f $nftTempFile"
        fi
-       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
+               output_warning "$(get_text 'warningSummary' "$(get_url '#WarningMessagesDetails')")"
+       fi
+       error="$(json get error)"
+       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
+               output_error "$(get_text 'errorSummary' "$(get_url '#ErrorMessagesDetails')")"
        fi
        touch "$packageLockFile"
        if [ -n "$error" ]; then
@@ -2215,24 +2234,21 @@ service_started() {
                return 0
        fi
 }
+service_stopped() { procd_set_config_changed firewall; }
 
 # shellcheck disable=SC2015
 service_triggers() {
        local n
-       load_environment 'on_triggers'
-# shellcheck disable=SC2034
-       PROCD_RELOAD_DELAY=$(( procd_reload_delay * 1000 ))
-       procd_open_validate
-               load_validate_config
-               load_validate_policy
-               load_validate_include
-       procd_close_validate
-       if [ -n "$pbr_boot_flag" ] && is_integer "$procd_boot_trigger_delay"; then
+       if [ -n "$pbrBootFlag" ]; then
                output "Setting trigger (on_boot) "
-               procd_open_trigger
-                       procd_add_raw_trigger "interface.*.up" "$procd_boot_trigger_delay" "/etc/init.d/${packageName}" start && output_okn || output_failn
-               procd_close_trigger
+               procd_add_raw_trigger "interface.*.up" "$procd_boot_trigger_delay" "/etc/init.d/${packageName}" start && output_okn || output_failn
        else
+               PROCD_RELOAD_DELAY=$(( procd_reload_delay * 1000 ))
+               procd_open_validate
+                       load_validate_config
+                       load_validate_policy
+                       load_validate_include
+               procd_close_validate
                procd_open_trigger
                        procd_add_config_trigger "config.change" 'openvpn' "/etc/init.d/${packageName}" reload 'on_openvpn_change'
                        procd_add_config_trigger "config.change" "${packageName}" "/etc/init.d/${packageName}" reload
@@ -2241,17 +2257,18 @@ service_triggers() {
                                output 2 "Setting interface trigger for $n "
                                procd_add_interface_trigger "interface.*" "$n" "/etc/init.d/${packageName}" on_interface_reload "$n" && output_ok || output_fail
                        done
-                       output '\n'
+                       output '\n'
                procd_close_trigger
-       fi
-       if [ "$serviceStartTrigger" = 'on_start' ]; then
-               output 3 "$serviceName monitoring interfaces: ${ifacesSupported}\n"
+               if [ "$serviceStartTrigger" = 'on_start' ]; then
+                       output 3 "$serviceName monitoring interfaces: ${ifacesSupported}\n"
+               fi
        fi
 }
 
 # shellcheck disable=SC2015
 stop_service() {
        local i nft_file_mode
+       json init
        ! is_service_running && [ "$(get_rt_tables_next_id)" = "$(get_rt_tables_non_pbr_next_id)" ] && return 0
        [ "$1" = 'quiet' ] && quiet_mode 'on'
        load_environment 'on_stop'
@@ -2274,9 +2291,9 @@ stop_service() {
        unset ifaceMark
        unset ifaceTableID
        resolver 'store_hash'
-       resolver 'cleanup_all'
+       resolver 'cleanup'
        resolver 'compare_hash' && resolver 'restart'
-       if [ "$enabled" -ne '0' ]; then
+       if [ -n "$enabled" ]; then
                if [ -n "$nft_file_mode" ]; then
                        output "$serviceName (fw4 nft file mode) stopped "; output_okn;
                else
@@ -2291,6 +2308,7 @@ version() { echo "$PKG_VERSION"; }
 # shellcheck disable=SC2317
 setup_netifd() {
        local param="$1"
+# shellcheck disable=SC2329
        _pbr_iface_setup() {
                local iface="${1}" param="$2" tid
                if is_supported_interface "${iface}"; then
@@ -2308,8 +2326,8 @@ setup_netifd() {
        }
        _pbr_default_route_setup() {
                local iface iface6 param="$1"
-               iface="$(uci_get 'pbr' 'config' 'procd_wan_interface')"
-               iface6="$(uci_get 'pbr' 'config' 'procd_wan6_interface')"
+               iface="$(uci_get 'pbr' 'config' 'uplink_interface')"
+               iface6="$(uci_get 'pbr' 'config' 'uplink_interface6')"
                [ -z "$iface" ] && { network_flush_cache; network_find_wan iface; }
                [ -z "$iface6" ] && { network_flush_cache; network_find_wan6 iface6; }
                output "Setting up ${packageName} default route for ${iface:-wan} ${param:+($param) }"
@@ -2340,7 +2358,7 @@ setup_netifd() {
 }
 
 status_service() {
-       local i dev dev6 wan_tid
+       local i dev dev6 wanTID
 
        json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
        if [ -n "$wanIface4" ]; then
@@ -2355,7 +2373,7 @@ status_service() {
        while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
        [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
 # shellcheck disable=SC2154
-       status="$serviceName running on $dist $vers."
+       status="$serviceName installed on $dist $vers."
        [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
        [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
 
@@ -2384,30 +2402,30 @@ status_service() {
        for i in $(get_nft_sets); do
                "$nft" -a list table inet "$nftTable" | sed -n "/set ${i} {/,/\t}/p"
        done
-       if [ -s "$dnsmasqFileDefault" ]; then
+       if [ -s "$packageDnsmasqFile" ]; then
                echo "$_SEPARATOR_"
-               echo "dnsmasq sets"
-               cat "$dnsmasqFileDefault"
+               echo "dnsmasq nft sets in $packageDnsmasqFile"
+               cat "$packageDnsmasqFile"
        fi
 #      echo "$_SEPARATOR_"
 #      ip rule list | grep "${packageName}_"
        echo "$_SEPARATOR_"
        echo "$packageName tables & routing"
        tableCount="$(grep -c "${packageName}_" "$rtTablesFile")" || tableCount=0
-       wan_tid=$(($(get_rt_tables_next_id)-tableCount))
+       wanTID=$(($(get_rt_tables_next_id)-tableCount))
        i=0; while [ "$i" -lt "$tableCount" ]; do
                local status_table
-               status_table="$(grep $((wan_tid + i)) "$rtTablesFile")"
+               status_table="$(grep $((wanTID + i)) "$rtTablesFile")"
                echo "IPv4 table $status_table route:"
-               ip -4 route show table "$((wan_tid + i))" | grep default
+               ip -4 route show table "$((wanTID + i))" | grep default
                echo "IPv4 table $status_table rule(s):"
-               ip -4 rule list table "$((wan_tid + i))"
+               ip -4 rule list table "$((wanTID + i))"
                if [ "$(uci_get "$packageName" config ipv6_enabled)" = "1" ]; then
                        echo "$_SEPARATOR_"
                        echo "IPv6 table $status_table route:"
-                       ip -6 route show table "$((wan_tid + i))" | grep default
+                       ip -6 route show table "$((wanTID + i))" | grep default
                        echo "IPv6 table $status_table rule(s):"
-                       ip -6 rule list table "$((wan_tid + i))"
+                       ip -6 rule list table "$((wanTID + i))"
                fi
                echo "$_SEPARATOR_"
                i=$((i + 1))
@@ -2417,24 +2435,23 @@ 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' \
                'resolver_set:or("", "none", "dnsmasq.nftset")' \
                'resolver_instance:list(or(integer, string)):*' \
                'verbosity:range(0,2):2' \
-               'wan_mark:regex("[A-Fa-f0-9]{8}"):010000' \
-               'fw_mask:regex("[A-Fa-f0-9]{8}"):ff0000' \
+               'uplink_mark:regex("[A-Fa-f0-9]{8}"):00010000' \
+               'uplink_ip_rules_priority:uinteger:30000' \
+               'fw_mask:regex("[A-Fa-f0-9]{8}"):00ff0000' \
                '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_boot_trigger_delay:range(1000,10000):5000' \
-               'procd_lan_device:list(or(network)):br-lan' \
+               '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' \
+               'uplink_interface:network:wan' \
+               'uplink_interface6:network:wan6' \
                'webui_supported_protocol:list(string)' \
                'nft_rule_counter:bool:0'\
                'nft_set_auto_merge:bool:1'\
@@ -2443,7 +2460,8 @@ load_validate_config() {
                'nft_set_flags_timeout:bool:0'\
                'nft_set_gc_interval:or("", string)'\
                'nft_set_policy:or("", memory, performance):performance'\
-               'nft_set_timeout:or("", string)'
+               'nft_set_timeout:or("", string)' \
+       ;
 }
 
 # shellcheck disable=SC2120
@@ -2452,11 +2470,14 @@ load_validate_dns_policy() {
        local enabled
        local src_addr
        local dest_dns
+       local dest_dns_port
        uci_load_validate "$packageName" 'policy' "$1" "${2}${3:+ $3}" \
                'name:string:Untitled' \
                'enabled:bool:1' \
                'src_addr:list(neg(or(host,network,macaddr,string)))' \
-               'dest_dns:list(or(host,network,string))'
+               'dest_dns:list(or(host,network,string))' \
+               'dest_dns_port:port:53' \
+       ;
 }
 
 # shellcheck disable=SC2120
@@ -2479,7 +2500,8 @@ load_validate_policy() {
                'src_addr:list(neg(or(host,network,macaddr,string)))' \
                'src_port:list(neg(or(portrange,string)))' \
                'dest_addr:list(neg(or(host,network,string)))' \
-               'dest_port:list(neg(or(portrange,string)))'
+               'dest_port:list(neg(or(portrange,string)))' \
+       ;
 }
 
 # shellcheck disable=SC2120
@@ -2488,5 +2510,6 @@ load_validate_include() {
        local enabled=
        uci_load_validate "$packageName" 'include' "$1" "${2}${3:+ $3}" \
                'path:file' \
-               'enabled:bool:0'
+               'enabled:bool:0' \
+       ;
 }
index ccad4cdfacb946ece4252b7717710dae4946a839..ccdf663df17b03c3c9bdd413297e9ea299256bf3 100644 (file)
@@ -9,28 +9,6 @@ else
        printf "%b: pbr init.d file (%s) not found! \n" '\033[0;31mERROR\033[0m' "$pbrFunctionsFile"
 fi
 
-# Transition from vpn-policy-routing
-if [ -s '/etc/config/vpn-policy-routing' ] && [ ! -s '/etc/config/pbr-opkg' ] \
-       && [ "$(uci_get pbr config enabled)" = '0' ]; then
-       if [ -x '/etc/init.d/vpn-policy-routing' ]; then
-               output "Stopping and disabling vpn-policy-routing."
-               if /etc/init.d/vpn-policy-routing stop \
-                       && /etc/init.d/vpn-policy-routing disable; then
-                       output_okn
-               else
-                       output_failn
-               fi
-       fi
-       output "Migrating vpn-policy-routing config file."
-       if mv '/etc/config/pbr' '/etc/config/pbr-opkg' \
-               && sed 's/vpn-policy-routing/pbr/g' /etc/config/vpn-policy-routing > /etc/config/pbr \
-               && uci_set vpn-policy-routing config enabled 0 && uci_commit vpn-policy-routing; then
-               output_okn
-       else
-               output_failn
-       fi
-fi
-
 # Transition from older versions of pbr
 sed -i "s/resolver_ipset/resolver_set/g" /etc/config/pbr
 sed -i "s/iptables_rule_option/rule_create_option/g" /etc/config/pbr
@@ -43,22 +21,14 @@ sed -i "s/option fw_mask '0x\(.*\)'/option fw_mask '\1'/g" /etc/config/pbr
 sed -i "s/option wan_mark '0x\(.*\)'/option wan_mark '\1'/g" /etc/config/pbr
 sed -i "s|option path '/etc/pbr/|option path '/usr/share/pbr/|g" /etc/config/pbr
 sed -i "/procd_lan_interface/d" /etc/config/pbr
-
-# add firewall include file to fw4 config
-# shellcheck source=../../usr/share/pbr/firewall.include
-if [ -s '/usr/share/pbr/firewall.include' ]; then
-uci -q batch <<-EOT
-       delete firewall.pbr
-       set firewall.pbr='include'
-       set firewall.pbr.fw4_compatible='1'
-       set firewall.pbr.type='script'
-       set firewall.pbr.path='/usr/share/pbr/firewall.include'
-       commit firewall
-EOT
-fi
+sed -i "s|procd_lan_device|lan_device|g" /etc/config/pbr
+sed -i "s|procd_wan_interface|uplink_interface|g" /etc/config/pbr
+sed -i "s|procd_wan6_interface|uplink_interface6|g" /etc/config/pbr
+sed -i "s|wan_ip_rules_priority|uplink_ip_rules_priority|g" /etc/config/pbr
+sed -i "s|wan_mark|uplink_mark|g" /etc/config/pbr
 
 # Transition from pre-1.1.7 versions
-# shellcheck disable=SC2317
+# shellcheck disable=SC2317,SC2329
 _remove_wg_server_client() {
        local path
        config_get path "$1" 'path'
diff --git a/net/pbr/files/etc/uci-defaults/99-pbr-version b/net/pbr/files/etc/uci-defaults/99-pbr-version
new file mode 100644 (file)
index 0000000..50cebfc
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+# shellcheck disable=SC2015,SC3037,SC3043
+
+readonly pbrFunctionsFile='/etc/init.d/pbr'
+if [ -s "$pbrFunctionsFile" ]; then
+# shellcheck source=../../etc/init.d/pbr
+       . "$pbrFunctionsFile"
+else
+       printf "%b: pbr init.d file (%s) not found! \n" '\033[0;31mERROR\033[0m' "$pbrFunctionsFile"
+fi
+
+uci_set "$packageName" 'config' 'config_compat'  "$packageCompat"
+uci_set "$packageName" 'config' 'config_version' "$PKG_VERSION"
+uci_commit "$packageName"
+
+exit 0
diff --git a/net/pbr/files/usr/share/pbr/pbr.user.dnsprefetch b/net/pbr/files/usr/share/pbr/pbr.user.dnsprefetch
new file mode 100644 (file)
index 0000000..1b46c23
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+# When using pbr with dnsmasq's nft set support, a domain-based policy will not take effect until
+# the remote domain name has been resolved by dnsmasq. Resolve all domain names in pbr policies in advance.
+
+(
+       timeout_nft='10'
+       timeout_dnsmasq='20'
+       pipe_ubus="/tmp/pipe.ubus.$$"
+       pipe_nslookup="/tmp/pipe.nslookup.$$"
+       log_abort='domain names in policies not resolved'
+
+       # shellcheck disable=SC2154
+       output()
+       {
+               msg="$*"
+               msg=$(printf '%b' "$msg" | sed 's/\x1b\[[0-9;]*m//g')
+               logger -t "$packageName [$$]" "$(printf '%b' "$msg")"
+       }
+
+       nft_ready()
+       {
+               while ! /usr/sbin/nft list sets 'inet' | grep -q "pbr"; do
+                       [ "$timeout_nft" -eq '0' ] && {
+                               output "Pbr's nft sets not found, $log_abort $__FAIL__"
+                               return 1
+                       }
+                       sleep '1' && timeout_nft=$((timeout_nft - 1))
+               done
+       }
+
+       run_nslookup()
+       {
+               output=$(nslookup "$1" 127.0.0.1) && { echo '0' > "$pipe_nslookup"; return; }
+               reason=$(printf '%s' "$output" | grep -Eo -m 1 'NXDOMAIN|SERVFAIL|timed out') && \
+               output "$_WARNING_ Lookup failed for $domain ($reason)"
+               echo '1' > "$pipe_nslookup"
+       }
+
+       # shellcheck disable=SC2162
+       nslookup_tracker()
+       {
+               while read ec; do
+                       entries=$((entries + 1))
+                       [ "$ec" -eq '1' ] && errors=$((errors + 1))
+               done < "$pipe_nslookup"
+
+               output "Finished resolving $entries domain names in policies (${errors:-0} failed) $__OK__"
+       }
+
+       [ -n "$resolverSetSupported" ] || {
+               output "Resolver set support disabled, $log_abort $__FAIL__"
+               exit
+       }
+       mkfifo "$pipe_ubus"
+       mkfifo "$pipe_nslookup"
+       ubus listen -m 'ubus.object.add' > "$pipe_ubus" & ubus_listen_pid=$!
+
+       # shellcheck disable=SC3045
+       while read -t "$timeout_dnsmasq" -r event; do
+               echo "$event" | grep -q "dnsmasq.dns" || continue
+               dnsmasq_restarted='1'
+               # shellcheck disable=SC2154
+               [ -f "$packageDnsmasqFile" ] || {
+                       output "File $packageDnsmasqFile not found, $log_abort $__FAIL__"
+                       break
+               }
+               nft_ready || break
+               nslookup_tracker & exec 3>"$pipe_nslookup"
+
+               (
+                       output "Resolving domain names in policies..."
+                       while IFS='/' read -r _ domain _; do
+                               [ -n "$domain" ] && run_nslookup "$domain" &
+                               entries=$((entries + 1))
+                       done < "$packageDnsmasqFile"
+                       wait
+               )
+
+               exec 3>&-
+               break
+       done < "$pipe_ubus"
+
+       [ -n "$dnsmasq_restarted" ] || output "Dnsmasq hasn't restarted, $log_abort $__FAIL__"
+       kill "$ubus_listen_pid"
+       rm "$pipe_ubus"
+       rm "$pipe_nslookup"
+) &