mirror of
https://github.com/openwrt/packages.git
synced 2025-12-21 17:04:32 +04:00
Remove many obsolete files.
Makefile:
* remove netifd-flavour related code
* remove trailing white spaces
Init-script:
* proper deletion of default network rules for IPv{4,6}
* fix netifd function error when IPv6 is enabled
* remove trailing white spaces
Signed-off-by: Stan Grishin <stangri@melmac.ca>
2759 lines
108 KiB
Bash
Executable File
2759 lines
108 KiB
Bash
Executable File
#!/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=20
|
|
# shellcheck disable=SC2034
|
|
USE_PROCD=1
|
|
|
|
if type extra_command >/dev/null 2>&1; then
|
|
extra_command 'netifd' "Netifd extensions operations"
|
|
extra_command 'on_interface_reload' "Run service on indicated interface reload"
|
|
extra_command 'status' "Generates output required to troubleshoot routing issues
|
|
Use '-d' option for more detailed output
|
|
Use '-p' option to automatically upload data under PBR paste.ee account
|
|
WARNING: while paste.ee uploads are unlisted, they are still publicly available
|
|
List domain names after options to include their lookup in report"
|
|
extra_command 'version' "Show version information"
|
|
else
|
|
# shellcheck disable=SC2034
|
|
EXTRA_COMMANDS='netifd on_interface_reload status version'
|
|
# shellcheck disable=SC2034
|
|
EXTRA_HELP=" status Generates output required to troubleshoot routing issues
|
|
Use '-d' option for more detailed output
|
|
Use '-p' option to automatically upload data under PBR paste.ee account
|
|
WARNING: while paste.ee uploads are unlisted, they are still publicly available
|
|
List domain names after options to include their lookup in report"
|
|
fi
|
|
|
|
readonly packageName='pbr'
|
|
readonly PKG_VERSION='dev-test'
|
|
readonly packageCompat='19'
|
|
readonly serviceName="$packageName $PKG_VERSION"
|
|
readonly packageConfigFile="/etc/config/${packageName}"
|
|
readonly packageDebugFile="/var/run/${packageName}.debug"
|
|
readonly packageLockFile="/var/run/${packageName}.lock"
|
|
readonly packageDnsmasqFile="/var/run/${packageName}.dnsmasq"
|
|
readonly runningStatusFile="/dev/shm/${packageName}.status.json"
|
|
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 ipTablePrefix="$packageName"
|
|
# shellcheck disable=SC2155
|
|
readonly agh="$(command -v AdGuardHome)"
|
|
# 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 nftNetifdPermFile="/usr/share/nftables.d/ruleset-post/20-${packageName}-netifd.nft"
|
|
readonly nftPrefix="$packageName"
|
|
readonly nftTable='fw4'
|
|
readonly chainsList='forward output prerouting'
|
|
readonly ssConfigFile='/etc/shadowsocks'
|
|
readonly torConfigFile='/etc/tor/torrc'
|
|
readonly xrayIfacePrefix='xray_'
|
|
readonly rtTablesFile='/etc/iproute2/rt_tables'
|
|
|
|
# Silence "Command failed: Not found" for redundant procd service delete calls
|
|
__UBUS_BIN="$(command -v ubus || echo /bin/ubus)"
|
|
ubus() {
|
|
if [ "$1" = "call" ] && [ "$2" = "service" ] && [ "$3" = "delete" ]; then
|
|
"$__UBUS_BIN" "$@" >/dev/null 2>&1 || true
|
|
else
|
|
"$__UBUS_BIN" "$@"
|
|
fi
|
|
}
|
|
# Wrap ip to emulate `ip rule replace` on builds where it's unavailable.
|
|
# We only intercept "rule replace"; everything else is passed through to ip-full.
|
|
ip() {
|
|
# If first arg is -4 or -6, we might be handling rules
|
|
if [ "$1" = "-4" ] || [ "$1" = "-6" ]; then
|
|
local fam="$1"
|
|
shift
|
|
# Intercept: ip -4|-6 rule replace ...
|
|
if [ "$1" = "rule" ] && [ "$2" = "replace" ]; then
|
|
shift 2
|
|
# Parse args: capture priority/pref value and rebuild the rest
|
|
local prio=
|
|
local newargs=
|
|
while [ -n "$1" ]; do
|
|
case "$1" in
|
|
priority|pref)
|
|
shift
|
|
prio="$1"
|
|
shift
|
|
continue
|
|
;;
|
|
esac
|
|
newargs="${newargs}${newargs:+ }$1"
|
|
shift
|
|
done
|
|
# If we found a priority, replace = del by priority + add with pref
|
|
if [ -n "$prio" ]; then
|
|
"$ip_full" "$fam" rule del priority "$prio" >/dev/null 2>&1 || true
|
|
# shellcheck disable=SC2086
|
|
"$ip_full" "$fam" rule add $newargs pref "$prio"
|
|
return $?
|
|
fi
|
|
# No priority found; best-effort: just add what we have
|
|
# shellcheck disable=SC2086
|
|
"$ip_full" "$fam" rule add $newargs
|
|
return $?
|
|
fi
|
|
# Not a rule replace: pass through
|
|
"$ip_full" "$fam" "$@"
|
|
return $?
|
|
fi
|
|
# No -4/-6 family: pass straight through
|
|
"$ip_full" "$@"
|
|
}
|
|
|
|
# package config options
|
|
enabled=
|
|
fw_mask=
|
|
icmp_interface=
|
|
ignored_interface=
|
|
ipv6_enabled=
|
|
nft_user_set_policy=
|
|
nft_user_set_counter=
|
|
procd_boot_trigger_delay=
|
|
procd_reload_delay=
|
|
lan_device=
|
|
uplink_interface=
|
|
uplink_interface6=
|
|
uplink_interface6_metric='128'
|
|
resolver_set=
|
|
resolver_instance=
|
|
strict_enforcement=
|
|
supported_interface=
|
|
verbosity=
|
|
uplink_ip_rules_priority=
|
|
uplink_mark=
|
|
nft_rule_counter=
|
|
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=
|
|
netifd_enabled=
|
|
netifd_strict_enforcement=
|
|
netifd_interface_default=
|
|
netifd_interface_default6=
|
|
netifd_interface_local=
|
|
config_compat=
|
|
config_version=
|
|
|
|
# run-time
|
|
aghConfigFile='/etc/AdGuardHome/AdGuardHome.yaml'
|
|
gatewaySummary=
|
|
wanIface4=
|
|
wanIface6=
|
|
ifaceMark=
|
|
ifaceTableID=
|
|
ifacePriority=
|
|
ifacesAll=
|
|
ifacesSupported=
|
|
ifacesTriggers=
|
|
firewallWanZone=
|
|
wanGW4=
|
|
wanGW6=
|
|
pbrBootFlag=
|
|
serviceStartTrigger=
|
|
processDnsPolicyError=
|
|
processPolicyError=
|
|
processPolicyWarning=
|
|
resolverSetSupported=
|
|
pbrNftPrevParam4=
|
|
pbrNftPrevParam6=
|
|
nftRuleParams=
|
|
nftSetParams=
|
|
torDnsPort=
|
|
torTrafficPort=
|
|
dnsmasq_features=
|
|
dnsmasq_ubus=
|
|
nft_fw4_dump=
|
|
loadEnvironmentFlag=
|
|
loadPackageConfigFlag=
|
|
|
|
# shellcheck disable=SC1091
|
|
. "${IPKG_INSTROOT}/lib/functions.sh"
|
|
# shellcheck disable=SC1091
|
|
. "${IPKG_INSTROOT}/lib/functions/network.sh"
|
|
# shellcheck disable=SC1091
|
|
. "${IPKG_INSTROOT}/usr/share/libubox/jshn.sh"
|
|
|
|
debug() { local i j; for i in "$@"; do eval "j=\$$i"; logger "${packageName:+-t $packageName}" "${i}: ${j} "; done; }
|
|
str_contains() { [ "${1//$2}" != "$1" ]; }
|
|
str_contains_word() { echo "$1" | grep -qw "$2"; }
|
|
str_extras_to_underscore() { echo "$1" | sed -E 's/[\. ~`!@#$%^&*()+=,<>?;:\/\\-]/_/g; s/_+/_/g'; }
|
|
str_extras_to_space() { echo "$1" | tr ',;{}' ' '; }
|
|
str_first_value_interface() { local i; for i in $1; do is_supported_interface "$i" && { echo "$i"; break; }; done; }
|
|
str_first_value_ipv4() { local i; for i in $1; do is_ipv4 "$i" && { echo "$i"; break; }; done; }
|
|
str_first_value_ipv6() { local i; for i in $1; do is_ipv6 "$i" && { echo "$i"; break; }; done; }
|
|
str_first_word() { echo "${1%% *}"; }
|
|
str_replace() { echo "${1//$2/$3}"; }
|
|
str_to_dnsmasq_nftset() { echo "$1" | tr ' ' '/'; }
|
|
str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
|
|
str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
|
|
# shellcheck disable=SC3060
|
|
output() {
|
|
[ -z "$verbosity" ] && verbosity="$(uci_get "$packageName" 'config' 'verbosity' '1')"
|
|
[ "$#" -ne '1' ] && {
|
|
case "$1" in [0-9]) [ $((verbosity & $1)) -gt 0 ] && shift || return 0;; esac }
|
|
local msg="$*" queue="/dev/shm/$packageName-output"
|
|
[ -t 1 ] && printf "%b" "$msg"
|
|
[ "$msg" != "${msg//\\n}" ] && {
|
|
[ -s "$queue" ] && msg="$(cat "$queue")${msg}" && rm -f "$queue"
|
|
msg="$(printf "%b" "$msg" | sed 's/\x1b\[[0-9;]*m//g')"
|
|
logger -t "$packageName [$$]" "$(printf "%b" "$msg")"
|
|
} || printf "%b" "$msg" >> "$queue"
|
|
}
|
|
output_1_newline() { output 1 '\n'; }
|
|
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"; }
|
|
output_error() { output "${_ERROR_} $*!\n"; }
|
|
output_warning() { output "${_WARNING_} $*.\n"; }
|
|
quiet_mode() {
|
|
case "$1" in
|
|
on) verbosity=0;;
|
|
off) verbosity="$(uci_get "$packageName" 'config' 'verbosity' '2')";;
|
|
esac
|
|
}
|
|
pbr_find_iface() {
|
|
local iface i param="$2"
|
|
case "$param" in
|
|
wan6) iface="$uplink_interface6";;
|
|
wan|*) iface="$uplink_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 -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
|
|
[ "$iface" = "$uplink_interface" ] && iface="$uplink_interface6"
|
|
network_get_gateway6 gw "$iface" true
|
|
if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then
|
|
gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | grep 'scope global' | awk '{print $2}')"
|
|
fi
|
|
eval "$1"='$gw'
|
|
}
|
|
filter_options() {
|
|
local opt="$1" values="$2" v _ret
|
|
for v in $values; do
|
|
if str_contains "$opt" '_negative'; then
|
|
is_negated "$v" || continue
|
|
opt="${opt/_negative}"
|
|
fi
|
|
eval "is_$opt" "${v/\!}" || continue
|
|
_ret="${_ret:+$_ret }$v"
|
|
done
|
|
echo "$_ret"
|
|
return 0
|
|
}
|
|
inline_set() {
|
|
local value="$1" inline_set i
|
|
for i in $value; do
|
|
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"; }
|
|
# shellcheck disable=SC2317
|
|
is_config_enabled() {
|
|
# shellcheck disable=SC2329
|
|
_check_config() { local en; config_get_bool en "$1" 'enabled' '1'; [ "$en" -gt '0' ] && _cfg_enabled=0; }
|
|
local cfg="$1" _cfg_enabled=1
|
|
[ -n "$1" ] || return 1
|
|
config_load "$packageName"
|
|
config_foreach _check_config "$cfg"
|
|
return "$_cfg_enabled"
|
|
}
|
|
uci_get_device() {
|
|
local __tmp
|
|
__tmp="$(uci_get 'network' "$2" 'device')"
|
|
[ -z "$__tmp" ] && unset "$1" && return 1
|
|
eval "$1=$__tmp"
|
|
}
|
|
uci_get_protocol() { uci_get 'network' "$1" 'proto'; }
|
|
is_default_dev() { [ "$1" = "$(ip -4 route show default | awk '{for(i=1;i<=NF;i++) if($i=="dev"){print $(i+1);exit}}')" ]; }
|
|
is_netifd_interface_default() {
|
|
is_netifd_interface "$1" || return 1
|
|
[ "$netifd_interface_default" = "$1" ] && return 0
|
|
[ "$netifd_interface_default6" = "$1" ] && return 0
|
|
return 1
|
|
}
|
|
is_disabled_interface() { [ "$(uci_get 'network' "$1" 'disabled')" = '1' ]; }
|
|
is_host() { echo "$1" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9_-]{0,61}[a-zA-Z0-9]$|^[a-zA-Z0-9]$'; }
|
|
is_hostname() { echo "$1" | grep -qE '^([a-zA-Z0-9]([a-zA-Z0-9_-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'; }
|
|
is_domain() { ! is_ipv4 "$1" && ! is_mac_address_bad_notation "$1" && { is_host "$1" || is_hostname "$1"; }; }
|
|
is_dslite() { local p; network_get_protocol p "$1"; [ "${p:0:6}" = "dslite" ]; }
|
|
is_family_mismatch() { ( is_ipv4 "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_ipv4 "${2//!}" ); }
|
|
is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
|
|
is_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 ''|*[!0-9]*) return 1;; esac; }
|
|
is_ipv4() { echo "$1" | grep -qE '^((25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(/(3[0-2]|[12]?[0-9]))?$'; }
|
|
is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ':'; }
|
|
is_ipv6_global_scope() { [ "${1:0:4}" = '2001' ]; }
|
|
is_ipv6_local_scope() { is_ipv6_local_link "$1" || is_ipv6_local_unique "$1"; }
|
|
is_ipv6_local_link() { [ "${1:0:4}" = 'fe80' ]; }
|
|
is_ipv6_local_unique() { [ "${1:0:2}" = 'fc' ] || [ "${1:0:2}" = 'fd' ]; }
|
|
is_list() { str_contains "$1" ',' || str_contains "$1" ' '; }
|
|
is_lan() { local d; network_get_device d "$1"; str_contains "$lan_device" "$d"; }
|
|
is_l2tp() { local p; network_get_protocol p "$1"; [ "${p:0:4}" = "l2tp" ]; }
|
|
is_mac_address() { echo "$1" | grep -qE '^([0-9A-Fa-f]{2}:){5}([0-9A-Fa-f]{2})$'; }
|
|
is_mac_address_bad_notation() { echo "$1" | grep -qE '^([0-9A-Fa-f]{2}-){5}([0-9A-Fa-f]{2})$'; }
|
|
is_negated() { [ "${1:0:1}" = '!' ]; }
|
|
is_netifd_table() { grep -q "ip.table.*$1" /etc/config/network; }
|
|
is_netifd_interface() { local iface="$1"; [ -n "$(uci_get 'network' "$iface" 'ip4table')" ]; }
|
|
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}" = "@" ] && [ -L "/sys/class/net/${1#@}" ]; }
|
|
is_present() { command -v "$1" >/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 -qi "^${1:--}" /etc/protocols;}
|
|
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" || is_disabled_interface "$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_netbird() { local d; network_get_device d "$1"; [ "${d:0:2}" = "wt" ]; }
|
|
is_tailscale() { local d; network_get_device d "$1"; [ "${d:0:9}" = "tailscale" ]; }
|
|
is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; }
|
|
is_tor_running() { ! is_ignored_interface 'tor' && [ -s "$torConfigFile" ] && str_contains "$(ubus call service list "{ 'name': 'tor' }" | jsonfilter -e '@.tor.instances.*.running')" 'true' && return 0 || return 1; }
|
|
is_tunnel() { is_dslite "$1" || is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_softether "$1" || is_netbird "$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_wg_server() { local p lp; network_get_protocol p "$1"; uci_get_listen_port lp "$1"; [ -n "$lp" ] && [ "${p:0:9}" = "wireguard" ]; }
|
|
is_xray() { [ -n "$(get_xray_traffic_port "$1")" ]; }
|
|
dnsmasq_kill() { pidof dnsmasq >/dev/null && kill -HUP $(pidof dnsmasq); }
|
|
dnsmasq_restart() { output 3 'Restarting dnsmasq '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; }
|
|
# shellcheck disable=SC2155
|
|
get_ss_traffic_ports() { local i="$(jsonfilter -i "$ssConfigFile" -q -e "@.inbounds[*].port")"; echo "${i:-443}"; }
|
|
# 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_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
|
|
ipv4_leases_to_nftset(){ [ -s '/tmp/dhcp.leases' ] && awk -v arg="$1" 'BEGIN{fs=""};$0~arg{printf fs$3;fs=","}' /tmp/dhcp.leases;}
|
|
# shellcheck disable=SC2016
|
|
ipv6_leases_to_nftset(){ [ -s '/tmp/hosts/odhcpd' ] && awk -v arg="$1" 'BEGIN{fs=""};$0~arg{printf fs$1;fs=","}' /tmp/hosts/odhcpd;}
|
|
# shellcheck disable=SC3037
|
|
ports_to_nftset() { echo -en "$1"; }
|
|
get_mark_nft_chains() { "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_" | awk '{ print $2 }'; }
|
|
get_nft_sets() { "$nft" list table inet "$nftTable" 2>/dev/null | grep 'set' | grep "${nftPrefix}_" | awk '{ print $2 }'; }
|
|
__ubus_get() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "$1"; }
|
|
ubus_get_status() { __ubus_get "@.${packageName}.instances.main.data.status.${1}"; }
|
|
ubus_get_interface() { __ubus_get "@.${packageName}.instances.main.data.gateways[@.name='${1}']${2:+.$2}"; }
|
|
ubus_get_gateways() { __ubus_get "@.${packageName}.instances.main.data.gateways"; }
|
|
uci_add_list_if_new() {
|
|
local PACKAGE="$1"
|
|
local CONFIG="$2"
|
|
local OPTION="$3"
|
|
local VALUE="$4"
|
|
local i
|
|
[ -n "$PACKAGE" ] && [ -n "$CONFIG" ] && [ -n "$OPTION" ] && [ -n "$VALUE" ] || return 1
|
|
for i in $(uci_get "$PACKAGE" "$CONFIG" "$OPTION"); do
|
|
[ "$i" = "$VALUE" ] && return 0
|
|
done
|
|
uci_add_list "$PACKAGE" "$CONFIG" "$OPTION" "$VALUE"
|
|
}
|
|
uci_changes() {
|
|
local PACKAGE="$1"
|
|
local CONFIG="$2"
|
|
local OPTION="$3"
|
|
[ -s "${UCI_CONFIG_DIR:-/etc/config/}${PACKAGE}" ] && \
|
|
[ -n "$(/sbin/uci ${UCI_CONFIG_DIR:+-c $UCI_CONFIG_DIR} changes "$PACKAGE${CONFIG:+.$CONFIG}${OPTION:+.$OPTION}")" ]
|
|
}
|
|
uci_get_listen_port() {
|
|
local __tmp
|
|
__tmp="$(uci_get 'network' "$2" 'listen_port')"
|
|
[ -z "$__tmp" ] && unset "$1" && return 1
|
|
eval "$1=$__tmp"
|
|
}
|
|
sanitize_list() { sed 's/#.*//;s/^[ \t]*//;s/[ \t]*$//;s/[ \t][ \t]*/ /g;/^[ \t]*$/d' "$1" | sort -u | tr '\n' ' '; }
|
|
|
|
# luci app specific
|
|
is_enabled() { uci_get "$1" 'config' 'enabled'; }
|
|
is_running_nft_file() { [ -s "$nftPermFile" ]; }
|
|
is_running_nft() { "$nft" list table inet fw4 | grep chain | grep -q pbr_mark_ >/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_dnsmasq_nftset() {
|
|
[ -z "$dnsmasq_features" ] && dnsmasq_features="$(dnsmasq --version | grep -m1 'Compile time options:' | cut -d: -f2) "
|
|
[ "${dnsmasq_features#* nftset }" != "$dnsmasq_features" ]
|
|
}
|
|
print_json_bool() { json_init; json_add_boolean "$1" "$2"; json_dump; json_cleanup; }
|
|
print_json_string() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
|
|
try() {
|
|
if ! "$@" >/dev/null 2>&1; then
|
|
json add error 'errorTryFailed' "$*"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
get_url() {
|
|
printf "https://docs.openwrt.melmac.ca/%s/%s/%s" "$packageName" "${PKG_VERSION%%-*}" "$1"
|
|
}
|
|
|
|
get_text() {
|
|
local r="$1"; shift;
|
|
case "$r" in
|
|
errorConfigValidation) printf "Config (%s) validation failure" "$packageConfigFile";;
|
|
errorNoNft) printf "Resolver set support (%s) requires nftables, but nft binary cannot be found" "$resolver_set";;
|
|
errorResolverNotSupported) printf "Resolver set (%s) is not supported on this system" "$resolver_set";;
|
|
errorServiceDisabled) printf "The %s service is currently disabled" "$packageName";;
|
|
errorNoWanGateway) printf "The %s service failed to discover WAN gateway" "$serviceName";;
|
|
errorNoUplinkInterface) printf "The %s interface not found, you need to set the 'pbr.config.uplink_interface' option" "$1";;
|
|
errorNoUplinkInterfaceHint) printf "Refer to %s" "$1";;
|
|
errorNftsetNameTooLong) printf "The nft set name '%s' is longer than allowed 255 characters" "$1";;
|
|
errorUnexpectedExit) printf "Unexpected exit or service termination: '%s'" "$1";;
|
|
errorPolicyNoSrcDest) printf "Policy '%s' has no source/destination parameters" "$1";;
|
|
errorPolicyNoInterface) printf "Policy '%s' has no assigned interface" "$1";;
|
|
errorPolicyNoDns) printf "Policy '%s' has no assigned DNS" "$1";;
|
|
errorPolicyProcessNoInterfaceDns) printf "Interface '%s' has no assigned DNS" "$1";;
|
|
errorPolicyUnknownInterface) printf "Policy '%s' has an unknown interface" "$1";;
|
|
errorPolicyProcessCMD) printf "'%s'" "$1";;
|
|
errorFailedSetup) printf "Failed to set up '%s'" "$1";;
|
|
errorFailedReload) printf "Failed to reload '%s'" "$1";;
|
|
errorUserFileNotFound) printf "Custom user file '%s' not found or empty" "$1";;
|
|
errorUserFileSyntax) printf "Syntax error in custom user file '%s'" "$1";;
|
|
errorUserFileRunning) printf "Error running custom user file '%s'" "$1";;
|
|
errorUserFileNoCurl) printf "Use of 'curl' is detected in custom user file '%s', but 'curl' isn't installed" "$1";;
|
|
errorNoGateways) printf "Failed to set up any gateway";;
|
|
errorResolver) printf "Resolver '%s'" "$1";;
|
|
errorPolicyProcessNoIpv6) printf "Skipping IPv6 policy '%s' as IPv6 support is disabled" "$1";;
|
|
errorPolicyProcessUnknownFwmark) printf "Unknown packet mark for interface '%s'" "$1";;
|
|
errorPolicyProcessMismatchFamily) printf "Mismatched IP family between in policy '%s'" "$1";;
|
|
errorPolicyProcessUnknownProtocol) printf "Unknown protocol in policy '%s'" "$1";;
|
|
errorPolicyProcessInsertionFailed) printf "Insertion failed for both IPv4 and IPv6 for policy '%s'" "$1";;
|
|
errorPolicyProcessInsertionFailedIpv4) printf "Insertion failed for IPv4 for policy '%s'" "$1";;
|
|
errorPolicyProcessUnknownEntry) printf "Unknown entry in policy '%s'" "$1";;
|
|
errorInterfaceRoutingEmptyValues) printf "Received empty tid/mark or interface name when setting up routing";;
|
|
errorFailedToResolve) printf "Failed to resolve '%s'" "$1";;
|
|
errorInvalidOVPNConfig) printf "Invalid OpenVPN config for '%s' interface" "$1";;
|
|
errorNftFileInstall) printf "Failed to install fw4 nft file '%s'" "$1";;
|
|
errorTryFailed) printf "Command failed: %s" "$1";;
|
|
errorDownloadUrlNoHttps) printf "Failed to download '%s', HTTPS is not supported" "$1";;
|
|
errorDownloadUrl) printf "Failed to download '%s'" "$1";;
|
|
errorNoDownloadWithSecureReload) printf "Policy '%s' refers to URL which can't be downloaded in 'secure_reload' mode" "$1";;
|
|
errorFileSchemaRequiresCurl) printf "The file:// schema requires curl, but it's not detected on this system";;
|
|
errorIncompatibleUserFile) printf "Incompatible custom user file detected '%s'" "$1";;
|
|
errorDefaultFw4TableMissing) printf "Default fw4 table '%s' is missing" "$1";;
|
|
errorDefaultFw4ChainMissing) printf "Default fw4 chain '%s' is missing" "$1";;
|
|
errorRequiredBinaryMissing) printf "Required binary '%s' is missing" "$1";;
|
|
errorInterfaceRoutingUnknownDevType) printf "Unknown IPv6 Link type for device '%s'" "$1";;
|
|
errorUplinkDown) printf "Uplink/WAN interface is still down, increase value of 'procd_boot_trigger_delay' option";;
|
|
errorMktempFileCreate) printf "Failed to create temporary file with mktemp mask: '%s'" "$1";;
|
|
errorSummary) printf "Errors encountered, please check %s" "$1";;
|
|
errorNetifdNftFileInstall) printf "Netifd setup: failed to install fw4 netifd nft file '%s'" "$1";;
|
|
errorNetifdNftFileRemove) printf "Netifd setup: failed to remove fw4 netifd nft file '%s'" "$1";;
|
|
errorNetifdMissingOption) printf "Netifd setup: required option '%s' is missing" "$1";;
|
|
errorNetifdInvalidGateway4) printf "Netifd setup: invalid value of netifd_interface_default option '%s'" "$1";;
|
|
errorNetifdInvalidGateway6) printf "Netifd setup: invalid value of netifd_interface_default6 option '%s'" "$1";;
|
|
warningInvalidOVPNConfig) printf "Invalid OpenVPN config for '%s' interface" "$1";;
|
|
warningResolverNotSupported) printf "Resolver set (%s) is not supported on this system" "$resolver_set";;
|
|
warningPolicyProcessCMD) printf "'%s'" "$1";;
|
|
warningTorUnsetParams) printf "Please unset 'src_addr', 'src_port' and 'dest_port' for policy '%s'" "$1";;
|
|
warningTorUnsetProto) printf "Please unset 'proto' or set 'proto' to 'all' for policy '%s'" "$1";;
|
|
warningTorUnsetChainNft) printf "Please unset 'chain' or set 'chain' to 'prerouting' for policy '%s'" "$1";;
|
|
warningOutdatedWebUIApp) printf "The WebUI application is outdated (version %s), please update it" "$1";;
|
|
warningDnsmasqInstanceNoConfdir) printf "Dnsmasq instance '%s' targeted in settings, but it doesn't have its own confdir" "$1";;
|
|
warningDhcpLanForce) printf "Please set 'dhcp.%s.force=1' to speed up service start-up" "$1";;
|
|
warningSummary) printf "Warnings encountered, please check %s" "$(get_url '#WarningMessagesDetails')";;
|
|
warningIncompatibleDHCPOption6) printf "Incompatible DHCP Option 6 for interface '%s'" "$1";;
|
|
warningNetifdMissingInterfaceLocal) printf "Netifd setup: option netifd_interface_local is missing, assuming '%s'" "$1";;
|
|
*) printf "Unknown error/warning '%s'" "$1";;
|
|
esac
|
|
}
|
|
|
|
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
|
|
dl_temp_file="$(mktemp -q -t "${packageName}_tmp.XXXXXXXX")"
|
|
if [ -z "$dl_temp_file" ] || [ ! -e "$dl_temp_file" ]; then
|
|
json add error 'errorMktempFileCreate' "${packageName}_tmp.XXXXXXXX"
|
|
return 1
|
|
fi
|
|
if is_url_file "$url" && ! is_present 'curl'; then
|
|
json add error 'errorFileSchemaRequiresCurl' "$url"
|
|
elif is_url_https "$url" && [ -z "$dl_https_supported" ]; then
|
|
json add error 'errorDownloadUrlNoHttps' "$url"
|
|
elif $dl_command "$url" "$dl_flag" "$dl_temp_file" 2>/dev/null; then
|
|
sanitize_list "$dl_temp_file"
|
|
else
|
|
json add error 'errorDownloadUrl' "$url"
|
|
fi
|
|
rm -f "$dl_temp_file"
|
|
}
|
|
|
|
load_package_config() {
|
|
local param="$1"
|
|
config_load "$packageName"
|
|
config_get config_compat 'config' 'config_compat'
|
|
config_get config_version 'config' 'config_version'
|
|
config_get_bool enabled 'config' 'enabled' '0'
|
|
config_get fw_mask 'config' 'fw_mask' '00ff0000'
|
|
config_get icmp_interface 'config' 'icmp_interface'
|
|
config_get ignored_interface 'config' 'ignored_interface'
|
|
config_get_bool ipv6_enabled 'config' 'ipv6_enabled' '0'
|
|
config_get lan_device 'config' 'lan_device' 'br-lan'
|
|
config_get_bool nft_rule_counter 'config' 'nft_rule_counter' '0'
|
|
config_get_bool nft_set_auto_merge 'config' 'nft_set_auto_merge' '1'
|
|
config_get_bool nft_set_counter 'config' 'nft_set_counter' '0'
|
|
config_get_bool nft_set_flags_interval 'config' 'nft_set_flags_interval' '1'
|
|
config_get_bool nft_set_flags_timeout 'config' 'nft_set_flags_timeout' '0'
|
|
config_get 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_bool nft_user_set_counter 'config' 'nft_user_set_counter' '0'
|
|
config_get procd_boot_trigger_delay 'config' 'procd_boot_trigger_delay' '5000'
|
|
config_get procd_reload_delay 'config' 'procd_reload_delay' '0'
|
|
config_get resolver_instance 'config' 'resolver_instance' '*'
|
|
config_get resolver_set 'config' 'resolver_set'
|
|
config_get_bool strict_enforcement 'config' 'strict_enforcement' '1'
|
|
config_get supported_interface 'config' 'supported_interface'
|
|
config_get uplink_interface 'config' 'uplink_interface' 'wan'
|
|
config_get uplink_interface6 'config' 'uplink_interface6' 'wan6'
|
|
config_get uplink_ip_rules_priority 'config' 'uplink_ip_rules_priority' '30000'
|
|
config_get uplink_mark 'config' 'uplink_mark' '00010000'
|
|
config_get verbosity 'config' 'verbosity' '2'
|
|
config_get_bool netifd_enabled 'config' 'netifd_enabled'
|
|
config_get_bool netifd_strict_enforcement 'config' 'netifd_strict_enforcement'
|
|
config_get netifd_interface_default 'config' 'netifd_interface_default'
|
|
config_get netifd_interface_default6 'config' 'netifd_interface_default6'
|
|
config_get netifd_interface_local 'config' 'netifd_interface_local'
|
|
|
|
fw_mask="0x${fw_mask}"
|
|
uplink_mark="0x${uplink_mark}"
|
|
|
|
[ "$resolver_set" = 'none' ] && unset resolver_set
|
|
[ "$enabled" = '1' ] || unset enabled
|
|
[ "$ipv6_enabled" = '1' ] || unset ipv6_enabled
|
|
[ "$strict_enforcement" = '1' ] || unset strict_enforcement
|
|
|
|
fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")"
|
|
fw_maskXor="${fw_maskXor:-0xff00ffff}"
|
|
|
|
is_integer "$procd_boot_trigger_delay" || procd_boot_trigger_delay='5000'
|
|
[ "$procd_boot_trigger_delay" -lt '1000' ] && procd_boot_trigger_delay='1000'
|
|
|
|
local nft_set_flags
|
|
case "${nft_set_flags_interval}:${nft_set_flags_timeout}" in
|
|
1:1) nft_set_flags="flags interval, timeout${nft_set_timeout:+; timeout $nft_set_timeout}";;
|
|
1:0) nft_set_flags='flags interval';;
|
|
0:1) nft_set_flags="flags timeout${nft_set_timeout:+; timeout $nft_set_timeout}";;
|
|
0:0) nft_set_flags='';;
|
|
esac
|
|
|
|
[ "$nft_user_set_counter" = '1' ] || unset nft_user_set_counter
|
|
[ "$nft_rule_counter" = '1' ] || unset nft_rule_counter
|
|
[ "$nft_set_auto_merge" = '1' ] || unset nft_set_auto_merge
|
|
[ "$nft_set_counter" = '1' ] || unset nft_set_counter
|
|
[ "$nft_set_flags_interval" = '1' ] || unset nft_set_flags_interval
|
|
[ "$nft_set_flags_timeout" = '1' ] || unset nft_set_flags_timeout
|
|
[ -n "${nft_set_flags_timeout}${nft_set_timeout}" ] || unset nft_set_gc_interval
|
|
|
|
nftRuleParams="${nft_rule_counter:+counter}"
|
|
|
|
nftSetParams=" \
|
|
${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";} \
|
|
"
|
|
|
|
if [ -x "$agh" ] && [ ! -s "$aghConfigFile" ]; then
|
|
[ -s "${agh%/*}/AdGuardHome.yaml" ] && aghConfigFile="${agh%/*}/AdGuardHome.yaml"
|
|
fi
|
|
unset loadEnvironmentFlag
|
|
loadPackageConfigFlag='true'
|
|
}
|
|
|
|
# shellcheck disable=SC2317
|
|
load_environment() {
|
|
_system_health_check() {
|
|
# shellcheck disable=SC2329
|
|
_check_lan_compatibility() {
|
|
is_lan "$1" || return 0
|
|
local force ipaddr dhcp_option i
|
|
config_get force "$1" force
|
|
config_get ipaddr "$1" ipaddr
|
|
if [ "$force" = '0' ]; then
|
|
json add warning 'warningDhcpLanForce' "$1"
|
|
fi
|
|
[ -n "$resolver_set" ] || return 0
|
|
for i in $(uci_get 'dhcp' "$1" 'dhcp_option'); do
|
|
local option="${i%%,*}" value="${i#*,}"
|
|
if [ "$option" = '6' ] && [ "$value" != "${ipaddr%%/*}" ]; then
|
|
json add warning 'warningIncompatibleDHCPOption6' "${1}: ${value}"
|
|
fi
|
|
done
|
|
}
|
|
local i _ret=0
|
|
if ! check_nft; then
|
|
json add error 'errorNoNft'
|
|
_ret='1'
|
|
fi
|
|
if [ "$(uci_get 'firewall' 'defaults' 'auto_includes')" = '0' ]; then
|
|
uci_remove 'firewall' 'defaults' 'auto_includes'
|
|
uci_commit 'firewall'
|
|
fi
|
|
if [ "$(readlink /sbin/ip)" != "$ip_full" ]; then
|
|
json add error 'errorRequiredBinaryMissing' 'ip-full'
|
|
_ret='1'
|
|
fi
|
|
if ! nft_check_element 'table' 'fw4'; then
|
|
json add error 'errorDefaultFw4TableMissing' 'fw4'
|
|
_ret='1'
|
|
fi
|
|
if is_config_enabled 'dns_policy' || is_tor_running; then
|
|
if ! nft_check_element 'chain' 'dstnat'; then
|
|
json add error 'errorDefaultFw4ChainMissing' 'dstnat'
|
|
_ret='1'
|
|
fi
|
|
fi
|
|
for i in $chainsList; do
|
|
if ! nft_check_element 'chain' "mangle_${i}"; then
|
|
json add error 'errorDefaultFw4ChainMissing' "mangle_${i}"
|
|
_ret='1'
|
|
fi
|
|
done
|
|
config_load 'network'
|
|
config_foreach _check_lan_compatibility 'interface'
|
|
return "$_ret"
|
|
}
|
|
local param="$1" validation_result="$2"
|
|
[ -z "$loadEnvironmentFlag" ] || return 0
|
|
case "$param" in
|
|
on_boot|on_start)
|
|
output 1 "Loading environment ($param) "
|
|
[ -n "$loadPackageConfigFlag" ] || load_package_config "$param"
|
|
if [ -z "$enabled" ]; then
|
|
output 1 "$_FAIL_\n"
|
|
json add error 'errorServiceDisabled'
|
|
output_error "$(get_text 'errorServiceDisabled')"
|
|
output "Run the following commands before starting service again:\n"
|
|
output "uci set ${packageName}.config.enabled='1'; uci commit $packageName;\n"
|
|
return 1
|
|
fi
|
|
if [ -n "$validation_result" ] && [ "$validation_result" != '0' ]; then
|
|
output 1 "$_FAIL_\n"
|
|
json add error 'errorConfigValidation'
|
|
output_error "$(get_text 'errorConfigValidation')"
|
|
output "Please check if the '$packageConfigFile' contains correct values for config options.\n"
|
|
return 1
|
|
fi
|
|
_system_health_check || { output 1 "$_FAIL_\n"; return 1; }
|
|
resolver 'check_support'
|
|
load_network "$param"
|
|
output 1 "$_OK_\n"
|
|
;;
|
|
on_triggers)
|
|
[ -n "$loadPackageConfigFlag" ] || load_package_config "$param"
|
|
# load_network "$param"
|
|
;;
|
|
on_interface_reload|on_reload|on_stop|*)
|
|
output 1 "Loading environment ($param) "
|
|
[ -n "$loadPackageConfigFlag" ] || load_package_config "$param"
|
|
load_network "$param"
|
|
resolver 'check_support'
|
|
output 1 "$_OK_\n"
|
|
;;
|
|
esac
|
|
loadEnvironmentFlag='true'
|
|
}
|
|
|
|
# shellcheck disable=SC2317
|
|
load_network() {
|
|
# shellcheck disable=SC2329
|
|
_build_ifaces_supported() { is_supported_interface "$1" && ! str_contains "$ifacesSupported" "$1" && ifacesSupported="${ifacesSupported}${1} "; }
|
|
# shellcheck disable=SC2329
|
|
_find_firewall_wan_zone() { [ "$(uci_get 'firewall' "$1" 'name')" = "wan" ] && firewallWanZone="$1"; }
|
|
local i param="$1"
|
|
local dev4 dev6
|
|
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="$uplink_interface"
|
|
network_get_device dev4 "$wanIface4"
|
|
[ -z "$dev4" ] && network_get_physdev dev4 "$wanIface4"
|
|
[ -z "$wanGW4" ] && pbr_get_gateway4 wanGW4 "$wanIface4" "$dev4"
|
|
if [ -n "$ipv6_enabled" ]; then
|
|
wanIface6="$uplink_interface6"
|
|
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 uplink${wanIface6:+ IPv4} interface (${param}): $wanIface4 $__OK__\n"
|
|
[ -n "$wanGW4" ] && output 2 "Found uplink${wanIface6:+ IPv4} gateway (${param}): $wanGW4 $__OK__\n"
|
|
[ -n "$wanIface6" ] && output 2 "Using uplink IPv6 interface (${param}): $wanIface6 $__OK__\n"
|
|
[ -n "$wanGW6" ] && output 2 "Found uplink IPv6 gateway (${param}): $wanGW6 $__OK__\n"
|
|
;;
|
|
esac
|
|
wanGW="${wanGW4:-$wanGW6}"
|
|
}
|
|
|
|
is_wan_up() {
|
|
local param="$1"
|
|
if [ -z "$(uci_get network "$uplink_interface")" ]; then
|
|
json add error 'errorNoUplinkInterface' "$uplink_interface"
|
|
json add error 'errorNoUplinkInterfaceHint' "$(get_url '#uplink_interface')"
|
|
return 1
|
|
fi
|
|
network_flush_cache
|
|
load_network "$param"
|
|
if [ -n "$wanGW" ]; then
|
|
return 0
|
|
else
|
|
json add error 'errorNoWanGateway'
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
nft() { [ -n "$*" ] && nft_file 'add_command' "$@"; }
|
|
nft4() { nft "$@"; }
|
|
nft6() { [ -n "$ipv6_enabled" ] || return 0; nft "$@"; }
|
|
nft_call() { "$nft" "$@" >/dev/null 2>&1; }
|
|
nft_check_element() {
|
|
[ -n "$nft_fw4_dump" ] || nft_fw4_dump="$("$nft" list table inet fw4 2>&1)"
|
|
case "${1}:${2}" in
|
|
table:fw4)
|
|
[ -n "$nft_fw4_dump" ]
|
|
;;
|
|
chain:*|*)
|
|
echo "$nft_fw4_dump" | grep "$1" | grep -q "$2"
|
|
;;
|
|
esac
|
|
}
|
|
nft_file() {
|
|
local i chain
|
|
case "$1" in
|
|
add|add_command)
|
|
shift
|
|
echo "$*" >> "$nftTempFile"
|
|
;;
|
|
create)
|
|
rm -f "$nftTempFile" "$nftPermFile"
|
|
for i in "$nftTempFile" "$nftPermFile"; do
|
|
mkdir -p "${i%/*}"
|
|
done
|
|
{ echo '#!/usr/sbin/nft -f'; echo ''; } > "$nftTempFile"
|
|
# Insert PBR guards at the top of main caller chains so first PBR match wins, while preserving foreign marks.
|
|
for chain in $chainsList; do
|
|
echo "add rule inet $nftTable ${nftPrefix}_${chain} ${nftRuleParams:+$nftRuleParams }meta mark & $fw_mask != 0 return" >> "$nftTempFile"
|
|
done
|
|
;;
|
|
delete|rm|remove)
|
|
rm -f "$nftTempFile" "$nftPermFile"
|
|
;;
|
|
enabled)
|
|
return 0
|
|
;;
|
|
exists)
|
|
[ -s "$nftPermFile" ] && return 0 || return 1
|
|
;;
|
|
install)
|
|
[ -s "$nftTempFile" ] || return 1
|
|
output "Installing fw4 nft file "
|
|
if nft_call -c -f "$nftTempFile" && \
|
|
cp -f "$nftTempFile" "$nftPermFile"; then
|
|
output_okn
|
|
else
|
|
json add error 'errorNftFileInstall' "$nftTempFile"
|
|
output_failn
|
|
fi
|
|
;;
|
|
netifd_exists)
|
|
[ -s "$nftNetifdPermFile" ] && return 0 || return 1
|
|
;;
|
|
netifd_create)
|
|
rm -f "$nftTempFile" "$nftNetifdPermFile"
|
|
for i in "$nftTempFile" "$nftNetifdPermFile"; do
|
|
mkdir -p "${i%/*}"
|
|
done
|
|
{ echo '#!/usr/sbin/nft -f'; echo ''; } > "$nftTempFile"
|
|
;;
|
|
netifd_delete|netifd_rm)
|
|
rm -f "$nftNetifdPermFile"
|
|
;;
|
|
netifd_install)
|
|
[ -s "$nftTempFile" ] || return 1
|
|
output "Installing fw4 netifd nft file "
|
|
if nft_call -c -f "$nftTempFile" && \
|
|
cp -f "$nftTempFile" "$nftNetifdPermFile"; then
|
|
output_okbn
|
|
else
|
|
json add error 'errorNetifdNftFileInstall' "$nftTempFile"
|
|
output_failn
|
|
fi
|
|
;;
|
|
netifd_remove)
|
|
output "Removing fw4 netifd nft file "
|
|
if rm -f "$nftNetifdPermFile"; then
|
|
output_okbn
|
|
else
|
|
json add error 'errorNetifdNftFileRemove' "$nftTempFile"
|
|
output_failn
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
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}"
|
|
|
|
if [ "${#nftset4}" -gt '255' ]; then
|
|
json add error '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 "$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
|
|
json add error 'errorFailedToResolve' "$param"
|
|
else
|
|
[ -n "$param4" ] && nft4 add element inet "$nftTable" "$nftset4" "{ $param4 }" && ipv4_error=0
|
|
[ -n "$param6" ] && nft6 add element inet "$nftTable" "$nftset6" "{ $param6 }" && ipv6_error=0
|
|
fi
|
|
fi
|
|
;;
|
|
add_dnsmasq_element)
|
|
[ -n "$ipv6_enabled" ] || unset nftset6
|
|
grep -qxF "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" "$packageDnsmasqFile" && return 0
|
|
echo "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" >> "$packageDnsmasqFile" && ipv4_error=0
|
|
;;
|
|
create)
|
|
case "$type" in
|
|
ip|net)
|
|
nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
|
|
nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
|
|
;;
|
|
mac)
|
|
nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
|
|
nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
|
|
;;
|
|
esac
|
|
;;
|
|
create_dnsmasq_set)
|
|
nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
|
|
nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
|
|
;;
|
|
create_user_set)
|
|
case "$type" in
|
|
ip|net)
|
|
nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; $nftSetParams comment \"$comment\";}" && ipv4_error=0
|
|
nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; $nftSetParams comment \"$comment\";}" && ipv6_error=0
|
|
case "$target" in
|
|
dst)
|
|
nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv4Flag}" daddr "@${nftset4}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
|
|
nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv6Flag}" daddr "@${nftset6}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
|
|
;;
|
|
src)
|
|
nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv4Flag}" saddr "@${nftset4}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
|
|
nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" "${nftIPv6Flag}" saddr "@${nftset6}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
|
|
;;
|
|
esac
|
|
;;
|
|
mac)
|
|
nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; $nftSetParams comment \"$comment\"; }" && ipv4_error=0
|
|
nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; $nftSetParams comment \"$comment\"; }" && ipv6_error=0
|
|
nft4 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0
|
|
nft6 add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" "${nftRuleParams}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0
|
|
;;
|
|
esac
|
|
;;
|
|
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_table() {
|
|
for prio in $(ip -4 rule show \
|
|
| awk -v mask="$fw_mask" '/fwmark/ && $0 ~ mask && /lookup main/ && /suppress_prefixlength 0/ {sub(":", "", $1); print $1}'
|
|
); do
|
|
ip -4 rule del priority "$prio"
|
|
done
|
|
}
|
|
|
|
cleanup_main_chains() {
|
|
local i j
|
|
for i in $chainsList dstnat; do
|
|
i="$(str_to_lower "$i")"
|
|
nft_call flush chain inet "$nftTable" "${nftPrefix}_${i}"
|
|
done
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
json() {
|
|
local status message stats i
|
|
local action="$1" param="$2" value="$3"; shift 3; local info="$*";
|
|
local _current_namespace="$_JSON_PREFIX"
|
|
json_set_namespace "${packageName//-/_}_"
|
|
[ "$param" = 'error' ] && param='errors'
|
|
[ "$param" = 'warning' ] && param='warnings'
|
|
{ json_load_file "$runningStatusFile" || json_init; } >/dev/null 2>&1
|
|
case "$action" in
|
|
'get')
|
|
json_select "$param" >/dev/null 2>&1 || return
|
|
if [ -n "$value" ]; then
|
|
{
|
|
if json_select "$value"; then
|
|
json_get_var 'i' "${info:-code}"
|
|
json_select ..
|
|
fi
|
|
} >/dev/null 2>&1
|
|
else
|
|
json_get_keys i
|
|
fi
|
|
printf "%b" "$i"
|
|
json_select ..
|
|
json_set_namespace "$_current_namespace"
|
|
return
|
|
;;
|
|
'add')
|
|
{ json_select "$param" || json_add_array "$param"; } >/dev/null 2>&1
|
|
json_add_object ""
|
|
json_add_string 'code' "$value"
|
|
json_add_string 'info' "$info"
|
|
json_close_object
|
|
json_select ..
|
|
;;
|
|
'init')
|
|
mkdir -p "${runningStatusFile%/*}"
|
|
json_init
|
|
json_add_array 'errors'
|
|
json_close_array
|
|
json_add_array 'warnings'
|
|
json_close_array
|
|
;;
|
|
esac
|
|
json_dump > "$runningStatusFile"
|
|
sync
|
|
json_set_namespace "$_current_namespace"
|
|
}
|
|
|
|
resolver() {
|
|
_dnsmasq_instance_get_confdir() {
|
|
local cfg_file
|
|
[ -z "$dnsmasq_ubus" ] && dnsmasq_ubus="$(ubus call service list '{"name":"dnsmasq"}')"
|
|
cfg_file="$(echo "$dnsmasq_ubus" | jsonfilter -e "@.dnsmasq.instances.${1}.command" \
|
|
| awk '{gsub(/\\\//,"/");gsub(/[][",]/,"");for(i=1;i<=NF;i++)if($i=="-C"){print $(i+1);exit}}')"
|
|
awk -F= '/^conf-dir=/{print $2; exit}' "$cfg_file"
|
|
}
|
|
_dnsmasq_instance_config() {
|
|
local cfg="$1" param="$2" confdir
|
|
case "$param" in
|
|
cleanup)
|
|
# clean up all dnsmasq configs
|
|
confdir="$(_dnsmasq_instance_get_confdir "$cfg")"
|
|
[ -n "$confdir" ] && rm -f "${confdir}/${packageName}"
|
|
uci_remove_list 'dhcp' "$cfg" 'addnmount' "$packageDnsmasqFile"
|
|
;;
|
|
setup)
|
|
# add dnsmasq conf addnmounts to point to pbr file
|
|
uci_add_list_if_new 'dhcp' "$cfg" 'addnmount' "$packageDnsmasqFile"
|
|
# add softlink to pbr file
|
|
confdir="$(_dnsmasq_instance_get_confdir "$cfg")"
|
|
[ -n "$confdir" ] || return 1
|
|
ln -sf "$packageDnsmasqFile" "${confdir}/${packageName}"
|
|
chmod 660 "${confdir}/${packageName}"
|
|
chown -h root:dnsmasq "${confdir}/${packageName}" >/dev/null 2>/dev/null
|
|
;;
|
|
esac
|
|
}
|
|
local agh_version
|
|
local param="$1" iface="$2" target="$3" type="$4" uid="$5" name="$6" value="$7"
|
|
shift
|
|
|
|
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;;
|
|
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 "$resolverSetSupported" ] || return 1
|
|
local d
|
|
for d in $value; do
|
|
nftset 'add_dnsmasq_element' "$iface" "$target" "$type" "$uid" "$name" "$d"
|
|
done
|
|
;;
|
|
create_resolver_set)
|
|
[ -n "$resolverSetSupported" ] || return 1
|
|
nftset 'create_dnsmasq_set' "$iface" "$target" "$type" "$uid" "$name" "$value"
|
|
;;
|
|
check_support)
|
|
if check_dnsmasq_nftset; then
|
|
resolverSetSupported='true'
|
|
return 0
|
|
else
|
|
json add warning 'warningResolverNotSupported'
|
|
return 1
|
|
fi
|
|
;;
|
|
cleanup)
|
|
[ -n "$resolverSetSupported" ] || return 1
|
|
rm -f "$packageDnsmasqFile"
|
|
config_load 'dhcp'
|
|
config_foreach _dnsmasq_instance_config 'dnsmasq' 'cleanup'
|
|
;;
|
|
configure)
|
|
[ -n "$resolverSetSupported" ] || return 1
|
|
rm -f "$packageDnsmasqFile"
|
|
touch "$packageDnsmasqFile"
|
|
config_load 'dhcp'
|
|
if [ "$resolver_instance" = "*" ]; then
|
|
config_foreach _dnsmasq_instance_config 'dnsmasq' 'setup'
|
|
else
|
|
config_foreach _dnsmasq_instance_config 'dnsmasq' 'cleanup'
|
|
for i in $resolver_instance; do
|
|
_dnsmasq_instance_config "@dnsmasq[$i]" \
|
|
|| _dnsmasq_instance_config "$i"
|
|
done
|
|
fi
|
|
;;
|
|
kill)
|
|
[ -n "$resolverSetSupported" ] && killall -q -s HUP dnsmasq;;
|
|
reload)
|
|
[ -z "$resolverSetSupported" ] && 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 "$resolverSetSupported" ] && 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 "$resolverSetSupported" ] && return 1
|
|
uci_changes 'dhcp' && uci_commit 'dhcp'
|
|
local resolverNewHash
|
|
if [ -s "$packageDnsmasqFile" ]; then
|
|
resolverNewHash="$(md5sum "$packageDnsmasqFile" | awk '{ print $1; }')"
|
|
fi
|
|
[ "$resolverNewHash" != "$resolverStoredHash" ]
|
|
;;
|
|
store_hash)
|
|
[ -s "$packageDnsmasqFile" ] && resolverStoredHash="$(md5sum "$packageDnsmasqFile" | awk '{ print $1; }')";;
|
|
esac
|
|
;;
|
|
unbound.nftset)
|
|
case "$param" in
|
|
add_resolver_element) :;;
|
|
create_resolver_set) :;;
|
|
check_support) :;;
|
|
cleanup) :;;
|
|
configure) :;;
|
|
kill) :;;
|
|
reload) :;;
|
|
restart) :;;
|
|
compare_hash) :;;
|
|
store_hash) :;;
|
|
esac
|
|
;;
|
|
esac
|
|
}
|
|
|
|
netifd() {
|
|
# Usage: netifd install [iface] | netifd remove [iface] | netifd uninstall
|
|
_netifd_process_interface() {
|
|
local iface="$1" action="${2:-install}"
|
|
local rt_name="${ipTablePrefix}_${iface%6}"
|
|
|
|
uci_remove 'network' 'rule' "${rt_name}_ipv4" 2>/dev/null
|
|
uci_remove 'network' 'rule6' "${rt_name}_ipv6" 2>/dev/null
|
|
|
|
if [ -n "$netifd_strict_enforcement" ] && str_contains "$netifd_interface_local" "$iface"; then
|
|
if [ -n "$netifd_interface_default" ]; then
|
|
uci_add 'network' 'rule' "${rt_name}_ipv4"
|
|
uci_set 'network' "${rt_name}_ipv4" 'in' "${iface}"
|
|
uci_set 'network' "${rt_name}_ipv4" 'lookup' "${ipTablePrefix}_${netifd_interface_default}"
|
|
uci_set 'network' "${rt_name}_ipv4" 'priority' "${lan_priority}"
|
|
fi
|
|
if [ -n "$ipv6_enabled" ] && [ -n "$netifd_interface_default6" ]; then
|
|
uci_add 'network' 'rule6' "${rt_name}_ipv6"
|
|
uci_set 'network' "${rt_name}_ipv6" 'in' "${iface}"
|
|
uci_set 'network' "${rt_name}_ipv6" 'lookup' "${ipTablePrefix}_${netifd_interface_default6}"
|
|
uci_set 'network' "${rt_name}_ipv6" 'priority' "${lan_priority}"
|
|
fi
|
|
lan_priority="$((lan_priority + 1))"
|
|
fi
|
|
is_supported_interface "$iface" || return 0
|
|
|
|
if [ -z "$target_iface" ] || [ "$target_ifance" = "$iface" ]; then
|
|
is_wan6 "$iface" && return # TODO: properly process wan/wan6 at some point
|
|
if [ -z "$netifd_strict_enforcement" ] && [ "$netifd_interface_default" = "$iface" ]; then
|
|
rt_name='main'
|
|
fi
|
|
case "$action" in
|
|
install)
|
|
output 2 "Setting up netifd extensions for $iface... "
|
|
[ "$rt_name" = 'main' ] || sed -i "\#${rt_name}\$#d" "$rtTablesFile" >/dev/null 2>&1
|
|
[ "$rt_name" = 'main' ] || echo "${tid} ${rt_name}" >> "$rtTablesFile"
|
|
uci_set 'network' "${iface}" 'ip4table' "${rt_name}"
|
|
uci_set 'network' "${iface}" 'ip6table' "${rt_name}"
|
|
uci_add 'network' 'rule' "${rt_name}_ipv4"
|
|
uci_set 'network' "${rt_name}_ipv4" 'priority' "${priority}"
|
|
uci_set 'network' "${rt_name}_ipv4" 'lookup' "${rt_name}"
|
|
uci_set 'network' "${rt_name}_ipv4" 'mark' "${mark}"
|
|
uci_set 'network' "${rt_name}_ipv4" 'mask' "${fw_mask}"
|
|
if [ -n "$ipv6_enabled" ]; then
|
|
uci_add 'network' 'rule6' "${rt_name}_ipv6"
|
|
uci_set 'network' "${rt_name}_ipv6" 'priority' "${priority}"
|
|
uci_set 'network' "${rt_name}_ipv6" 'lookup' "${rt_name}"
|
|
uci_set 'network' "${rt_name}_ipv6" 'mark' "${mark}"
|
|
uci_set 'network' "${rt_name}_ipv6" 'mask' "${fw_mask}"
|
|
fi
|
|
sed -i "\#${mark}#d" "$nftTempFile" >/dev/null 2>&1
|
|
nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}"
|
|
nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${mark}"
|
|
nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return"
|
|
output_okb
|
|
;;
|
|
remove|uninstall)
|
|
output 2 "Removing netifd extensions for $iface... "
|
|
[ "$rt_name" = 'main' ] || sed -i "\#${rt_name}\$#d" "$rtTablesFile" >/dev/null 2>&1
|
|
sed -i "\#${mark}#d" "$nftNetifdPermFile" >/dev/null 2>&1
|
|
uci_remove 'network' "${iface}" 'ip4table' "${rt_name}" 2>/dev/null
|
|
uci_remove 'network' "${iface}" 'ip6table' "${rt_name}" 2>/dev/null
|
|
output_okb
|
|
;;
|
|
esac
|
|
fi
|
|
mark="$(printf '0x%06x' $((mark + uplink_mark)))"
|
|
priority="$((priority - 1))"
|
|
tid="$((tid + 1))"
|
|
}
|
|
|
|
load_package_config
|
|
json 'init'
|
|
|
|
local action="${1:-install}"
|
|
local target_iface="$2"
|
|
local mark="$(printf '0x%06x' "$uplink_mark")"
|
|
local priority="$uplink_ip_rules_priority"
|
|
local lan_priority="$((uplink_ip_rules_priority + 1000))"
|
|
local tid="$(get_rt_tables_non_pbr_next_id)"
|
|
|
|
case "$action" in
|
|
check)
|
|
[ "$netifd_enabled" = '1' ]
|
|
return "$?"
|
|
;;
|
|
install)
|
|
if [ -z "$netifd_strict_enforcement" ]; then
|
|
json add error 'errorNetifdMissingOption' 'netifd_strict_enforcement'
|
|
output_error 'errorNetifdMissingOption' 'netifd_strict_enforcement'
|
|
return 1
|
|
fi
|
|
if [ -z "$netifd_interface_default" ]; then
|
|
json add error 'errorNetifdMissingOption' 'netifd_interface_default'
|
|
output_error 'errorNetifdMissingOption' 'netifd_interface_default'
|
|
return 1
|
|
fi
|
|
if [ "$(uci_get 'network' "$netifd_interface_default")" != 'interface' ]; then
|
|
json add error 'errorNetifdInvalidGateway4' "$netifd_interface_default"
|
|
output_error 'errorNetifdInvalidGateway4' "$netifd_interface_default"
|
|
return 1
|
|
fi
|
|
if [ -n "$netifd_interface_default6" ] && [ "$(uci_get 'network' "$netifd_interface_default6")" != 'interface' ]; then
|
|
json add error 'errorNetifdInvalidGateway6' "$netifd_interface_default6"
|
|
output_error 'errorNetifdInvalidGateway6' "$netifd_interface_default6"
|
|
return 1
|
|
fi
|
|
if [ -z "$netifd_interface_local" ]; then
|
|
json add warning 'warningNetifdMissingInterfaceLocal' 'lan'
|
|
output_error 'warningNetifdMissingInterfaceLocal' 'lan'
|
|
netifd_interface_local='lan'
|
|
fi
|
|
[ "$netifd_strict_enforcement" = '1' ] || unset netifd_strict_enforcement
|
|
# [ -n "$netifd_interface_default6" ] || unset ipv6_enabled
|
|
;;
|
|
uninstall)
|
|
unset target_iface
|
|
;;
|
|
esac
|
|
|
|
nft_file 'netifd_create'
|
|
output 1 "Netifd extensions $action ${target_iface:+on $target_iface }"
|
|
config_load 'network'
|
|
config_foreach _netifd_process_interface 'interface' "$action"
|
|
output_1_newline
|
|
|
|
case "$action" in
|
|
install)
|
|
nft_file 'netifd_install'
|
|
if [ -z "$target_iface" ]; then
|
|
uci_set "$packageName" 'config' 'netifd_enabled' '1'
|
|
fi
|
|
;;
|
|
remove)
|
|
if [ -z "$target_iface" ]; then
|
|
nft_file 'netifd_remove'
|
|
uci_remove "$packageName" 'config' 'netifd_enabled' 2>/dev/null
|
|
fi
|
|
;;
|
|
uninstall)
|
|
nft_file 'netifd_remove'
|
|
;;
|
|
esac
|
|
uci_commit "$packageName"
|
|
# cat "$nftNetifdPermFile"
|
|
# cat "$rtTablesFile"
|
|
# uci changes
|
|
# uci revert network
|
|
uci_commit 'network'
|
|
sync
|
|
output "Restarting network ${action:+(on_$action) }"
|
|
{ /etc/init.d/network 'reload'; /etc/init.d/firewall 'reload'; } >/dev/null 2>&1 && output_okbn || output_failn
|
|
}
|
|
|
|
# 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" dest_dns_port="$5"
|
|
local chain='dstnat' iface='dns'
|
|
|
|
if [ -z "${dest_dns_ipv4}${dest_dns_ipv6}" ]; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessNoInterfaceDns' "'$dest_dns'"
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "$ipv6_enabled" ] && is_ipv6 "$(str_first_word "$src_addr")"; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessNoIpv6' "$name"
|
|
return 1
|
|
fi
|
|
|
|
if { is_ipv4 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv4" ]; } || \
|
|
{ is_ipv6 "$(str_first_word "$src_addr")" && [ -z "$dest_dns_ipv6" ]; }; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessMismatchFamily' "${name}: '$src_addr' '$dest_dns':'$dest_dns_port'"
|
|
return 1
|
|
fi
|
|
|
|
for proto_i in $proto; do
|
|
unset param4
|
|
unset param6
|
|
|
|
dest4="dport 53 dnat ip to ${dest_dns_ipv4}:${dest_dns_port}"
|
|
dest6="dport 53 dnat ip6 to ${dest_dns_ipv6}:${dest_dns_port}"
|
|
|
|
if [ -n "$src_addr" ]; then
|
|
if [ "${src_addr:0:1}" = "!" ]; then
|
|
negation='!='; src_addr="${src_addr//\!}"; nftset_suffix='_neg';
|
|
else
|
|
unset negation; unset nftset_suffix;
|
|
fi
|
|
value="$src_addr"
|
|
first_value="$(str_first_word "$value")"
|
|
if is_phys_dev "$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
|
|
json add error 'errorFailedToResolve' "$d"
|
|
else
|
|
[ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
|
|
[ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
|
|
fi
|
|
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
|
|
param4="${param4:+$param4 }${nftIPv4Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
|
|
param6="${param6:+$param6 }${nftIPv6Flag} saddr ${negation:+$negation }{ $(inline_set "$value") }"
|
|
fi
|
|
fi
|
|
|
|
param4="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param4} ${nftRuleParams} meta nfproto ipv4 ${proto_i} ${dest4} comment \"$name\""
|
|
param6="$nftInsertOption rule inet ${nftTable} ${nftPrefix}_${chain} ${param6} ${nftRuleParams} meta nfproto ipv6 ${proto_i} ${dest6} comment \"$name\""
|
|
|
|
local ipv4_error='0' ipv6_error='0'
|
|
if [ "$pbrNftPrevParam4" != "$param4" ] && \
|
|
[ -n "$first_value" ] && ! is_ipv6 "$first_value" && \
|
|
[ -z "$inline_set_ipv4_empty_flag" ] && [ -n "$dest_dns_ipv4" ]; then
|
|
nft4 "$param4" || ipv4_error='1'
|
|
pbrNftPrevParam4="$param4"
|
|
fi
|
|
if [ "$pbrNftPrevParam6" != "$param6" ] && [ "$param4" != "$param6" ] && \
|
|
[ -n "$first_value" ] && ! is_ipv4 "$first_value" && \
|
|
[ -z "$inline_set_ipv6_empty_flag" ] && [ -n "$dest_dns_ipv6" ]; then
|
|
nft6 "$param6" || ipv6_error='1'
|
|
pbrNftPrevParam6="$param6"
|
|
fi
|
|
|
|
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessInsertionFailed' "$name"
|
|
json add error 'errorPolicyProcessCMD' "nft $param4"
|
|
json add error 'errorPolicyProcessCMD' "nft $param6"
|
|
logger -t "$packageName" "ERROR: nft $param4"
|
|
logger -t "$packageName" "ERROR: nft $param6"
|
|
elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessInsertionFailedIpv4' "$name"
|
|
json add error 'errorPolicyProcessCMD' "nft $param4"
|
|
logger -t "$packageName" "ERROR: nft $param4"
|
|
fi
|
|
done
|
|
}
|
|
|
|
policy_routing() {
|
|
local mark i nftInsertOption='add'
|
|
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 "$(str_first_word "$src_addr")" || is_ipv6 "$(str_first_word "$dest_addr")"; }; then
|
|
processPolicyError='true'
|
|
json add error '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'
|
|
json add error 'errorPolicyProcessUnknownFwmark' "$iface"
|
|
return 1
|
|
fi
|
|
|
|
# TODO: implement actual family mismatch check on lists
|
|
# if is_family_mismatch "$src_addr" "$dest_addr"; then
|
|
# processPolicyError='true'
|
|
# json add error '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'
|
|
json add error 'errorPolicyProcessUnknownProtocol' "${name}: '$proto_i'"
|
|
return 1
|
|
fi
|
|
|
|
if [ -n "$src_addr" ]; then
|
|
if [ "${src_addr:0:1}" = "!" ]; then
|
|
negation='!='; value="${src_addr//\!}"; nftset_suffix='_neg';
|
|
else
|
|
unset negation; value="$src_addr"; unset nftset_suffix;
|
|
fi
|
|
first_value_src="$(str_first_word "$value")"
|
|
if is_phys_dev "$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
|
|
json add error 'errorFailedToResolve' "$d"
|
|
else
|
|
[ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
|
|
[ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
|
|
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
|
|
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//\!}"; nftset_suffix='_neg';
|
|
else
|
|
unset negation; value="$dest_addr"; unset nftset_suffix;
|
|
fi
|
|
first_value_dest="$(str_first_word "$value")"
|
|
if is_phys_dev "$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}${nftset_suffix}"
|
|
param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }@${nftPrefix}_${iface}_6_${target}_${type}_${uid}${nftset_suffix}"
|
|
else
|
|
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
|
|
json add error 'errorFailedToResolve' "$d"
|
|
else
|
|
[ -n "$resolved_ipv4" ] && inline_set_ipv4="${inline_set_ipv4:+$inline_set_ipv4, }$resolved_ipv4"
|
|
[ -n "$resolved_ipv6" ] && inline_set_ipv6="${inline_set_ipv6:+$inline_set_ipv6, }$resolved_ipv6"
|
|
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
|
|
param4="${param4:+$param4 }${nftIPv4Flag} daddr ${negation:+$negation }{ $(inline_set "$value") }"
|
|
param6="${param6:+$param6 }${nftIPv6Flag} daddr ${negation:+$negation }{ $(inline_set "$value") }"
|
|
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 }{ $(inline_set "$value") }"
|
|
param6="${param6:+$param6 }${proto_i:+$proto_i }sport ${negation:+$negation }{ $(inline_set "$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 }{ $(inline_set "$value") }"
|
|
param6="${param6:+$param6 }${proto_i:+$proto_i }dport ${negation:+$negation }{ $(inline_set "$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
|
|
chain='dstnat'
|
|
param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nftRuleParams} meta nfproto ipv4 $param4"
|
|
param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${nftRuleParams} meta nfproto ipv6 $param6"
|
|
dest_udp_53="udp dport 53 redirect to :${torDnsPort} comment \"Tor-DNS-UDP\""
|
|
dest_tcp_80="tcp dport 80 redirect to :${torTrafficPort} comment \"Tor-HTTP-TCP\""
|
|
dest_udp_80="udp dport 80 redirect to :${torTrafficPort} comment \"Tor-HTTP-UDP\""
|
|
dest_tcp_443="tcp dport 443 redirect to :${torTrafficPort} comment \"Tor-HTTPS-TCP\""
|
|
dest_udp_443="udp dport 443 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'
|
|
json add error 'errorPolicyProcessInsertionFailed' "$name"
|
|
json add error 'errorPolicyProcessCMD' "nft $param4 $dest4"
|
|
json add error 'errorPolicyProcessCMD' "nft $param6 $dest6"
|
|
logger -t "$packageName" "ERROR: nft $param4 $dest4"
|
|
logger -t "$packageName" "ERROR: nft $param6 $dest6"
|
|
elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessInsertionFailedIpv4' "$name"
|
|
json add error 'errorPolicyProcessCMD' "nft $param4 $dest4"
|
|
logger -t "$packageName" "ERROR: nft $param4 $dest4"
|
|
fi
|
|
done
|
|
else
|
|
param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${param4} ${nftRuleParams} ${dest4} comment \"$name\""
|
|
param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain} ${param6} ${nftRuleParams} ${dest6} comment \"$name\""
|
|
local ipv4_error='0' ipv6_error='0'
|
|
if [ "$pbrNftPrevParam4" != "$param4" ] && \
|
|
[ -z "$src_inline_set_ipv4_empty_flag" ] && [ -z "$dest_inline_set_ipv4_empty_flag" ] && \
|
|
[ "$filter_group_src_addr" != 'ipv6' ] && [ "$filter_group_src_addr" != 'ipv6_negative' ] && \
|
|
[ "$filter_group_dest_addr" != 'ipv6' ] && [ "$filter_group_dest_addr" != 'ipv6_negative' ]; then
|
|
nft4 "$param4" || ipv4_error='1'
|
|
pbrNftPrevParam4="$param4"
|
|
fi
|
|
if [ "$pbrNftPrevParam6" != "$param6" ] && [ "$param4" != "$param6" ] && \
|
|
[ -z "$src_inline_set_ipv6_empty_flag" ] && [ -z "$dest_inline_set_ipv6_empty_flag" ] && \
|
|
[ "$filter_group_src_addr" != 'ipv4' ] && [ "$filter_group_src_addr" != 'ipv4_negative' ] && \
|
|
[ "$filter_group_dest_addr" != 'ipv4' ] && [ "$filter_group_dest_addr" != 'ipv4_negative' ]; then
|
|
nft6 "$param6" || ipv6_error='1'
|
|
pbrNftPrevParam6="$param6"
|
|
fi
|
|
if [ -n "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessInsertionFailed' "$name"
|
|
json add error 'errorPolicyProcessCMD' "nft $param4"
|
|
json add error 'errorPolicyProcessCMD' "nft $param6"
|
|
logger -t "$packageName" "ERROR: nft $param4"
|
|
logger -t "$packageName" "ERROR: nft $param6"
|
|
elif [ -z "$ipv6_enabled" ] && [ "$ipv4_error" -eq '1' ]; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessInsertionFailedIpv4' "$name"
|
|
json add error 'errorPolicyProcessCMD' "nft $param4"
|
|
logger -t "$packageName" "ERROR: nft $param4"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
dns_policy_process() {
|
|
local i j uid="$1"
|
|
|
|
[ "$enabled" = '1' ] || 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:$dest_dns_port "
|
|
if [ -z "$src_addr" ]; then
|
|
json add error 'errorPolicyNoSrcDest' "$name"
|
|
output_fail; return 1;
|
|
fi
|
|
if [ -z "$dest_dns" ]; then
|
|
json add error '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" "$dest_dns_port"
|
|
fi
|
|
done
|
|
|
|
if [ -n "$processDnsPolicyError" ]; then
|
|
output_fail
|
|
else
|
|
output_ok
|
|
fi
|
|
}
|
|
|
|
policy_process() {
|
|
local i j uid="$1"
|
|
|
|
[ "$enabled" = '1' ] || 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
|
|
json add error 'errorPolicyNoSrcDest' "$name"
|
|
output_fail; return 1;
|
|
fi
|
|
if [ -z "$interface" ]; then
|
|
json add error 'errorPolicyNoInterface' "$name"
|
|
output_fail; return 1;
|
|
fi
|
|
if ! is_supported_interface "$interface"; then
|
|
json add error 'errorPolicyUnknownInterface' "$name"
|
|
output_fail; return 1;
|
|
fi
|
|
|
|
unset j
|
|
for i in $src_addr; do
|
|
if 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 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
|
|
local processed_value_src_addr processed_value_dest_addr
|
|
[ -z "$src_addr" ] && filter_list_src_addr='none'
|
|
for filter_group_src_addr in $filter_list_src_addr; do
|
|
filtered_value_src_addr="$(filter_options "$filter_group_src_addr" "$src_addr")"
|
|
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"
|
|
processed_value_src_addr="${processed_value_src_addr:+$processed_value_src_addr }$filtered_value_src_addr"
|
|
processed_value_dest_addr="${processed_value_dest_addr:+$processed_value_dest_addr }$filtered_value_dest_addr"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
for i in $src_addr; do
|
|
if ! str_contains "$processed_value_src_addr" "$i"; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessUnknownEntry' "$name: $i"
|
|
fi
|
|
done
|
|
|
|
for i in $dest_addr; do
|
|
if ! str_contains "$processed_value_dest_addr" "$i"; then
|
|
processPolicyError='true'
|
|
json add error 'errorPolicyProcessUnknownEntry' "$name: $i"
|
|
fi
|
|
done
|
|
|
|
if [ -n "$processPolicyError" ]; then
|
|
output_fail
|
|
else
|
|
output_ok
|
|
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
|
|
json add error 'errorInterfaceRoutingEmptyValues'
|
|
return 1
|
|
fi
|
|
case "$action" in
|
|
create)
|
|
is_netifd_interface "$iface" && return 0
|
|
ifacesTriggers="${ifacesTriggers:+$ifacesTriggers }$iface"
|
|
if ! grep -q "$tid ${ipTablePrefix}_${iface}" "$rtTablesFile"; then
|
|
sed -i "/${ipTablePrefix}_${iface}/d" "$rtTablesFile"
|
|
echo "$tid ${ipTablePrefix}_${iface}" >> "$rtTablesFile"
|
|
sync
|
|
fi
|
|
# Ensure a clean slate for this table before adding routes/rules
|
|
ip -4 rule flush table "$tid" >/dev/null 2>&1
|
|
ip -4 route flush table "$tid" >/dev/null 2>&1
|
|
if [ -n "$ipv6_enabled" ]; then
|
|
ip -6 rule flush table "$tid" >/dev/null 2>&1
|
|
ip -6 route flush table "$tid" >/dev/null 2>&1
|
|
fi
|
|
if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
|
|
ipv4_error=0
|
|
if [ -z "$gw4" ]; then
|
|
try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
|
|
else
|
|
try ip -4 route replace default via "$gw4" dev "$dev" table "$tid" || ipv4_error=1
|
|
fi
|
|
# try ip -4 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv4_error=1
|
|
{
|
|
for prio in $(ip -4 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
|
|
rule="$(ip -4 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
|
|
[ -n "$rule" ] || continue
|
|
rule="${rule/lookup main/lookup $tid}"
|
|
ip -4 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv4_error=1
|
|
done
|
|
}
|
|
try ip -4 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1
|
|
fi
|
|
try nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1
|
|
try nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} ${nftRuleParams} meta mark set (meta mark & ${fw_maskXor}) | ${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
|
|
if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ -n "$strict_enforcement" ]; then
|
|
if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
|
|
try ip -6 route replace unreachable default table "$tid" || ipv6_error=1
|
|
elif ip -6 route list table main | grep -q " dev $dev6 "; then
|
|
if ip -6 address show dev "$dev6" | grep -q "BROADCAST"; then
|
|
try ip -6 route replace default via "$gw6" dev "$dev6" table "$tid" metric "$uplink_interface6_metric" || ipv6_error=1
|
|
elif ip -6 address show dev "$dev6" | grep -q "POINTOPOINT"; then
|
|
try ip -6 route replace default dev "$dev6" table "$tid" metric "$uplink_interface6_metric" || ipv6_error=1
|
|
else
|
|
json add error 'errorInterfaceRoutingUnknownDevType' "$dev6"
|
|
fi
|
|
else
|
|
try ip -6 route replace "$(ip -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" || ipv6_error=1
|
|
try ip -6 route replace default dev "$dev6" table "$tid" || ipv6_error=1
|
|
fi
|
|
# try ip -6 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv6_error=1
|
|
{
|
|
for prio in $(ip -6 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
|
|
rule="$(ip -6 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
|
|
[ -n "$rule" ] || continue
|
|
rule="${rule/lookup main/lookup $tid}"
|
|
ip -6 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv6_error=1
|
|
done
|
|
}
|
|
try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
|
|
fi
|
|
fi
|
|
if [ "$ipv4_error" -eq '0' ] || [ "$ipv6_error" -eq '0' ]; then
|
|
dscp="$(uci_get "$packageName" 'config' "${iface}_dscp")"
|
|
if [ "${dscp:-0}" -ge '1' ] && [ "${dscp:-0}" -le '63' ]; then
|
|
try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv4Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
|
|
if [ -n "$ipv6_enabled" ]; then
|
|
try nft add rule inet "$nftTable" "${nftPrefix}_prerouting ${nftIPv6Flag} dscp ${dscp} ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
|
|
fi
|
|
fi
|
|
if [ "$iface" = "$icmp_interface" ]; then
|
|
try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv4Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
|
|
if [ -n "$ipv6_enabled" ]; then
|
|
try nft add rule inet "$nftTable" "${nftPrefix}_output ${nftIPv6Flag} protocol icmp ${nftRuleParams} goto ${nftPrefix}_mark_${mark}" || s=1
|
|
fi
|
|
fi
|
|
else
|
|
s=1
|
|
fi
|
|
return "$s"
|
|
;;
|
|
create_user_set)
|
|
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)
|
|
is_netifd_interface "$iface" && return 0
|
|
ip -4 rule del table 'main' prio "$((priority - 1000))" >/dev/null 2>&1
|
|
ip -4 rule del table "$tid" prio "$priority" >/dev/null 2>&1
|
|
ip -6 rule del table 'main' prio "$((priority - 1000))" >/dev/null 2>&1
|
|
ip -6 rule del table "$tid" prio "$priority" >/dev/null 2>&1
|
|
ip -4 rule flush table "$tid" >/dev/null 2>&1
|
|
ip -4 route flush table "$tid" >/dev/null 2>&1
|
|
ip -6 rule flush table "$tid" >/dev/null 2>&1
|
|
ip -6 route flush table "$tid" >/dev/null 2>&1
|
|
sed -i "/${ipTablePrefix}_${iface}\$/d" "$rtTablesFile"
|
|
sync
|
|
return "$s"
|
|
;;
|
|
reload_interface)
|
|
is_netifd_interface "$iface" && return 0
|
|
ipv4_error=0
|
|
ip -4 rule flush table "$tid" >/dev/null 2>&1
|
|
ip -4 route flush table "$tid" >/dev/null 2>&1
|
|
if [ -n "$ipv6_enabled" ]; then
|
|
ip -6 rule flush table "$tid" >/dev/null 2>&1
|
|
ip -6 route flush table "$tid" >/dev/null 2>&1
|
|
fi
|
|
if [ -n "$gw4" ] || [ -n "$strict_enforcement" ]; then
|
|
if [ -z "$gw4" ]; then
|
|
try ip -4 route replace unreachable default table "$tid" || ipv4_error=1
|
|
else
|
|
try ip -4 route replace default via "$gw4" dev "$dev" table "$tid" || ipv4_error=1
|
|
fi
|
|
# try ip -4 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv4_error=1
|
|
{
|
|
for prio in $(ip -4 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
|
|
rule="$(ip -4 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
|
|
[ -n "$rule" ] || continue
|
|
rule="${rule/lookup main/lookup $tid}"
|
|
ip -4 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv4_error=1
|
|
done
|
|
}
|
|
try ip -4 rule replace 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" ]; } || [ -n "$strict_enforcement" ]; then
|
|
if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then
|
|
try ip -6 route replace unreachable default table "$tid" || ipv6_error=1
|
|
elif ip -6 route list table main | grep -q " dev $dev6 "; then
|
|
if ip -6 address show dev "$dev6" | grep -q "BROADCAST"; then
|
|
try ip -6 route replace default via "$gw6" dev "$dev6" table "$tid" metric "$uplink_interface6_metric" || ipv6_error=1
|
|
elif ip -6 address show dev "$dev6" | grep -q "POINTOPOINT"; then
|
|
try ip -6 route replace default dev "$dev6" table "$tid" metric "$uplink_interface6_metric" || ipv6_error=1
|
|
else
|
|
json add error 'errorInterfaceRoutingUnknownDevType' "$dev6"
|
|
fi
|
|
else
|
|
try ip -6 route replace "$(ip -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" || ipv6_error=1
|
|
try ip -6 route replace default dev "$dev6" table "$tid" || ipv6_error=1
|
|
fi
|
|
# try ip -6 rule replace fwmark "${mark}/${fw_mask}" lookup 'main' suppress_prefixlength 0 priority "$((priority - 1000))" || ipv6_error=1
|
|
{
|
|
for prio in $(ip -6 rule show | awk '/lookup main/ && /suppress_prefixlength 0/ {gsub(":", "", $1); print $1}'); do
|
|
rule="$(ip -6 rule show | awk -v p="$prio" '($1==p":"){ $1=""; sub(/^ /,""); print }')"
|
|
[ -n "$rule" ] || continue
|
|
rule="${rule/lookup main/lookup $tid}"
|
|
ip -6 rule replace priority "$prio" $rule >/dev/null 2>&1 || ipv6_error=1
|
|
done
|
|
}
|
|
try ip -6 rule replace fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1
|
|
fi
|
|
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 'gateways'
|
|
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
|
|
}
|
|
|
|
process_interface() {
|
|
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' "$uplink_mark")"
|
|
ifacePriority="$uplink_ip_rules_priority"
|
|
unset ifaceTableID
|
|
return 0
|
|
fi
|
|
|
|
if [ "$iface" = 'tor' ]; then
|
|
case "$action" in
|
|
create|reload|reload_interface)
|
|
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
|
|
|
|
if is_wg_server "$iface" && ! is_ignored_interface "$iface"; then
|
|
local disabled listen_port
|
|
disabled="$(uci_get 'network' "$iface" 'disabled')"
|
|
listen_port="$(uci_get 'network' "$iface" 'listen_port')"
|
|
case "$action" in
|
|
create|reload|reload_interface)
|
|
if [ "$disabled" != '1' ] && [ -n "$listen_port" ]; then
|
|
if [ -n "$wanIface4" ]; then
|
|
ip rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
|
|
ip rule add sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
|
|
fi
|
|
if [ -n "$ipv6_enabled" ] && [ -n "$wanIface6" ]; then
|
|
ip -6 rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
|
|
ip -6 rule add sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
|
|
fi
|
|
fi
|
|
;;
|
|
destroy)
|
|
if [ -n "$listen_port" ]; then
|
|
ip rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
|
|
ip -6 rule del sport "$listen_port" table "pbr_${wanIface4}" >/dev/null 2>&1
|
|
fi
|
|
;;
|
|
esac
|
|
str_contains_word "$supported_interface" "$iface" || 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
|
|
: || json add warning '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' "$uplink_mark")"
|
|
[ -z "$ifacePriority" ] && ifacePriority="$uplink_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 + uplink_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
|
|
if is_netifd_interface_default "$iface"; then
|
|
[ "$verbosity" = '1' ] && dispStatus="$_OKB_" || dispStatus="$__OKB__"
|
|
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_interface "$iface"; then output_okb; else output_ok; fi
|
|
else
|
|
json add error '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
|
|
if is_netifd_interface_default "$iface"; then
|
|
[ "$verbosity" = '1' ] && dispStatus="$_OKB_" || dispStatus="$__OKB__"
|
|
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
|
|
if is_netifd_interface_default "$iface"; then
|
|
[ "$verbosity" = '1' ] && dispStatus="$_OKB_" || dispStatus="$__OKB__"
|
|
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}"
|
|
interface_routing 'destroy' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority"
|
|
if is_netifd_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
|
|
if is_netifd_interface_default "$iface"; then
|
|
[ "$verbosity" = '1' ] && dispStatus="$_OKB_" || dispStatus="$__OKB__"
|
|
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
|
|
if is_netifd_interface_default "$iface"; then
|
|
[ "$verbosity" = '1' ] && dispStatus="$_OKB_" || dispStatus="$__OKB__"
|
|
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_interface "$iface"; then output_okb; else output_ok; fi
|
|
else
|
|
json add error 'errorFailedReload' "$displayText"
|
|
output_fail
|
|
fi
|
|
else
|
|
json_add_gateway 'skip_interface' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" "$dispStatus"
|
|
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\n"
|
|
fi
|
|
;;
|
|
esac
|
|
ifaceMark="$(printf '0x%06x' $((ifaceMark + uplink_mark)))"
|
|
ifacePriority="$((ifacePriority - 1))"
|
|
return $s
|
|
}
|
|
|
|
user_file_process() {
|
|
local shellBin="${SHELL:-/bin/ash}"
|
|
[ "$enabled" = '1' ] || return 0
|
|
if [ ! -s "$path" ]; then
|
|
json add error 'errorUserFileNotFound' "$path"
|
|
output_fail
|
|
return 1
|
|
fi
|
|
if ! $shellBin -n "$path"; then
|
|
json add error 'errorUserFileSyntax' "$path"
|
|
output_fail
|
|
return 1
|
|
fi
|
|
if is_bad_user_file_nft_call "$path"; then
|
|
json add error 'errorIncompatibleUserFile' "$path"
|
|
output_fail
|
|
return 1
|
|
fi
|
|
output 2 "Running $path "
|
|
# shellcheck disable=SC1090
|
|
if ! . "$path"; then
|
|
json add error 'errorUserFileRunning' "$path"
|
|
if grep -q -w 'curl' "$path" && ! is_present 'curl'; then
|
|
json add error 'errorUserFileNoCurl' "$path"
|
|
fi
|
|
output_fail
|
|
return 1
|
|
else
|
|
output_ok
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
boot() {
|
|
nft_file 'delete'
|
|
rc_procd start_service 'on_boot' && service_started 'on_boot'
|
|
}
|
|
|
|
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 param="$1"
|
|
local resolverStoredHash resolverNewHash reloadedIface
|
|
local i k
|
|
|
|
load_package_config "$param"
|
|
[ "$param" = 'on_boot' ] && pbrBootFlag=1 && return 0
|
|
json init
|
|
load_environment "${param:-on_start}" "$(load_validate_config)" || return 1
|
|
|
|
output "Processing environment (${param:-on_start}) "
|
|
is_wan_up "$param" || { output_error "$(get_text 'errorUplinkDown')"; return 1; }
|
|
|
|
process_interface 'all' 'prepare'
|
|
config_foreach process_interface 'interface' 'pre_init'
|
|
|
|
case "$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
|
|
serviceStartTrigger='on_interface_reload'
|
|
else
|
|
serviceStartTrigger='on_start'
|
|
unset reloadedIface
|
|
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
|
|
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_okn
|
|
output 1 "Reloading Interface: $reloadedIface "
|
|
json_add_array 'gateways'
|
|
process_interface 'all' 'prepare'
|
|
config_foreach process_interface 'interface' 'reload_interface' "$reloadedIface"
|
|
json_close_array
|
|
output_1_newline
|
|
;;
|
|
on_reload|on_start|*)
|
|
resolver 'store_hash'
|
|
resolver 'configure'
|
|
# cleanup_main_table
|
|
# cleanup_main_chains
|
|
# cleanup_sets
|
|
# cleanup_marking_chains
|
|
cleanup_rt_tables
|
|
nft_file 'create'
|
|
output_okn
|
|
output 1 'Processing interfaces '
|
|
json_add_array 'gateways'
|
|
process_interface 'all' 'prepare'
|
|
config_foreach process_interface 'interface' 'create'
|
|
process_interface 'tor' 'destroy'
|
|
is_tor_running && process_interface 'tor' 'create'
|
|
json_close_array
|
|
ip route flush cache
|
|
output_1_newline
|
|
if is_config_enabled 'policy'; then
|
|
output 1 'Processing policies '
|
|
config_load "$packageName"
|
|
config_foreach load_validate_policy 'policy' policy_process
|
|
output_1_newline
|
|
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_newline
|
|
fi
|
|
if is_config_enabled 'include' || [ -d "/etc/${packageName}.d/" ]; then
|
|
process_interface 'all' 'prepare'
|
|
config_foreach process_interface 'interface' 'create_user_set'
|
|
output 1 'Processing user file(s) '
|
|
config_load "$packageName"
|
|
config_foreach load_validate_include 'include' user_file_process
|
|
if [ -d "/etc/${packageName}.d/" ]; then
|
|
local i
|
|
for i in "/etc/${packageName}.d/"*; do
|
|
local enabled='1' path="$i"
|
|
[ -f "$i" ] && user_file_process
|
|
done
|
|
fi
|
|
output_1_newline
|
|
fi
|
|
nft_file 'install'
|
|
resolver 'compare_hash' && resolver 'restart'
|
|
;;
|
|
esac
|
|
|
|
json_add_int 'packageCompat' "$packageCompat"
|
|
json_add_object 'status'
|
|
[ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary" || json add error 'errorNoGateways'
|
|
json_close_object
|
|
json_add_array 'errors'
|
|
for k in $(json get errors); do
|
|
json_add_object "$k"
|
|
json_add_string 'code' "$(json get error "$k" 'code')"
|
|
json_add_string 'info' "$(json get error "$k" 'info')"
|
|
json_close_object
|
|
done
|
|
json_close_array
|
|
json_add_array 'warnings'
|
|
for k in $(json get warnings); do
|
|
json_add_object "$k"
|
|
json_add_string 'code' "$(json get warning "$k" 'code')"
|
|
json_add_string 'info' "$(json get warning "$k" 'info')"
|
|
json_close_object
|
|
done
|
|
json_close_array
|
|
if [ -n "$strict_enforcement" ] && str_contains "$gatewaySummary" '0.0.0.0'; then
|
|
json_add_string 'mode' 'strict'
|
|
fi
|
|
procd_close_data
|
|
procd_close_instance
|
|
}
|
|
|
|
service_running() { procd_set_config_changed firewall; }
|
|
service_started() {
|
|
[ -n "$pbrBootFlag" ] && return 0
|
|
local error warning c
|
|
if nft_file 'exists'; then
|
|
procd_set_config_changed firewall
|
|
[ -n "$gatewaySummary" ] && output "$serviceName (fw4 nft file mode) started with gateways:\n${gatewaySummary}"
|
|
else
|
|
output "$serviceName FAILED TO START in fw4 nft file mode!!!\n"
|
|
output "Check the output of nft -c -f $nftTempFile\n"
|
|
fi
|
|
warning="$(json get warning)"
|
|
if [ -n "$warning" ]; then
|
|
for c in $warning; do
|
|
code="$(json get warning "$c" 'code')"
|
|
info="$(json get warning "$c" 'info')"
|
|
output_warning "$(get_text "$code" "$info")"
|
|
done
|
|
output_warning "$(get_text 'warningSummary' "$(get_url '#WarningMessagesDetails')")"
|
|
fi
|
|
error="$(json get error)"
|
|
if [ -n "$error" ]; then
|
|
for c in $error; do
|
|
code="$(json get error "$c" 'code')"
|
|
info="$(json get error "$c" 'info')"
|
|
output_error "$(get_text "$code" "$info")"
|
|
done
|
|
output_error "$(get_text 'errorSummary' "$(get_url '#ErrorMessagesDetails')")"
|
|
fi
|
|
touch "$packageLockFile"
|
|
if [ -n "$error" ]; then
|
|
return 2
|
|
elif [ -n "$warning" ]; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
service_stopped() { procd_set_config_changed firewall; }
|
|
|
|
# shellcheck disable=SC2015
|
|
service_triggers() {
|
|
local n
|
|
if [ -n "$pbrBootFlag" ]; then
|
|
output "Setting trigger (on_boot) "
|
|
procd_add_raw_trigger "interface.*.up" "$procd_boot_trigger_delay" "/etc/init.d/${packageName}" start && output_okn || output_failn
|
|
else
|
|
PROCD_RELOAD_DELAY=$(( procd_reload_delay * 1000 ))
|
|
procd_open_validate
|
|
load_validate_config
|
|
load_validate_policy
|
|
load_validate_include
|
|
procd_close_validate
|
|
procd_open_trigger
|
|
procd_add_config_trigger "config.change" 'openvpn' "/etc/init.d/${packageName}" reload 'on_openvpn_change'
|
|
procd_add_config_trigger "config.change" "${packageName}" "/etc/init.d/${packageName}" reload
|
|
if [ -n "$ifacesTriggers" ]; then
|
|
output 1 "Setting interface triggers "
|
|
for n in $ifacesTriggers; do
|
|
output 2 "Setting interface trigger for $n "
|
|
procd_add_interface_trigger "interface.*" "$n" "/etc/init.d/${packageName}" on_interface_reload "$n" && output_ok || output_fail
|
|
done
|
|
output_1_newline
|
|
fi
|
|
procd_close_trigger
|
|
if [ "$serviceStartTrigger" = 'on_start' ] && [ -n "$ifacesTriggers" ]; then
|
|
output 3 "$serviceName monitoring interfaces: ${ifacesTriggers}\n"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# shellcheck disable=SC2015
|
|
stop_service() {
|
|
local i nft_file_mode
|
|
json init
|
|
! is_service_running && [ "$(get_rt_tables_next_id)" = "$(get_rt_tables_non_pbr_next_id)" ] && return 0
|
|
[ "$1" = 'quiet' ] && quiet_mode 'on'
|
|
load_environment 'on_stop'
|
|
if nft_file 'exists'; then
|
|
nft_file_mode=1
|
|
fi
|
|
output 'Resetting chains and sets '
|
|
# if nft_file 'delete' && cleanup_main_table && cleanup_main_chains && cleanup_sets && cleanup_marking_chains; then
|
|
if nft_file 'delete'; then
|
|
output_okn
|
|
else
|
|
output_failn
|
|
fi
|
|
output 1 'Resetting interfaces '
|
|
config_load 'network'
|
|
config_foreach process_interface 'interface' 'destroy'
|
|
process_interface 'tor' 'destroy'
|
|
cleanup_rt_tables
|
|
output 1 "\n"
|
|
ip route flush cache
|
|
unset ifaceMark
|
|
unset ifaceTableID
|
|
resolver 'store_hash'
|
|
resolver 'cleanup'
|
|
resolver 'compare_hash' && resolver 'restart'
|
|
if [ -n "$enabled" ]; then
|
|
if [ -n "$nft_file_mode" ]; then
|
|
output "$serviceName (fw4 nft file mode) stopped "; output_okn;
|
|
else
|
|
output "$serviceName (nft mode) stopped "; output_okn;
|
|
fi
|
|
fi
|
|
rm -f "$packageLockFile"
|
|
}
|
|
|
|
version() { echo "$PKG_VERSION"; }
|
|
|
|
status_service() {
|
|
local i dev dev6 wanTID ipv6_enabled
|
|
|
|
[ "$(uci_get "$packageName" config ipv6_enabled)" = "1" ] && ipv6_enabled='true'
|
|
|
|
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 -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 installed on $dist $vers."
|
|
[ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}."
|
|
[ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}."
|
|
|
|
echo "$_SEPARATOR_"
|
|
echo "$packageName - environment"
|
|
echo "$status"
|
|
echo "$_SEPARATOR_"
|
|
dnsmasq --version 2>/dev/null | sed '/^$/,$d'
|
|
if nft_file 'netifd_exists'; then
|
|
echo "$_SEPARATOR_"
|
|
echo "$packageName fw4 netifd nft file: $nftNetifdPermFile"
|
|
sed '1d;2d;' "$nftNetifdPermFile"
|
|
fi
|
|
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; 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 "$packageDnsmasqFile" ]; then
|
|
echo "$_SEPARATOR_"
|
|
echo "dnsmasq nft sets in $packageDnsmasqFile"
|
|
cat "$packageDnsmasqFile"
|
|
fi
|
|
# echo "$_SEPARATOR_"
|
|
# ip rule list | grep "${packageName}_"
|
|
echo "$_SEPARATOR_"
|
|
echo "$packageName tables & routing"
|
|
tableCount="$(grep -c "${packageName}_" "$rtTablesFile")" || tableCount=0
|
|
wanTID=$(($(get_rt_tables_next_id)-tableCount))
|
|
for tid in main $(seq "$wanTID" $((wanTID + tableCount - 1))); do
|
|
status_table="$(grep "^${tid}[[:space:]]" "$rtTablesFile" | awk '{print $2}')"
|
|
echo "IPv4 table ${tid}${status_table:+ ($status_table)} routes:"
|
|
ip -4 route show table "$tid" | sed 's/^/ /'
|
|
echo "IPv4 table ${tid}${status_table:+ ($status_table)} rules:"
|
|
ip -4 rule list table "$tid" | sed 's/^/ /'
|
|
if [ -n "$ipv6_enabled" ]; then
|
|
echo "$_SEPARATOR_"
|
|
echo "IPv6 table $tid routes:"
|
|
ip -6 route show table "$tid" | sed 's/^/ /'
|
|
echo "IPv6 table $tid rules:"
|
|
ip -6 rule list table "$tid" | sed 's/^/ /'
|
|
fi
|
|
echo "$_SEPARATOR_"
|
|
done
|
|
}
|
|
|
|
# shellcheck disable=SC2120
|
|
load_validate_config() {
|
|
uci_load_validate "$packageName" "$packageName" "$1" "${2}${3:+ $3}" \
|
|
'enabled:bool:0' \
|
|
'strict_enforcement:bool:1' \
|
|
'ipv6_enabled:bool:0' \
|
|
'resolver_set:or("", "none", "dnsmasq.nftset")' \
|
|
'resolver_instance:list(or(integer, string)):*' \
|
|
'verbosity:range(0,2):2' \
|
|
'uplink_mark:regex("[A-Fa-f0-9]{8}"):00010000' \
|
|
'uplink_ip_rules_priority:uinteger:30000' \
|
|
'fw_mask:regex("[A-Fa-f0-9]{8}"):00ff0000' \
|
|
'icmp_interface:or("", tor, uci("network", "@interface"))' \
|
|
'ignored_interface:list(or(tor, uci("network", "@interface")))' \
|
|
'supported_interface:list(or(ignore, tor, regex("xray_.*"), uci("network", "@interface")))' \
|
|
'procd_boot_trigger_delay:range(1000,10000):5000' \
|
|
'lan_device:list(or(network)):br-lan' \
|
|
'procd_reload_delay:uinteger:0' \
|
|
'uplink_interface:network:wan' \
|
|
'uplink_interface6:network:wan6' \
|
|
'webui_supported_protocol:list(string)' \
|
|
'nft_rule_counter:bool:0'\
|
|
'nft_set_auto_merge: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_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
|
|
local dest_dns_port
|
|
uci_load_validate "$packageName" 'policy' "$1" "${2}${3:+ $3}" \
|
|
'name:string:Untitled' \
|
|
'enabled:bool:1' \
|
|
'src_addr:list(neg(or(host,network,macaddr,string)))' \
|
|
'dest_dns:list(or(host,network,string))' \
|
|
'dest_dns_port:port:53' \
|
|
;
|
|
}
|
|
|
|
# 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", "output", "prerouting"):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' \
|
|
;
|
|
}
|