#!/bin/sh /etc/rc.common
-# Copyright 2020-2023 MOSSDeF, Stan Grishin (stangri@melmac.ca)
-# shellcheck disable=SC2018,SC2019,SC3043,SC3057,SC3060
+# Copyright 2020-2024 MOSSDeF, Stan Grishin (stangri@melmac.ca)
+# shellcheck disable=SC2018,SC2019,SC2034,SC3043,SC3057,SC3060
# sysctl net.ipv4.conf.default.rp_filter=1
# sysctl net.ipv4.conf.all.rp_filter=1
# shellcheck disable=SC2034
USE_PROCD=1
-#!/bin/sh
-# Copyright 2023 MOSSDeF, Stan Grishin (stangri@melmac.ca)
-# shellcheck disable=SC2018,SC2019,SC2034,SC3043,SC3057,SC3060
+[ -n "${IPKG_INSTROOT}" ] && return 0
readonly packageName='pbr'
readonly PKG_VERSION='dev-test'
+readonly packageCompat='5'
readonly serviceName="$packageName $PKG_VERSION"
readonly serviceTrapSignals='exit SIGHUP SIGQUIT SIGKILL'
readonly packageConfigFile="/etc/config/${packageName}"
readonly _WARNING_='\033[0;33mWARNING\033[0m'
readonly ip_full='/usr/libexec/ip-full'
# shellcheck disable=SC2155
-readonly ip_bin="$(command -v ip)"
-readonly ipTablePrefix='pbr'
-# shellcheck disable=SC2155
-readonly iptables="$(command -v iptables)"
-# shellcheck disable=SC2155
-readonly ip6tables="$(command -v ip6tables)"
-# shellcheck disable=SC2155
-readonly ipset="$(command -v ipset)"
-readonly ipsPrefix='pbr'
-readonly iptPrefix='PBR'
+readonly ipTablePrefix="$packageName"
# shellcheck disable=SC2155
readonly agh="$(command -v AdGuardHome)"
-readonly aghConfigFile='/etc/adguardhome.yaml'
-readonly aghIpsetFile="/var/run/${packageName}.adguardhome.ipsets"
# shellcheck disable=SC2155
readonly nft="$(command -v nft)"
readonly nftIPv4Flag='ip'
readonly nftIPv6Flag='ip6'
readonly nftTempFile="/var/run/${packageName}.nft"
readonly nftPermFile="/usr/share/nftables.d/ruleset-post/30-${packageName}.nft"
-readonly nftPrefix='pbr'
+readonly nftPrefix="$packageName"
readonly nftTable='fw4'
readonly chainsList='forward input output postrouting prerouting'
readonly ssConfigFile='/etc/shadowsocks'
icmp_interface=
ignored_interface=
ipv6_enabled=
-nft_file_support=
+nft_file_support='1'
nft_user_set_policy=
nft_user_set_counter=
procd_boot_delay=
procd_wan6_interface=
resolver_set=
resolver_instance=
-rule_create_option=
secure_reload=
strict_enforcement=
supported_interface=
verbosity=
wan_ip_rules_priority=
wan_mark=
+nft_rule_counter=
nft_set_auto_merge=
nft_set_counter=
nft_set_flags_interval=
nft_set_timeout=
# run-time
+aghConfigFile='/etc/AdGuardHome/AdGuardHome.yaml'
gatewaySummary=
errorSummary=
warningSummary=
wanGW4=
wanGW6=
serviceStartTrigger=
+processDnsPolicyError=
processPolicyError=
processPolicyWarning=
resolver_set_supported=
policy_routing_nft_prev_param4=
policy_routing_nft_prev_param6=
+nft_rule_params=
nft_set_params=
torDnsPort=
torTrafficPort=
output_okbn() { output 1 "$_OKB_\\n"; output 2 "$__OKB__\\n"; }
output_fail() { output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
+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_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%% *}"; }
# shellcheck disable=SC2317
str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
str_replace() { echo "${1//$2/$3}"; }
-str_contains() { [ -n "$1" ] && [ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
-str_contains_word() { echo "$1" | grep -q -w "$2"; }
+str_to_dnsmsaq_nftset() { echo "$1" | tr ' ' '/'; }
str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
-str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
-str_extras_to_space() { echo "$1" | tr ';{}' ' '; }
-debug() { local i j; for i in "$@"; do eval "j=\$$i"; echo "${i}: ${j} "; done; }
+debug() { local i j; for i in "$@"; do eval "j=\$$i"; logger "${i}: ${j} "; done; }
quiet_mode() {
case "$1" in
on) verbosity=0;;
if [ -z "$verbosity" ] && [ -n "$packageName" ]; then
verbosity="$(uci_get "$packageName" 'config' 'verbosity' '2')"
fi
- if [ $# -ne 1 ] && is_integer "$1"; then
- if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; text="$*"; else return 0; fi
+ if [ "$#" -ne '1' ] && is_integer "$1"; then
+ if [ "$((verbosity & $1))" -gt '0' ] || [ "$verbosity" = "$1" ]; then shift; text="$*"; else return 0; fi
fi
text="${text:-$*}";
[ -t 1 ] && printf "%b" "$text"
msg="${text//$serviceName /service }";
- if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
+ if [ "$(printf "%b" "$msg" | wc -l)" -gt '0' ]; then
[ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
network_get_gateway gw "$iface" true
if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
# gw="$(ubus call "network.interface.${iface}" status | jsonfilter -e "@.route[0].nexthop")"
- gw="$($ip_bin -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
+ gw="$(ip -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
fi
eval "$1"='$gw'
}
local iface="$2" dev="$3" gw
network_get_gateway6 gw "$iface" true
if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
- gw="$($ip_bin -6 a list dev "$dev" 2>/dev/null | grep inet6 | grep 'scope global' | awk '{print $2}')"
+ gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | grep 'scope global' | awk '{print $2}')"
fi
eval "$1"='$gw'
}
+filter_options() {
+ local opt="$1" value="$2"
+ local i _ret=
+
+ case "$opt" in
+ phys_dev)
+ for i in $value; do
+ if is_phys_dev "$i"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ phys_dev_negative)
+ for i in $value; do
+ if is_negation "$i" && is_phys_dev "${i:1}"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ mac_address)
+ for i in $value; do
+ if is_mac_address "$i"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ mac_address_negative)
+ for i in $value; do
+ if is_negation "$i" && is_mac_address "${i:1}"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ domain)
+ for i in $value; do
+ if is_domain "$i"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ domain_negative)
+ for i in $value; do
+ if is_negation "$i" && is_domain "${i:1}"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ ipv4)
+ for i in $value; do
+ if is_ipv4 "$i" || is_ipv4_netmask "$i"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ ipv4_negative)
+ for i in $value; do
+ if is_negation "$i" && { is_ipv4 "${i:1}" || is_ipv4_netmask "${i:1}"; }; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ ipv6)
+ for i in $value; do
+ if is_ipv6 "$i"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ ipv6_negative)
+ for i in $value; do
+ if is_negation "$i" && is_ipv6 "${i:1}"; then
+ _ret="${_ret:+$_ret }$i"
+ fi
+ done
+ ;;
+ none)
+ :
+ ;;
+ *)
+ echo ''
+ return 1
+ ;;
+ esac
+ echo "$_ret"
+ return 0
+}
+inline_set() {
+ local value="$1" inline_set i
+ for i in $value; do
+ [ "${i:0:1}" = "!" ] && i=${i:1}
+ [ "${i:0:1}" = "@" ] && i=${i:1}
+ inline_set="${inline_set:+$inline_set, }$i"
+ done
+ echo "$inline_set"
+}
# shellcheck disable=SC2016
is_bad_user_file_nft_call() { grep -q '"\$nft" list' "$1" || grep '"\$nft" -f' "$1";}
is_config_enabled() {
- _check_config() { local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; }
+ _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
config_load "$packageName"
}
uci_get_device() { uci_get 'network' "$1" 'device' || uci_get 'network' "$1" 'dev'; }
uci_get_protocol() { uci_get 'network' "$1" 'proto'; }
-is_default_dev() { [ "$1" = "$($ip_bin -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
+is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
is_domain() { ! is_ipv6 "$1" && str_contains "$1" '[a-zA-Z]'; }
is_dslite() { local p; network_get_protocol p "$1"; [ "${p:0:6}" = "dslite" ]; }
is_family_mismatch() { ( is_ipv4_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_ipv4_netmask "${2//!}" ); }
is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
-is_greater_or_equal() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" = "$2"; }
+is_greater_or_equal() { test "$(printf '%s\n' "$@" | sort -V | head -n '1')" = "$2"; }
is_ignored_interface() { str_contains_word "$ignored_interface" "$1"; }
is_ignore_target() { [ "$(str_to_lower "$1")" = 'ignore' ]; }
is_integer() {
(*) return 0;;
esac
}
-is_ipset_type_supported() { ipset help hash:"$1" >/dev/null 2>&1; }
-is_nft_mode() { [ -x "$nft" ] && ! str_contains "$resolver_set" 'ipset' && "$nft" list chains inet | grep -q "${nftPrefix}_prerouting"; }
is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ':'; }
is_ipv6_global() { [ "${1:0:4}" = '2001' ]; }
is_lan() { local d; network_get_device d "$1"; str_contains "$d" 'br-lan'; }
is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp" ]; }
is_mac_address() { expr "$1" : '[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]$' >/dev/null; }
+is_negation() { [ "${1:0:1}" = '!' ]; }
is_netifd_table() { grep -q "ip.table.*$1" /etc/config/network; }
is_netifd_table_interface() { local iface="$1"; [ "$(uci_get 'network' "$iface" 'ip4table')" = "${packageName}_${iface%6}" ]; }
is_oc() { local p; network_get_protocol p "$1"; [ "${p:0:11}" = "openconnect" ]; }
is_ovpn() { local d; uci_get_device d "$1"; [ "${d:0:3}" = "tun" ] || [ "${d:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${d}/tun_flags" ]; }
is_ovpn_valid() { local dev_net dev_ovpn; uci_get_device dev_net "$1"; dev_ovpn="$(uci_get 'openvpn' "$1" 'dev')"; [ -n "$dev_net" ] && [ -n "$dev_ovpn" ] && [ "$dev_net" = "$dev_ovpn" ]; }
is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
+is_phys_dev_quick() { [ "${1:0:1}" = "@" ]; }
is_present() { command -v "$1" >/dev/null 2>&1; }
-is_service_running() { if is_nft_mode; then is_service_running_nft; else is_service_running_iptables; fi; }
-is_service_running_iptables() { [ -x "$iptables" ] && "$iptables" -t mangle -L | grep -q "${iptPrefix}_PREROUTING" >/dev/null 2>&1; }
+is_service_running() { is_service_running_nft; }
is_service_running_nft() { [ -x "$nft" ] && [ -n "$(get_mark_nft_chains)" ]; }
is_supported_iface_dev() { local n dev; for n in $ifacesSupported; do network_get_device dev "$n"; [ "$1" = "$dev" ] && return 0; done; return 1; }
is_supported_protocol() { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; }
get_rt_tables_next_id() { echo "$(($(sort -r -n "$rtTablesFile" | grep -o -E -m 1 "^[0-9]+")+1))"; }
get_rt_tables_non_pbr_next_id() { echo "$(($(grep -v "${ipTablePrefix}_" "$rtTablesFile" | sort -r -n | grep -o -E -m 1 "^[0-9]+")+1))"; }
# shellcheck disable=SC2016
-resolveip_to_ipt() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
-resolveip_to_ipt4() { resolveip_to_ipt -4 "$@"; }
-resolveip_to_ipt6() { [ -n "$ipv6_enabled" ] && resolveip_to_ipt -6 "$@"; }
-# shellcheck disable=SC2016
-resolveip_to_nftset() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
+resolveip_to_nftset() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
resolveip_to_nftset4() { resolveip_to_nftset -4 "$@"; }
resolveip_to_nftset6() { [ -n "$ipv6_enabled" ] && resolveip_to_nftset -6 "$@"; }
# shellcheck disable=SC2016
# shellcheck disable=SC2016
ipv6_leases_to_nftset() { [ -s '/tmp/hosts/odhcpd' ] || return 1; grep -v '^#' '/tmp/hosts/odhcpd' | grep "$1" | awk '{print $1}' | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
# shellcheck disable=SC3037
-ports_to_nftset() { echo -ne "$1"; }
-get_mark_ipt_chains() { [ -n "$(command -v iptables-save)" ] && iptables-save | grep ":${iptPrefix}_MARK_" | awk '{ print $1 }' | sed 's/://'; }
+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_ipsets() { [ -x "$(command -v ipset)" ] && ipset list | grep "${ipsPrefix}_" | awk '{ print $2 }'; }
get_nft_sets() { [ -x "$nft" ] && "$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}"; }
# luci app specific
is_enabled() { uci_get "$1" 'config' 'enabled'; }
-is_running_iptables() { iptables -t mangle -L | grep -q PBR_PREROUTING >/dev/null 2>&1; }
is_running_nft_file() { [ -s "$nftPermFile" ]; }
is_running_nft() { "$nft" list table inet fw4 | grep chain | grep -q pbr_mark_ >/dev/null 2>&1; }
-is_running() { is_running_iptables || is_running_nft; }
-check_ipset() { { [ -n "$ipset" ] && "$ipset" help hash:net; } >/dev/null 2>&1; }
-check_nft() { [ -n "$nft" ]; }
-check_agh() { [ -n "$agh" ] && [ -s "$aghConfigFile" ]; }
+check_nft() { [ -x "$nft" ]; }
+check_agh() { [ -x "$agh" ] && { [ -s "$aghConfigFile" ] || [ -s "${agh%/*}/AdGuardHome.yaml" ]; }; }
check_dnsmasq() { command -v dnsmasq >/dev/null 2>&1; }
check_unbound() { command -v unbound >/dev/null 2>&1; }
-check_agh_ipset() {
- check_ipset || return 1
- check_agh || return 1
- is_greater_or_equal "$($agh --version | sed 's|AdGuard Home, version v\(.*\)|\1|' | sed 's|-.*||')" '0.107.13'
-}
-check_dnsmasq_ipset() {
- local o;
- check_ipset || return 1
- check_dnsmasq || return 1
- o="$(dnsmasq -v 2>/dev/null)"
- ! echo "$o" | grep -q 'no-ipset' && echo "$o" | grep -q 'ipset'
-}
check_dnsmasq_nftset() {
local o;
check_nft || return 1
}
print_json_bool() { json_init; json_add_boolean "$1" "$2"; json_dump; json_cleanup; }
print_json_string() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
+try() {
+ if ! "$@"; then
+ state add 'errorSummary' 'errorTryFailed' "$*"
+ return 1
+ fi
+}
if type extra_command >/dev/null 2>&1; then
extra_command 'status' "Generates output required to troubleshoot routing issues
case "$1" in
errorConfigValidation) r="Config ($packageConfigFile) validation failure!";;
errorNoIpFull) r="ip-full binary cannot be found!";;
- errorNoIptables) r="iptables binary cannot be found!";;
- errorNoIpset) r="Resolver set support (${resolver_set}) requires ipset, but ipset binary cannot be found!";;
errorNoNft) r="Resolver set support (${resolver_set}) requires nftables, but nft binary cannot be found!";;
errorResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system!";;
errorServiceDisabled) r="The ${packageName} service is currently disabled!";;
errorNoWanGateway) r="The ${serviceName} service failed to discover WAN gateway!";;
errorNoWanInterface) r="The %s inteface not found, you need to set the 'pbr.config.procd_wan_interface' option!";;
errorNoWanInterfaceHint) r="Refer to https://docs.openwrt.melmac.net/pbr/#procd_wan_interface.";;
- errorIpsetNameTooLong) r="The ipset name '%s' is longer than allowed 31 characters!";;
errorNftsetNameTooLong) r="The nft set name '%s' is longer than allowed 255 characters!";;
errorUnexpectedExit) r="Unexpected exit or service termination: '%s'!";;
errorPolicyNoSrcDest) r="Policy '%s' has no source/destination parameters!";;
errorPolicyNoInterface) r="Policy '%s' has no assigned interface!";;
+ errorPolicyNoDns) r="Policy '%s' has no assigned DNS!";;
+ errorPolicyProcessNoInterfaceDns) r="Interface '%s' has no assigned DNS!";;
errorPolicyUnknownInterface) r="Policy '%s' has an unknown interface!";;
errorPolicyProcessCMD) r="'%s'!";;
errorFailedSetup) r="Failed to set up '%s'!";;
errorFileSchemaRequiresCurl) r="The file:// schema requires curl, but it's not detected on this system!";;
warningInvalidOVPNConfig) r="Invalid OpenVPN config for '%s' interface.";;
warningResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system.";;
- warningAGHVersionTooLow) r="Installed AdGuardHome ('%s') doesn't support 'ipset_file' option.";;
warningPolicyProcessCMD) r="'%s'";;
warningTorUnsetParams) r="Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'.";;
warningTorUnsetProto) r="Please unset 'proto' or set 'proto' to 'all' for policy '%s'.";;
- warningTorUnsetChainIpt) r="Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '%s'.";;
warningTorUnsetChainNft) r="Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'.";;
warningOutdatedWebUIApp) r="The WebUI application is outdated (version %s), please update it.";;
warningBadNftCallsInUserFile) r="Incompatible nft calls detected in user include file, disabling fw4 nft file support.";;
_check_user_files_for_bad_nft_calls() {
local cfg="$1"
local en path
- config_get_bool en "$cfg" 'enabled' 1
+ config_get_bool en "$cfg" 'enabled' '1'
config_get path "$cfg" 'path'
- [ "$en" -eq 0 ] && return 0
+ [ "$en" -eq '0' ] && return 0
[ -z "$path" ] && return 0
[ -s "$path" ] || return 0
is_bad_user_file_nft_call "$path" && user_file_check_result='bad'
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_file_support 'config' 'nft_file_support' '1'
+ 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' '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 nft_set_gc_interval 'config' 'nft_set_gc_interval'
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 rule_create_option 'config' 'rule_create_option' 'add'
config_get_bool secure_reload 'config' 'secure_reload' '0'
config_get_bool strict_enforcement 'config' 'strict_enforcement' '1'
config_get supported_interface 'config' 'supported_interface'
config_get wan_mark 'config' 'wan_mark' '010000'
fw_mask="0x${fw_mask}"
wan_mark="0x${wan_mark}"
- [ -n "$ipv6_enabled" ] && [ "$ipv6_enabled" -eq 0 ] && unset ipv6_enabled
- [ -n "$nft_file_support" ] && [ "$nft_file_support" -eq 0 ] && unset nft_file_support
- [ -n "$nft_user_set_counter" ] && [ "$nft_user_set_counter" -eq 0 ] && unset nft_user_set_counter
- [ -n "$secure_reload" ] && [ "$secure_reload" -eq 0 ] && unset secure_reload
+ 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_file_support" ] && [ "$nft_file_support" -eq '0' ] && unset nft_file_support
+ [ -n "$nft_user_set_counter" ] && [ "$nft_user_set_counter" -eq '0' ] && unset nft_user_set_counter
+ [ -n "$secure_reload" ] && [ "$secure_reload" -eq '0' ] && unset secure_reload
config_foreach _check_user_files_for_bad_nft_calls 'include'
[ -n "$user_file_check_result" ] && unset nft_file_support
[ -n "$nft_file_support" ] && unset secure_reload
is_config_enabled 'include' && unset secure_reload
- if is_nft_mode; then
- fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")"
- fw_maskXor="${fw_maskXor:-0xff00ffff}"
- else
- case $rule_create_option in
- insert|-i|-I) rule_create_option='-I';;
- add|-a|-A|*) rule_create_option='-A';;
- esac
- fi
+ fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")"
+ fw_maskXor="${fw_maskXor:-0xff00ffff}"
+ [ "$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
fi
fi
fi
+
+ nft_rule_params="${nft_rule_counter:+counter}"
+
nft_set_params=" \
${nft_set_auto_merge:+ auto-merge;} \
${nft_set_counter:+ counter;} \
load_package_config "$param"
case "$param" in
on_start)
- if [ "$enabled" -eq 0 ]; then
+ if [ "$enabled" -eq '0' ]; then
state add 'errorSummary' 'errorServiceDisabled'
return 1
fi
state add 'errorSummary' 'errorConfigValidation'
return 1
fi
- if [ ! -x "$ip_bin" ]; then
- state add 'errorSummary' 'errorNoIpFull'
- return 1
- fi
- if is_nft_mode; then
- if [ "$(uci_get 'firewall' 'defaults' 'auto_includes')" = '0' ]; then
- uci_remove 'firewall' 'defaults' 'auto_includes'
- uci_commit firewall
- fi
- else
- if [ -z "$iptables" ] || [ ! -x "$iptables" ]; then
- state add 'errorSummary' 'errorNoIptables'
- return 1
- fi
+ # TODO: implement ip-full check
+# if [ ! -x ip ]; then
+# state add 'errorSummary' 'errorNoIpFull'
+# return 1
+# fi
+ if [ "$(uci_get 'firewall' 'defaults' 'auto_includes')" = '0' ]; then
+ uci_remove 'firewall' 'defaults' 'auto_includes'
+ uci_commit firewall
fi
;;
on_stop)
fi
while [ -z "$wanGW" ] ; do
load_network "$param"
- if [ $((sleepCount)) -gt $((procd_boot_timeout)) ] || [ -n "$wanGW" ]; then break; fi
+ if [ "$((sleepCount))" -gt "$((procd_boot_timeout))" ] || [ -n "$wanGW" ]; then break; fi
output "$serviceName waiting for $procd_wan_interface gateway...\\n"
sleep 1
network_flush_cache
fi
}
-# shellcheck disable=SC2086
-ipt4() {
- local d
- [ -x "$iptables" ] || return 1
- for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
- [ "$d" != "$*" ] && "$iptables" $d >/dev/null 2>&1
- done
- d="$*"; "$iptables" $d >/dev/null 2>&1
-}
-
-# shellcheck disable=SC2086
-ipt6() {
- local d
- [ -n "$ipv6_enabled" ] || return 0
- [ -x "$ip6tables" ] || return 1
- for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
- [ "$d" != "$*" ] && "$ip6tables" $d >/dev/null 2>&1
- done
- d="$*"
- "$ip6tables" $d >/dev/null 2>&1
-}
-
-# shellcheck disable=SC2086
-ipt() {
- local d failFlagIpv4=1 failFlagIpv6=1
- [ -x "$iptables" ] || return 1
- for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
- if [ "$d" != "$*" ]; then
- "$iptables" $d >/dev/null 2>&1
- if [ -x "$ip6tables" ]; then
- "$ip6tables" $d >/dev/null 2>&1
- fi
- fi
- done
- d="$*"; "$iptables" $d >/dev/null 2>&1 && failFlagIpv4=0;
- if [ -n "$ipv6_enabled" ] && [ -x "$ip6tables" ]; then
- "$ip6tables" $d >/dev/null 2>&1 && failFlagIpv6=0
- fi
- [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ]
-}
-
-# shellcheck disable=SC2086
-ips4() { [ -x "$ipset" ] && "$ipset" "$@" >/dev/null 2>&1; }
-ips6() { [ -x "$ipset" ] && { if [ -n "$ipv6_enabled" ] && [ -n "$*" ]; then "$ipset" "$@" >/dev/null 2>&1; else return 1; fi; }; }
-ips() {
- local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
- local ipset4 ipset6 i
- local ipv4_error=1 ipv6_error=1
- ipset4="${ipsPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
- ipset6="${ipsPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
-
- [ -x "$ipset" ] || return 1
-
- if [ "${#ipset4}" -gt 31 ]; then
- state add 'errorSummary' 'errorIpsetNameTooLong' "$ipset4"
- return 1
- fi
-
- case "$command" in
- add)
- ips4 -q -! add "$ipset4" ["$param"] comment "$comment" && ipv4_error=0
- ips6 -q -! add "$ipset6" ["$param"] comment "$comment" && ipv6_error=0
- ;;
- add_agh_element)
- [ -n "$ipv6_enabled" ] || unset ipset6
- echo "${param}/${ipset4}${ipset6:+,$ipset6}" >> "$aghIpsetFile" && ipv4_error=0
- ;;
- add_dnsmasq_element)
- [ -n "$ipv6_enabled" ] || unset ipset6
- # shellcheck disable=SC2086
- echo "ipset=/${param}/${ipset4}${ipset6:+,$ipset6} # $comment" | tee -a $dnsmasqFileList >/dev/null 2>&1 && ipv4_error=0
- ;;
- create)
- ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
- ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
- ;;
- create_agh_set)
- ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
- ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
- ;;
- create_dnsmasq_set)
- ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
- ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
- ;;
- create_user_set)
- case "$type" in
- ip|net)
- ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
- ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
- case "$target" in
- dst)
- ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
- ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
- ;;
- src)
- ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
- ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
- ;;
- esac
- ;;
- mac)
- ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
- ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0
- ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
- ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
- ;;
- esac
- ;;
- delete|destroy)
- ips4 -q -! destroy "$ipset4" && ipv4_error=0
- ips6 -q -! destroy "$ipset6" && ipv6_error=0
- ;;
- delete_user_set)
- ips4 -q -! destroy "$ipset4" && ipv4_error=0
- ips6 -q -! destroy "$ipset6" family inet6 && ipv6_error=0
- case "$type" in
- ip|net)
- case "$target" in
- dst)
- ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
- ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
- ;;
- src)
- ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
- ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
- ;;
- esac
- ;;
- mac)
- ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
- ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
- ;;
- esac
- ;;
- flush|flush_user_set)
- ips4 -q -! flush "$ipset4" && ipv4_error=0
- ips6 -q -! flush "$ipset6" && ipv6_error=0
- ;;
- esac
- if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
- return 0
- else
- return 1
- fi
-}
-
nft_call() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
nft_file() {
local i
[ -x "$nft" ] || return 1
- if [ "${#nftset4}" -gt 255 ]; then
+ if [ "${#nftset4}" -gt '255' ]; then
state add 'errorSummary' 'errorNftsetNameTooLong' "$nftset4"
return 1
fi
nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nft_set_params comment \"$comment\"; }" && ipv6_error=0
case "$target" in
dst)
- nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
- nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ 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
;;
src)
- nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
- nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ 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
;;
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}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
- nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && 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
;;
esac
;;
esac
;;
mac)
- nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
- nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" "ether" saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" "ether" saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
;;
esac
;;
}
cleanup_main_chains() {
- local i
- for i in $chainsList; do
+ local i j
+ for i in $chainsList dstnat_lan; do
i="$(str_to_lower "$i")"
nft_call flush chain inet "$nftTable" "${nftPrefix}_${i}"
done
- for i in $chainsList; do
- i="$(str_to_upper "$i")"
- ipt -t mangle -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
- ipt -t mangle -F "${iptPrefix}_${i}"
- ipt -t mangle -X "${iptPrefix}_${i}"
- done
}
cleanup_marking_chains() {
- local i
+ local i j
for i in $(get_mark_nft_chains); do
nft_call flush chain inet "$nftTable" "$i"
nft_call delete chain inet "$nftTable" "$i"
done
- for i in $(get_mark_ipt_chains); do
- ipt -t mangle -F "$i"
- ipt -t mangle -X "$i"
- done
}
cleanup_sets() {
nft_call flush set inet "$nftTable" "$i"
nft_call delete set inet "$nftTable" "$i"
done
- for i in $(get_ipsets); do
- ipset -q -! flush "$i" >/dev/null 2>&1
- ipset -q -! destroy "$i" >/dev/null 2>&1
- done
}
state() {
resolver() {
local agh_version
- local param="$1"
+ local param="$1" iface="$2" target="$3" type="$4" uid="$5" name="$6" value="$7"
shift
if [ "$param" = 'cleanup_all' ]; then
- sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
- rm -f "$aghIpsetFile"
local dfl
for dfl in $dnsmasqFileList; do
rm -f "$dfl"
cleanup) return 0;;
configure) return 0;;
init) return 0;;
- init_end) return 0;;
- kill) return 0;;
- reload) return 0;;
- restart) return 0;;
- compare_hash) return 0;;
- store_hash) return 0;;
- esac
- ;;
- adguardhome.ipset)
- case "$param" in
- add_resolver_element)
- [ -n "$resolver_set_supported" ] && ips 'add_agh_element' "$@";;
- create_resolver_set)
- [ -n "$resolver_set_supported" ] && ips 'create_agh_set' "$@";;
- check_support)
- if [ ! -x "$ipset" ]; then
- state add 'errorSummary' 'errorNoIpset'
- return 1
- fi
- if [ -n "$agh" ] && [ -s "$aghConfigFile" ]; then
- agh_version="$($agh --version | sed 's|AdGuard Home, version v\(.*\)|\1|' | sed 's|-.*||')"
- if is_greater_or_equal "$agh_version" '0.107.13'; then
- resolver_set_supported='true'
- return 0
- else
- state add 'warningSummary' 'warningAGHVersionTooLow' "$agh_version"
- return 1
- fi
- else
- state add 'warningSummary' 'warningResolverNotSupported'
- return 1
- fi
- ;;
- cleanup)
- [ -z "$resolver_set_supported" ] && return 0
- rm -f "$aghIpsetFile"
- sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
- ;;
- configure)
- [ -z "$resolver_set_supported" ] && return 1
- mkdir -p "${aghIpsetFile%/*}"
- touch "$aghIpsetFile"
- sed -i '/ipset_file/d' "$aghConfigFile" >/dev/null 2>&1
- sed -i "/ ipset:/a \ \ ipset_file: $aghIpsetFile" "$aghConfigFile"
- ;;
- init) :;;
- init_end) :;;
- kill)
- [ -n "$resolver_set_supported" ] && [ -n "$agh" ] && killall -q -s HUP "$agh";;
- reload)
- [ -z "$resolver_set_supported" ] && return 1
- output 3 'Reloading adguardhome '
- if /etc/init.d/adguardhome reload >/dev/null 2>&1; then
- output_okn
- return 0
- else
- output_failn
- return 1
- fi
- ;;
- restart)
- [ -z "$resolver_set_supported" ] && return 1
- output 3 'Restarting adguardhome '
- if /etc/init.d/adguardhome restart >/dev/null 2>&1; then
- output_okn
- return 0
- else
- output_failn
- return 1
- fi
- ;;
- compare_hash)
- [ -z "$resolver_set_supported" ] && return 1
- local resolverNewHash
- if [ -s "$aghIpsetFile" ]; then
- resolverNewHash="$(md5sum "$aghIpsetFile" | awk '{ print $1; }')"
- fi
- [ "$resolverNewHash" != "$resolverStoredHash" ]
- ;;
- store_hash)
- [ -s "$aghIpsetFile" ] && resolverStoredHash="$(md5sum "$aghIpsetFile" | awk '{ print $1; }')";;
- esac
- ;;
- dnsmasq.ipset)
- case "$param" in
- add_resolver_element)
- [ -n "$resolver_set_supported" ] && ips 'add_dnsmasq_element' "$@";;
- create_resolver_set)
- [ -n "$resolver_set_supported" ] && ips 'create_dnsmasq_set' "$@";;
- check_support)
- if [ ! -x "$ipset" ]; then
- state add 'errorSummary' 'errorNoIpset'
- return 1
- fi
- if ! dnsmasq -v 2>/dev/null | grep -q 'no-ipset' && dnsmasq -v 2>/dev/null | grep -q 'ipset'; then
- resolver_set_supported='true'
- return 0
- else
- state add 'warningSummary' 'warningResolverNotSupported'
- return 1
- fi
- ;;
- cleanup)
- if [ -n "$resolver_set_supported" ]; then
- local dfl
- for dfl in $dnsmasqFileList; do
- rm -f "$dfl"
- done
- fi
- ;;
- configure)
- if [ -n "$resolver_set_supported" ]; then
- local dfl
- for dfl in $dnsmasqFileList; do
- 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)
- config_load 'dhcp'
- if [ "$resolver_instance" = "*" ]; then
- config_foreach _resolver_dnsmasq_confdir 'dnsmasq'
- dnsmasqFile="${dnsmasqFile:-$dnsmasqFileDefault}"
- str_contains "$dnsmasqFileList" "$dnsmasqFileDefault" || \
- dnsmasqFileList="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFileDefault}"
- else
- for i in $resolver_instance; do
- _resolver_dnsmasq_confdir "@dnsmasq[$i]" \
- || _resolver_dnsmasq_confdir "$i"
- done
- dnsmasqFile="${dnsmasqFile:-$dnsmasqFileDefault}"
- str_contains "$dnsmasqFileList" "$dnsmasqFileDefault" || \
- dnsmasqFileList="${dnsmasqFileList:-$dnsmasqFileDefault}"
- fi
- ;;
- init) :;;
- init_end) :;;
- kill)
- [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
- reload)
- [ -z "$resolver_set_supported" ] && return 1
- output 3 'Reloading dnsmasq '
- if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
- output_okn
- return 0
- else
- output_failn
- return 1
- fi
- ;;
- restart)
- [ -z "$resolver_set_supported" ] && return 1
- output 3 'Restarting dnsmasq '
- if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
- output_okn
- return 0
- else
- output_failn
- return 1
- fi
- ;;
- compare_hash)
- [ -z "$resolver_set_supported" ] && return 1
- local resolverNewHash
- if [ -s "$dnsmasqFile" ]; then
- resolverNewHash="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')"
- fi
- [ "$resolverNewHash" != "$resolverStoredHash" ]
- ;;
- store_hash)
- [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')";;
+ init_end) return 0;;
+ kill) return 0;;
+ reload) return 0;;
+ restart) return 0;;
+ compare_hash) return 0;;
+ store_hash) return 0;;
esac
;;
dnsmasq.nftset)
case "$param" in
add_resolver_element)
- [ -n "$resolver_set_supported" ] && nftset 'add_dnsmasq_element' "$@";;
+ [ -n "$resolver_set_supported" ] || return 1
+ local d
+ for d in $value; do
+ nftset 'add_dnsmasq_element' "$iface" "$target" "$type" "$uid" "$name" "$d"
+ done
+# nftset 'add_dnsmasq_element' "$iface" "$target" "$type" "$uid" "$name" "$(str_to_dnsmsaq_nftset "$value")"
+ ;;
create_resolver_set)
- [ -n "$resolver_set_supported" ] && nftset 'create_dnsmasq_set' "$@";;
+ [ -n "$resolver_set_supported" ] || return 1
+ nftset 'create_dnsmasq_set' "$iface" "$target" "$type" "$uid" "$name" "$value"
+ ;;
check_support)
if [ ! -x "$nft" ]; then
state add 'errorSummary' 'errorNoNft'
[ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')";;
esac
;;
- unbound.ipset)
- case "$param" in
- add_resolver_element) :;;
- create_resolver_set) :;;
- check_support) :;;
- cleanup) :;;
- configure) :;;
- init) :;;
- init_end) :;;
- kill) :;;
- reload) :;;
- restart) :;;
- compare_hash) :;;
- store_hash) :;;
- esac
- ;;
unbound.nftset)
case "$param" in
add_resolver_element) :;;
network_get_subnet lan_subnet "${procd_lan_interface:-lan}"
network_get_physdev wan_device "${wanIface4:-wan}"
network_get_physdev wan6_device "${wanIface6:-wan6}"
- if is_nft_mode; then
- nft_call add chain inet "$nftTable" "${nftPrefix}_killswitch" '{ type filter hook forward priority 0; policy accept; }' || s=1
- nft_call add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan_device" "$nftIPv4Flag" saddr "$lan_subnet" counter reject || s=1
- nft_call add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan6_device" "$nftIPv6Flag" saddr "$lan_subnet" counter reject
- else
- ipt -N "${iptPrefix}_KILLSWITCH" || s=1
- ipt -A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan_device" -j REJECT || s=1
- ipt -A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan6_device" -j REJECT
- ipt -I FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
- fi
- if [ "$s" -eq 0 ]; then
+ nft_call add chain inet "$nftTable" "${nftPrefix}_killswitch" '{ type filter hook forward priority 0; policy accept; }' || s=1
+ nft_call add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan_device" "$nftIPv4Flag" saddr "$lan_subnet" counter reject || s=1
+ nft_call add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan6_device" "$nftIPv6Flag" saddr "$lan_subnet" counter reject
+ if [ "$s" -eq '0' ]; then
output_okn
else
output_failn
if [ -n "$secure_reload" ] && ! nft_file 'enabled'; then
output 3 'Deactivating traffic killswitch '
fi
- if is_nft_mode; then
- nft_call flush chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
- nft_call delete chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
- else
- ipt -D FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
- ipt -F "${iptPrefix}_KILLSWITCH" || s=1
- ipt -X "${iptPrefix}_KILLSWITCH" || s=1
- fi
+ nft_call flush chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
+ nft_call delete chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
if [ -n "$secure_reload" ] && ! nft_file 'enabled'; then
- if [ "$s" -eq 0 ]; then
+ if [ "$s" -eq '0' ]; then
output_okn
else
output_failn
esac
}
-policy_routing() { if is_nft_mode; then policy_routing_nft "$@"; else policy_routing_iptables "$@"; fi; }
-policy_routing_iptables() {
- local mark param4 param6 i negation value dest4 dest6 ipInsertOption="-A"
- local ip4error='1' ip6error='1'
- local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9"
- proto="$(str_to_lower "$7")"
- chain="$(str_to_upper "$8")"
- chain="${chain:-PREROUTING}"
- mark=$(eval echo "\$mark_${iface//-/_}")
+# original idea by @egc112: https://github.com/egc112/OpenWRT-egc-add-on/tree/main/stop-dns-leak
+dns_policy_routing() {
+ local mark i nftInsertOption='add' proto='tcp udp' proto_i
+ 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 chain='dstnat_lan' iface='dns'
- if [ -n "$ipv6_enabled" ] && { is_ipv6 "$laddr" || is_ipv6 "$raddr"; }; then
+ if [ -z "${dest_dns_ipv4}${dest_dns_ipv6}" ]; then
processPolicyError='true'
- state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
+ state add 'errorSummary' 'errorPolicyProcessNoInterfaceDns' "'$dest_dns'"
return 1
fi
- if is_tor "$iface"; then
- return 1
- elif is_xray "$iface"; then
- unset rport
- [ -z "$lport" ] && lport='0-52,54-65535'
- proto='tcp udp'
- dest4="-j TPROXY --on-ip 0.0.0.0 --on-port $(get_xray_traffic_port "$iface")"
- dest6="-j TPROXY --on-ip :: --on-port $(get_xray_traffic_port "$iface")"
- elif [ -n "$mark" ]; then
- dest4="-g ${iptPrefix}_MARK_${mark}"
- dest6="-g ${iptPrefix}_MARK_${mark}"
- elif [ "$iface" = "ignore" ]; then
- dest4="-j RETURN"
- dest6="-j RETURN"
- else
+ if [ -z "$ipv6_enabled" ] && is_ipv6 "$(str_first_word "$src_addr")"; then
processPolicyError='true'
- state add 'errorSummary' 'errorPolicyProcessUnknownFwmark' "$iface"
+ state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
return 1
fi
- if is_family_mismatch "$laddr" "$raddr"; then
+ if { is_ipv4 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv4" ]; } || \
+ { is_ipv6 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv6" ]; }; then
processPolicyError='true'
- state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$laddr' '$raddr'"
+ state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns'"
return 1
fi
- if [ -z "$proto" ]; then
- if [ -n "${lport}${rport}" ]; then
- proto='tcp udp'
- else
- proto='all'
- fi
- fi
+ for proto_i in $proto; do
+ unset param4
+ unset param6
- for i in $proto; do
- if [ "$i" = 'all' ]; then
- param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest4"
- param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest6"
- elif ! is_supported_protocol "$i"; then
- processPolicyError='true'
- state add 'errorSummary' 'errorPolicyProcessUnknownProtocol' "${name}: '$i'"
- return 1
- else
- param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest4 -p $i"
- param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest6 -p $i"
- fi
+ dest4="dport 53 counter dnat ip to ${dest_dns_ipv4}:53"
+ dest6="dport 53 counter dnat ip6 to ${dest_dns_ipv6}:53"
- if [ -n "$laddr" ]; then
- if [ "${laddr:0:1}" = "!" ]; then
- negation='!'; value="${laddr:1}"
+ if [ -n "$src_addr" ]; then
+ if [ "${src_addr:0:1}" = "!" ]; then
+ negation='!='; src_addr="${src_addr//\!}"; nftset_suffix='_neg';
else
- unset negation; value="$laddr";
+ unset negation; unset nftset_suffix;
fi
- if is_phys_dev "$value"; then
- param4="$param4 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
- param6="$param6 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
- elif is_ipv4_netmask "$value"; then
- local target='src' type='net'
- if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
- ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
- param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
- param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
- else
- param4="$param4 ${negation:+$negation }-s $value"
- param6="$param6 ${negation:+$negation }-s $value"
- fi
- elif is_mac_address "$value"; then
- local target='src' type='mac'
- if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
- ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
- param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
- param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
- else
- param4="$param4 -m mac ${negation:+$negation }--mac-source $value"
- param6="$param6 -m mac ${negation:+$negation }--mac-source $value"
- fi
- else
- local target='src' type='ip'
- if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
- ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
- param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
- param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
- else
- local resolvedIP4 resolvedIP6
- resolvedIP4="$(resolveip_to_ipt4 "$value")"
- resolvedIP6="$(resolveip_to_ipt6 "$value")"
- if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
- state add 'errorSummary' 'errorFailedToResolve' "$value"
+ value="$src_addr"
+ first_value="$(str_first_word "$value")"
+ if is_phys_dev_quick "$first_value"; then
+ param4="${param4:+$param4 }iifname ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }iifname ${negation:+$negation }{ $(inline_set "$value") }"
+ elif is_mac_address "$first_value"; then
+ param4="${param4:+$param4 }ether saddr ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }ether saddr ${negation:+$negation }{ $(inline_set "$value") }"
+ elif is_domain "$first_value"; then
+ local inline_set_ipv4='' inline_set_ipv6='' d=''
+ for d in $value; do
+ local resolved_ipv4 resolved_ipv6
+ resolved_ipv4="$(resolveip_to_nftset4 "$d")"
+ resolved_ipv6="$(resolveip_to_nftset6 "$d")"
+ if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$d"
+ else
+ [ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
+ [ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
fi
- param4="$param4 ${negation:+$negation }-s $resolvedIP4"
- param6="$param6 ${negation:+$negation }-s $resolvedIP6"
- fi
- fi
- fi
-
- if [ -n "$lport" ]; then
- if [ "${lport:0:1}" = "!" ]; then
- negation='!'; value="${lport:1}"
+ done
+ [ -n "$inline_set_ipv4" ] || inline_set_ipv4_empty_flag='true'
+ [ -n "$inline_set_ipv6" ] || inline_set_ipv6_empty_flag='true'
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $inline_set_ipv4 }"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $inline_set_ipv6 }"
else
- unset negation; value="$lport";
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
fi
- param4="$param4 -m multiport ${negation:+$negation }--sport ${value//-/:}"
- param6="$param6 -m multiport ${negation:+$negation }--sport ${value//-/:}"
fi
- if [ -n "$raddr" ]; then
- if [ "${raddr:0:1}" = "!" ]; then
- negation='!'; value="${raddr:1}"
- else
- unset negation; value="$raddr";
- fi
- if is_ipv4_netmask "$value"; then
- local target='dst' type='net'
- if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
- ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
- param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
- param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
- else
- param4="$param4 ${negation:+$negation }-d ${value}"
- param6="$param6 ${negation:+$negation }-d ${value}"
- fi
- elif is_domain "$value"; then
- local target='dst' type='ip'
- if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
- resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
- param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
- param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
- elif ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
- ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
- param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
- param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
- else
- local resolvedIP4 resolvedIP6
- resolvedIP4="$(resolveip_to_ipt4 "$value")"
- resolvedIP6="$(resolveip_to_ipt6 "$value")"
- if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
- state add 'errorSummary' 'errorFailedToResolve' "$value"
- fi
- param4="$param4 ${negation:+$negation }-d $resolvedIP4"
- param6="$param6 ${negation:+$negation }-d $resolvedIP6"
- fi
- else
- local target='dst' type='ip'
- if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
- ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
- param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
- param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
- else
- param4="$param4 ${negation:+$negation }-d ${value}"
- param6="$param6 ${negation:+$negation }-d ${value}"
- fi
- fi
- fi
+ param4="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param4} ${proto_i} ${nft_rule_params} ${dest4} comment \"$name\""
+ param6="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param6} ${proto_i} ${nft_rule_params} ${dest6} comment \"$name\""
- if [ -n "$rport" ]; then
- if [ "${rport:0:1}" = "!" ]; then
- negation='!'; value="${rport:1}"
- else
- unset negation; value="$rport";
- fi
- param4="$param4 -m multiport ${negation:+$negation }--dport ${value//-/:}"
- param6="$param6 -m multiport ${negation:+$negation }--dport ${value//-/:}"
+ local ipv4_error='0' ipv6_error='0'
+ if [ "$policy_routing_nft_prev_param4" != "$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"
fi
-
- if [ -n "$name" ]; then
- param4="$param4 -m comment --comment $(str_extras_to_underscore "$name")"
- param6="$param6 -m comment --comment $(str_extras_to_underscore "$name")"
+ if [ "$policy_routing_nft_prev_param6" != "$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"
fi
- local ipv4_error='0' ipv6_error='0'
- if [ "$param4" = "$param6" ]; then
- ipt4 "$param4" || ipv4_error='1'
- else
- ipt4 "$param4" || ipv4_error='1'
- ipt6 "$param6" || ipv6_error='1'
+ if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param6"
+ logger -t "$packageName" "ERROR: nft $param4"
+ logger -t "$packageName" "ERROR: nft $param6"
+ elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
+ logger -t "$packageName" "ERROR: nft $param4"
fi
-
- if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
- processPolicyError='true'
- state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
- state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
- state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param6"
- logger -t "$packageName" "ERROR: iptables $param4"
- logger -t "$packageName" "ERROR: iptables $param6"
- elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
- processPolicyError='true'
- state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
- state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
- logger -t "$packageName" "ERROR: iptables $param4"
- fi
-
done
}
-policy_routing_nft() {
+
+policy_routing() {
local mark i nftInsertOption='add'
- local param4 param6 proto_i negation value dest4 dest6
+ local param4 param6 proto_i negation value dest4 dest6
+ local nftset_suffix first_value_src first_value_dest
+ local src_inline_set_ipv4_empty_flag src_inline_set_ipv6_empty_flag
+ local dest_inline_set_ipv4_empty_flag dest_inline_set_ipv6_empty_flag
local name="$1" iface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto chain uid="$9"
proto="$(str_to_lower "$7")"
chain="$(str_to_lower "$8")"
chain="${chain:-prerouting}"
mark=$(eval echo "\$mark_${iface//-/_}")
- if [ -z "$ipv6_enabled" ] && { is_ipv6 "$src_addr" || is_ipv6 "$dest_addr"; }; then
+ if [ -z "$ipv6_enabled" ] && \
+ { is_ipv6 "$(str_first_word "$src_addr")" || is_ipv6 "$(str_first_word "$dest_addr")"; }; then
processPolicyError='true'
state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
return 1
return 1
fi
- if is_family_mismatch "$src_addr" "$dest_addr"; then
- processPolicyError='true'
- state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_addr'"
- return 1
- fi
+ # TODO: implement actual family mismatch check on lists
+# if is_family_mismatch "$src_addr" "$dest_addr"; then
+# processPolicyError='true'
+# state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_addr'"
+# return 1
+# fi
if [ -z "$proto" ]; then
if [ -n "${src_port}${dest_port}" ]; then
if [ -n "$src_addr" ]; then
if [ "${src_addr:0:1}" = "!" ]; then
- negation='!='; value="${src_addr:1}"
+ negation='!='; value="${src_addr//\!}"; nftset_suffix='_neg';
else
- unset negation; value="$src_addr";
+ unset negation; value="$src_addr"; unset nftset_suffix;
fi
- if is_phys_dev "$value"; then
- param4="${param4:+$param4 }iifname ${negation:+$negation }${value:1}"
- param6="${param6:+$param6 }iifname ${negation:+$negation }${value:1}"
- elif is_mac_address "$value"; then
- local target='src' type='mac'
- if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
- nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
- param4="${param4:+$param4 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
- param6="${param6:+$param6 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
- else
- param4="${param4:+$param4 }ether saddr ${negation:+$negation }${value}"
- param6="${param6:+$param6 }ether saddr ${negation:+$negation }${value}"
- fi
+ first_value_src="$(str_first_word "$value")"
+ if is_phys_dev_quick "$first_value_src"; then
+ param4="${param4:+$param4 }iifname ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }iifname ${negation:+$negation }{ $(inline_set "$value") }"
+ elif is_mac_address "$first_value_src"; then
+ param4="${param4:+$param4 }ether saddr ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }ether saddr ${negation:+$negation }{ $(inline_set "$value") }"
+ elif is_domain "$first_value_src"; then
+ local inline_set_ipv4='' inline_set_ipv6='' d=''
+ unset src_inline_set_ipv4_empty_flag
+ unset src_inline_set_ipv6_empty_flag
+ for d in $value; do
+ local resolved_ipv4 resolved_ipv6
+ resolved_ipv4="$(resolveip_to_nftset4 "$d")"
+ resolved_ipv6="$(resolveip_to_nftset6 "$d")"
+ if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$d"
+ else
+ [ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
+ [ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
+ fi
+ done
+ [ -n "$inline_set_ipv4" ] || src_inline_set_ipv4_empty_flag='true'
+ [ -n "$inline_set_ipv6" ] || src_inline_set_ipv6_empty_flag='true'
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $inline_set_ipv4 }"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $inline_set_ipv6 }"
else
- local target='src' type='ip'
- if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
- nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
- param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
- param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
- else
- param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }${value}"
- param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }${value}"
- fi
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
fi
fi
if [ -n "$dest_addr" ]; then
if [ "${dest_addr:0:1}" = "!" ]; then
- negation='!='; value="${dest_addr:1}"
+ negation='!='; value="${src_addr//\!}"; nftset_suffix='_neg';
else
- unset negation; value="$dest_addr";
+ unset negation; value="$dest_addr"; unset nftset_suffix;
fi
- if is_phys_dev "$value"; then
- param4="${param4:+$param4 }oifname ${negation:+$negation }${value:1}"
- param6="${param6:+$param6 }oifname ${negation:+$negation }${value:1}"
- elif is_domain "$value"; then
+ first_value_dest="$(str_first_word "$value")"
+ if is_phys_dev_quick "$first_value_dest"; then
+ param4="${param4:+$param4 }oifname ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }oifname ${negation:+$negation }{ $(inline_set "$value") }"
+ elif is_domain "$first_value_dest"; then
local target='dst' type='ip'
if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "$name" && \
resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
- param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
- param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
- elif nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
- nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
- param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
- param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}${nftset_suffix}"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}${nftset_suffix}"
else
- local resolvedIP4 resolvedIP6
- resolvedIP4="$(resolveip_to_nftset4 "$value")"
- resolvedIP6="$(resolveip_to_nftset6 "$value")"
- if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
- state add 'errorSummary' 'errorFailedToResolve' "$value"
- fi
- param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }{ $resolvedIP4 }"
- param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }{ $resolvedIP6 }"
+ local inline_set_ipv4='' inline_set_ipv6='' d=''
+ unset dest_inline_set_ipv4_empty_flag
+ unset dest_inline_set_ipv6_empty_flag
+ for d in $value; do
+ local resolved_ipv4 resolved_ipv6
+ resolved_ipv4="$(resolveip_to_nftset4 "$d")"
+ resolved_ipv6="$(resolveip_to_nftset6 "$d")"
+ if [ -z "${resolved_ipv4}${resolved_ipv6}" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$d"
+ else
+ [ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
+ [ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
+ fi
+ done
+ [ -n "$inline_set_ipv4" ] || dest_inline_set_ipv4_empty_flag='true'
+ [ -n "$inline_set_ipv6" ] || dest_inline_set_ipv6_empty_flag='true'
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }{ $inline_set_ipv4 }"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }{ $inline_set_ipv6 }"
fi
else
- local target='dst' type='ip'
- if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
- nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
- param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
- param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
- else
- param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }${value}"
- param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }${value}"
- fi
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }{ $(inline_set "$value") }"
fi
fi
else
unset negation; value="$src_port";
fi
- param4="${param4:+$param4 }${proto_i:+$proto_i }sport ${negation:+$negation }{$(ports_to_nftset "$value")}"
- param6="${param6:+$param6 }${proto_i:+$proto_i }sport ${negation:+$negation }{$(ports_to_nftset "$value")}"
+ param4="${param4:+$param4 }${proto_i:+$proto_i }sport ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }${proto_i:+$proto_i }sport ${negation:+$negation }{ $(inline_set "$value") }"
fi
if [ -n "$dest_port" ]; then
else
unset negation; value="$dest_port";
fi
- param4="${param4:+$param4 }${proto_i:+$proto_i }dport ${negation:+$negation }{$(ports_to_nftset "$value")}"
- param6="${param6:+$param6 }${proto_i:+$proto_i }dport ${negation:+$negation }{$(ports_to_nftset "$value")}"
+ param4="${param4:+$param4 }${proto_i:+$proto_i }dport ${negation:+$negation }{ $(inline_set "$value") }"
+ param6="${param6:+$param6 }${proto_i:+$proto_i }dport ${negation:+$negation }{ $(inline_set "$value") }"
fi
if is_tor "$iface"; then
fi
done
else
- param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param4 $dest4 comment \"$name\""
- param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param6 $dest6 comment \"$name\""
+ 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\""
local ipv4_error='0' ipv6_error='0'
- if [ "$policy_routing_nft_prev_param4" != "$param4" ]; then
- nft4 "$param4" || ipv4_error='1'
- policy_routing_nft_prev_param4="$param4"
+ if [ "$policy_routing_nft_prev_param4" != "$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"
fi
- if [ "$policy_routing_nft_prev_param6" != "$param6" ] && \
- [ "$param4" != "$param6" ]; then
- nft6 "$param6" || ipv6_error='1'
- policy_routing_nft_prev_param6="$param6"
+ if [ "$policy_routing_nft_prev_param6" != "$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"
fi
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
done
}
-policy_process() {
- local i j uid="$9"
- if [ -z "$uid" ]; then # first non-recursive call
- [ "$enabled" -gt 0 ] || return 0
- unset processPolicyError
- uid="$1"
- if is_nft_mode; then
- chain="$(str_to_lower "$chain")"
- else
- chain="$(str_to_upper "$chain")"
- fi
- proto="$(str_to_lower "$proto")"
- [ "$proto" = 'auto' ] && unset proto
- [ "$proto" = 'all' ] && unset proto
- output 2 "Routing '$name' via $interface "
- if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then
- state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
- output_fail; return 1;
- fi
- if [ -z "$interface" ]; then
- state add 'errorSummary' 'errorPolicyNoInterface' "$name"
- output_fail; return 1;
- fi
- if ! is_supported_interface "$interface"; then
- state add 'errorSummary' 'errorPolicyUnknownInterface' "$name"
- output_fail; return 1;
- fi
- src_port="${src_port// / }"; src_port="${src_port// /,}"; src_port="${src_port//,\!/ !}";
- dest_port="${dest_port// / }"; dest_port="${dest_port// /,}"; dest_port="${dest_port//,\!/ !}";
- policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
- if [ -n "$processPolicyError" ]; then
- output_fail
- else
- output_ok
- fi
- else # recursive call, get options from passed variables
- local name="$1" interface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto="$7" chain="$8"
- if str_contains "$src_addr" '[ ;\{\}]'; then
- for i in $(str_extras_to_space "$src_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$i" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done
- elif str_contains "$src_port" '[ ;\{\}]'; then
- for i in $(str_extras_to_space "$src_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$i" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done
- elif str_contains "$dest_addr" '[ ;\{\}]'; then
- for i in $(str_extras_to_space "$dest_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$i" "$dest_port" "$proto" "$chain" "$uid"; done
- elif str_contains "$dest_port" '[ ;\{\}]'; then
- for i in $(str_extras_to_space "$dest_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$i" "$proto" "$chain" "$uid"; done
- elif str_contains "$proto" '[ ;\{\}]'; then
- for i in $(str_extras_to_space "$proto"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$i" "$chain" "$uid"; done
- else
- if [ -n "$secure_reload" ] && { is_url_dl "$src_addr" || is_url_dl "$dest_addr"; }; then
- state add 'errorSummary' 'errorNoDownloadWithSecureReload' "$name"
- elif is_url "$src_addr"; then
- src_addr="$(process_url "$src_addr")"
- [ -n "$src_addr" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
- elif is_url "$dest_addr"; then
- dest_addr="$(process_url "$dest_addr")"
- [ -n "$dest_addr" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
- else
- policy_routing "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+dns_policy_process() {
+ local i j uid="$1"
+
+ [ "$enabled" -gt '0' ] || return 0
+
+ src_addr="$(str_extras_to_space "$src_addr")"
+ dest_dns="$(str_extras_to_space "$dest_dns")"
+
+ local dest_dns_interface dest_dns_ipv4 dest_dns_ipv6
+ dest_dns_interface="$(str_first_value_interface "$dest_dns")"
+ dest_dns_ipv4="$(str_first_value_ipv4 "$dest_dns")"
+ dest_dns_ipv6="$(str_first_value_ipv6 "$dest_dns")"
+ if is_supported_interface "$dest_dns_interface"; then
+ local d
+ for d in $(uci -q get network."$dest_dns_interface".dns); do
+ if ! is_family_mismatch "$src_addr" "$d"; then
+ if is_ipv4 "$d"; then
+ dest_dns_ipv4="${dest_dns_ipv4:-$d}"
+ elif is_ipv6 "$d"; then
+ dest_dns_ipv6="${dest_dns_ipv6:-$d}"
+ fi
fi
+ done
+ fi
+
+ unset processDnsPolicyError
+ output 2 "Routing '$name' DNS to $dest_dns "
+ if [ -z "$src_addr" ]; then
+ state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
+ output_fail; return 1;
+ fi
+ if [ -z "$dest_dns" ]; then
+ state add 'errorSummary' 'errorPolicyNoDns' "$name"
+ output_fail; return 1;
+ fi
+
+ # group by type of src_addr values so that one nft set can be created per type within policy
+ local filter_list_src_addr='phys_dev phys_dev_negative mac_address mac_address_negative domain domain_negative ipv4 ipv4_negative ipv6 ipv6_negative'
+ local filter_group_src_addr filtered_value_src_addr
+ for filter_group_src_addr in $filter_list_src_addr; do
+ filtered_value_src_addr=$(filter_options "$filter_group_src_addr" "$src_addr")
+ if [ -n "$src_addr" ] && [ -n "$filtered_value_src_addr" ]; then
+ if str_contains "$filter_group_src_addr" 'ipv4' && [ -z "$dest_dns_ipv4" ] ; then
+ continue
+ fi
+ 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"
fi
+ done
+
+ if [ -n "$processDnsPolicyError" ]; then
+ output_fail
+ else
+ output_ok
fi
}
-try() {
- if ! "$@"; then
- state add 'errorSummary' 'errorTryFailed' "$*"
- return 1
+policy_process() {
+ local i j uid="$1"
+
+ [ "$enabled" -gt '0' ] || return 0
+
+ src_addr="$(str_extras_to_space "$src_addr")"
+ src_port="$(str_extras_to_space "$src_port")"
+ dest_addr="$(str_extras_to_space "$dest_addr")"
+ dest_port="$(str_extras_to_space "$dest_port")"
+
+ unset processPolicyError
+ proto="$(str_to_lower "$proto")"
+ [ "$proto" = 'auto' ] && unset proto
+ [ "$proto" = 'all' ] && unset proto
+ output 2 "Routing '$name' via $interface "
+ if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then
+ state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
+ output_fail; return 1;
+ fi
+ if [ -z "$interface" ]; then
+ state add 'errorSummary' 'errorPolicyNoInterface' "$name"
+ output_fail; return 1;
+ fi
+ if ! is_supported_interface "$interface"; then
+ state add 'errorSummary' 'errorPolicyUnknownInterface' "$name"
+ output_fail; return 1;
+ fi
+
+ unset j
+ for i in $src_addr; do
+ if [ -n "$secure_reload" ] && is_url_dl "$i"; then
+ state add 'errorSummary' 'errorNoDownloadWithSecureReload' "$name"
+ elif is_url "$i"; then
+ i="$(process_url "$i")"
+ fi
+ j="${j:+$j }$i"
+ done
+ src_addr="$j"
+
+ unset j
+ for i in $dest_addr; do
+ if [ -n "$secure_reload" ] && is_url_dl "$i"; then
+ state add 'errorSummary' 'errorNoDownloadWithSecureReload' "$name"
+ elif is_url "$i"; then
+ i="$(process_url "$i")"
+ fi
+ j="${j:+$j }$i"
+ done
+ dest_addr="$j"
+
+ # TODO: if only src_addr is set add option 121 to dhcp leases?
+
+ local filter_list_src_addr='phys_dev phys_dev_negative mac_address mac_address_negative domain domain_negative ipv4 ipv4_negative ipv6 ipv6_negative'
+ local filter_list_dest_addr='domain domain_negative ipv4 ipv4_negative ipv6 ipv6_negative'
+ local filter_group_src_addr filtered_value_src_addr filter_group_dest_addr filtered_value_dest_addr
+ [ -z "$src_addr" ] && filter_list_src_addr='none'
+ for filter_group_src_addr in $filter_list_src_addr; do
+ filtered_value_src_addr=$(filter_options "$filter_group_src_addr" "$src_addr")
+ if [ -z "$src_addr" ] || { [ -n "$src_addr" ] && [ -n "$filtered_value_src_addr" ]; }; then
+ [ -z "$dest_addr" ] && filter_list_dest_addr='none'
+ for filter_group_dest_addr in $filter_list_dest_addr; do
+ filtered_value_dest_addr=$(filter_options "$filter_group_dest_addr" "$dest_addr")
+ if [ -z "$dest_addr" ] || { [ -n "$dest_addr" ] && [ -n "$filtered_value_dest_addr" ]; }; then
+ if str_contains "$filter_group_src_addr" 'ipv4' && str_contains "$filter_group_dest_addr" 'ipv6'; then
+ continue
+ fi
+ if str_contains "$filter_group_src_addr" 'ipv6' && str_contains "$filter_group_dest_addr" 'ipv4'; then
+ continue
+ fi
+ policy_routing "$name" "$interface" "$filtered_value_src_addr" "$src_port" "$filtered_value_dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+ fi
+ done
+ fi
+ done
+
+ if [ -n "$processPolicyError" ]; then
+ output_fail
+ else
+ output_ok
fi
}
create)
if is_netifd_table_interface "$iface"; then
ipv4_error=0
- $ip_bin -4 rule del table "$tid" >/dev/null 2>&1
- try "$ip_bin" -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
- if is_nft_mode; then
- try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
- try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
- try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
- else
- ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
- ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
- ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
- fi
+ ip -4 rule del table "$tid" >/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} return" || ipv4_error=1
if [ -n "$ipv6_enabled" ]; then
ipv6_error=0
- $ip_bin -6 rule del table "$tid" >/dev/null 2>&1
- try "$ip_bin" -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$((priority-1))" || ipv6_error=1
+ ip -6 rule del table "$tid" >/dev/null 2>&1
+ try ip -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$((priority-1))" || ipv6_error=1
fi
else
if ! grep -q "$tid ${ipTablePrefix}_${iface}" "$rtTablesFile"; then
echo "$tid ${ipTablePrefix}_${iface}" >> "$rtTablesFile"
sync
fi
- $ip_bin -4 rule del table "$tid" >/dev/null 2>&1
- $ip_bin -4 route flush table "$tid" >/dev/null 2>&1
- if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then
+ ip -4 rule del table "$tid" >/dev/null 2>&1
+ ip -4 route flush table "$tid" >/dev/null 2>&1
+ if [ -n "$gw4" ] || [ "$strict_enforcement" -ne '0' ]; then
ipv4_error=0
if [ -z "$gw4" ]; then
- try "$ip_bin" -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ try ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
else
- try "$ip_bin" -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ try ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
fi
# shellcheck disable=SC2086
while read -r i; do
i="$(echo "$i" | sed 's/ onlink$//')"
idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
if ! is_supported_iface_dev "$idev"; then
- try "$ip_bin" -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ try ip -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
fi
done << EOF
- $($ip_bin -4 route list table main)
+ $(ip -4 route list table main)
EOF
- try "$ip_bin" -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
- if is_nft_mode; then
- try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
- try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
- try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
- else
- ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
- ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
- ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
- fi
+ 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} return" || ipv4_error=1
fi
if [ -n "$ipv6_enabled" ]; then
ipv6_error=0
- $ip_bin -6 rule del table "$tid" >/dev/null 2>&1
- $ip_bin -6 route flush table "$tid" >/dev/null 2>&1
- if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne 0 ]; then
+ ip -6 rule del 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 [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
- try "$ip_bin" -6 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv6_error=1
- elif "$ip_bin" -6 route list table main | grep -q " dev $dev6 "; then
- "$ip_bin" -6 route add default via "$gw6" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ elif ip -6 route list table main | grep -q " dev $dev6 "; then
+ ip -6 route add default via "$gw6" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
while read -r i; do
i="$(echo "$i" | sed 's/ linkdown$//')"
i="$(echo "$i" | sed 's/ onlink$//')"
# shellcheck disable=SC2086
- try "$ip_bin" -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
done << EOF
- $($ip_bin -6 route list table main | grep " dev $dev6 ")
+ $(ip -6 route list table main | grep " dev $dev6 ")
EOF
else
- try "$ip_bin" -6 route add "$($ip_bin -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
- try "$ip_bin" -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 route add "$(ip -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
fi
fi
- try "$ip_bin" -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$((priority-1))" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$((priority-1))" >/dev/null 2>&1 || ipv6_error=1
fi
fi
- if [ "$ipv4_error" -eq 0 ] || [ "$ipv6_error" -eq 0 ]; then
+ if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
dscp="$(uci_get "$packageName" 'config' "${iface}_dscp")"
- if is_nft_mode; then
- if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s=1
- if [ -n "$ipv6_enabled" ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s=1
- fi
- fi
- if [ "$iface" = "$icmp_interface" ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp goto ${nftPrefix}_mark_${mark}" || s=1
- if [ -n "$ipv6_enabled" ]; then
- try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp goto ${nftPrefix}_mark_${mark}" || s=1
- fi
- fi
- else
- if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then
- ipt -t mangle -I "${iptPrefix}_PREROUTING" -m dscp --dscp "${dscp}" -g "${iptPrefix}_MARK_${mark}" || s=1
+ 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
+ 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
fi
- if [ "$iface" = "$icmp_interface" ]; then
- ipt -t mangle -I "${iptPrefix}_OUTPUT" -p icmp -g "${iptPrefix}_MARK_${mark}" || s=1
+ 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
+ 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
fi
fi
else
return "$s"
;;
create_user_set)
- if is_nft_mode; then
- nftset 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
- nftset 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
- nftset 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
- else
- ips 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
- ips 'create_user_set' "$iface" 'dst' 'net' 'user' '' "$mark" || s=1
- ips 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
- ips 'create_user_set' "$iface" 'src' 'net' 'user' '' "$mark" || s=1
- ips 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
- fi
+ nftset 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
+ nftset 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
+ nftset 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
return "$s"
;;
delete|destroy)
- $ip_bin rule del table "$tid" >/dev/null 2>&1
+ ip rule del table "$tid" >/dev/null 2>&1
if ! is_netifd_table_interface "$iface"; then
- $ip_bin route flush table "$tid" >/dev/null 2>&1
+ ip route flush table "$tid" >/dev/null 2>&1
sed -i "/${ipTablePrefix}_${iface}\$/d" "$rtTablesFile"
sync
fi
reload_interface)
is_netifd_table_interface "$iface" && return 0;
ipv4_error=0
- $ip_bin rule del table "$tid" >/dev/null 2>&1
+ ip rule del table "$tid" >/dev/null 2>&1
if ! is_netifd_table_interface "$iface"; then
- $ip_bin route flush table "$tid" >/dev/null 2>&1
+ ip route flush table "$tid" >/dev/null 2>&1
fi
- if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then
+ if [ -n "$gw4" ] || [ "$strict_enforcement" -ne '0' ]; then
if [ -z "$gw4" ]; then
- try "$ip_bin" -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ try ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
else
- try "$ip_bin" -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ try ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
fi
- try "$ip_bin" rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
+ try ip 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" ]; } || [ "$strict_enforcement" -ne '0' ]; then
if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
- try "$ip_bin" -6 route add unreachable default table "$tid" || ipv6_error=1
- elif $ip_bin -6 route list table main | grep -q " dev $dev6 "; 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
while read -r i; do
# shellcheck disable=SC2086
- try "$ip_bin" -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
done << EOF
- $($ip_bin -6 route list table main | grep " dev $dev6 ")
+ $(ip -6 route list table main | grep " dev $dev6 ")
EOF
else
- try "$ip_bin" -6 route add "$($ip_bin -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
- try "$ip_bin" -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 route add "$(ip -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try ip -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
fi
fi
- try "$ip_bin" -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
+ try ip -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
fi
- if [ "$ipv4_error" -eq 0 ] || [ "$ipv6_error" -eq 0 ]; then
+ if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
s=0
else
s=1
is_supported_interface "$iface" || return 0
is_wan6 "$iface" && return 0
- [ $((ifaceMark)) -gt $((fw_mask)) ] && return 1
+ [ "$((ifaceMark))" -gt "$((fw_mask))" ] && return 1
if is_ovpn "$iface" && ! is_ovpn_valid "$iface"; then
: || state add 'warningSummary' 'warningInvalidOVPNConfig' "$iface"
user_file_process() {
local shellBin="${SHELL:-/bin/ash}"
- [ "$enabled" -gt 0 ] || return 0
+ [ "$enabled" -gt '0' ] || return 0
if [ ! -s "$path" ]; then
state add 'errorSummary' 'errorUserFileNotFound' "$path"
output_fail
tid="$(get_rt_tables_id "$reloadedIface")"
pre_init_tid="$(eval echo "\$pre_init_tid_${reloadedIface//-/_}")"
if [ "$tid" = "$pre_init_tid" ]; then
-# logger -t "$packageName" "Updated interface $reloadedIface TID: ${tid}; Pre-Init TID: ${pre_init_tid}. Reloading..."
serviceStartTrigger='on_interface_reload'
else
-# logger -t "$packageName" "Updated interface $reloadedIface TID: ${tid}; Pre-Init TID: ${pre_init_tid}. Restarting..."
serviceStartTrigger='on_start'
unset reloadedIface
fi
-# if is_ovpn "$reloadedIface"; then
-# logger -t "$packageName" "Updated interface is an OpenVPN tunnel, restarting."
-# serviceStartTrigger='on_start'
-# unset reloadedIface
-# else
-# serviceStartTrigger='on_interface_reload'
-# fi
;;
on_reload)
serviceStartTrigger='on_reload'
elif [ -z "$(ubus_get_status gateways)" ]; then
serviceStartTrigger='on_start'
unset reloadedIface
-# elif [ "$serviceStartTrigger" = 'on_interface_reload' ] && \
-# [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_ipv4')" ] && \
-# [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_ipv6')" ]; then
-# serviceStartTrigger='on_start'
-# unset reloadedIface
else
serviceStartTrigger="${serviceStartTrigger:-on_start}"
fi
cleanup_main_chains
cleanup_sets
nft_file 'create'
- if ! is_nft_mode; then
- for i in $chainsList; do
- i="$(str_to_upper "$i")"
- ipt -t mangle -N "${iptPrefix}_${i}"
- ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
- done
- fi
json_add_array 'gateways'
interface_process 'all' 'prepare'
config_foreach interface_process 'interface' 'reload'
config_foreach load_validate_policy 'policy' policy_process
output 1 '\n'
fi
+ if is_config_enabled 'dns_policy'; then
+ output 1 'Processing dns policies '
+ config_load "$packageName"
+ config_foreach load_validate_dns_policy 'dns_policy' dns_policy_process
+ output 1 '\n'
+ fi
if is_config_enabled 'include'; then
interface_process 'all' 'prepare'
config_foreach interface_process 'interface' 'create_user_set'
cleanup_marking_chains
cleanup_rt_tables
nft_file 'create'
- if ! is_nft_mode; then
- for i in $chainsList; do
- i="$(str_to_upper "$i")"
- ipt -t mangle -N "${iptPrefix}_${i}"
- ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
- done
- fi
output 1 'Processing interfaces '
json_add_array 'gateways'
interface_process 'all' 'prepare'
config_foreach load_validate_policy 'policy' policy_process
output 1 '\n'
fi
+ if is_config_enabled 'dns_policy'; then
+ output 1 'Processing dns policies '
+ config_load "$packageName"
+ config_foreach load_validate_dns_policy 'dns_policy' dns_policy_process
+ output 1 '\n'
+ fi
if is_config_enabled 'include'; then
interface_process 'all' 'prepare'
config_foreach interface_process 'interface' 'create_user_set'
[ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary"
[ -n "$errorSummary" ] && json_add_string 'errors' "$errorSummary"
[ -n "$warningSummary" ] && json_add_string 'warnings' "$warningSummary"
- if [ "$strict_enforcement" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then
+ if [ "$strict_enforcement" -ne '0' ] && str_contains "$gatewaySummary" '0.0.0.0'; then
json_add_string 'mode' 'strict'
fi
json_close_object
output "$serviceName FAILED TO START in fw4 nft file mode!!!"
output "Check the output of nft -c -f $nftTempFile"
fi
- elif is_nft_mode; then
- [ -n "$gatewaySummary" ] && output "$serviceName (nft mode) started with gateways:\\n${gatewaySummary}"
else
- [ -n "$gatewaySummary" ] && output "$serviceName (iptables mode) started with gateways:\\n${gatewaySummary}"
+ [ -n "$gatewaySummary" ] && output "$serviceName (nft mode) started with gateways:\\n${gatewaySummary}"
fi
state print 'errorSummary'
state print 'warningSummary'
resolver 'cleanup_all'
resolver 'compare_hash' && resolver 'restart'
traffic_killswitch 'remove'
- if [ "$enabled" -ne 0 ]; then
+ if [ "$enabled" -ne '0' ]; then
if [ -n "$nft_file_mode" ]; then
output "$serviceName (fw4 nft file mode) stopped "; output_okn;
- elif is_nft_mode; then
- output "$serviceName (nft mode) stopped "; output_okn;
else
- output "$serviceName (iptables mode) stopped "; output_okn;
+ output "$serviceName (nft mode) stopped "; output_okn;
fi
fi
rm -f "$packageLockFile"
version() { echo "$PKG_VERSION"; }
status_service() {
- local _SEPARATOR_='============================================================'
- load_environment 'on_status'
- if is_nft_mode; then
- status_service_nft "$@"
- else
- status_service_iptables "$@"
- fi
-}
-
-status_service_nft() {
local i dev dev6 wan_tid
json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
fi
if [ -n "$wanIface6" ]; then
network_get_device dev6 "$wanIface6"
- wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
- [ "$wanGW6" = "default" ] && wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
+ wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
+ [ "$wanGW6" = "default" ] && wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
fi
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."
[ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
[ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
fi
echo "$_SEPARATOR_"
echo "$packageName chains - policies"
- for i in forward input output prerouting postrouting; do
+ for i in $chainsList dstnat_lan; do
"$nft" -a list table inet "$nftTable" | sed -n "/chain ${nftPrefix}_${i} {/,/\t}/p"
done
echo "$_SEPARATOR_"
for i in $(get_nft_sets); do
"$nft" -a list table inet "$nftTable" | sed -n "/set ${i} {/,/\t}/p"
done
- if [ -s "$dnsmasqFile" ]; then
+ if [ -s "$dnsmasqFileDefault" ]; then
echo "$_SEPARATOR_"
echo "dnsmasq sets"
- cat "$dnsmasqFile"
+ cat "$dnsmasqFileDefault"
fi
# echo "$_SEPARATOR_"
# ip rule list | grep "${packageName}_"
echo "$_SEPARATOR_"
tableCount="$(grep -c "${packageName}_" $rtTablesFile)" || tableCount=0
wan_tid=$(($(get_rt_tables_next_id)-tableCount))
- i=0; while [ $i -lt "$tableCount" ]; do
- echo "IPv4 table $((wan_tid + i)) route: $($ip_bin -4 route show table $((wan_tid + i)) | grep default)"
+ i=0; while [ "$i" -lt "$tableCount" ]; do
+ echo "IPv4 table $((wan_tid + i)) route: $(ip -4 route show table $((wan_tid + i)) | grep default)"
echo "IPv4 table $((wan_tid + i)) rule(s):"
- $ip_bin -4 rule list table "$((wan_tid + i))"
+ ip -4 rule list table "$((wan_tid + i))"
if [ -n "$ipv6_enabled" ]; then
- echo "IPv6 table $((wan_tid + i)) route: $($ip_bin -6 route show table $((wan_tid + i)) | grep default)"
+ echo "IPv6 table $((wan_tid + i)) route: $(ip -6 route show table $((wan_tid + i)) | grep default)"
echo "IPv6 table $((wan_tid + i)) rule(s):"
- $ip_bin -6 route show table $((wan_tid + i))
+ ip -6 route show table $((wan_tid + i))
fi
i=$((i + 1))
done
}
-status_service_iptables() {
- local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j wan_tid
-
- json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
- if [ -n "$wanIface4" ]; then
- network_get_gateway wanGW4 "$wanIface4"
- network_get_device dev "$wanIface4"
- fi
- if [ -n "$wanIface6" ]; then
- network_get_device dev6 "$wanIface6"
- wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
- [ "$wanGW6" = "default" ] && wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
- fi
- while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
- [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
- status="$serviceName running 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}."
- {
- echo "$status"
- echo "$_SEPARATOR_"
- dnsmasq --version 2>/dev/null | sed '/^$/,$d'
- if [ -n "$1" ]; then
- echo "$_SEPARATOR_"
- echo "Resolving domains"
- for i in $1; do
- echo "$i: $(resolveip "$i" | tr '\n' ' ')"
- done
- fi
-
- echo "$_SEPARATOR_"
- echo "Routes/IP Rules"
- tableCount="$(grep -c "${packageName}_" $rtTablesFile)" || tableCount=0
- if [ -n "$set_d" ]; then route; else route | grep '^default'; fi
- if [ -n "$set_d" ]; then ip rule list; fi
- wan_tid=$(($(get_rt_tables_next_id)-tableCount))
- i=0; while [ $i -lt "$tableCount" ]; do
- echo "IPv4 table $((wan_tid + i)) route: $($ip_bin -4 route show table $((wan_tid + i)) | grep default)"
- echo "IPv4 table $((wan_tid + i)) rule(s):"
- $ip_bin -4 rule list table "$((wan_tid + i))"
- i=$((i + 1))
- done
-
- if [ -n "$ipv6_enabled" ]; then
- i=0; while [ $i -lt "$tableCount" ]; do
- $ip_bin -6 route show table $((wan_tid + i)) | while read -r param; do
- echo "IPv6 Table $((wan_tid + i)): $param"
- done
- i=$((i + 1))
- done
- fi
-
- for j in Mangle NAT; do
- if [ -z "$set_d" ]; then
- for i in $chainsList; do
- i="$(str_to_upper "$i")"
- if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" >/dev/null 2>&1; then
- echo "$_SEPARATOR_"
- echo "$j IP Table: $i"
- iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
- if [ -n "$ipv6_enabled" ]; then
- echo "$_SEPARATOR_"
- echo "$j IPv6 Table: $i"
- iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
- fi
- fi
- done
- else
- echo "$_SEPARATOR_"
- echo "$j IP Table"
- iptables -L -t "$(str_to_lower $j)"
- if [ -n "$ipv6_enabled" ]; then
- echo "$_SEPARATOR_"
- echo "$j IPv6 Table"
- iptables -L -t "$(str_to_lower $j)"
- fi
- fi
- i=0; ifaceMark="$wan_mark";
- while [ $i -lt "$tableCount" ]; do
- if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}" >/dev/null 2>&1; then
- echo "$_SEPARATOR_"
- echo "$j IP Table MARK Chain: ${iptPrefix}_MARK_${ifaceMark}"
- iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}"
- ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))";
- fi
- i=$((i + 1))
- done
- done
-
- echo "$_SEPARATOR_"
- echo "Current ipsets"
- ipset save
- if [ -s "$dnsmasqFile" ]; then
- echo "$_SEPARATOR_"
- echo "DNSMASQ sets"
- cat "$dnsmasqFile"
- fi
- if [ -s "$aghIpsetFile" ]; then
- echo "$_SEPARATOR_"
- echo "AdGuardHome sets"
- cat "$aghIpsetFile"
- fi
- echo "$_SEPARATOR_"
- } | tee -a /var/${packageName}-support
- if [ -n "$set_p" ]; then
- printf "%b" "Pasting to paste.ee... "
- if curl --version 2>/dev/null | grep -q "Protocols: .*https.*"; then
- json_init; json_add_string 'description' "${packageName}-support"
- json_add_array 'sections'; json_add_object '0'
- json_add_string 'name' "$(uci_get 'system' '@system[0]' 'hostname')"
- json_add_string 'contents' "$(cat /var/${packageName}-support)"
- json_close_object; json_close_array; payload=$(json_dump)
- out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
- json_load "$out"; json_get_var id id; json_get_var s success
- [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n"
- [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
- else
- printf "%b" "${__FAIL__}\\n"
- printf "%b" "${_ERROR_}: The curl, libopenssl or ca-bundle packages were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n"
- fi
- else
- printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
- fi
-}
-
# shellcheck disable=SC2120
load_validate_config() {
uci_load_validate "$packageName" "$packageName" "$1" "${2}${3:+ $3}" \
'strict_enforcement:bool:1' \
'secure_reload:bool:0' \
'ipv6_enabled:bool:0' \
- 'resolver_set:or("", "none", "dnsmasq.ipset", "dnsmasq.nftset")' \
+ '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' \
'procd_wan_interface:network:wan' \
'procd_wan6_interface:network:wan6' \
'wan_ip_rules_priority:uinteger:30000' \
- 'rule_create_option:or("", add, insert):add' \
'webui_supported_protocol:list(string)' \
- 'nft_file_support:bool:1'\
+ 'nft_rule_counter:bool:0'\
'nft_set_auto_merge:bool:1'\
- 'nft_set_counter:bool:1'\
+ 'nft_set_counter:bool:0'\
'nft_set_flags_interval:bool:1'\
'nft_set_flags_timeout:bool:0'\
'nft_set_gc_interval:or("", string)'\
'nft_set_timeout:or("", string)'
}
+# shellcheck disable=SC2120
+load_validate_dns_policy() {
+ local name
+ local enabled
+ local src_addr
+ local dest_dns
+ 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))'
+}
+
# shellcheck disable=SC2120
load_validate_policy() {
local name
--- /dev/null
+#!/bin/sh /etc/rc.common
+# Copyright 2020-2024 MOSSDeF, Stan Grishin (stangri@melmac.ca)
+# shellcheck disable=SC2018,SC2019,SC2034,SC3043,SC3057,SC3060
+
+# sysctl net.ipv4.conf.default.rp_filter=1
+# sysctl net.ipv4.conf.all.rp_filter=1
+
+# shellcheck disable=SC2034
+START=94
+# shellcheck disable=SC2034
+USE_PROCD=1
+
+[ -n "${IPKG_INSTROOT}" ] && return 0
+
+readonly packageName='pbr'
+readonly PKG_VERSION='dev-test'
+readonly packageCompat='5'
+readonly serviceName="$packageName $PKG_VERSION"
+readonly serviceTrapSignals='exit SIGHUP SIGQUIT SIGKILL'
+readonly packageConfigFile="/etc/config/${packageName}"
+readonly packageLockFile="/var/run/${packageName}.lock"
+readonly dnsmasqFileDefault="/var/dnsmasq.d/${packageName}"
+readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m'
+readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m'
+readonly _OKB_='\033[1;34m\xe2\x9c\x93\033[0m'
+readonly __OKB__='\033[1;34m[\xe2\x9c\x93]\033[0m'
+readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m'
+readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m'
+readonly _ERROR_='\033[0;31mERROR\033[0m'
+readonly _WARNING_='\033[0;33mWARNING\033[0m'
+readonly ip_full='/usr/libexec/ip-full'
+# shellcheck disable=SC2155
+readonly ip_bin="$(command -v ip)"
+readonly ipTablePrefix='pbr'
+# shellcheck disable=SC2155
+readonly iptables="$(command -v iptables)"
+# shellcheck disable=SC2155
+readonly ip6tables="$(command -v ip6tables)"
+# shellcheck disable=SC2155
+readonly ipset="$(command -v ipset)"
+readonly ipsPrefix='pbr'
+readonly iptPrefix='PBR'
+# shellcheck disable=SC2155
+readonly agh="$(command -v AdGuardHome)"
+readonly aghIpsetFile="/var/run/${packageName}.adguardhome.ipsets"
+# shellcheck disable=SC2155
+readonly nft="$(command -v nft)"
+readonly nftIPv4Flag='ip'
+readonly nftIPv6Flag='ip6'
+readonly nftTempFile="/var/run/${packageName}.nft"
+readonly nftPermFile="/usr/share/nftables.d/ruleset-post/30-${packageName}.nft"
+readonly nftPrefix='pbr'
+readonly nftTable='fw4'
+readonly chainsList='forward input output postrouting prerouting'
+readonly ssConfigFile='/etc/shadowsocks'
+readonly torConfigFile='/etc/tor/torrc'
+readonly xrayIfacePrefix='xray_'
+readonly rtTablesFile='/etc/iproute2/rt_tables'
+
+# package config options
+procd_boot_timeout=
+enabled=
+fw_mask=
+icmp_interface=
+ignored_interface=
+ipv6_enabled=
+nft_file_support=
+nft_user_set_policy=
+nft_user_set_counter=
+procd_boot_delay=
+procd_reload_delay=
+procd_lan_interface=
+procd_wan_ignore_status=
+procd_wan_interface=
+procd_wan6_interface=
+resolver_set=
+resolver_instance=
+rule_create_option=
+secure_reload=
+strict_enforcement=
+supported_interface=
+verbosity=
+wan_ip_rules_priority=
+wan_mark=
+nft_set_auto_merge=
+nft_set_counter=
+nft_set_flags_interval=
+nft_set_flags_timeout=
+nft_set_flags_gc_interval=
+nft_set_policy=
+nft_set_timeout=
+
+# run-time
+aghConfigFile='/etc/AdGuardHome/AdGuardHome.yaml'
+gatewaySummary=
+errorSummary=
+warningSummary=
+wanIface4=
+wanIface6=
+dnsmasqFile=
+dnsmasqFileList=
+ifaceMark=
+ifaceTableID=
+ifacePriority=
+ifacesAll=
+ifacesSupported=
+firewallWanZone=
+wanGW4=
+wanGW6=
+serviceStartTrigger=
+processDnsPolicyError=
+processPolicyError=
+processPolicyWarning=
+resolver_set_supported=
+policy_routing_nft_prev_param4=
+policy_routing_nft_prev_param6=
+nft_set_params=
+torDnsPort=
+torTrafficPort=
+
+# shellcheck disable=SC1091
+. /lib/functions.sh
+# shellcheck disable=SC1091
+. /lib/functions/network.sh
+# shellcheck disable=SC1091
+. /usr/share/libubox/jshn.sh
+
+output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; }
+output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; }
+output_okb() { output 1 "$_OKB_"; output 2 "$__OKB__\\n"; }
+output_okbn() { output 1 "$_OKB_\\n"; output 2 "$__OKB__\\n"; }
+output_fail() { output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
+output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
+# shellcheck disable=SC2317
+str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
+str_replace() { echo "${1//$2/$3}"; }
+str_contains() { [ -n "$1" ] && [ -n "$2" ] && [ "${1//$2}" != "$1" ]; }
+str_contains_word() { echo "$1" | grep -q -w "$2"; }
+str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
+str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
+str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; }
+str_extras_to_space() { echo "$1" | tr ';{}' ' '; }
+debug() { local i j; for i in "$@"; do eval "j=\$$i"; echo "${i}: ${j} "; done; }
+quiet_mode() {
+ case "$1" in
+ on) verbosity=0;;
+ off) verbosity="$(uci_get "$packageName" 'config' 'verbosity' '2')";;
+ esac
+}
+output() {
+# Target verbosity level with the first parameter being an integer
+ is_integer() {
+ case "$1" in
+ (*[!0123456789]*) return 1;;
+ ('') return 1;;
+ (*) return 0;;
+ esac
+ }
+ local msg memmsg logmsg text
+ local sharedMemoryOutput="/dev/shm/$packageName-output"
+ if [ -z "$verbosity" ] && [ -n "$packageName" ]; then
+ verbosity="$(uci_get "$packageName" 'config' 'verbosity' '2')"
+ fi
+ if [ "$#" -ne '1' ] && is_integer "$1"; then
+ if [ "$((verbosity & $1))" -gt '0' ] || [ "$verbosity" = "$1" ]; then shift; text="$*"; else return 0; fi
+ fi
+ text="${text:-$*}";
+ [ -t 1 ] && printf "%b" "$text"
+ msg="${text//$serviceName /service }";
+ if [ "$(printf "%b" "$msg" | wc -l)" -gt '0' ]; then
+ [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
+ logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
+ logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
+ rm -f "$sharedMemoryOutput"
+ else
+ printf "%b" "$msg" >> "$sharedMemoryOutput"
+ fi
+}
+pbr_find_iface() {
+ local iface i param="$2"
+ case "$param" in
+ wan6) iface="$procd_wan6_interface";;
+ wan|*) iface="$procd_wan_interface";;
+ esac
+ eval "$1"='${iface}'
+}
+pbr_get_gateway4() {
+ local iface="$2" dev="$3" gw
+ network_get_gateway gw "$iface" true
+ if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then
+# gw="$(ubus call "network.interface.${iface}" status | jsonfilter -e "@.route[0].nexthop")"
+ gw="$($ip_bin -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')"
+ fi
+ eval "$1"='$gw'
+}
+pbr_get_gateway6() {
+ local iface="$2" dev="$3" gw
+ network_get_gateway6 gw "$iface" true
+ if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
+ gw="$($ip_bin -6 a list dev "$dev" 2>/dev/null | grep inet6 | grep 'scope global' | awk '{print $2}')"
+ fi
+ eval "$1"='$gw'
+}
+
+# shellcheck disable=SC2016
+is_bad_user_file_nft_call() { grep -q '"\$nft" list' "$1" || grep '"\$nft" -f' "$1";}
+is_config_enabled() {
+ _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
+ config_load "$packageName"
+ config_foreach _check_config "$cfg"
+ return "$_cfg_enabled"
+}
+uci_get_device() { uci_get 'network' "$1" 'device' || uci_get 'network' "$1" 'dev'; }
+uci_get_protocol() { uci_get 'network' "$1" 'proto'; }
+is_default_dev() { [ "$1" = "$($ip_bin -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; }
+is_domain() { ! is_ipv6 "$1" && str_contains "$1" '[a-zA-Z]'; }
+is_dslite() { local p; network_get_protocol p "$1"; [ "${p:0:6}" = "dslite" ]; }
+is_family_mismatch() { ( is_ipv4_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_ipv4_netmask "${2//!}" ); }
+is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
+is_greater_or_equal() { test "$(printf '%s\n' "$@" | sort -V | head -n '1')" = "$2"; }
+is_ignored_interface() { str_contains_word "$ignored_interface" "$1"; }
+is_ignore_target() { [ "$(str_to_lower "$1")" = 'ignore' ]; }
+is_integer() {
+ case "$1" in
+ (*[!0123456789]*) return 1;;
+ ('') return 1;;
+ (*) return 0;;
+ esac
+}
+is_ipset_type_supported() { ipset help hash:"$1" >/dev/null 2>&1; }
+is_nft_mode() { return 1; }
+is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; }
+is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ':'; }
+is_ipv6_global() { [ "${1:0:4}" = '2001' ]; }
+is_ipv6_link_local() { [ "${1:0:4}" = 'fe80' ]; }
+is_ipv6_unique_local() { [ "${1:0:2}" = 'fc' ] || [ "${1:0:2}" = 'fd' ]; }
+is_list() { str_contains "$1" ',' || str_contains "$1" ' '; }
+is_ipv4_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; }
+is_lan() { local d; network_get_device d "$1"; str_contains "$d" 'br-lan'; }
+is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp" ]; }
+is_mac_address() { expr "$1" : '[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]$' >/dev/null; }
+is_netifd_table() { grep -q "ip.table.*$1" /etc/config/network; }
+is_netifd_table_interface() { local iface="$1"; [ "$(uci_get 'network' "$iface" 'ip4table')" = "${packageName}_${iface%6}" ]; }
+is_oc() { local p; network_get_protocol p "$1"; [ "${p:0:11}" = "openconnect" ]; }
+is_ovpn() { local d; uci_get_device d "$1"; [ "${d:0:3}" = "tun" ] || [ "${d:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${d}/tun_flags" ]; }
+is_ovpn_valid() { local dev_net dev_ovpn; uci_get_device dev_net "$1"; dev_ovpn="$(uci_get 'openvpn' "$1" 'dev')"; [ -n "$dev_net" ] && [ -n "$dev_ovpn" ] && [ "$dev_net" = "$dev_ovpn" ]; }
+is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; }
+is_present() { command -v "$1" >/dev/null 2>&1; }
+is_service_running() { if is_nft_mode; then is_service_running_nft; else is_service_running_iptables; fi; }
+is_service_running_iptables() { [ -x "$iptables" ] && "$iptables" -t mangle -L | grep -q "${iptPrefix}_PREROUTING" >/dev/null 2>&1; }
+is_service_running_nft() { [ -x "$nft" ] && [ -n "$(get_mark_nft_chains)" ]; }
+is_supported_iface_dev() { local n dev; for n in $ifacesSupported; do network_get_device dev "$n"; [ "$1" = "$dev" ] && return 0; done; return 1; }
+is_supported_protocol() { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; }
+is_pptp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "pptp" ]; }
+is_softether() { local d; network_get_device d "$1"; [ "${d:0:4}" = "vpn_" ]; }
+is_supported_interface() { is_lan "$1" && return 1; str_contains_word "$supported_interface" "$1" || { ! is_ignored_interface "$1" && { is_wan "$1" || is_wan6 "$1" || is_tunnel "$1"; }; } || is_ignore_target "$1" || is_xray "$1"; }
+is_tailscale() { local d; network_get_device d "$1"; [ "${d:0:9}" = "tailscale" ]; }
+is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; }
+is_tor_running() {
+ local ret=0
+ is_ignored_interface 'tor' && return 1
+ [ -s "$torConfigFile" ] || return 1
+ json_load "$(ubus call service list "{ 'name': 'tor' }")" >/dev/null || return 1
+ json_select 'tor' >/dev/null || return 1
+ json_select 'instances' >/dev/null || return 1
+ json_select 'instance1' >/dev/null || return 1
+ json_get_var ret 'running' >/dev/null || return 1
+ json_cleanup
+ if [ "$ret" = "0" ]; then return 1; else return 0; fi
+}
+is_tunnel() { is_dslite "$1" || is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_softether "$1" || is_tailscale "$1" || is_tor "$1" || is_wg "$1"; }
+is_url() { is_url_file "$1" || is_url_dl "$1"; }
+is_url_dl() { is_url_ftp "$1" || is_url_http "$1" || is_url_https "$1"; }
+is_url_file() { [ "$1" != "${1#file://}" ];}
+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_wg() { local p lp; network_get_protocol p "$1"; uci_get_listen_port lp "$1"; [ -z "$lp" ] && [ "${p:0:9}" = "wireguard" ]; }
+is_xray() { [ -n "$(get_xray_traffic_port "$1")" ]; }
+dnsmasq_kill() { killall -q -s HUP 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}"; }
+# shellcheck disable=SC2155
+get_tor_dns_port() { local i="$(grep -m1 DNSPort "$torConfigFile" | awk -F: '{print $2}')"; echo "${i:-9053}"; }
+# shellcheck disable=SC2155
+get_tor_traffic_port() { local i="$(grep -m1 TransPort "$torConfigFile" | awk -F: '{print $2}')"; echo "${i:-9040}"; }
+get_xray_traffic_port() { local i="${1//$xrayIfacePrefix}"; [ "$i" = "$1" ] && unset i; echo "$i"; }
+get_rt_tables_id() { local iface="$1"; grep "${ipTablePrefix}_${iface}\$" "$rtTablesFile" | awk '{print $1;}'; }
+get_rt_tables_next_id() { echo "$(($(sort -r -n "$rtTablesFile" | grep -o -E -m 1 "^[0-9]+")+1))"; }
+get_rt_tables_non_pbr_next_id() { echo "$(($(grep -v "${ipTablePrefix}_" "$rtTablesFile" | sort -r -n | grep -o -E -m 1 "^[0-9]+")+1))"; }
+# shellcheck disable=SC2016
+resolveip_to_ipt() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; }
+resolveip_to_ipt4() { resolveip_to_ipt -4 "$@"; }
+resolveip_to_ipt6() { [ -n "$ipv6_enabled" ] && resolveip_to_ipt -6 "$@"; }
+# shellcheck disable=SC2016
+resolveip_to_nftset() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
+resolveip_to_nftset4() { resolveip_to_nftset -4 "$@"; }
+resolveip_to_nftset6() { [ -n "$ipv6_enabled" ] && resolveip_to_nftset -6 "$@"; }
+# shellcheck disable=SC2016
+ipv4_leases_to_nftset() { [ -s '/tmp/dhcp.leases' ] || return 1; grep "$1" '/tmp/dhcp.leases' | awk '{print $3}' | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
+# shellcheck disable=SC2016
+ipv6_leases_to_nftset() { [ -s '/tmp/hosts/odhcpd' ] || return 1; grep -v '^#' '/tmp/hosts/odhcpd' | grep "$1" | awk '{print $1}' | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; }
+# shellcheck disable=SC3037
+ports_to_nftset() { echo -en "$1"; }
+get_mark_ipt_chains() { [ -n "$(command -v iptables-save)" ] && iptables-save | grep ":${iptPrefix}_MARK_" | awk '{ print $1 }' | sed 's/://'; }
+get_mark_nft_chains() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_" | awk '{ print $2 }'; }
+get_ipsets() { [ -x "$(command -v ipset)" ] && ipset list | grep "${ipsPrefix}_" | awk '{ print $2 }'; }
+get_nft_sets() { [ -x "$nft" ] && "$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_get_device() {
+ local __tmp
+ __tmp="$(uci_get 'network' "$2" 'device')"
+ [ -z "$__tmp" ] && unset "$1" && return 1
+ eval "$1=$__tmp"
+}
+uci_get_listen_port() {
+ local __tmp
+ __tmp="$(uci_get 'network' "$2" 'listen_port')"
+ [ -z "$__tmp" ] && unset "$1" && return 1
+ eval "$1=$__tmp"
+}
+
+# luci app specific
+is_enabled() { uci_get "$1" 'config' 'enabled'; }
+is_running_iptables() { iptables -t mangle -L | grep -q PBR_PREROUTING >/dev/null 2>&1; }
+is_running_nft_file() { [ -s "$nftPermFile" ]; }
+is_running_nft() { "$nft" list table inet fw4 | grep chain | grep -q pbr_mark_ >/dev/null 2>&1; }
+is_running() { is_running_iptables || is_running_nft; }
+check_ipset() { { [ -n "$ipset" ] && "$ipset" help hash:net; } >/dev/null 2>&1; }
+check_nft() { [ -x "$nft" ]; }
+check_agh() { [ -x "$agh" ] && { [ -s "$aghConfigFile" ] || [ -s "${agh%/*}/AdGuardHome.yaml" ]; }; }
+check_dnsmasq() { command -v dnsmasq >/dev/null 2>&1; }
+check_unbound() { command -v unbound >/dev/null 2>&1; }
+check_agh_ipset() {
+ check_ipset || return 1
+ check_agh || return 1
+ is_greater_or_equal "$($agh --version | sed 's|AdGuard Home, version v\(.*\)|\1|' | sed 's|-.*||')" '0.107.13'
+}
+check_dnsmasq_ipset() {
+ local o;
+ check_ipset || return 1
+ check_dnsmasq || return 1
+ o="$(dnsmasq -v 2>/dev/null)"
+ ! echo "$o" | grep -q 'no-ipset' && echo "$o" | grep -q 'ipset'
+}
+check_dnsmasq_nftset() {
+ local o;
+ check_nft || return 1
+ check_dnsmasq || return 1
+ o="$(dnsmasq -v 2>/dev/null)"
+ ! echo "$o" | grep -q 'no-nftset' && echo "$o" | grep -q 'nftset'
+}
+print_json_bool() { json_init; json_add_boolean "$1" "$2"; json_dump; json_cleanup; }
+print_json_string() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
+
+if type extra_command >/dev/null 2>&1; then
+ extra_command 'status' "Generates output required to troubleshoot routing issues
+ Use '-d' option for more detailed output
+ Use '-p' option to automatically upload data under VPR paste.ee account
+ WARNING: while paste.ee uploads are unlisted, they are still publicly available
+ List domain names after options to include their lookup in report"
+ extra_command 'version' 'Show version information'
+ extra_command 'on_firewall_reload' ' Run service on firewall reload'
+ extra_command 'on_interface_reload' ' Run service on indicated interface reload'
+else
+# shellcheck disable=SC2034
+ EXTRA_COMMANDS='on_firewall_reload on_interface_reload status version'
+# shellcheck disable=SC2034
+ EXTRA_HELP=" status Generates output required to troubleshoot routing issues
+ Use '-d' option for more detailed output
+ Use '-p' option to automatically upload data under VPR paste.ee account
+ WARNING: while paste.ee uploads are unlisted, they are still publicly available
+ List domain names after options to include their lookup in report"
+fi
+
+get_text() {
+ local r
+ case "$1" in
+ errorConfigValidation) r="Config ($packageConfigFile) validation failure!";;
+ errorNoIpFull) r="ip-full binary cannot be found!";;
+ errorNoIptables) r="iptables binary cannot be found!";;
+ errorNoIpset) r="Resolver set support (${resolver_set}) requires ipset, but ipset binary cannot be found!";;
+ errorNoNft) r="Resolver set support (${resolver_set}) requires nftables, but nft binary cannot be found!";;
+ errorResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system!";;
+ errorServiceDisabled) r="The ${packageName} service is currently disabled!";;
+ errorNoWanGateway) r="The ${serviceName} service failed to discover WAN gateway!";;
+ errorNoWanInterface) r="The %s inteface not found, you need to set the 'pbr.config.procd_wan_interface' option!";;
+ errorNoWanInterfaceHint) r="Refer to https://docs.openwrt.melmac.net/pbr/#procd_wan_interface.";;
+ errorIpsetNameTooLong) r="The ipset name '%s' is longer than allowed 31 characters!";;
+ errorNftsetNameTooLong) r="The nft set name '%s' is longer than allowed 255 characters!";;
+ errorUnexpectedExit) r="Unexpected exit or service termination: '%s'!";;
+ errorPolicyNoSrcDest) r="Policy '%s' has no source/destination parameters!";;
+ errorPolicyNoInterface) r="Policy '%s' has no assigned interface!";;
+ errorPolicyNoDns) r="Policy '%s' has no assigned DNS!";;
+ errorPolicyProcessNoInterfaceDns) r="Interface '%s' has no assigned DNS!";;
+ errorPolicyUnknownInterface) r="Policy '%s' has an unknown interface!";;
+ errorPolicyProcessCMD) r="'%s'!";;
+ errorFailedSetup) r="Failed to set up '%s'!";;
+ errorFailedReload) r="Failed to reload '%s'!";;
+ errorUserFileNotFound) r="Custom user file '%s' not found or empty!";;
+ errorUserFileSyntax) r="Syntax error in custom user file '%s'!";;
+ errorUserFileRunning) r="Error running custom user file '%s'!";;
+ errorUserFileNoCurl) r="Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed!";;
+ errorNoGateways) r="Failed to set up any gateway!";;
+ errorResolver) r="Resolver '%s'!";;
+ errorPolicyProcessNoIpv6) r="Skipping IPv6 policy '%s' as IPv6 support is disabled!";;
+ errorPolicyProcessUnknownFwmark) r="Unknown packet mark for interface '%s'!";;
+ errorPolicyProcessMismatchFamily) r="Mismatched IP family between in policy '%s'!";;
+ errorPolicyProcessUnknownProtocol) r="Unknown protocol in policy '%s'!";;
+ errorPolicyProcessInsertionFailed) r="Insertion failed for both IPv4 and IPv6 for policy '%s'!";;
+ errorPolicyProcessInsertionFailedIpv4) r="Insertion failed for IPv4 for policy '%s'!";;
+ errorInterfaceRoutingEmptyValues) r="Received empty tid/mark or interface name when setting up routing!";;
+ errorFailedToResolve) r="Failed to resolve '%s'!";;
+ errorTryFailed) r="Command failed: %s";;
+ errorNftFileInstall) r="Failed to install fw4 nft file '%s'!";;
+ errorDownloadUrlNoHttps) r="Failed to download '%s', HTTPS is not supported!";;
+ errorDownloadUrl) r="Failed to download '%s'!";;
+ errorNoDownloadWithSecureReload) r="Policy '%s' refers to URL which can't be downloaded in 'secure_reload' mode!";;
+ errorFileSchemaRequiresCurl) r="The file:// schema requires curl, but it's not detected on this system!";;
+ warningInvalidOVPNConfig) r="Invalid OpenVPN config for '%s' interface.";;
+ warningResolverNotSupported) r="Resolver set (${resolver_set}) is not supported on this system.";;
+ warningAGHVersionTooLow) r="Installed AdGuardHome ('%s') doesn't support 'ipset_file' option.";;
+ warningPolicyProcessCMD) r="'%s'";;
+ warningTorUnsetParams) r="Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'.";;
+ warningTorUnsetProto) r="Please unset 'proto' or set 'proto' to 'all' for policy '%s'.";;
+ warningTorUnsetChainIpt) r="Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '%s'.";;
+ warningTorUnsetChainNft) r="Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'.";;
+ warningOutdatedWebUIApp) r="The WebUI application is outdated (version %s), please update it.";;
+ warningBadNftCallsInUserFile) r="Incompatible nft calls detected in user include file, disabling fw4 nft file support.";;
+ warningDnsmasqInstanceNoConfdir) r="Dnsmasq instance (%s) targeted in settings, but it doesn't have its own confdir.";;
+ esac
+ echo "$r"
+}
+
+process_url() {
+ local url="$1"
+ local dl_command dl_https_supported dl_temp_file
+# TODO: check for FILE schema and missing curl
+ if is_present 'curl'; then
+ dl_command="curl --silent --insecure"
+ dl_flag="-o"
+ elif is_present '/usr/libexec/wget-ssl'; then
+ dl_command="/usr/libexec/wget-ssl --no-check-certificate -q"
+ dl_flag="-O"
+ elif is_present wget && wget --version 2>/dev/null | grep -q "+https"; then
+ dl_command="wget --no-check-certificate -q"
+ dl_flag="-O"
+ else
+ dl_command="uclient-fetch --no-check-certificate -q"
+ dl_flag="-O"
+ fi
+ if curl --version 2>/dev/null | grep -q "Protocols: .*https.*" \
+ || wget --version 2>/dev/null | grep -q "+ssl"; then
+ dl_https_supported=1
+ 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
+ if is_url_file "$url" && ! is_present 'curl'; then
+ state add 'errorSummary' 'errorFileSchemaRequiresCurl' "$url"
+ elif is_url_https "$url" && [ -z "$dl_https_supported" ]; then
+ state add 'errorSummary' 'errorDownloadUrlNoHttps' "$url"
+ elif $dl_command "$url" "$dl_flag" "$dl_temp_file" 2>/dev/null; then
+ sed 'N;s/\n/ /;s/\s\+/ /g;' "$dl_temp_file"
+ else
+ state add 'errorSummary' 'errorDownloadUrl' "$url"
+ fi
+ rm -f "$dl_temp_file"
+}
+
+load_package_config() {
+ _check_user_files_for_bad_nft_calls() {
+ local cfg="$1"
+ local en path
+ config_get_bool en "$cfg" 'enabled' '1'
+ config_get path "$cfg" 'path'
+ [ "$en" -eq '0' ] && return 0
+ [ -z "$path" ] && return 0
+ [ -s "$path" ] || return 0
+ is_bad_user_file_nft_call "$path" && user_file_check_result='bad'
+ }
+ local param="$1"
+ local user_file_check_result i
+ config_load "$packageName"
+ config_get_bool enabled 'config' 'enabled' '0'
+ config_get fw_mask 'config' 'fw_mask' 'ff0000'
+ 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_file_support 'config' 'nft_file_support' '1'
+ config_get_bool nft_set_auto_merge 'config' 'nft_set_auto_merge' '1'
+ config_get_bool nft_set_counter 'config' 'nft_set_counter' '1'
+ 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 nft_set_gc_interval 'config' 'nft_set_gc_interval'
+ 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 rule_create_option 'config' 'rule_create_option' 'add'
+ config_get_bool secure_reload 'config' 'secure_reload' '0'
+ config_get_bool strict_enforcement 'config' 'strict_enforcement' '1'
+ config_get supported_interface 'config' 'supported_interface'
+ config_get verbosity 'config' 'verbosity' '2'
+ config_get procd_boot_delay 'config' 'procd_boot_delay' '0'
+ config_get procd_boot_timeout 'config' 'procd_boot_timeout' '30'
+ config_get procd_lan_interface 'config' 'procd_lan_interface'
+ config_get procd_wan_ignore_status 'config' 'procd_wan_ignore_status' '0'
+ config_get procd_wan_interface 'config' 'procd_wan_interface' 'wan'
+ config_get procd_wan6_interface 'config' 'procd_wan6_interface' 'wan6'
+ config_get wan_ip_rules_priority 'config' 'wan_ip_rules_priority' '30000'
+ config_get wan_mark 'config' 'wan_mark' '010000'
+ fw_mask="0x${fw_mask}"
+ wan_mark="0x${wan_mark}"
+ if [ -x "$agh" ] && [ ! -s "$aghConfigFile" ]; then
+ [ -s "${agh%/*}/AdGuardHome.yaml" ] && aghConfigFile="${agh%/*}/AdGuardHome.yaml"
+ fi
+ [ -n "$ipv6_enabled" ] && [ "$ipv6_enabled" -eq '0' ] && unset ipv6_enabled
+ [ -n "$nft_file_support" ] && [ "$nft_file_support" -eq '0' ] && unset nft_file_support
+ [ -n "$nft_user_set_counter" ] && [ "$nft_user_set_counter" -eq '0' ] && unset nft_user_set_counter
+ [ -n "$secure_reload" ] && [ "$secure_reload" -eq '0' ] && unset secure_reload
+ config_foreach _check_user_files_for_bad_nft_calls 'include'
+ [ -n "$user_file_check_result" ] && unset nft_file_support
+ [ -n "$nft_file_support" ] && unset secure_reload
+ is_config_enabled 'include' && unset secure_reload
+ if is_nft_mode; then
+ fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")"
+ fw_maskXor="${fw_maskXor:-0xff00ffff}"
+ else
+ case $rule_create_option in
+ insert|-i|-I) rule_create_option='-I';;
+ add|-a|-A|*) rule_create_option='-A';;
+ esac
+ fi
+
+ [ "$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
+ nft_set_params=" \
+ ${nft_set_auto_merge:+ auto-merge;} \
+ ${nft_set_counter:+ counter;} \
+ ${nft_set_flags:+ $nft_set_flags;} \
+ ${nft_set_gc_interval:+ gc_interval "$nft_set_gc_interval";} \
+ ${nft_set_policy:+ policy "$nft_set_policy";} \
+ ${nft_set_timeout:+ timeout "$nft_set_timeout";} \
+ "
+
+ resolver 'check_support' && resolver 'configure_instances'
+}
+
+load_environment() {
+ local param="$1" validation_result="$2"
+ load_package_config "$param"
+ case "$param" in
+ on_start)
+ if [ "$enabled" -eq '0' ]; then
+ state add 'errorSummary' 'errorServiceDisabled'
+ return 1
+ fi
+ if [ -n "$validation_result" ] && [ "$validation_result" != '0' ]; then
+ output "${_ERROR_}: The $packageName config validation failed!\\n"
+ output "Please check if the '$packageConfigFile' contains correct values for config options.\\n"
+ state add 'errorSummary' 'errorConfigValidation'
+ return 1
+ fi
+ if [ ! -x "$ip_bin" ]; then
+ state add 'errorSummary' 'errorNoIpFull'
+ return 1
+ fi
+ if is_nft_mode; then
+ if [ "$(uci_get 'firewall' 'defaults' 'auto_includes')" = '0' ]; then
+ uci_remove 'firewall' 'defaults' 'auto_includes'
+ uci_commit firewall
+ fi
+ else
+ if [ -z "$iptables" ] || [ ! -x "$iptables" ]; then
+ state add 'errorSummary' 'errorNoIptables'
+ return 1
+ fi
+ fi
+ ;;
+ on_stop)
+ :
+ ;;
+ esac
+ load_network "$param"
+}
+
+load_network() {
+ _build_ifaces_supported() { is_supported_interface "$1" && ! str_contains "$ifacesSupported" "$1" && ifacesSupported="${ifacesSupported}${1} "; }
+ _find_firewall_wan_zone() { [ "$(uci_get 'firewall' "$1" 'name')" = "wan" ] && firewallWanZone="$1"; }
+ local i param="$1"
+ local dev4 dev6
+ if [ -z "$ifacesSupported" ]; then
+ config_load 'firewall'
+ config_foreach _find_firewall_wan_zone 'zone'
+ for i in $(uci_get 'firewall' "$firewallWanZone" 'network'); do
+ is_supported_interface "$i" && ! str_contains "$ifacesSupported" "$1" && ifacesSupported="${ifacesSupported}${i} "
+ done
+ config_load 'network'
+ config_foreach _build_ifaces_supported 'interface'
+ fi
+ wanIface4="$procd_wan_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"
+ network_get_device dev6 "$wanIface6"
+ [ -z "$dev6" ] && network_get_physdev dev6 "$wanIface6"
+ [ -z "$wanGW6" ] && pbr_get_gateway6 wanGW6 "$wanIface6" "$dev6"
+ fi
+
+ case "$param" in
+ on_boot|on_start)
+ [ -n "$wanIface4" ] && output 2 "Using wan interface (${param}): $wanIface4 \\n"
+ [ -n "$wanGW4" ] && output 2 "Found wan gateway (${param}): $wanGW4 \\n"
+ [ -n "$wanIface6" ] && output 2 "Using wan6 interface (${param}): $wanIface6 \\n"
+ [ -n "$wanGW6" ] && output 2 "Found wan6 gateway (${param}): $wanGW6 \\n"
+ ;;
+ esac
+ wanGW="${wanGW4:-$wanGW6}"
+}
+
+is_wan_up() {
+ local sleepCount='1' param="$1"
+ load_network "$param"
+ [ "$procd_wan_ignore_status" -eq '0' ] || return 0
+ [ "$param" = 'on_boot' ] || procd_boot_timeout='1'
+ if [ -z "$(uci_get network "$procd_wan_interface")" ]; then
+ state add 'errorSummary' 'errorNoWanInterface' "$procd_wan_interface"
+ state add 'errorSummary' 'errorNoWanInterfaceHint'
+ return 1
+ fi
+ while [ -z "$wanGW" ] ; do
+ load_network "$param"
+ if [ "$((sleepCount))" -gt "$((procd_boot_timeout))" ] || [ -n "$wanGW" ]; then break; fi
+ output "$serviceName waiting for $procd_wan_interface gateway...\\n"
+ sleep 1
+ network_flush_cache
+ sleepCount=$((sleepCount+1))
+ done
+ if [ -n "$wanGW" ]; then
+ return 0
+ else
+ state add 'errorSummary' 'errorNoWanGateway'
+ return 1
+ fi
+}
+
+# shellcheck disable=SC2086
+ipt4() {
+ local d
+ [ -x "$iptables" ] || return 1
+ for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
+ [ "$d" != "$*" ] && "$iptables" $d >/dev/null 2>&1
+ done
+ d="$*"; "$iptables" $d >/dev/null 2>&1
+}
+
+# shellcheck disable=SC2086
+ipt6() {
+ local d
+ [ -n "$ipv6_enabled" ] || return 0
+ [ -x "$ip6tables" ] || return 1
+ for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
+ [ "$d" != "$*" ] && "$ip6tables" $d >/dev/null 2>&1
+ done
+ d="$*"
+ "$ip6tables" $d >/dev/null 2>&1
+}
+
+# shellcheck disable=SC2086
+ipt() {
+ local d failFlagIpv4=1 failFlagIpv6=1
+ [ -x "$iptables" ] || return 1
+ for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do
+ if [ "$d" != "$*" ]; then
+ "$iptables" $d >/dev/null 2>&1
+ if [ -x "$ip6tables" ]; then
+ "$ip6tables" $d >/dev/null 2>&1
+ fi
+ fi
+ done
+ d="$*"; "$iptables" $d >/dev/null 2>&1 && failFlagIpv4=0;
+ if [ -n "$ipv6_enabled" ] && [ -x "$ip6tables" ]; then
+ "$ip6tables" $d >/dev/null 2>&1 && failFlagIpv6=0
+ fi
+ [ "$failFlagIpv4" -eq '0' ] || [ "$failFlagIpv6" -eq '0' ]
+}
+
+# shellcheck disable=SC2086
+ips4() { [ -x "$ipset" ] && "$ipset" "$@" >/dev/null 2>&1; }
+ips6() { [ -x "$ipset" ] && { if [ -n "$ipv6_enabled" ] && [ -n "$*" ]; then "$ipset" "$@" >/dev/null 2>&1; else return 1; fi; }; }
+ips() {
+ local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
+ local ipset4 ipset6 i
+ local ipv4_error=1 ipv6_error=1
+ ipset4="${ipsPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}"
+ ipset6="${ipsPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}"
+
+ [ -x "$ipset" ] || return 1
+
+ if [ "${#ipset4}" -gt '31' ]; then
+ state add 'errorSummary' 'errorIpsetNameTooLong' "$ipset4"
+ return 1
+ fi
+
+ case "$command" in
+ add)
+ ips4 -q -! add "$ipset4" ["$param"] comment "$comment" && ipv4_error=0
+ ips6 -q -! add "$ipset6" ["$param"] comment "$comment" && ipv6_error=0
+ ;;
+ add_agh_element)
+ [ -n "$ipv6_enabled" ] || unset ipset6
+ echo "${param}/${ipset4}${ipset6:+,$ipset6}" >> "$aghIpsetFile" && ipv4_error=0
+ ;;
+ add_dnsmasq_element)
+ [ -n "$ipv6_enabled" ] || unset ipset6
+ # shellcheck disable=SC2086
+ echo "ipset=/${param}/${ipset4}${ipset6:+,$ipset6} # $comment" | tee -a $dnsmasqFileList >/dev/null 2>&1 && ipv4_error=0
+ ;;
+ create)
+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
+ ;;
+ create_agh_set)
+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
+ ;;
+ create_dnsmasq_set)
+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
+ ;;
+ create_user_set)
+ case "$type" in
+ ip|net)
+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0
+ case "$target" in
+ dst)
+ ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+ ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+ ;;
+ src)
+ ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+ ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+ ;;
+ esac
+ ;;
+ mac)
+ ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0
+ ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0
+ ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+ ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+ ;;
+ esac
+ ;;
+ delete|destroy)
+ ips4 -q -! destroy "$ipset4" && ipv4_error=0
+ ips6 -q -! destroy "$ipset6" && ipv6_error=0
+ ;;
+ delete_user_set)
+ ips4 -q -! destroy "$ipset4" && ipv4_error=0
+ ips6 -q -! destroy "$ipset6" family inet6 && ipv6_error=0
+ case "$type" in
+ ip|net)
+ case "$target" in
+ dst)
+ ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+ ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+ ;;
+ src)
+ ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+ ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+ ;;
+ esac
+ ;;
+ mac)
+ ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0
+ ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0
+ ;;
+ esac
+ ;;
+ flush|flush_user_set)
+ ips4 -q -! flush "$ipset4" && ipv4_error=0
+ ips6 -q -! flush "$ipset6" && ipv6_error=0
+ ;;
+ esac
+ if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+nft_call() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; }
+nft_file() {
+ local i
+ [ -x "$nft" ] || return 1
+ case "$1" in
+ add|add_command)
+ [ -n "$nft_file_support" ] || return 1
+ shift
+ grep -q "$*" "$nftTempFile" || echo "$*" >> "$nftTempFile"
+ ;;
+ create)
+ rm -f "$nftTempFile" "$nftPermFile"
+ for i in "$nftTempFile" "$nftPermFile"; do
+ mkdir -p "${i%/*}"
+ done
+ [ -n "$nft_file_support" ] || return 1
+ { echo '#!/usr/sbin/nft -f'; echo ''; } > "$nftTempFile"
+ ;;
+ delete|rm|remove)
+ rm -f "$nftTempFile" "$nftPermFile"
+ ;;
+ enabled)
+ [ -n "$nft_file_support" ] && return 0 || return 1
+ ;;
+ exists)
+ [ -s "$nftPermFile" ] && return 0 || return 1
+ ;;
+ install)
+ [ -n "$nft_file_support" ] || return 1
+ [ -s "$nftTempFile" ] || return 1
+ output "Installing fw4 nft file "
+ if nft_call -c -f "$nftTempFile" && \
+ cp -f "$nftTempFile" "$nftPermFile"; then
+ output_okn
+ else
+ state add 'errorSummary' 'errorNftFileInstall' "$nftTempFile"
+ output_failn
+ fi
+ ;;
+ esac
+}
+nft() { [ -x "$nft" ] && [ -n "$*" ] && { nft_file 'add_command' "$@" || "$nft" "$@" >/dev/null 2>&1;} }
+nft4() { nft "$@"; }
+nft6() { [ -n "$ipv6_enabled" ] || return 0; nft "$@"; }
+nftset() {
+ local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7"
+ local nftset4 nftset6 i param4 param6
+ local ipv4_error=1 ipv6_error=1
+ 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
+ state add 'errorSummary' 'errorNftsetNameTooLong' "$nftset4"
+ return 1
+ fi
+
+ case "$command" in
+ add)
+ if is_mac_address "$param" || is_list "$param"; then
+ nft4 add element inet "$nftTable" "$nftset4" "{ $param }" && ipv4_error=0
+ nft6 add element inet "$nftTable" "$nftset6" "{ $param }" && ipv6_error=0
+ elif is_ipv4_netmask "$param" || is_ipv4 "$param"; then
+ nft4 add element inet "$nftTable" "$nftset4" "{ $param }" && ipv4_error=0
+ elif is_ipv6 "$param"; then
+ nft6 add element inet "$nftTable" "$nftset6" "{ $param }" && ipv6_error=0
+ else
+ if [ "$target" = 'src' ]; then
+ param4="$(ipv4_leases_to_nftset "$param")"
+ param6="$(ipv6_leases_to_nftset "$param")"
+ fi
+ [ -z "$param4" ] && param4="$(resolveip_to_nftset4 "$param")"
+ [ -z "$param6" ] && param6="$(resolveip_to_nftset6 "$param")"
+ if [ -z "$param4" ] && [ -z "$param6" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$param"
+ else
+ [ -n "$param4" ] && nft4 add element inet "$nftTable" "$nftset4" "{ $param4 }" && ipv4_error=0
+ [ -n "$param6" ] && nft6 add element inet "$nftTable" "$nftset6" "{ $param6 }" && ipv6_error=0
+ fi
+ fi
+ ;;
+ 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
+ ;;
+ 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
+ ;;
+ 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
+ ;;
+ 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
+ ;;
+ 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
+ case "$target" in
+ dst)
+ nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+ nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ ;;
+ src)
+ nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+ nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" saddr "@${nftset6}" 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}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+ nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ ;;
+ esac
+ ;;
+ delete|destroy)
+ nft_call delete set inet "$nftTable" "$nftset4" && ipv4_error=0
+ nft_call delete set inet "$nftTable" "$nftset6" && ipv6_error=0
+ ;;
+ delete_user_set)
+ nft_call delete set inet "$nftTable" "$nftset4" && ipv4_error=0
+ nft_call delete set inet "$nftTable" "$nftset6" && ipv6_error=0
+ case "$type" in
+ ip|net)
+ case "$target" in
+ dst)
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ ;;
+ src)
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv4Flag" saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" "$nftIPv6Flag" saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ ;;
+ esac
+ ;;
+ mac)
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
+ nft_call delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
+ ;;
+ esac
+ ;;
+ flush|flush_user_set)
+ nft_call flush set inet "$nftTable" "$nftset4" && ipv4_error=0
+ nft_call flush set inet "$nftTable" "$nftset6" && ipv6_error=0
+ ;;
+ esac
+# nft6 returns true if IPv6 support is not enabled
+ [ -z "$ipv6_enabled" ] && ipv6_error='1'
+ if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+cleanup_rt_tables() {
+ local i
+# shellcheck disable=SC2013
+ for i in $(grep -oh "${ipTablePrefix}_.*" $rtTablesFile); do
+ ! is_netifd_table "$i" && sed -i "/${i}/d" "$rtTablesFile"
+ done
+ sync
+}
+
+cleanup_main_chains() {
+ local i j
+ for i in $chainsList dstnat_lan; do
+ i="$(str_to_lower "$i")"
+ nft_call flush chain inet "$nftTable" "${nftPrefix}_${i}"
+ done
+ for i in $chainsList; do
+ i="$(str_to_upper "$i")"
+ ipt -t mangle -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
+ ipt -t mangle -F "${iptPrefix}_${i}"
+ ipt -t mangle -X "${iptPrefix}_${i}"
+ done
+ ipt -t nat -F "${iptPrefix}_PREROUTING"
+ ipt -t nat -X "${iptPrefix}_PREROUTING"
+}
+
+cleanup_marking_chains() {
+ local i j
+ for i in $(get_mark_nft_chains); do
+ nft_call flush chain inet "$nftTable" "$i"
+ nft_call delete chain inet "$nftTable" "$i"
+ done
+ for i in $(get_mark_ipt_chains); do
+ ipt -t mangle -F "$i"
+ ipt -t mangle -X "$i"
+ done
+}
+
+cleanup_sets() {
+ local i
+ for i in $(get_nft_sets); do
+ nft_call flush set inet "$nftTable" "$i"
+ nft_call delete set inet "$nftTable" "$i"
+ done
+ for i in $(get_ipsets); do
+ ipset -q -! flush "$i" >/dev/null 2>&1
+ ipset -q -! destroy "$i" >/dev/null 2>&1
+ done
+}
+
+state() {
+ local action="$1" param="$2" value="${3//#/_}"
+ shift 3
+# shellcheck disable=SC2124
+ local extras="$@"
+ local line error_id error_extra label
+ case "$action" in
+ add)
+ line="$(eval echo "\$$param")"
+ eval "$param"='${line:+$line#}${value}${extras:+ $extras}'
+ ;;
+ json)
+ json_init
+ json_add_object "$packageName"
+ case "$param" in
+ errorSummary)
+ json_add_array 'errors';;
+ warningSummary)
+ json_add_array 'warnings';;
+ esac
+ if [ -n "$(eval echo "\$$param")" ]; then
+ while read -r line; do
+ if str_contains "$line" ' '; then
+ error_id="${line% *}"
+ error_extra="${line#* }"
+ else
+ error_id="$line"
+ fi
+ json_add_object
+ json_add_string 'id' "$error_id"
+ json_add_string 'extra' "$error_extra"
+ json_close_object
+ done <<EOF
+$(eval echo "\$$param" | tr \# \\n)
+EOF
+ fi
+ json_close_array
+ json_close_object
+ json_dump
+ ;;
+ print)
+ [ -z "$(eval echo "\$$param")" ] && return 0
+ case "$param" in
+ errorSummary)
+ label="${_ERROR_}:";;
+ warningSummary)
+ label="${_WARNING_}:";;
+ esac
+ while read -r line; do
+ if str_contains "$line" ' '; then
+ error_id="${line% *}"
+ error_extra="${line#* }"
+ printf "%b $(get_text "$error_id")\\n" "$label" "$error_extra"
+ else
+ error_id="$line"
+ printf "%b $(get_text "$error_id")\\n" "$label"
+ fi
+ done <<EOF
+$(eval echo "\$$param" | tr \# \\n)
+EOF
+ ;;
+ set)
+ eval "$param"='${value}${extras:+ $extras}'
+ ;;
+ esac
+}
+
+_resolver_dnsmasq_confdir() {
+ local cfg="$1"
+ local confdir
+ [ -z "$(uci_get 'dhcp' "$cfg")" ] && return 1;
+ config_get confdir "$1" 'confdir'
+ if [ -z "$confdir" ] && [ "$resolver_instance" != "*" ]; then
+ state add 'warningSummary' 'warningDnsmasqInstanceNoConfdir' "$cfg"
+ fi
+ if [ -n "$confdir" ] && ! str_contains "$dnsmasqFileList" "$confdir"; then
+ dnsmasqFile="${confdir}/${packageName}"
+ dnsmasqFileList="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFile}"
+ fi
+}
+
+resolver() {
+ local agh_version
+ local param="$1"
+ shift
+
+ if [ "$param" = 'cleanup_all' ]; then
+ sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
+ rm -f "$aghIpsetFile"
+ local dfl
+ for dfl in $dnsmasqFileList; do
+ rm -f "$dfl"
+ done
+ return 0
+ fi
+
+ case "$resolver_set" in
+ ''|none)
+ case "$param" in
+ add_resolver_element) return 1;;
+ create_resolver_set) return 1;;
+ 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;;
+ compare_hash) return 0;;
+ store_hash) return 0;;
+ esac
+ ;;
+ adguardhome.ipset)
+ case "$param" in
+ add_resolver_element)
+ [ -n "$resolver_set_supported" ] && ips 'add_agh_element' "$@";;
+ create_resolver_set)
+ [ -n "$resolver_set_supported" ] && ips 'create_agh_set' "$@";;
+ check_support)
+ if [ ! -x "$ipset" ]; then
+ state add 'errorSummary' 'errorNoIpset'
+ return 1
+ fi
+ if [ -n "$agh" ] && [ -s "$aghConfigFile" ]; then
+ agh_version="$($agh --version | sed 's|AdGuard Home, version v\(.*\)|\1|' | sed 's|-.*||')"
+ if is_greater_or_equal "$agh_version" '0.107.13'; then
+ resolver_set_supported='true'
+ return 0
+ else
+ state add 'warningSummary' 'warningAGHVersionTooLow' "$agh_version"
+ return 1
+ fi
+ else
+ state add 'warningSummary' 'warningResolverNotSupported'
+ return 1
+ fi
+ ;;
+ cleanup)
+ [ -z "$resolver_set_supported" ] && return 0
+ rm -f "$aghIpsetFile"
+ sed -i "/ipset_file: ${aghIpsetFile}/d" "$aghConfigFile" >/dev/null 2>&1
+ ;;
+ configure)
+ [ -z "$resolver_set_supported" ] && return 1
+ mkdir -p "${aghIpsetFile%/*}"
+ touch "$aghIpsetFile"
+ sed -i '/ipset_file/d' "$aghConfigFile" >/dev/null 2>&1
+ sed -i "/ ipset:/a \ \ ipset_file: $aghIpsetFile" "$aghConfigFile"
+ ;;
+ init) :;;
+ init_end) :;;
+ kill)
+ [ -n "$resolver_set_supported" ] && [ -n "$agh" ] && killall -q -s HUP "$agh";;
+ reload)
+ [ -z "$resolver_set_supported" ] && return 1
+ output 3 'Reloading adguardhome '
+ if /etc/init.d/adguardhome reload >/dev/null 2>&1; then
+ output_okn
+ return 0
+ else
+ output_failn
+ return 1
+ fi
+ ;;
+ restart)
+ [ -z "$resolver_set_supported" ] && return 1
+ output 3 'Restarting adguardhome '
+ if /etc/init.d/adguardhome restart >/dev/null 2>&1; then
+ output_okn
+ return 0
+ else
+ output_failn
+ return 1
+ fi
+ ;;
+ compare_hash)
+ [ -z "$resolver_set_supported" ] && return 1
+ local resolverNewHash
+ if [ -s "$aghIpsetFile" ]; then
+ resolverNewHash="$(md5sum "$aghIpsetFile" | awk '{ print $1; }')"
+ fi
+ [ "$resolverNewHash" != "$resolverStoredHash" ]
+ ;;
+ store_hash)
+ [ -s "$aghIpsetFile" ] && resolverStoredHash="$(md5sum "$aghIpsetFile" | awk '{ print $1; }')";;
+ esac
+ ;;
+ dnsmasq.ipset)
+ case "$param" in
+ add_resolver_element)
+ [ -n "$resolver_set_supported" ] && ips 'add_dnsmasq_element' "$@";;
+ create_resolver_set)
+ [ -n "$resolver_set_supported" ] && ips 'create_dnsmasq_set' "$@";;
+ check_support)
+ if [ ! -x "$ipset" ]; then
+ state add 'errorSummary' 'errorNoIpset'
+ return 1
+ fi
+ if ! dnsmasq -v 2>/dev/null | grep -q 'no-ipset' && dnsmasq -v 2>/dev/null | grep -q 'ipset'; then
+ resolver_set_supported='true'
+ return 0
+ else
+ state add 'warningSummary' 'warningResolverNotSupported'
+ return 1
+ fi
+ ;;
+ cleanup)
+ if [ -n "$resolver_set_supported" ]; then
+ local dfl
+ for dfl in $dnsmasqFileList; do
+ rm -f "$dfl"
+ done
+ fi
+ ;;
+ configure)
+ if [ -n "$resolver_set_supported" ]; then
+ local dfl
+ for dfl in $dnsmasqFileList; do
+ 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)
+ config_load 'dhcp'
+ if [ "$resolver_instance" = "*" ]; then
+ config_foreach _resolver_dnsmasq_confdir 'dnsmasq'
+ dnsmasqFile="${dnsmasqFile:-$dnsmasqFileDefault}"
+ str_contains "$dnsmasqFileList" "$dnsmasqFileDefault" || \
+ dnsmasqFileList="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFileDefault}"
+ else
+ for i in $resolver_instance; do
+ _resolver_dnsmasq_confdir "@dnsmasq[$i]" \
+ || _resolver_dnsmasq_confdir "$i"
+ done
+ dnsmasqFile="${dnsmasqFile:-$dnsmasqFileDefault}"
+ str_contains "$dnsmasqFileList" "$dnsmasqFileDefault" || \
+ dnsmasqFileList="${dnsmasqFileList:-$dnsmasqFileDefault}"
+ fi
+ ;;
+ init) :;;
+ init_end) :;;
+ kill)
+ [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
+ reload)
+ [ -z "$resolver_set_supported" ] && return 1
+ output 3 'Reloading dnsmasq '
+ if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
+ output_okn
+ return 0
+ else
+ output_failn
+ return 1
+ fi
+ ;;
+ restart)
+ [ -z "$resolver_set_supported" ] && return 1
+ output 3 'Restarting dnsmasq '
+ if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
+ output_okn
+ return 0
+ else
+ output_failn
+ return 1
+ fi
+ ;;
+ compare_hash)
+ [ -z "$resolver_set_supported" ] && return 1
+ local resolverNewHash
+ if [ -s "$dnsmasqFile" ]; then
+ resolverNewHash="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')"
+ fi
+ [ "$resolverNewHash" != "$resolverStoredHash" ]
+ ;;
+ store_hash)
+ [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')";;
+ esac
+ ;;
+ dnsmasq.nftset)
+ case "$param" in
+ add_resolver_element)
+ [ -n "$resolver_set_supported" ] && nftset 'add_dnsmasq_element' "$@";;
+ create_resolver_set)
+ [ -n "$resolver_set_supported" ] && nftset 'create_dnsmasq_set' "$@";;
+ check_support)
+ if [ ! -x "$nft" ]; then
+ state add 'errorSummary' 'errorNoNft'
+ return 1
+ fi
+ if ! dnsmasq -v 2>/dev/null | grep -q 'no-nftset' && dnsmasq -v 2>/dev/null | grep -q 'nftset'; then
+ resolver_set_supported='true'
+ return 0
+ else
+ state add 'warningSummary' 'warningResolverNotSupported'
+ return 1
+ fi
+ ;;
+ cleanup)
+ if [ -n "$resolver_set_supported" ]; then
+ local dfl
+ for dfl in $dnsmasqFileList; do
+ rm -f "$dfl"
+ done
+ fi
+ ;;
+ configure)
+ if [ -n "$resolver_set_supported" ]; then
+ local dfl
+ for dfl in $dnsmasqFileList; do
+ 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)
+ config_load 'dhcp'
+ if [ "$resolver_instance" = "*" ]; then
+ config_foreach _resolver_dnsmasq_confdir 'dnsmasq'
+ dnsmasqFile="${dnsmasqFile:-$dnsmasqFileDefault}"
+ str_contains "$dnsmasqFileList" "$dnsmasqFileDefault" || \
+ dnsmasqFileList="${dnsmasqFileList:+$dnsmasqFileList }${dnsmasqFileDefault}"
+ else
+ for i in $resolver_instance; do
+ _resolver_dnsmasq_confdir "@dnsmasq[$i]" \
+ || _resolver_dnsmasq_confdir "$i"
+ done
+ dnsmasqFile="${dnsmasqFile:-$dnsmasqFileDefault}"
+ str_contains "$dnsmasqFileList" "$dnsmasqFileDefault" || \
+ dnsmasqFileList="${dnsmasqFileList:-$dnsmasqFileDefault}"
+ fi
+ ;;
+ init) :;;
+ init_end) :;;
+ kill)
+ [ -n "$resolver_set_supported" ] && killall -q -s HUP dnsmasq;;
+ reload)
+ [ -z "$resolver_set_supported" ] && return 1
+ output 3 'Reloading dnsmasq '
+ if /etc/init.d/dnsmasq reload >/dev/null 2>&1; then
+ output_okn
+ return 0
+ else
+ output_failn
+ return 1
+ fi
+ ;;
+ restart)
+ [ -z "$resolver_set_supported" ] && return 1
+ output 3 'Restarting dnsmasq '
+ if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then
+ output_okn
+ return 0
+ else
+ output_failn
+ return 1
+ fi
+ ;;
+ compare_hash)
+ [ -z "$resolver_set_supported" ] && return 1
+ local resolverNewHash
+ if [ -s "$dnsmasqFile" ]; then
+ resolverNewHash="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')"
+ fi
+ [ "$resolverNewHash" != "$resolverStoredHash" ]
+ ;;
+ store_hash)
+ [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum "$dnsmasqFile" | awk '{ print $1; }')";;
+ esac
+ ;;
+ unbound.ipset)
+ case "$param" in
+ add_resolver_element) :;;
+ create_resolver_set) :;;
+ check_support) :;;
+ cleanup) :;;
+ configure) :;;
+ init) :;;
+ init_end) :;;
+ kill) :;;
+ reload) :;;
+ restart) :;;
+ compare_hash) :;;
+ store_hash) :;;
+ esac
+ ;;
+ unbound.nftset)
+ case "$param" in
+ add_resolver_element) :;;
+ create_resolver_set) :;;
+ check_support) :;;
+ cleanup) :;;
+ configure) :;;
+ init) :;;
+ init_end) :;;
+ kill) :;;
+ reload) :;;
+ restart) :;;
+ compare_hash) :;;
+ store_hash) :;;
+ esac
+ ;;
+ esac
+}
+
+trap_process() {
+ output "\\n"
+ output "Unexpected exit or service termination: '${1}'!\\n"
+ state add 'errorSummary' 'errorUnexpectedExit' "$1"
+ traffic_killswitch 'remove'
+}
+
+traffic_killswitch() {
+ local s=0
+ case "$1" in
+ insert)
+ local lan_subnet wan_device wan6_device
+ [ -n "$secure_reload" ] || return 0
+ nft_file 'enabled' && return 0
+ for i in $serviceTrapSignals; do
+# shellcheck disable=SC2064
+ trap "trap_process $i" "$i"
+ done
+ output 3 'Activating traffic killswitch '
+ network_get_subnet lan_subnet "${procd_lan_interface:-lan}"
+ network_get_physdev wan_device "${wanIface4:-wan}"
+ network_get_physdev wan6_device "${wanIface6:-wan6}"
+ if is_nft_mode; then
+ nft_call add chain inet "$nftTable" "${nftPrefix}_killswitch" '{ type filter hook forward priority 0; policy accept; }' || s=1
+ nft_call add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan_device" "$nftIPv4Flag" saddr "$lan_subnet" counter reject || s=1
+ nft_call add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan6_device" "$nftIPv6Flag" saddr "$lan_subnet" counter reject
+ else
+ ipt -N "${iptPrefix}_KILLSWITCH" || s=1
+ ipt -A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan_device" -j REJECT || s=1
+ ipt -A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan6_device" -j REJECT
+ ipt -I FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
+ fi
+ if [ "$s" -eq '0' ]; then
+ output_okn
+ else
+ output_failn
+ fi
+ ;;
+ remove)
+ if [ -n "$secure_reload" ] && ! nft_file 'enabled'; then
+ output 3 'Deactivating traffic killswitch '
+ fi
+ if is_nft_mode; then
+ nft_call flush chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
+ nft_call delete chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1
+ else
+ ipt -D FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1
+ ipt -F "${iptPrefix}_KILLSWITCH" || s=1
+ ipt -X "${iptPrefix}_KILLSWITCH" || s=1
+ fi
+ if [ -n "$secure_reload" ] && ! nft_file 'enabled'; then
+ if [ "$s" -eq '0' ]; then
+ output_okn
+ else
+ output_failn
+ fi
+ fi
+# shellcheck disable=SC2086
+ trap - $serviceTrapSignals
+ ;;
+ esac
+}
+
+# original idea by @egc112: https://github.com/egc112/OpenWRT-egc-add-on/tree/main/stop-dns-leak
+dns_policy_routing() { if is_nft_mode; then dns_policy_routing_nft "$@"; else dns_policy_routing_iptables "$@"; fi; }
+dns_policy_routing_iptables() {
+ local mark param4 param6 i negation value dest4 dest6 ipInsertOption="-A"
+ local ip4error='1' ip6error='1' iface='dns'
+ local name="$1" src_addr="$2" dest_dns="$3" uid="$4"
+ local proto='tcp udp' chain='PREROUTING'
+
+ if [ -n "$ipv6_enabled" ] && { is_ipv6 "$src_addr" || is_ipv6 "$dest_dns"; }; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
+ return 1
+ fi
+
+ if is_family_mismatch "$src_addr" "$dest_dns"; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '${src_addr}' '${dest_dns}'"
+ return 1
+ fi
+
+ if is_supported_interface "$dest_dns"; then
+ local d
+ for d in $(uci -q get network."$dest_dns".dns); do
+ if ! is_family_mismatch "$src_addr" "$d"; then
+ if is_ipv4 "$d"; then
+ dest_dns4="${dest_dns4:-$d}"
+ elif is_ipv6 "$d"; then
+ dest_dns6="${dest_dns6:-$d}"
+ fi
+ fi
+ done
+ else
+ dest_dns4="$dest_dns"
+ dest_dns6="$dest_dns"
+ fi
+
+ if [ -z "${dest_dns4}${dest_dns6}" ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessNoInterfaceDns' "'$dest_dns'"
+ return 1
+ fi
+
+ dest4="--dport 53 -j DNAT --to $dest_dns4"
+ dest6="--dport 53 -j DNAT --to $dest_dns6"
+
+ for i in $proto; do
+ param4="-t nat ${ipInsertOption} ${iptPrefix}_${chain} ${dest4} -p $i"
+ param6="-t nat ${ipInsertOption} ${iptPrefix}_${chain} ${dest6} -p $i"
+ if [ -n "$src_addr" ]; then
+ if [ "${src_addr:0:1}" = "!" ]; then
+ negation='!'; value="${src_addr:1}"
+ else
+ unset negation; value="$src_addr";
+ fi
+ if is_phys_dev "$value"; then
+ param4="$param4 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
+ param6="$param6 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
+ elif is_ipv4_netmask "$value"; then
+ local target='src' type='net'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $src_addr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $src_addr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ param4="$param4 ${negation:+$negation }-s $value"
+ param6="$param6 ${negation:+$negation }-s $value"
+ fi
+ elif is_mac_address "$value"; then
+ local target='src' type='mac'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $src_addr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $src_addr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ param4="$param4 -m mac ${negation:+$negation }--mac-source $value"
+ param6="$param6 -m mac ${negation:+$negation }--mac-source $value"
+ fi
+ else
+ local target='src' type='ip'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $src_addr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $src_addr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ local resolvedIP4 resolvedIP6
+ resolvedIP4="$(resolveip_to_ipt4 "$value")"
+ resolvedIP6="$(resolveip_to_ipt6 "$value")"
+ if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$value"
+ fi
+ param4="$param4 ${negation:+$negation }-s $resolvedIP4"
+ param6="$param6 ${negation:+$negation }-s $resolvedIP6"
+ fi
+ fi
+ fi
+
+ if [ -n "$name" ]; then
+ param4="$param4 -m comment --comment $(str_extras_to_underscore "$name")"
+ param6="$param6 -m comment --comment $(str_extras_to_underscore "$name")"
+ fi
+
+ local ipv4_error='0' ipv6_error='0'
+ if [ "$param4" = "$param6" ]; then
+ ipt4 "$param4" || ipv4_error='1'
+ else
+ ipt4 "$param4" || ipv4_error='1'
+ ipt6 "$param6" || ipv6_error='1'
+ fi
+
+ if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param6"
+ logger -t "$packageName" "ERROR: iptables $param4"
+ logger -t "$packageName" "ERROR: iptables $param6"
+ elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
+ logger -t "$packageName" "ERROR: iptables $param4"
+ fi
+
+ done
+}
+dns_policy_routing_nft() {
+ local mark i nftInsertOption='add'
+ local param4 param6 proto_i negation value dest4 dest6 dest_dns4 dest_dns6
+ local name="$1" src_addr="$2" dest_dns="$3" uid="$4"
+ local proto='tcp udp' chain='dstnat_lan' iface='dns'
+
+ if [ -z "$ipv6_enabled" ] && { is_ipv6 "$src_addr" || is_ipv6 "$dest_dns"; }; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
+ return 1
+ fi
+
+ if is_family_mismatch "$src_addr" "$dest_dns"; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns'"
+ return 1
+ fi
+
+ if is_supported_interface "$dest_dns"; then
+ local d
+ for d in $(uci -q get network."$dest_dns".dns); do
+ if ! is_family_mismatch "$src_addr" "$d"; then
+ if is_ipv4 "$d"; then
+ dest_dns4="${dest_dns4:-$d}"
+ elif is_ipv6 "$d"; then
+ dest_dns6="${dest_dns6:-$d}"
+ fi
+ fi
+ done
+ else
+ dest_dns4="$dest_dns"
+ dest_dns6="$dest_dns"
+ fi
+
+ if [ -z "${dest_dns4}${dest_dns6}" ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessNoInterfaceDns' "'$dest_dns'"
+ return 1
+ fi
+
+ dest4="dport 53 counter dnat ip to $dest_dns4"
+ dest6="dport 53 counter dnat ip to $dest_dns6"
+
+ for proto_i in $proto; do
+ unset param4
+ unset param6
+
+ if [ -n "$src_addr" ]; then
+ if [ "${src_addr:0:1}" = "!" ]; then
+ negation='!='; value="${src_addr:1}"
+ else
+ unset negation; value="$src_addr";
+ fi
+ if is_phys_dev "$value"; then
+ param4="${param4:+$param4 }iifname ${negation:+$negation }${value:1}"
+ param6="${param6:+$param6 }iifname ${negation:+$negation }${value:1}"
+ elif is_mac_address "$value"; then
+ local target='src' type='mac'
+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+ param6="${param6:+$param6 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ else
+ param4="${param4:+$param4 }ether saddr ${negation:+$negation }${value}"
+ param6="${param6:+$param6 }ether saddr ${negation:+$negation }${value}"
+ fi
+ else
+ local target='src' type='ip'
+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ else
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }${value}"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }${value}"
+ fi
+ fi
+ fi
+
+ param4="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param4} ${proto_i:+$proto_i }${dest4} comment \"$name\""
+ param6="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param6} ${proto_i:+$proto_i }${dest6} comment \"$name\""
+ local ipv4_error='0' ipv6_error='0'
+ if [ "$policy_routing_nft_prev_param4" != "$param4" ]; then
+ nft4 "$param4" || ipv4_error='1'
+ policy_routing_nft_prev_param4="$param4"
+ fi
+ if [ "$policy_routing_nft_prev_param6" != "$param6" ] && \
+ [ "$param4" != "$param6" ]; then
+ nft6 "$param6" || ipv6_error='1'
+ policy_routing_nft_prev_param6="$param6"
+ fi
+ if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param6"
+ logger -t "$packageName" "ERROR: nft $param4"
+ logger -t "$packageName" "ERROR: nft $param6"
+ elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
+ logger -t "$packageName" "ERROR: nft $param4"
+ fi
+ done
+}
+
+dns_policy_process() {
+ local i j uid="$4"
+ if [ -z "$uid" ]; then # first call
+ [ "$enabled" -gt '0' ] || return 0
+ unset processDnsPolicyError
+ uid="$1"
+ output 2 "Routing '$name' DNS to $dest_dns "
+ if [ -z "${src_addr}" ]; then
+ state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
+ output_fail; return 1;
+ fi
+ if [ -z "$dest_dns" ]; then
+ state add 'errorSummary' 'errorPolicyNoDns' "$name"
+ output_fail; return 1;
+ fi
+ dns_policy_process "$name" "$src_addr" "$dest_dns" "$uid"
+ if [ -n "$processPolicyError" ]; then
+ output_fail
+ else
+ output_ok
+ fi
+ else # recursive call, get options from passed variables
+ local name="$1" src_addr="$2" dest_dns="$3"
+ if str_contains "$src_addr" '[ ;\{\}]'; then
+ for i in $(str_extras_to_space "$src_addr"); do [ -n "$i" ] && dns_policy_process "$name" "$i" "$dest_dns" "$uid"; done
+ elif str_contains "$dest_dns" '[ ;\{\}]'; then
+ for i in $(str_extras_to_space "$dest_dns"); do [ -n "$i" ] && dns_policy_process "$name" "$src_addr" "$i" "$uid"; done
+ else
+ if is_url "$src_addr"; then
+ src_addr="$(process_url "$src_addr")"
+ [ -n "$src_addr" ] && dns_policy_process "$name" "$src_addr" "$dest_dns" "$uid"
+ else
+ # if only src_addr is set add option 121 to dhcp leases?
+ dns_policy_routing "$name" "$src_addr" "$dest_dns" "$uid"
+ fi
+ fi
+ fi
+}
+
+policy_routing() { if is_nft_mode; then policy_routing_nft "$@"; else policy_routing_iptables "$@"; fi; }
+policy_routing_iptables() {
+ local mark param4 param6 i negation value dest4 dest6 ipInsertOption="-A"
+ local ip4error='1' ip6error='1'
+ local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9"
+ proto="$(str_to_lower "$7")"
+ chain="$(str_to_upper "$8")"
+ chain="${chain:-PREROUTING}"
+ mark=$(eval echo "\$mark_${iface//-/_}")
+
+ if [ -n "$ipv6_enabled" ] && { is_ipv6 "$laddr" || is_ipv6 "$raddr"; }; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
+ return 1
+ fi
+
+ if is_tor "$iface"; then
+ return 1
+ elif is_xray "$iface"; then
+ unset rport
+ [ -z "$lport" ] && lport='0-52,54-65535'
+ proto='tcp udp'
+ dest4="-j TPROXY --on-ip 0.0.0.0 --on-port $(get_xray_traffic_port "$iface")"
+ dest6="-j TPROXY --on-ip :: --on-port $(get_xray_traffic_port "$iface")"
+ elif [ -n "$mark" ]; then
+ dest4="-g ${iptPrefix}_MARK_${mark}"
+ dest6="-g ${iptPrefix}_MARK_${mark}"
+ elif [ "$iface" = "ignore" ]; then
+ dest4="-j RETURN"
+ dest6="-j RETURN"
+ else
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessUnknownFwmark' "$iface"
+ return 1
+ fi
+
+ if is_family_mismatch "$laddr" "$raddr"; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$laddr' '$raddr'"
+ return 1
+ fi
+
+ if [ -z "$proto" ]; then
+ if [ -n "${lport}${rport}" ]; then
+ proto='tcp udp'
+ else
+ proto='all'
+ fi
+ fi
+
+ for i in $proto; do
+ if [ "$i" = 'all' ]; then
+ param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest4"
+ param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest6"
+ elif ! is_supported_protocol "$i"; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessUnknownProtocol' "${name}: '$i'"
+ return 1
+ else
+ param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest4 -p $i"
+ param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest6 -p $i"
+ fi
+
+ if [ -n "$laddr" ]; then
+ if [ "${laddr:0:1}" = "!" ]; then
+ negation='!'; value="${laddr:1}"
+ else
+ unset negation; value="$laddr";
+ fi
+ if is_phys_dev "$value"; then
+ param4="$param4 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
+ param6="$param6 ${negation:+$negation }-m physdev --physdev-in ${value:1}"
+ elif is_ipv4_netmask "$value"; then
+ local target='src' type='net'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ param4="$param4 ${negation:+$negation }-s $value"
+ param6="$param6 ${negation:+$negation }-s $value"
+ fi
+ elif is_mac_address "$value"; then
+ local target='src' type='mac'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ param4="$param4 -m mac ${negation:+$negation }--mac-source $value"
+ param6="$param6 -m mac ${negation:+$negation }--mac-source $value"
+ fi
+ else
+ local target='src' type='ip'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ local resolvedIP4 resolvedIP6
+ resolvedIP4="$(resolveip_to_ipt4 "$value")"
+ resolvedIP6="$(resolveip_to_ipt6 "$value")"
+ if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$value"
+ fi
+ param4="$param4 ${negation:+$negation }-s $resolvedIP4"
+ param6="$param6 ${negation:+$negation }-s $resolvedIP6"
+ fi
+ fi
+ fi
+
+ if [ -n "$lport" ]; then
+ if [ "${lport:0:1}" = "!" ]; then
+ negation='!'; value="${lport:1}"
+ else
+ unset negation; value="$lport";
+ fi
+ param4="$param4 -m multiport ${negation:+$negation }--sport ${value//-/:}"
+ param6="$param6 -m multiport ${negation:+$negation }--sport ${value//-/:}"
+ fi
+
+ if [ -n "$raddr" ]; then
+ if [ "${raddr:0:1}" = "!" ]; then
+ negation='!'; value="${raddr:1}"
+ else
+ unset negation; value="$raddr";
+ fi
+ if is_ipv4_netmask "$value"; then
+ local target='dst' type='net'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ param4="$param4 ${negation:+$negation }-d ${value}"
+ param6="$param6 ${negation:+$negation }-d ${value}"
+ fi
+ elif is_domain "$value"; then
+ local target='dst' type='ip'
+ if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+ resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ elif ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ local resolvedIP4 resolvedIP6
+ resolvedIP4="$(resolveip_to_ipt4 "$value")"
+ resolvedIP6="$(resolveip_to_ipt6 "$value")"
+ if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$value"
+ fi
+ param4="$param4 ${negation:+$negation }-d $resolvedIP4"
+ param6="$param6 ${negation:+$negation }-d $resolvedIP6"
+ fi
+ else
+ local target='dst' type='ip'
+ if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \
+ ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then
+ param4="$param4 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target"
+ param6="$param6 -m set ${negation:+$negation }--match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target"
+ else
+ param4="$param4 ${negation:+$negation }-d ${value}"
+ param6="$param6 ${negation:+$negation }-d ${value}"
+ fi
+ fi
+ fi
+
+ if [ -n "$rport" ]; then
+ if [ "${rport:0:1}" = "!" ]; then
+ negation='!'; value="${rport:1}"
+ else
+ unset negation; value="$rport";
+ fi
+ param4="$param4 -m multiport ${negation:+$negation }--dport ${value//-/:}"
+ param6="$param6 -m multiport ${negation:+$negation }--dport ${value//-/:}"
+ fi
+
+ if [ -n "$name" ]; then
+ param4="$param4 -m comment --comment $(str_extras_to_underscore "$name")"
+ param6="$param6 -m comment --comment $(str_extras_to_underscore "$name")"
+ fi
+
+ local ipv4_error='0' ipv6_error='0'
+ if [ "$param4" = "$param6" ]; then
+ ipt4 "$param4" || ipv4_error='1'
+ else
+ ipt4 "$param4" || ipv4_error='1'
+ ipt6 "$param6" || ipv6_error='1'
+ fi
+
+ if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param6"
+ logger -t "$packageName" "ERROR: iptables $param4"
+ logger -t "$packageName" "ERROR: iptables $param6"
+ elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "iptables $param4"
+ logger -t "$packageName" "ERROR: iptables $param4"
+ fi
+
+ done
+}
+policy_routing_nft() {
+ local mark i nftInsertOption='add'
+ local param4 param6 proto_i negation value dest4 dest6
+ local name="$1" iface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto chain uid="$9"
+ proto="$(str_to_lower "$7")"
+ chain="$(str_to_lower "$8")"
+ chain="${chain:-prerouting}"
+ mark=$(eval echo "\$mark_${iface//-/_}")
+
+ if [ -z "$ipv6_enabled" ] && { is_ipv6 "$src_addr" || is_ipv6 "$dest_addr"; }; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessNoIpv6' "$name"
+ return 1
+ fi
+
+ if is_tor "$iface"; then
+ unset dest_port
+ unset proto
+ elif is_xray "$iface"; then
+ unset dest_port
+ [ -z "$src_port" ] && src_port='0-65535'
+ dest4="tproxy $nftIPv4Flag to: $(get_xray_traffic_port "$iface") accept"
+ dest6="tproxy $nftIPv6Flag to: $(get_xray_traffic_port "$iface") accept"
+ elif [ -n "$mark" ]; then
+ dest4="goto ${nftPrefix}_mark_${mark}"
+ dest6="goto ${nftPrefix}_mark_${mark}"
+ elif [ "$iface" = "ignore" ]; then
+ dest4="return"
+ dest6="return"
+ else
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessUnknownFwmark' "$iface"
+ return 1
+ fi
+
+ if is_family_mismatch "$src_addr" "$dest_addr"; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_addr'"
+ return 1
+ fi
+
+ if [ -z "$proto" ]; then
+ if [ -n "${src_port}${dest_port}" ]; then
+ proto='tcp udp'
+ else
+ proto='all'
+ fi
+ fi
+
+ for proto_i in $proto; do
+ unset param4
+ unset param6
+ if [ "$proto_i" = 'all' ]; then
+ unset proto_i
+ elif ! is_supported_protocol "$proto_i"; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessUnknownProtocol' "${name}: '$proto_i'"
+ return 1
+ fi
+
+ if [ -n "$src_addr" ]; then
+ if [ "${src_addr:0:1}" = "!" ]; then
+ negation='!='; value="${src_addr:1}"
+ else
+ unset negation; value="$src_addr";
+ fi
+ if is_phys_dev "$value"; then
+ param4="${param4:+$param4 }iifname ${negation:+$negation }${value:1}"
+ param6="${param6:+$param6 }iifname ${negation:+$negation }${value:1}"
+ elif is_mac_address "$value"; then
+ local target='src' type='mac'
+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+ param6="${param6:+$param6 }ether saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ else
+ param4="${param4:+$param4 }ether saddr ${negation:+$negation }${value}"
+ param6="${param6:+$param6 }ether saddr ${negation:+$negation }${value}"
+ fi
+ else
+ local target='src' type='ip'
+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ else
+ param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }${value}"
+ param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }${value}"
+ fi
+ fi
+ fi
+
+ if [ -n "$dest_addr" ]; then
+ if [ "${dest_addr:0:1}" = "!" ]; then
+ negation='!='; value="${dest_addr:1}"
+ else
+ unset negation; value="$dest_addr";
+ fi
+ if is_phys_dev "$value"; then
+ param4="${param4:+$param4 }oifname ${negation:+$negation }${value:1}"
+ param6="${param6:+$param6 }oifname ${negation:+$negation }${value:1}"
+ elif is_domain "$value"; then
+ local target='dst' type='ip'
+ if resolver 'create_resolver_set' "$iface" "$target" "$type" "$uid" "$name" && \
+ resolver 'add_resolver_element' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ elif nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ else
+ local resolvedIP4 resolvedIP6
+ resolvedIP4="$(resolveip_to_nftset4 "$value")"
+ resolvedIP6="$(resolveip_to_nftset6 "$value")"
+ if [ -z "$resolvedIP4" ] && [ -z "$resolvedIP6" ]; then
+ state add 'errorSummary' 'errorFailedToResolve' "$value"
+ fi
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }{ $resolvedIP4 }"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }{ $resolvedIP6 }"
+ fi
+ else
+ local target='dst' type='ip'
+ if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \
+ nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_4_${target}_${type}_${uid}"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}"
+ else
+ param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }${value}"
+ param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }${value}"
+ fi
+ fi
+ fi
+
+ if [ -n "$src_port" ]; then
+ if [ "${src_port:0:1}" = "!" ]; then
+ negation='!='; value="${src_port:1}"
+ else
+ unset negation; value="$src_port";
+ fi
+ param4="${param4:+$param4 }${proto_i:+$proto_i }sport ${negation:+$negation }{$(ports_to_nftset "$value")}"
+ param6="${param6:+$param6 }${proto_i:+$proto_i }sport ${negation:+$negation }{$(ports_to_nftset "$value")}"
+ fi
+
+ if [ -n "$dest_port" ]; then
+ if [ "${dest_port:0:1}" = "!" ]; then
+ negation='!='; value="${dest_port:1}"
+ else
+ unset negation; value="$dest_port";
+ fi
+ param4="${param4:+$param4 }${proto_i:+$proto_i }dport ${negation:+$negation }{$(ports_to_nftset "$value")}"
+ param6="${param6:+$param6 }${proto_i:+$proto_i }dport ${negation:+$negation }{$(ports_to_nftset "$value")}"
+ fi
+
+ if is_tor "$iface"; then
+ local dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443
+ local ipv4_error='0' ipv6_error='0'
+ local dest_i dest4 dest6
+ param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} dstnat meta nfproto ipv4 $param4"
+ param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} dstnat meta nfproto ipv6 $param6"
+ dest_udp_53="udp dport 53 counter redirect to :${torDnsPort} comment 'Tor-DNS-UDP'"
+ dest_tcp_80="tcp dport 80 counter redirect to :${torTrafficPort} comment 'Tor-HTTP-TCP'"
+ dest_udp_80="udp dport 80 counter redirect to :${torTrafficPort} comment 'Tor-HTTP-UDP'"
+ dest_tcp_443="tcp dport 443 counter redirect to :${torTrafficPort} comment 'Tor-HTTPS-TCP'"
+ dest_udp_443="udp dport 443 counter redirect to :${torTrafficPort} comment 'Tor-HTTPS-UDP'"
+ for dest_i in dest_udp_53 dest_tcp_80 dest_udp_80 dest_tcp_443 dest_udp_443; do
+ eval "dest4=\$$dest_i"
+ eval "dest6=\$$dest_i"
+ nft4 "$param4" "$dest4" || ipv4_error='1'
+ nft6 "$param6" "$dest6" || ipv6_error='1'
+ if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4 $dest4"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param6 $dest6"
+ logger -t "$packageName" "ERROR: nft $param4 $dest4"
+ logger -t "$packageName" "ERROR: nft $param6 $dest6"
+ elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4 $dest4"
+ logger -t "$packageName" "ERROR: nft $param4 $dest4"
+ fi
+ done
+ else
+ param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param4 $dest4 comment \"$name\""
+ param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} $param6 $dest6 comment \"$name\""
+ local ipv4_error='0' ipv6_error='0'
+ if [ "$policy_routing_nft_prev_param4" != "$param4" ]; then
+ nft4 "$param4" || ipv4_error='1'
+ policy_routing_nft_prev_param4="$param4"
+ fi
+ if [ "$policy_routing_nft_prev_param6" != "$param6" ] && \
+ [ "$param4" != "$param6" ]; then
+ nft6 "$param6" || ipv6_error='1'
+ policy_routing_nft_prev_param6="$param6"
+ fi
+
+ if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailed' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param6"
+ logger -t "$packageName" "ERROR: nft $param4"
+ logger -t "$packageName" "ERROR: nft $param6"
+ elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
+ processPolicyError='true'
+ state add 'errorSummary' 'errorPolicyProcessInsertionFailedIpv4' "$name"
+ state add 'errorSummary' 'errorPolicyProcessCMD' "nft $param4"
+ logger -t "$packageName" "ERROR: nft $param4"
+ fi
+ fi
+ done
+}
+
+policy_process() {
+ local i j uid="$9"
+ if [ -z "$uid" ]; then # first call
+ [ "$enabled" -gt '0' ] || return 0
+ unset processPolicyError
+ uid="$1"
+ if is_nft_mode; then
+ chain="$(str_to_lower "$chain")"
+ else
+ chain="$(str_to_upper "$chain")"
+ fi
+ proto="$(str_to_lower "$proto")"
+ [ "$proto" = 'auto' ] && unset proto
+ [ "$proto" = 'all' ] && unset proto
+ output 2 "Routing '$name' via $interface "
+ if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then
+ state add 'errorSummary' 'errorPolicyNoSrcDest' "$name"
+ output_fail; return 1;
+ fi
+ if [ -z "$interface" ]; then
+ state add 'errorSummary' 'errorPolicyNoInterface' "$name"
+ output_fail; return 1;
+ fi
+ if ! is_supported_interface "$interface"; then
+ state add 'errorSummary' 'errorPolicyUnknownInterface' "$name"
+ output_fail; return 1;
+ fi
+ src_port="${src_port// / }"; src_port="${src_port// /,}"; src_port="${src_port//,\!/ !}";
+ dest_port="${dest_port// / }"; dest_port="${dest_port// /,}"; dest_port="${dest_port//,\!/ !}";
+ policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+ if [ -n "$processPolicyError" ]; then
+ output_fail
+ else
+ output_ok
+ fi
+ else # recursive call, get options from passed variables
+ local name="$1" interface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto="$7" chain="$8"
+ if str_contains "$src_addr" '[ ;\{\}]'; then
+ for i in $(str_extras_to_space "$src_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$i" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done
+ elif str_contains "$src_port" '[ ;\{\}]'; then
+ for i in $(str_extras_to_space "$src_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$i" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done
+ elif str_contains "$dest_addr" '[ ;\{\}]'; then
+ for i in $(str_extras_to_space "$dest_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$i" "$dest_port" "$proto" "$chain" "$uid"; done
+ elif str_contains "$dest_port" '[ ;\{\}]'; then
+ for i in $(str_extras_to_space "$dest_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$i" "$proto" "$chain" "$uid"; done
+ elif str_contains "$proto" '[ ;\{\}]'; then
+ for i in $(str_extras_to_space "$proto"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$i" "$chain" "$uid"; done
+ else
+ if [ -n "$secure_reload" ] && { is_url_dl "$src_addr" || is_url_dl "$dest_addr"; }; then
+ state add 'errorSummary' 'errorNoDownloadWithSecureReload' "$name"
+ elif is_url "$src_addr"; then
+ src_addr="$(process_url "$src_addr")"
+ [ -n "$src_addr" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+ elif is_url "$dest_addr"; then
+ dest_addr="$(process_url "$dest_addr")"
+ [ -n "$dest_addr" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+ else
+ # if only src_addr is set add option 121 to dhcp leases?
+ policy_routing "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"
+ fi
+ fi
+ fi
+}
+
+try() {
+ if ! "$@"; then
+ state add 'errorSummary' 'errorTryFailed' "$*"
+ return 1
+ fi
+}
+
+interface_routing() {
+ local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev="$6" gw6="$7" dev6="$8" priority="$9"
+ local dscp s=0 i ipv4_error=1 ipv6_error=1
+ if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then
+ state add 'errorSummary' 'errorInterfaceRoutingEmptyValues'
+ return 1
+ fi
+ case "$action" in
+ create)
+ if is_netifd_table_interface "$iface"; then
+ ipv4_error=0
+ $ip_bin -4 rule del table "$tid" >/dev/null 2>&1
+ try "$ip_bin" -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
+ if is_nft_mode; then
+ try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
+ else
+ ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
+ fi
+ if [ -n "$ipv6_enabled" ]; then
+ ipv6_error=0
+ $ip_bin -6 rule del table "$tid" >/dev/null 2>&1
+ try "$ip_bin" -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$((priority-1))" || ipv6_error=1
+ fi
+ else
+ if ! grep -q "$tid ${ipTablePrefix}_${iface}" "$rtTablesFile"; then
+ sed -i "/${ipTablePrefix}_${iface}/d" "$rtTablesFile"
+ sync
+ echo "$tid ${ipTablePrefix}_${iface}" >> "$rtTablesFile"
+ sync
+ fi
+ $ip_bin -4 rule del table "$tid" >/dev/null 2>&1
+ $ip_bin -4 route flush table "$tid" >/dev/null 2>&1
+ if [ -n "$gw4" ] || [ "$strict_enforcement" -ne '0' ]; then
+ ipv4_error=0
+ if [ -z "$gw4" ]; then
+ try "$ip_bin" -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ else
+ try "$ip_bin" -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ fi
+# shellcheck disable=SC2086
+ while read -r i; do
+ i="$(echo "$i" | sed 's/ linkdown$//')"
+ i="$(echo "$i" | sed 's/ onlink$//')"
+ idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')"
+ if ! is_supported_iface_dev "$idev"; then
+ try "$ip_bin" -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ fi
+ done << EOF
+ $($ip_bin -4 route list table main)
+EOF
+ try "$ip_bin" -4 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
+ if is_nft_mode; then
+ try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1
+ try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1
+ else
+ ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1
+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1
+ ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1
+ fi
+ fi
+ if [ -n "$ipv6_enabled" ]; then
+ ipv6_error=0
+ $ip_bin -6 rule del table "$tid" >/dev/null 2>&1
+ $ip_bin -6 route flush table "$tid" >/dev/null 2>&1
+ if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne '0' ]; then
+ if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
+ try "$ip_bin" -6 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ elif "$ip_bin" -6 route list table main | grep -q " dev $dev6 "; then
+ "$ip_bin" -6 route add default via "$gw6" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ while read -r i; do
+ i="$(echo "$i" | sed 's/ linkdown$//')"
+ i="$(echo "$i" | sed 's/ onlink$//')"
+ # shellcheck disable=SC2086
+ try "$ip_bin" -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ done << EOF
+ $($ip_bin -6 route list table main | grep " dev $dev6 ")
+EOF
+ else
+ try "$ip_bin" -6 route add "$($ip_bin -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try "$ip_bin" -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ fi
+ fi
+ try "$ip_bin" -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$((priority-1))" >/dev/null 2>&1 || ipv6_error=1
+ fi
+ fi
+ if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
+ dscp="$(uci_get "$packageName" 'config' "${iface}_dscp")"
+ if is_nft_mode; then
+ if [ "${dscp:-0}" -ge '1' ] && [ "${dscp:-0}" -le '63' ]; then
+ try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s=1
+ if [ -n "$ipv6_enabled" ]; then
+ try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s=1
+ fi
+ fi
+ if [ "$iface" = "$icmp_interface" ]; then
+ try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp goto ${nftPrefix}_mark_${mark}" || s=1
+ if [ -n "$ipv6_enabled" ]; then
+ try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp goto ${nftPrefix}_mark_${mark}" || s=1
+ fi
+ fi
+ else
+ if [ "${dscp:-0}" -ge '1' ] && [ "${dscp:-0}" -le '63' ]; then
+ ipt -t mangle -I "${iptPrefix}_PREROUTING" -m dscp --dscp "${dscp}" -g "${iptPrefix}_MARK_${mark}" || s=1
+ fi
+ if [ "$iface" = "$icmp_interface" ]; then
+ ipt -t mangle -I "${iptPrefix}_OUTPUT" -p icmp -g "${iptPrefix}_MARK_${mark}" || s=1
+ fi
+ fi
+ else
+ s=1
+ fi
+ return "$s"
+ ;;
+ create_user_set)
+ if is_nft_mode; then
+ nftset 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
+ nftset 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
+ nftset 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
+ else
+ ips 'create_user_set' "$iface" 'dst' 'ip' 'user' '' "$mark" || s=1
+ ips 'create_user_set' "$iface" 'dst' 'net' 'user' '' "$mark" || s=1
+ ips 'create_user_set' "$iface" 'src' 'ip' 'user' '' "$mark" || s=1
+ ips 'create_user_set' "$iface" 'src' 'net' 'user' '' "$mark" || s=1
+ ips 'create_user_set' "$iface" 'src' 'mac' 'user' '' "$mark" || s=1
+ fi
+ return "$s"
+ ;;
+ delete|destroy)
+ $ip_bin rule del table "$tid" >/dev/null 2>&1
+ if ! is_netifd_table_interface "$iface"; then
+ $ip_bin route flush table "$tid" >/dev/null 2>&1
+ sed -i "/${ipTablePrefix}_${iface}\$/d" "$rtTablesFile"
+ sync
+ fi
+ return "$s"
+ ;;
+ reload_interface)
+ is_netifd_table_interface "$iface" && return 0;
+ ipv4_error=0
+ $ip_bin rule del table "$tid" >/dev/null 2>&1
+ if ! is_netifd_table_interface "$iface"; then
+ $ip_bin route flush table "$tid" >/dev/null 2>&1
+ fi
+ if [ -n "$gw4" ] || [ "$strict_enforcement" -ne '0' ]; then
+ if [ -z "$gw4" ]; then
+ try "$ip_bin" -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ else
+ try "$ip_bin" -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1
+ fi
+ try "$ip_bin" 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 [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
+ try "$ip_bin" -6 route add unreachable default table "$tid" || ipv6_error=1
+ elif $ip_bin -6 route list table main | grep -q " dev $dev6 "; then
+ while read -r i; do
+ # shellcheck disable=SC2086
+ try "$ip_bin" -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ done << EOF
+ $($ip_bin -6 route list table main | grep " dev $dev6 ")
+EOF
+ else
+ try "$ip_bin" -6 route add "$($ip_bin -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ try "$ip_bin" -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1
+ fi
+ fi
+ try "$ip_bin" -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
+ fi
+ if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
+ s=0
+ else
+ s=1
+ fi
+ return "$s"
+ ;;
+ esac
+}
+
+json_add_gateway() {
+ local action="$1" tid="$2" mark="$3" iface="$4" gw4="$5" dev4="$6" gw6="$7" dev6="$8" priority="$9" default="${10}"
+ json_add_object ''
+ json_add_string 'name' "$iface"
+ json_add_string 'device_ipv4' "$dev4"
+ json_add_string 'gateway_ipv4' "$gw4"
+ json_add_string 'device_ipv6' "$dev6"
+ json_add_string 'gateway_ipv6' "$gw6"
+ if [ -n "$default" ]; then
+ json_add_boolean 'default' '1'
+ else
+ json_add_boolean 'default' '0'
+ fi
+ json_add_string 'action' "$action"
+ json_add_string 'table_id' "$tid"
+ json_add_string 'mark' "$mark"
+ json_add_string 'priority' "$priority"
+ json_close_object
+}
+
+interface_process() {
+ local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3"
+ local displayText dispDev dispGw4 dispGw6 dispStatus
+
+ if [ "$iface" = 'all' ] && [ "$action" = 'prepare' ]; then
+ config_load 'network'
+ ifaceMark="$(printf '0x%06x' "$wan_mark")"
+ ifacePriority="$wan_ip_rules_priority"
+ unset ifaceTableID
+ return 0
+ fi
+
+ if [ "$iface" = 'tor' ]; then
+ case "$action" in
+ create|reload)
+ torDnsPort="$(get_tor_dns_port)"
+ torTrafficPort="$(get_tor_traffic_port)"
+ displayText="${iface}/53->${torDnsPort}/80,443->${torTrafficPort}"
+ gatewaySummary="${gatewaySummary}${displayText}\\n"
+ ;;
+ destroy)
+ ;;
+ esac
+ return 0
+ fi
+
+ is_supported_interface "$iface" || return 0
+ is_wan6 "$iface" && return 0
+ [ "$((ifaceMark))" -gt "$((fw_mask))" ] && return 1
+
+ if is_ovpn "$iface" && ! is_ovpn_valid "$iface"; then
+ : || state add 'warningSummary' 'warningInvalidOVPNConfig' "$iface"
+ fi
+
+ network_get_device dev "$iface"
+ [ -z "$dev" ] && network_get_physdev dev "$iface"
+ if is_wan "$iface" && [ -n "$wanIface6" ] && str_contains "$wanIface6" "$iface"; then
+ network_get_device dev6 "$wanIface6"
+ [ -z "$dev6" ] && network_get_physdev dev6 "$wanIface6"
+ fi
+
+ [ -z "$dev6" ] && dev6="$dev"
+ [ -z "$ifaceMark" ] && ifaceMark="$(printf '0x%06x' "$wan_mark")"
+ [ -z "$ifacePriority" ] && ifacePriority="$wan_ip_rules_priority"
+
+ case "$action" in
+ pre_init)
+ [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_non_pbr_next_id)"
+ 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)))"
+ ifacePriority="$((ifacePriority - 1))"
+ ifaceTableID="$((ifaceTableID + 1))"
+ return 0
+ ;;
+ create)
+ ifaceTableID="$(get_rt_tables_id "$iface")"
+ [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
+ eval "mark_${iface//-/_}"='$ifaceMark'
+ eval "tid_${iface//-/_}"='$ifaceTableID'
+ pbr_get_gateway4 gw4 "$iface" "$dev"
+ pbr_get_gateway6 gw6 "$iface" "$dev6"
+ dispGw4="${gw4:-0.0.0.0}"
+ dispGw6="${gw6:-::/0}"
+ [ "$iface" != "$dev" ] && dispDev="$dev"
+ if is_default_dev "$dev"; then
+ [ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
+ fi
+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+ output 2 "Setting up routing for '$displayText' "
+ if interface_routing 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
+ json_add_gateway 'create' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+ if is_netifd_table_interface "$iface"; then output_okb; else output_ok; fi
+ else
+ state add 'errorSummary' 'errorFailedSetup' "$displayText"
+ output_fail
+ fi
+ ;;
+ create_user_set)
+ ifaceTableID="$(get_rt_tables_id "$iface")"
+ [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
+ eval "mark_${iface//-/_}"='$ifaceMark'
+ eval "tid_${iface//-/_}"='$ifaceTableID'
+ pbr_get_gateway4 gw4 "$iface" "$dev"
+ pbr_get_gateway6 gw6 "$iface" "$dev6"
+ dispGw4="${gw4:-0.0.0.0}"
+ dispGw6="${gw6:-::/0}"
+ [ "$iface" != "$dev" ] && dispDev="$dev"
+ if is_default_dev "$dev"; then
+ [ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
+ fi
+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+ interface_routing 'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"
+ ;;
+ destroy)
+ ifaceTableID="$(get_rt_tables_id "$iface")"
+ [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
+ eval "mark_${iface//-/_}"='$ifaceMark'
+ eval "tid_${iface//-/_}"='$ifaceTableID'
+ pbr_get_gateway4 gw4 "$iface" "$dev"
+ pbr_get_gateway6 gw6 "$iface" "$dev6"
+ dispGw4="${gw4:-0.0.0.0}"
+ dispGw6="${gw6:-::/0}"
+ [ "$iface" != "$dev" ] && dispDev="$dev"
+ if is_default_dev "$dev"; then
+ [ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
+ fi
+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+ output 2 "Removing routing for '$displayText' "
+ interface_routing 'destroy' "${ifaceTableID}" "${ifaceMark}" "${iface}"
+ if is_netifd_table_interface "$iface"; then output_okb; else output_ok; fi
+ ;;
+ reload)
+ ifaceTableID="$(get_rt_tables_id "$iface")"
+ [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
+ eval "mark_${iface//-/_}"='$ifaceMark'
+ eval "tid_${iface//-/_}"='$ifaceTableID'
+ pbr_get_gateway4 gw4 "$iface" "$dev"
+ pbr_get_gateway6 gw6 "$iface" "$dev6"
+ dispGw4="${gw4:-0.0.0.0}"
+ dispGw6="${gw6:-::/0}"
+ [ "$iface" != "$dev" ] && dispDev="$dev"
+ if is_default_dev "$dev"; then
+ [ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
+ fi
+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+ ;;
+ reload_interface)
+ ifaceTableID="$(get_rt_tables_id "$iface")"
+ [ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)"
+ eval "mark_${iface//-/_}"='$ifaceMark'
+ eval "tid_${iface//-/_}"='$ifaceTableID'
+ pbr_get_gateway4 gw4 "$iface" "$dev"
+ pbr_get_gateway6 gw6 "$iface" "$dev6"
+ dispGw4="${gw4:-0.0.0.0}"
+ dispGw6="${gw6:-::/0}"
+ [ "$iface" != "$dev" ] && dispDev="$dev"
+ if is_default_dev "$dev"; then
+ [ "$verbosity" = '1' ] && dispStatus="$_OK_" || dispStatus="$__OK__"
+ fi
+ displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}"
+ if [ "$iface" = "$reloadedIface" ]; then
+ output 2 "Reloading routing for '$displayText' "
+ if interface_routing 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"; then
+ json_add_gateway 'reload_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+ if is_netifd_table_interface "$iface"; then output_okb; else output_ok; fi
+ else
+ state add 'errorSummary' 'errorFailedReload' "$displayText"
+ output_fail
+ fi
+ else
+ gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n"
+ fi
+ ;;
+ esac
+# ifaceTableID="$((ifaceTableID + 1))"
+ ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))"
+ ifacePriority="$((ifacePriority - 2))"
+ return $s
+}
+
+user_file_process() {
+ local shellBin="${SHELL:-/bin/ash}"
+ [ "$enabled" -gt '0' ] || return 0
+ if [ ! -s "$path" ]; then
+ state add 'errorSummary' 'errorUserFileNotFound' "$path"
+ output_fail
+ return 1
+ fi
+ if ! $shellBin -n "$path"; then
+ state add 'errorSummary' 'errorUserFileSyntax' "$path"
+ output_fail
+ return 1
+ fi
+ output 2 "Running $path "
+# shellcheck disable=SC1090
+ if ! . "$path"; then
+ state add 'errorSummary' 'errorUserFileRunning' "$path"
+ if grep -q -w 'curl' "$path" && ! is_present 'curl'; then
+ state add 'errorSummary' 'errorUserFileNoCurl' "$path"
+ fi
+ output_fail
+ return 1
+ else
+ output_ok
+ return 0
+ fi
+}
+
+boot() {
+ local procd_boot_delay
+ config_load "$packageName"
+ config_get procd_boot_delay 'config' 'procd_boot_delay' '0'
+ nft_file 'delete'
+ ubus -t 30 wait_for network.interface 2>/dev/null
+ { is_integer "$procd_boot_delay" && sleep "$procd_boot_delay"; \
+ rc_procd start_service 'on_boot' && service_started 'on_boot'; } &
+}
+
+on_firewall_reload() {
+ if [ ! -e "$packageLockFile" ]; then
+ logger -t "$packageName" "Reload on firewall action aborted: service is stopped."
+ return 0
+ else
+ if nft_file 'exists'; then
+ logger -t "$packageName" "Reusing the fw4 nft file."
+ else
+ rc_procd start_service 'on_firewall_reload' "$1"
+ fi
+ fi
+}
+
+on_interface_reload() {
+ if [ ! -e "$packageLockFile" ]; then
+ logger -t "$packageName" "Reload on interface change aborted: service is stopped."
+ return 0
+ else
+ rc_procd start_service 'on_interface_reload' "$1"
+ fi
+}
+
+start_service() {
+ local resolverStoredHash resolverNewHash i param="$1" reloadedIface
+
+ load_environment "${param:-on_start}" "$(load_validate_config)" || return 1
+ is_wan_up "$param" || return 1
+
+ interface_process 'all' 'prepare'
+ config_foreach interface_process 'interface' 'pre_init'
+
+ case "$param" in
+ on_boot)
+ serviceStartTrigger='on_start'
+ ;;
+ on_firewall_reload)
+ serviceStartTrigger='on_start'
+ ;;
+ on_interface_reload)
+ reloadedIface="$2"
+ local tid pre_init_tid
+ tid="$(get_rt_tables_id "$reloadedIface")"
+ pre_init_tid="$(eval echo "\$pre_init_tid_${reloadedIface//-/_}")"
+ if [ "$tid" = "$pre_init_tid" ]; then
+# logger -t "$packageName" "Updated interface $reloadedIface TID: ${tid}; Pre-Init TID: ${pre_init_tid}. Reloading..."
+ serviceStartTrigger='on_interface_reload'
+ else
+# logger -t "$packageName" "Updated interface $reloadedIface TID: ${tid}; Pre-Init TID: ${pre_init_tid}. Restarting..."
+ serviceStartTrigger='on_start'
+ unset reloadedIface
+ fi
+# if is_ovpn "$reloadedIface"; then
+# logger -t "$packageName" "Updated interface is an OpenVPN tunnel, restarting."
+# serviceStartTrigger='on_start'
+# unset reloadedIface
+# else
+# serviceStartTrigger='on_interface_reload'
+# fi
+ ;;
+ on_reload)
+ serviceStartTrigger='on_reload'
+ ;;
+ on_restart)
+ serviceStartTrigger='on_start'
+ ;;
+ esac
+
+ if [ -n "$reloadedIface" ] && ! is_supported_interface "$reloadedIface"; then
+ return 0
+ fi
+
+ if [ -n "$(ubus_get_status error)" ] || [ -n "$(ubus_get_status warning)" ]; then
+ serviceStartTrigger='on_start'
+ unset reloadedIface
+ elif ! is_service_running; then
+ serviceStartTrigger='on_start'
+ unset reloadedIface
+ elif [ -z "$(ubus_get_status gateways)" ]; then
+ serviceStartTrigger='on_start'
+ unset reloadedIface
+# elif [ "$serviceStartTrigger" = 'on_interface_reload' ] && \
+# [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_ipv4')" ] && \
+# [ -z "$(ubus_get_interface "$reloadedIface" 'gateway_ipv6')" ]; then
+# serviceStartTrigger='on_start'
+# unset reloadedIface
+ else
+ serviceStartTrigger="${serviceStartTrigger:-on_start}"
+ fi
+
+ procd_open_instance 'main'
+ procd_set_param command /bin/true
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_open_data
+
+ case $serviceStartTrigger in
+ on_interface_reload)
+ output 1 "Reloading Interface: $reloadedIface "
+ json_add_array 'gateways'
+ interface_process 'all' 'prepare'
+ config_foreach interface_process 'interface' 'reload_interface' "$reloadedIface"
+ json_close_array
+ output 1 '\n'
+ ;;
+ on_reload)
+ traffic_killswitch 'insert'
+ resolver 'store_hash'
+ resolver 'cleanup_all'
+ resolver 'configure'
+ resolver 'init'
+ cleanup_main_chains
+ cleanup_sets
+ nft_file 'create'
+ if ! is_nft_mode; then
+ for i in $chainsList; do
+ i="$(str_to_upper "$i")"
+ ipt -t mangle -N "${iptPrefix}_${i}"
+ ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
+ done
+ ipt -t nat -N "${iptPrefix}_PREROUTING"
+ fi
+ json_add_array 'gateways'
+ interface_process 'all' 'prepare'
+ config_foreach interface_process 'interface' 'reload'
+ interface_process 'tor' 'destroy'
+ is_tor_running && interface_process 'tor' 'reload'
+ json_close_array
+ if is_config_enabled 'policy'; then
+ output 1 'Processing policies '
+ config_load "$packageName"
+ config_foreach load_validate_policy 'policy' policy_process
+ output 1 '\n'
+ fi
+ if is_config_enabled 'dns_policy'; then
+ output 1 'Processing dns policies '
+ config_load "$packageName"
+ config_foreach load_validate_dns_policy 'dns_policy' dns_policy_process
+ output 1 '\n'
+ fi
+ if is_config_enabled 'include'; then
+ interface_process 'all' 'prepare'
+ config_foreach interface_process 'interface' 'create_user_set'
+ output 1 'Processing user file(s) '
+ config_load "$packageName"
+ config_foreach load_validate_include 'include' user_file_process
+ output 1 '\n'
+ fi
+ nft_file 'install'
+ resolver 'init_end'
+ ! nft_file 'exists' && resolver 'compare_hash' && resolver 'restart'
+ traffic_killswitch 'remove'
+ ;;
+ on_start|*)
+ traffic_killswitch 'insert'
+ resolver 'store_hash'
+ resolver 'cleanup_all'
+ resolver 'configure'
+ resolver 'init'
+ cleanup_main_chains
+ cleanup_sets
+ cleanup_marking_chains
+ cleanup_rt_tables
+ nft_file 'create'
+ if ! is_nft_mode; then
+ for i in $chainsList; do
+ i="$(str_to_upper "$i")"
+ ipt -t mangle -N "${iptPrefix}_${i}"
+ ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}"
+ done
+ ipt -t nat -N "${iptPrefix}_PREROUTING"
+ fi
+ output 1 'Processing interfaces '
+ json_add_array 'gateways'
+ interface_process 'all' 'prepare'
+ config_foreach interface_process 'interface' 'create'
+ interface_process 'tor' 'destroy'
+ is_tor_running && interface_process 'tor' 'create'
+ json_close_array
+ ip route flush cache
+ output 1 '\n'
+ if is_config_enabled 'policy'; then
+ output 1 'Processing policies '
+ config_load "$packageName"
+ config_foreach load_validate_policy 'policy' policy_process
+ output 1 '\n'
+ fi
+ if is_config_enabled 'dns_policy'; then
+ output 1 'Processing dns policies '
+ config_load "$packageName"
+ config_foreach load_validate_dns_policy 'dns_policy' dns_policy_process
+ output 1 '\n'
+ fi
+ if is_config_enabled 'include'; then
+ interface_process 'all' 'prepare'
+ config_foreach interface_process 'interface' 'create_user_set'
+ output 1 'Processing user file(s) '
+ config_load "$packageName"
+ config_foreach load_validate_include 'include' user_file_process
+ output 1 '\n'
+ fi
+ nft_file 'install'
+ resolver 'init_end'
+ ! nft_file 'exists' && resolver 'compare_hash' && resolver 'restart'
+ traffic_killswitch 'remove'
+ ;;
+ esac
+
+ if [ -z "$gatewaySummary" ]; then
+ state add 'errorSummary' 'errorNoGateways'
+ fi
+ json_add_object 'status'
+ [ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary"
+ [ -n "$errorSummary" ] && json_add_string 'errors' "$errorSummary"
+ [ -n "$warningSummary" ] && json_add_string 'warnings' "$warningSummary"
+ if [ "$strict_enforcement" -ne '0' ] && str_contains "$gatewaySummary" '0.0.0.0'; then
+ json_add_string 'mode' 'strict'
+ fi
+ json_close_object
+ procd_close_data
+ procd_close_instance
+}
+
+service_started() {
+ if nft_file 'exists'; then
+ procd_set_config_changed firewall
+ if nft_file 'exists'; then
+ resolver 'compare_hash' && resolver 'restart'
+ [ -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
+ elif is_nft_mode; then
+ [ -n "$gatewaySummary" ] && output "$serviceName (nft mode) started with gateways:\\n${gatewaySummary}"
+ else
+ [ -n "$gatewaySummary" ] && output "$serviceName (iptables mode) started with gateways:\\n${gatewaySummary}"
+ fi
+ state print 'errorSummary'
+ state print 'warningSummary'
+ touch "$packageLockFile"
+ if [ -n "$errorSummary" ]; then
+ return 2
+ elif [ -n "$warningSummary" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+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
+ 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
+ for n in $ifacesSupported; do
+ procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} on_interface_reload "$n"
+ done
+ procd_close_trigger
+# procd_add_raw_trigger "interface.*.up" 4000 "/etc/init.d/${packageName}" restart 'on_interface_up'
+ if [ "$serviceStartTrigger" = 'on_start' ]; then
+ output 3 "$serviceName monitoring interfaces: ${ifacesSupported}\\n"
+ fi
+}
+
+stop_service() {
+ local i nft_file_mode
+ load_environment 'on_stop'
+ ! is_service_running && [ "$(get_rt_tables_next_id)" = "$(get_rt_tables_non_pbr_next_id)" ] && return 0
+ [ "$1" = 'quiet' ] && quiet_mode 'on'
+ traffic_killswitch 'insert'
+ if nft_file 'exists'; then
+ nft_file_mode=1
+ fi
+ nft_file 'delete'
+ cleanup_main_chains
+ cleanup_sets
+ cleanup_marking_chains
+ output 1 'Resetting interfaces '
+ config_load 'network'
+ config_foreach interface_process 'interface' 'destroy'
+ interface_process 'tor' 'destroy'
+ cleanup_rt_tables
+ output 1 "\\n"
+ ip route flush cache
+ unset ifaceMark
+ unset ifaceTableID
+ resolver 'store_hash'
+ resolver 'cleanup_all'
+ resolver 'compare_hash' && resolver 'restart'
+ traffic_killswitch 'remove'
+ if [ "$enabled" -ne '0' ]; then
+ if [ -n "$nft_file_mode" ]; then
+ output "$serviceName (fw4 nft file mode) stopped "; output_okn;
+ elif is_nft_mode; then
+ output "$serviceName (nft mode) stopped "; output_okn;
+ else
+ output "$serviceName (iptables mode) stopped "; output_okn;
+ fi
+ fi
+ rm -f "$packageLockFile"
+}
+
+version() { echo "$PKG_VERSION"; }
+
+status_service() {
+ local _SEPARATOR_='============================================================'
+ load_environment 'on_status'
+ if is_nft_mode; then
+ status_service_nft "$@"
+ else
+ status_service_iptables "$@"
+ fi
+}
+
+status_service_iptables() {
+ local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j wan_tid
+
+ json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
+ if [ -n "$wanIface4" ]; then
+ network_get_gateway wanGW4 "$wanIface4"
+ network_get_device dev "$wanIface4"
+ fi
+ if [ -n "$wanIface6" ]; then
+ network_get_device dev6 "$wanIface6"
+ wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
+ [ "$wanGW6" = "default" ] && wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
+ fi
+ while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
+ [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
+ status="$serviceName running 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}."
+ {
+ echo "$status"
+ echo "$_SEPARATOR_"
+ dnsmasq --version 2>/dev/null | sed '/^$/,$d'
+ if [ -n "$1" ]; then
+ echo "$_SEPARATOR_"
+ echo "Resolving domains"
+ for i in $1; do
+ echo "$i: $(resolveip "$i" | tr '\n' ' ')"
+ done
+ fi
+
+ echo "$_SEPARATOR_"
+ echo "Routes/IP Rules"
+ tableCount="$(grep -c "${packageName}_" $rtTablesFile)" || tableCount=0
+ if [ -n "$set_d" ]; then route; else route | grep '^default'; fi
+ if [ -n "$set_d" ]; then ip rule list; fi
+ wan_tid=$(($(get_rt_tables_next_id)-tableCount))
+ i=0; while [ "$i" -lt "$tableCount" ]; do
+ echo "IPv4 table $((wan_tid + i)) route: $($ip_bin -4 route show table $((wan_tid + i)) | grep default)"
+ echo "IPv4 table $((wan_tid + i)) rule(s):"
+ $ip_bin -4 rule list table "$((wan_tid + i))"
+ i=$((i + 1))
+ done
+
+ if [ -n "$ipv6_enabled" ]; then
+ i=0; while [ "$i" -lt "$tableCount" ]; do
+ $ip_bin -6 route show table $((wan_tid + i)) | while read -r param; do
+ echo "IPv6 Table $((wan_tid + i)): $param"
+ done
+ i=$((i + 1))
+ done
+ fi
+
+ for j in Mangle NAT; do
+ if [ -z "$set_d" ]; then
+ for i in $chainsList; do
+ i="$(str_to_upper "$i")"
+ if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" >/dev/null 2>&1; then
+ echo "$_SEPARATOR_"
+ echo "$j IP Table: $i"
+ iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
+ if [ -n "$ipv6_enabled" ]; then
+ echo "$_SEPARATOR_"
+ echo "$j IPv6 Table: $i"
+ iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}"
+ fi
+ fi
+ done
+ else
+ echo "$_SEPARATOR_"
+ echo "$j IP Table"
+ iptables -L -t "$(str_to_lower $j)"
+ if [ -n "$ipv6_enabled" ]; then
+ echo "$_SEPARATOR_"
+ echo "$j IPv6 Table"
+ iptables -L -t "$(str_to_lower $j)"
+ fi
+ fi
+ i=0; ifaceMark="$wan_mark";
+ while [ "$i" -lt "$tableCount" ]; do
+ if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}" >/dev/null 2>&1; then
+ echo "$_SEPARATOR_"
+ echo "$j IP Table MARK Chain: ${iptPrefix}_MARK_${ifaceMark}"
+ iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}"
+ ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))";
+ fi
+ i=$((i + 1))
+ done
+ done
+
+ echo "$_SEPARATOR_"
+ echo "Current ipsets"
+ ipset save
+ if [ -s "$dnsmasqFileDefault" ]; then
+ echo "$_SEPARATOR_"
+ echo "DNSMASQ sets"
+ cat "$dnsmasqFileDefault"
+ fi
+ if [ -s "$aghIpsetFile" ]; then
+ echo "$_SEPARATOR_"
+ echo "AdGuardHome sets"
+ cat "$aghIpsetFile"
+ fi
+ echo "$_SEPARATOR_"
+ } | tee -a /var/${packageName}-support
+ if [ -n "$set_p" ]; then
+ printf "%b" "Pasting to paste.ee... "
+ if curl --version 2>/dev/null | grep -q "Protocols: .*https.*"; then
+ json_init; json_add_string 'description' "${packageName}-support"
+ json_add_array 'sections'; json_add_object '0'
+ json_add_string 'name' "$(uci_get 'system' '@system[0]' 'hostname')"
+ json_add_string 'contents' "$(cat /var/${packageName}-support)"
+ json_close_object; json_close_array; payload=$(json_dump)
+ out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload")
+ json_load "$out"; json_get_var id id; json_get_var s success
+ [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n"
+ [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
+ else
+ printf "%b" "${__FAIL__}\\n"
+ printf "%b" "${_ERROR_}: The curl, libopenssl or ca-bundle packages were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n"
+ fi
+ else
+ printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n"
+ fi
+}
+
+status_service_nft() {
+ local i dev dev6 wan_tid
+
+ json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version
+ if [ -n "$wanIface4" ]; then
+ network_get_gateway wanGW4 "$wanIface4"
+ network_get_device dev "$wanIface4"
+ fi
+ if [ -n "$wanIface6" ]; then
+ network_get_device dev6 "$wanIface6"
+ wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}')
+ [ "$wanGW6" = "default" ] && wanGW6=$($ip_bin -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}')
+ fi
+ while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done
+ [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support"
+ status="$serviceName running 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}."
+
+ echo "$_SEPARATOR_"
+ echo "$packageName - environment"
+ echo "$status"
+ echo "$_SEPARATOR_"
+ dnsmasq --version 2>/dev/null | sed '/^$/,$d'
+ if nft_file 'exists'; then
+ echo "$_SEPARATOR_"
+ echo "$packageName fw4 nft file: $nftPermFile"
+ sed '1d;2d;' "$nftPermFile"
+ fi
+ echo "$_SEPARATOR_"
+ echo "$packageName chains - policies"
+ for i in $chainsList dstnat_lan; do
+ "$nft" -a list table inet "$nftTable" | sed -n "/chain ${nftPrefix}_${i} {/,/\t}/p"
+ done
+ echo "$_SEPARATOR_"
+ echo "$packageName chains - marking"
+ for i in $(get_mark_nft_chains); do
+ "$nft" -a list table inet "$nftTable" | sed -n "/chain ${i} {/,/\t}/p"
+ done
+ echo "$_SEPARATOR_"
+ echo "$packageName nft sets"
+ for i in $(get_nft_sets); do
+ "$nft" -a list table inet "$nftTable" | sed -n "/set ${i} {/,/\t}/p"
+ done
+ if [ -s "$dnsmasqFileDefault" ]; then
+ echo "$_SEPARATOR_"
+ echo "dnsmasq sets"
+ cat "$dnsmasqFileDefault"
+ fi
+# echo "$_SEPARATOR_"
+# ip rule list | grep "${packageName}_"
+ echo "$_SEPARATOR_"
+ tableCount="$(grep -c "${packageName}_" $rtTablesFile)" || tableCount=0
+ wan_tid=$(($(get_rt_tables_next_id)-tableCount))
+ i=0; while [ "$i" -lt "$tableCount" ]; do
+ echo "IPv4 table $((wan_tid + i)) route: $($ip_bin -4 route show table $((wan_tid + i)) | grep default)"
+ echo "IPv4 table $((wan_tid + i)) rule(s):"
+ $ip_bin -4 rule list table "$((wan_tid + i))"
+ if [ -n "$ipv6_enabled" ]; then
+ echo "IPv6 table $((wan_tid + i)) route: $($ip_bin -6 route show table $((wan_tid + i)) | grep default)"
+ echo "IPv6 table $((wan_tid + i)) rule(s):"
+ $ip_bin -6 route show table $((wan_tid + i))
+ fi
+ i=$((i + 1))
+ done
+}
+
+# shellcheck disable=SC2120
+load_validate_config() {
+ uci_load_validate "$packageName" "$packageName" "$1" "${2}${3:+ $3}" \
+ 'enabled:bool:0' \
+ 'strict_enforcement:bool:1' \
+ 'secure_reload:bool:0' \
+ 'ipv6_enabled:bool:0' \
+ 'resolver_set:or("", "none", "adguardhome.ipset", "dnsmasq.ipset", "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' \
+ '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_delay:integer:0' \
+ 'procd_boot_timeout:integer:30' \
+ 'procd_lan_interface:string' \
+ 'procd_reload_delay:integer:0' \
+ 'procd_wan_ignore_status:bool:0' \
+ 'procd_wan_interface:network:wan' \
+ 'procd_wan6_interface:network:wan6' \
+ 'wan_ip_rules_priority:uinteger:30000' \
+ 'rule_create_option:or("", add, insert):add' \
+ 'webui_supported_protocol:list(string)' \
+ 'nft_file_support:bool:1'\
+ 'nft_set_auto_merge:bool:1'\
+ 'nft_set_counter:bool:1'\
+ 'nft_set_flags_interval:bool:1'\
+ 'nft_set_flags_timeout:bool:0'\
+ 'nft_set_gc_interval:or("", string)'\
+ 'nft_set_policy:or("", memory, performance):performance'\
+ 'nft_set_timeout:or("", string)'
+}
+
+# shellcheck disable=SC2120
+load_validate_dns_policy() {
+ local name
+ local enabled
+ local src_addr
+ local dest_dns
+ 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))'
+}
+
+# shellcheck disable=SC2120
+load_validate_policy() {
+ local name
+ local enabled
+ local interface
+ local proto
+ local chain
+ local src_addr
+ local src_port
+ local dest_addr
+ local dest_port
+ uci_load_validate "$packageName" 'policy' "$1" "${2}${3:+ $3}" \
+ 'name:string:Untitled' \
+ 'enabled:bool:1' \
+ 'interface:or("ignore", "tor", regex("xray_.*"), uci("network", "@interface")):wan' \
+ 'proto:or(string)' \
+ 'chain:or("", "forward", "input", "output", "prerouting", "postrouting", "FORWARD", "INPUT", "OUTPUT", "PREROUTING", "POSTROUTING"):prerouting' \
+ '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)))'
+}
+
+# shellcheck disable=SC2120
+load_validate_include() {
+ local path=
+ local enabled=
+ uci_load_validate "$packageName" 'include' "$1" "${2}${3:+ $3}" \
+ 'path:file' \
+ 'enabled:bool:0'
+}