-
-
Save stangri/8afddf154a806465f2f9f3febbb5554a to your computer and use it in GitHub Desktop.
/etc/init.d/pbr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh /etc/rc.common | |
# Copyright 2020-2022 Stan Grishin (stangri@melmac.ca) | |
# shellcheck disable=SC1091,SC2018,SC2019,SC3043,SC3057,SC3060 | |
# sysctl net.ipv4.conf.default.rp_filter=1 | |
# sysctl net.ipv4.conf.all.rp_filter=1 | |
# shellcheck disable=SC2034 | |
START=94 | |
# shellcheck disable=SC2034 | |
USE_PROCD=1 | |
if type extra_command >/dev/null 2>&1; then | |
extra_command 'status' "Generates output required to troubleshoot routing issues | |
Use '-d' option for more detailed output | |
Use '-p' option to automatically upload data under VPR paste.ee account | |
WARNING: while paste.ee uploads are unlisted, they are still publicly available | |
List domain names after options to include their lookup in report" | |
extra_command 'version' 'Show version information' | |
extra_command 'reload_firewall' 'Run service on firewall reload' | |
extra_command 'reload_interface' 'Run service on indicated interface reload' | |
else | |
# shellcheck disable=SC2034 | |
EXTRA_COMMANDS='reload_firewall reload_interface status version' | |
# shellcheck disable=SC2034 | |
EXTRA_HELP=" status Generates output required to troubleshoot routing issues | |
Use '-d' option for more detailed output | |
Use '-p' option to automatically upload data under VPR paste.ee account | |
WARNING: while paste.ee uploads are unlisted, they are still publicly available | |
List domain names after options to include their lookup in report" | |
fi | |
readonly PKG_VERSION='dev-test' | |
readonly packageName='pbr' | |
readonly serviceName="$packageName $PKG_VERSION" | |
readonly packageConfigFile="/etc/config/${packageName}" | |
readonly nftTempFile="/var/run/${packageName}.nft" | |
#readonly nftPermFile="/etc/nftables.d/table-post/30-pbr.nft" | |
readonly dnsmasqFile="/var/dnsmasq.d/${packageName}" | |
readonly sharedMemoryOutput="/dev/shm/$packageName-output" | |
readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m' | |
readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m' | |
readonly __OK__='\033[0;32m[\xe2\x9c\x93]\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 ipTablePrefix='pbr' | |
# shellcheck disable=SC2155 | |
readonly iptables="$(command -v iptables)" | |
# shellcheck disable=SC2155 | |
readonly ip6tables="$(command -v ip6tables)" | |
# shellcheck disable=SC2155 | |
readonly ipset="$(command -v ipset)" | |
readonly ipsPrefix='pbr' | |
readonly iptPrefix='PBR' | |
# shellcheck disable=SC2155 | |
readonly nft="$(command -v nft)" | |
readonly nftTable="fw4" | |
readonly nftPrefix='pbr' | |
readonly chainsList='forward input output postrouting prerouting' | |
# package config options | |
boot_timeout= | |
enabled= | |
fw_mask= | |
icmp_interface= | |
ignored_interface= | |
ipv6_enabled= | |
procd_boot_delay= | |
procd_reload_delay= | |
resolver_set= | |
rule_create_option= | |
secure_reload= | |
strict_enforcement= | |
supported_interface= | |
verbosity= | |
wan_ip_rules_priority= | |
wan_mark= | |
# run-time | |
gatewaySummary= | |
errorSummary= | |
warningSummary= | |
wanIface4= | |
wanIface6= | |
ifaceMark= | |
ifaceTableID= | |
ifacePriority= | |
ifacesAll= | |
ifacesSupported= | |
wanGW4= | |
wanGW6= | |
serviceStartTrigger= | |
processPolicyError= | |
processPolicyWarning= | |
resolver_setSupported='0' | |
nftPrevParam4= | |
nftPrevParam6= | |
version() { echo "$PKG_VERSION"; } | |
output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; } | |
output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; } | |
output_fail() { s=1; output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; } | |
output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; } | |
str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; } | |
str_replace() { echo "${1//$2/$3}"; } | |
str_contains() { [ -n "$1" ] &&[ -n "$2" ] && [ "${1//$2}" != "$1" ]; } | |
str_contains_word() { echo "$1" | grep -q -w "$2"; } | |
str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; } | |
str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; } | |
str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; } | |
str_extras_to_space() { echo "$1" | tr ';{}' ' '; } | |
debug() { local i j; for i in "$@"; do eval "j=\$$i"; echo "${i}: ${j} "; done; } | |
output() { | |
# Can take a single parameter (text) to be output at any verbosity | |
# Or target verbosity level and text to be output at specifc verbosity | |
local msg memmsg logmsg | |
verbosity="${verbosity:-2}" | |
if [ "$#" -ne 1 ]; then | |
if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi | |
fi | |
[ -t 1 ] && printf "%b" "$1" | |
msg="${1//$serviceName /service }"; | |
if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then | |
[ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")" | |
logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')" | |
logger -t "${packageName:-service}" "$(printf "%b" "$logmsg")" | |
rm -f "$sharedMemoryOutput" | |
else | |
printf "%b" "$msg" >> "$sharedMemoryOutput" | |
fi | |
} | |
is_present() { command -v "$1" >/dev/null 2>&1; } | |
is_installed() { [ -s "/usr/lib/opkg/info/${1}.control" ]; } | |
is_variant_installed() { [ "$(echo /usr/lib/opkg/info/"${1}"*.control)" != "/usr/lib/opkg/info/${1}*.control" ]; } | |
is_nft() { [ -x "$nft" ] && [ "$resolver_set" != 'dnsmasq.ipset' ] && "$nft" list chains inet | grep -q "${nftPrefix}_prerouting"; } | |
_build_ifaces_all() { ifacesAll="${ifacesAll}${1} "; } | |
_build_ifaces_supported() { is_supported_interface "$1" && ifacesSupported="${ifacesSupported}${1} "; } | |
pbr_find_iface() { | |
local iface i param="$2" | |
[ "$param" = 'wan6' ] || param='wan' | |
"network_find_${param}" iface | |
is_tunnel "$iface" && unset iface | |
if [ -z "$iface" ]; then | |
for i in $ifacesAll; do | |
if "is_${param}" "$i"; then break; else unset i; fi | |
done | |
fi | |
eval "$1"='${iface:-$i}' | |
} | |
pbr_get_gateway() { | |
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")" | |
# fi | |
if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then | |
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 | |
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 | awk '{print $2}')" | |
fi | |
eval "$1"='$gw' | |
} | |
is_dslite() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:6}" = "dslite" ]; } | |
is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "l2tp" ]; } | |
is_oc() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:11}" = "openconnect" ]; } | |
is_ovpn() { local dev; network_get_device dev "$1"; [ "${dev:0:3}" = "tun" ] || [ "${dev:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${dev}/tun_flags" ]; } | |
is_pptp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "pptp" ]; } | |
is_softether() { local dev; network_get_device dev "$1"; [ "${dev:0:4}" = "vpn_" ]; } | |
is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; } | |
is_tor_running() { | |
local ret=0 | |
if [ -s "/etc/tor/torrc" ]; then | |
json_load "$(ubus call service list "{ 'name': 'tor' }")" | |
json_select 'tor'; json_select 'instances'; json_select 'instance1'; | |
json_get_var ret 'running'; json_cleanup | |
fi | |
if [ "$ret" = "0" ]; then return 1; else return 0; fi | |
} | |
is_wg() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:9}" = "wireguard" ]; } | |
is_tunnel() { is_dslite "$1" || is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_softether "$1" || is_tor "$1" || is_wg "$1"; } | |
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_ignored_interface() { str_contains_word "$ignored_interface" "$1"; } | |
is_supported_interface() { str_contains_word "$supported_interface" "$1" || { ! is_ignored_interface "$1" && { is_wan "$1" || is_wan6 "$1" || is_tunnel "$1"; }; } || is_ignore_target "$1"; } | |
is_ignore_target() { [ "$(str_to_lower "$1")" = 'ignore' ]; } | |
is_mac_address() { expr "$1" : '[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]$' >/dev/null; } | |
is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; } | |
is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ":"; } | |
is_family_mismatch() { ( is_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_netmask "${2//!}" ); } | |
is_ipv6_link_local() { [ "${1:0:4}" = "fe80" ]; } | |
is_ipv6_unique_local() { [ "${1:0:2}" = "fc" ] || [ "${1:0:2}" = "fd" ]; } | |
is_ipv6_global() { [ "${1:0:4}" = "2001" ]; } | |
# is_ipv6_global() { is_ipv6 "$1" && ! is_ipv6_link_local "$1" && ! is_ipv6_link_local "$1"; } | |
is_list() { str_contains "$1" "," || str_contains "$1" " "; } | |
is_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; } | |
is_domain() { str_contains "$1" '[a-zA-Z]'; } | |
is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; } | |
dnsmasq_kill() { killall -q -s HUP dnsmasq; } | |
dnsmasq_restart() { output 3 'Restarting dnsmasq '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; } | |
is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; } | |
is_supported_iface_dev() { local n dev; for n in $ifacesSupported; do network_get_device dev "$n"; [ "$1" = "$dev" ] && return 0; done; return 1; } | |
is_supported_protocol() { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; } | |
is_service_running_iptables() { [ -x "$iptables" ] && "$iptables" -t mangle -L | grep -q "${iptPrefix}_PREROUTING" >/dev/null 2>&1; } | |
is_service_running_nft() { [ -x "$nft" ] && [ -n "$(get_mark_nft_chains)" ]; } | |
# atomic | |
# is_service_running_nft() { [ -x "$nft" ] && [ -s "$nftPermFile" ]; } | |
is_service_running() { if is_nft; then is_service_running_nft; else is_service_running_iptables; fi; } | |
is_netifd_table() { local iface="$1"; [ "$(uci -q get "network.${iface}.ip4table")" = "${packageName}_${iface%6}" ]; } | |
get_rt_tables_id() { grep "${packageName}_${iface}" /etc/iproute2/rt_tables | awk '{print $1;}'; } | |
get_rt_tables_next_id() { echo "$(($(sort -r -n /etc/iproute2/rt_tables | grep -o -E -m 1 "^[0-9]+")+1))"; } | |
_check_config() { local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; } | |
is_config_enabled() { | |
local cfg="$1" _cfg_enabled=1 | |
[ -n "$1" ] || return 1 | |
config_load "$packageName" | |
config_foreach _check_config "$cfg" | |
return "$_cfg_enabled" | |
} | |
# shellcheck disable=SC2016 | |
resolveip_to_ipt() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d'; } | |
# shellcheck disable=SC2016 | |
resolveip_to_nftset() { resolveip "$@" | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; } | |
resolveip_to_nftset4() { resolveip_to_nftset -4 "$@"; } | |
resolveip_to_nftset6() { [ -n "$ipv6_enabled" ] && resolveip_to_nftset -6 "$@"; } | |
# shellcheck disable=SC2016 | |
ipv4_leases_to_nftset() { [ -s '/tmp/dhcp.leases' ] || return 1; grep "$1" '/tmp/dhcp.leases' | awk '{print $3}' | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; } | |
# shellcheck disable=SC2016 | |
ipv6_leases_to_nftset() { [ -s '/tmp/hosts/odhcpd' ] || return 1; grep "$1" '/tmp/hosts/odhcpd' | awk '{print $1}' | sed -n 'H;${x;s/\n/,/g;s/^,//;p;};d' | tr '\n' ' '; } | |
# shellcheck disable=SC3037 | |
ports_to_nftset() { echo -ne "$value"; } | |
get_mark_ipt_chains() { [ -n "$(command -v iptables-save)" ] && iptables-save | grep ":${iptPrefix}_MARK_" | awk '{ print $1 }' | sed 's/://'; } | |
get_mark_nft_chains() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep chain | grep "${nftPrefix}_mark_" | awk '{ print $2 }'; } | |
get_ipsets() { [ -x "$(command -v ipset)" ] && ipset list | grep "${ipsPrefix}_" | awk '{ print $2 }'; } | |
get_nft_sets() { [ -x "$nft" ] && "$nft" list table inet "$nftTable" 2>/dev/null | grep 'set' | grep "${nftPrefix}_" | awk '{ print $2 }'; } | |
is_ipset_type_supported() { ipset help hash:"$1" >/dev/null 2>&1; } | |
ubus_get_status() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@.${packageName}.instances.main.data.status.${1}"; } | |
ubus_get_iface() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@.${packageName}.instances.main.data.interfaces[@.name='${1}']${2:+.$2}"; } | |
resolver() { | |
local param="$1" | |
shift | |
case "$resolver_set" in | |
none) return 0;; | |
adguardhome.ipset) | |
case "$param" in | |
add_resolver) :;; | |
create_resolver) :;; | |
check_support) :;; | |
cleanup) :;; | |
configure) :;; | |
init) :;; | |
init_end) :;; | |
kill) :;; | |
reload) :;; | |
restart) :;; | |
compare_hash) :;; | |
store_hash) :;; | |
esac | |
;; | |
dnsmasq.ipset) | |
case "$param" in | |
add_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
ips 'add_resolver' "$@" | |
return "$?" | |
;; | |
create_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
ips 'create_resolver' "$@" | |
return "$?" | |
;; | |
check_support) | |
if [ ! -x "$ipset" ]; then | |
errorSummary="${errorSummary}${_ERROR_}: The ipset binary cannot be found, make sure to install ipset!\\n" | |
resolver_set='none' | |
return 1 | |
fi | |
if ! dnsmasq -v 2>/dev/null | grep -q 'no-ipset' && dnsmasq -v 2>/dev/null | grep -q 'ipset'; then | |
resolver_setSupported='1' | |
return 0 | |
else | |
warningSummary="${warningSummary}${_WARNING_}: Resolver set support (${resolver_set}) is enabled in $packageName, but this resolver set is not supported on this system!\\n" | |
resolver_set='none' | |
return 1 | |
fi | |
;; | |
cleanup) :;; | |
configure) :;; | |
init) :;; | |
init_end) :;; | |
kill) if killall -q -s HUP dnsmasq; then return 0; else return 1; fi;; | |
reload) | |
output 3 'Reloading dnsmasq ' | |
if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then | |
output_okn | |
return 0 | |
else | |
output_failn | |
return 1 | |
fi | |
;; | |
restart) | |
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) :;; | |
store_hash) :;; | |
esac | |
;; | |
dnsmasq.nftset) | |
case "$param" in | |
add_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
nftset 'add_resolver' "$@" | |
return "$?" | |
;; | |
create_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
nftset 'create_resolver' "$@" | |
return "$?" | |
;; | |
check_support) | |
if [ ! -x "$nft" ]; then | |
errorSummary="${errorSummary}${_ERROR_}: This version of $packageName supports only nftables, but nft binary cannot be found!\\n" | |
resolver_set='none' | |
return 1 | |
fi | |
if ! dnsmasq -v 2>/dev/null | grep -q 'no-nftset' && dnsmasq -v 2>/dev/null | grep -q 'nftset'; then | |
resolver_setSupported='1' | |
return 0 | |
else | |
warningSummary="${warningSummary}${_WARNING_}: Resolver set support (${resolver_set}) is enabled in $packageName, but this resolver set is not supported on this system!\\n" | |
resolver_set='none' | |
return 1 | |
fi | |
;; | |
cleanup) :;; | |
configure) :;; | |
init) :;; | |
init_end) :;; | |
kill) if killall -q -s HUP dnsmasq; then return 0; else return 1; fi;; | |
reload) | |
output 3 'Reloading dnsmasq ' | |
if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then | |
output_okn | |
return 0 | |
else | |
output_failn | |
return 1 | |
fi | |
;; | |
restart) | |
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) :;; | |
store_hash) :;; | |
esac | |
;; | |
unbound.ipset) | |
case "$param" in | |
add_resolver) :;; | |
create_resolver) :;; | |
check_support) :;; | |
cleanup) :;; | |
configure) :;; | |
init) :;; | |
init_end) :;; | |
kill) :;; | |
reload) :;; | |
restart) :;; | |
compare_hash) :;; | |
store_hash) :;; | |
esac | |
;; | |
unbound.nftset) | |
case "$param" in | |
add_resolver) :;; | |
create_resolver) :;; | |
check_support) :;; | |
cleanup) :;; | |
configure) :;; | |
init) :;; | |
init_end) :;; | |
kill) :;; | |
reload) :;; | |
restart) :;; | |
compare_hash) :;; | |
store_hash) :;; | |
esac | |
;; | |
esac | |
} | |
load_package_config() { | |
config_load "$packageName" | |
config_get boot_timeout 'config' 'boot_timeout' '30' | |
config_get_bool enabled 'config' 'enabled' '0' | |
config_get fw_mask 'config' 'fw_mask' 'ff0000' | |
config_get icmp_interface 'config' 'icmp_interface' | |
config_get ignored_interface 'config' 'ignored_interface' | |
config_get_bool ipv6_enabled 'config' 'ipv6_enabled' '0' | |
config_get procd_boot_delay 'config' 'procd_boot_delay' '0' | |
config_get resolver_set 'config' 'resolver_set' 'dnsmasq.nftset' | |
config_get rule_create_option 'config' 'rule_create_option' 'add' | |
config_get_bool secure_reload 'config' 'secure_reload' '1' | |
config_get_bool strict_enforcement 'config' 'strict_enforcement' '0' | |
config_get supported_interface 'config' 'supported_interface' | |
config_get verbosity 'config' 'verbosity' '2' | |
config_get wan_ip_rules_priority 'config' 'wan_ip_rules_priority' '30000' | |
config_get wan_mark 'config' 'wan_mark' '010000' | |
fw_mask="0x${fw_mask}" | |
wan_mark="0x${wan_mark}" | |
[ -n "$ipv6_enabled" ] && [ "$ipv6_enabled" -eq 0 ] && unset ipv6_enabled | |
. /lib/functions/network.sh | |
. /usr/share/libubox/jshn.sh | |
mkdir -p "${dnsmasqFile%/*}" | |
if is_nft; then | |
fw_maskXor="$(printf '%#x' "$((fw_mask ^ 0xffffffff))")" | |
fw_maskXor="${fw_maskXor:-0xff00ffff}" | |
else | |
case $rule_create_option in | |
insert|-i|-I) rule_create_option='-I';; | |
add|-a|-A|*) rule_create_option='-A';; | |
esac | |
fi | |
} | |
load_environment() { | |
local param="$1" validation_result="$2" | |
load_package_config | |
if [ "$param" = 'on_start' ]; then | |
if [ -n "$validation_result" ] && [ "$validation_result" != '0' ]; then | |
output "${_ERROR_}: The $packageName config validation failed!\\n" | |
output "Please check if the '$packageConfigFile' contains correct values for config options.\\n" | |
return 1 | |
fi | |
if [ "$enabled" -eq 0 ]; then | |
errorSummary="${errorSummary}${_ERROR_}: The ${packageName} is currently disabled.\\n" | |
errorSummary="${errorSummary}Enable ${packageName} from WebUI or run the following commands:\\n" | |
errorSummary="${errorSummary}uci set $packageName.config.enabled='1'; uci commit $packageName;\\n" | |
return 1 | |
fi | |
if is_nft; then | |
# if [ ! -x "$nft" ]; then | |
# errorSummary="${errorSummary}${_ERROR_}: This version of $packageName supports only nftables, but nft binary cannot be found!\\n" | |
# return 1 | |
# fi | |
case $resolver_set in | |
none) : ;; | |
dnsmasq.nftset) | |
if ! dnsmasq -v 2>/dev/null | grep -q 'no-nftset' && dnsmasq -v 2>/dev/null | grep -q 'nftset'; then | |
resolver_setSupported='1' | |
else | |
errorSummary="${errorSummary}${_ERROR_}: Resolver set support (${resolver_set}) is enabled in $packageName, but this resolver set is not supported on this system!\\n" | |
resolver_set='none' | |
fi | |
;; | |
*) | |
errorSummary="${errorSummary}${_ERROR_}: Resolver set support (${resolver_set}) is enabled in $packageName, but this resolver set is not supported in this version!\\n" | |
resolver_set='none' | |
;; | |
esac | |
else | |
case $resolver_set in | |
none) : ;; | |
adguardhome.ipset) : ;; | |
dnsmasq.ipset) | |
if ! dnsmasq -v 2>/dev/null | grep -q 'no-ipset' && dnsmasq -v 2>/dev/null | grep -q 'ipset'; then | |
resolver_setSupported='1' | |
fi | |
;; | |
*) | |
errorSummary="${errorSummary}${_ERROR_}: Resolver set support (${resolver_set}) is enabled in $packageName, but this resolver set is not supported in this version!\\n" | |
resolver_set='none' | |
;; | |
esac | |
fi | |
fi | |
load_network "$param" | |
} | |
load_network() { | |
config_load 'network' | |
[ -z "$ifacesAll" ] && config_foreach _build_ifaces_all 'interface' | |
[ -z "$ifacesSupported" ] && config_foreach _build_ifaces_supported 'interface' | |
pbr_find_iface wanIface4 'wan' | |
[ -n "$ipv6_enabled" ] && pbr_find_iface wanIface6 'wan6' | |
[ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4" | |
[ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6" | |
wanGW="${wanGW4:-$wanGW6}" | |
} | |
is_wan_up() { | |
local sleepCount='1' | |
load_network | |
while [ -z "$wanGW" ] ; do | |
load_network | |
if [ $((sleepCount)) -gt $((boot_timeout)) ] || [ -n "$wanGW" ]; then break; fi | |
output "$serviceName waiting for wan gateway...\\n" | |
sleep 1 | |
network_flush_cache | |
sleepCount=$((sleepCount+1)) | |
done | |
if [ -n "$wanGW" ]; then | |
return 0 | |
else | |
errorSummary="${errorSummary}${_ERROR_}: ${serviceName} failed to discover WAN gateway!\\n" | |
return 1 | |
fi | |
} | |
# shellcheck disable=SC2086 | |
ipt4() { | |
local d | |
[ -x "$iptables" ] || return 1 | |
for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do | |
[ "$d" != "$*" ] && "$iptables" $d >/dev/null 2>&1 | |
done | |
d="$*"; "$iptables" $d >/dev/null 2>&1 | |
} | |
# shellcheck disable=SC2086 | |
ipt6() { | |
local d | |
[ -n "$ipv6_enabled" ] || return 0 | |
[ -x "$ip6tables" ] || return 1 | |
for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do | |
[ "$d" != "$*" ] && "$ip6tables" $d >/dev/null 2>&1 | |
done | |
d="$*" | |
"$ip6tables" $d >/dev/null 2>&1 | |
} | |
# shellcheck disable=SC2086 | |
ipt() { | |
local d failFlagIpv4=1 failFlagIpv6=1 | |
[ -x "$iptables" ] || return 1 | |
for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do | |
if [ "$d" != "$*" ]; then | |
"$iptables" $d >/dev/null 2>&1 | |
[ -x "$ip6tables" ] && "$ip6tables" $d >/dev/null 2>&1 | |
fi | |
done | |
d="$*"; "$iptables" $d >/dev/null 2>&1 && failFlagIpv4=0; | |
if [ -n "$ipv6_enabled" ] && [ -x "$ip6tables" ]; then | |
"$ip6tables" $d >/dev/null 2>&1 && failFlagIpv6=0 | |
fi | |
[ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ] | |
} | |
# shellcheck disable=SC2086 | |
ips4() { [ -x "$ipset" ] && "$ipset" "$@" >/dev/null 2>&1; } | |
ips6() { [ -x "$ipset" ] && { if [ -n "$ipv6_enabled" ] && [ -n "$*" ]; then "$ipset" "$@" >/dev/null 2>&1; else return 1; fi; }; } | |
ips() { | |
local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7" | |
local ipset4 ipset6 i | |
local ipv4_error=1 ipv6_error=1 | |
ipset4="${ipsPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}" | |
ipset6="${ipsPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}" | |
[ -x "$ipset" ] || return 1 | |
if [ "${#ipset4}" -gt 31 ]; then | |
errorSummary="${errorSummary}${_ERROR_}: ipset name '$ipset4' is longer than allowed 31 characters. Shorten the name of relevant interface!\\n" | |
return 1 | |
fi | |
case "$command" in | |
add) | |
ips4 -q -! add "$ipset4" comment "$comment" && ipv4_error=0 | |
ips6 -q -! add "$ipset6" comment "$comment" && ipv6_error=0 | |
;; | |
add_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
case "$resolver_set" in | |
dnsmasq.ipset) | |
[ -n "$ipv6_enabled" ] || unset ipset6 | |
echo "ipset=/${param}/${ipset4}${ipset6:+,$ipset6} # $comment" >> "$dnsmasqFile" && ipv4_error=0 | |
;; | |
*) return 1;; | |
esac | |
;; | |
create) | |
ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0 | |
ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0 | |
;; | |
create_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
case "$resolver_set" in | |
dnsmasq.ipset) | |
ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0 | |
ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv6_error=0 | |
;; | |
*) return 1;; | |
esac | |
;; | |
create_user_set) | |
case "$type" in | |
ip|net) | |
ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0 | |
ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0 | |
case "$target" in | |
dst) | |
ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0 | |
ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0 | |
;; | |
src) | |
ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0 | |
ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
mac) | |
ips4 -q -! create "$ipset4" "hash:$type" comment && ipv4_error=0 | |
ips6 -q -! create "$ipset6" "hash:$type" comment family inet6 && ipv4_error=0 | |
ipt4 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0 | |
ipt6 -t mangle -A "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
delete|destroy) | |
ips4 -q -! destroy "$ipset4" && ipv4_error=0 | |
ips6 -q -! destroy "$ipset6" && ipv6_error=0 | |
;; | |
delete_user_set) | |
ips4 -q -! destroy "$ipset4" && ipv4_error=0 | |
ips6 -q -! destroy "$ipset6" family inet6 && ipv6_error=0 | |
case "$type" in | |
ip|net) | |
case "$target" in | |
dst) | |
ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" dst -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0 | |
ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" dst -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0 | |
;; | |
src) | |
ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0 | |
ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
mac) | |
ipt4 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset4" src -g "${iptPrefix}_MARK_${mark}" && ipv4_error=0 | |
ipt6 -t mangle -D "${iptPrefix}_PREROUTING" -m set --match-set "$ipset6" src -g "${iptPrefix}_MARK_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
flush|flush_user_set) | |
ips4 -q -! flush "$ipset4" && ipv4_error=0 | |
ips6 -q -! flush "$ipset6" && ipv6_error=0 | |
;; | |
esac | |
return $ipv4_error || $ipv6_error | |
} | |
# atomic | |
#nfta() { echo "$@" >> "$nftTempFile"; } | |
#nfta4() { echo "$@" >> "$nftTempFile"; } | |
#nfta6() { [ -z "$ipv6_enabled" ] || echo "$@" >> "$nftTempFile"; } | |
#nft() { nfta "$@"; [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; } | |
#nft4() { nfta "$@"; [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; } | |
#nft6() { nfta "$@"; [ -n "$ipv6_enabled" ] || return 0; [ -x "$nft" ] && [ -n "$*" ] && "$nft" "$@" >/dev/null 2>&1; } | |
nft() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; } | |
nft4() { [ -x "$nft" ] && "$nft" "$@" >/dev/null 2>&1; } | |
nft6() { [ -n "$ipv6_enabled" ] || return 0; [ -x "$nft" ] && [ -n "$*" ] && "$nft" "$@" >/dev/null 2>&1; } | |
nftset() { | |
local command="$1" iface="$2" target="${3:-dst}" type="${4:-ip}" uid="$5" comment="$6" param="$7" mark="$7" | |
local nftset4 nftset6 i param4 param6 | |
local ipv4_error=1 ipv6_error=1 | |
nftset4="${nftPrefix}${iface:+_$iface}_4${target:+_$target}${type:+_$type}${uid:+_$uid}" | |
nftset6="${nftPrefix}${iface:+_$iface}_6${target:+_$target}${type:+_$type}${uid:+_$uid}" | |
[ -x "$nft" ] || return 1 | |
if [ "${#nftset4}" -gt 255 ]; then | |
errorSummary="${errorSummary}${_ERROR_}: nft set name '$nftset4' is longer than allowed 255 characters. Shorten the name of relevant interface!\\n" | |
return 1 | |
fi | |
case "$command" in | |
add) | |
if is_netmask "$param" || is_ipv4 "$param" || is_ipv6 "$param" \ | |
|| 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 | |
else | |
# elif is_domain "$param"; then | |
if [ "$target" = 'src' ]; then | |
param4="$(ipv4_leases_to_nftset "$param")" | |
param6="$(ipv6_leases_to_nftset "$param")" | |
echo "target: src, param: $param, param6: $param6" | |
fi | |
[ -z "$param4" ] && param4="$(resolveip_to_nftset4 "$param")" | |
[ -z "$param6" ] && param6="$(resolveip_to_nftset6 "$param")" | |
echo "param: $param, param6: $param6" | |
nft4 add element inet "$nftTable" "$nftset4" "{ $param4 }" && ipv4_error=0 | |
nft6 add element inet "$nftTable" "$nftset6" "{ $param6 }" && ipv6_error=0 | |
echo "ipv4_error: $ipv4_error, ipv6_error: $ipv6_error" | |
fi | |
;; | |
add_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
case "$resolver_set" in | |
dnsmasq.nftset) | |
[ -n "$ipv6_enabled" ] || unset nftset6 | |
echo "nftset=/${param}/4#inet#${nftTable}#${nftset4}${nftset6:+,6#inet#${nftTable}#$nftset6} # $comment" >> "$dnsmasqFile" && ipv4_error=0 | |
;; | |
*) return 1;; | |
esac | |
;; | |
create) | |
case "$type" in | |
ip|net) | |
nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0 | |
nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0 | |
;; | |
mac) | |
nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0 | |
nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0 | |
;; | |
esac | |
;; | |
create_resolver) | |
[ "$resolver_setSupported" -eq 0 ] && return 1 | |
case "$resolver_set" in | |
dnsmasq.nftset) | |
nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv4_error=0 | |
nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; comment \"$comment\"; }" && ipv6_error=0 | |
;; | |
*) return 1;; | |
esac | |
;; | |
create_user_set) | |
case "$type" in | |
ip|net) | |
nft4 add set inet "$nftTable" "$nftset4" "{ type ipv4_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv4_error=0 | |
nft6 add set inet "$nftTable" "$nftset6" "{ type ipv6_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv6_error=0 | |
case "$target" in | |
dst) | |
nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0 | |
nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0 | |
;; | |
src) | |
nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0 | |
nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
mac) | |
nft4 add set inet "$nftTable" "$nftset4" "{ type ether_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv4_error=0 | |
nft6 add set inet "$nftTable" "$nftset6" "{ type ether_addr; flags interval; auto-merge; policy memory; comment \"$comment\"; }" && ipv6_error=0 | |
nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0 | |
nft add rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
delete|destroy) | |
nft delete set inet "$nftTable" "$nftset4" && ipv4_error=0 | |
nft delete set inet "$nftTable" "$nftset6" && ipv6_error=0 | |
;; | |
delete_user_set) | |
nft delete set inet "$nftTable" "$nftset4" && ipv4_error=0 | |
nft delete set inet "$nftTable" "$nftset6" && ipv6_error=0 | |
case "$type" in | |
ip|net) | |
case "$target" in | |
dst) | |
nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0 | |
nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip daddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0 | |
;; | |
src) | |
nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0 | |
nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ip saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
mac) | |
nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset4}" goto "${nftPrefix}_mark_${mark}" && ipv4_error=0 | |
nft delete rule inet "$nftTable" "${nftPrefix}_prerouting" ether saddr "@${nftset6}" goto "${nftPrefix}_mark_${mark}" && ipv6_error=0 | |
;; | |
esac | |
;; | |
flush|flush_user_set) | |
nft flush set inet "$nftTable" "$nftset4" && ipv4_error=0 | |
nft flush set inet "$nftTable" "$nftset6" && ipv6_error=0 | |
;; | |
esac | |
# nft6 returns true if IPv6 support is not enabled | |
[ -z "$ipv6_enabled" ] && ipv6_error='1' | |
return $ipv4_error || $ipv6_error | |
} | |
cleanup_dnsmasq() { [ -s "$dnsmasqFile" ] && resolverStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')" && rm "$dnsmasqFile" >/dev/null 2>&1; } | |
cleanup_main_chains() { | |
local i | |
for i in $chainsList; do | |
i="$(str_to_lower "$i")" | |
nft flush chain inet "$nftTable" "${nftPrefix}_${i}" | |
done | |
for i in $chainsList; do | |
i="$(str_to_upper "$i")" | |
ipt -t mangle -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}" | |
ipt -t mangle -F "${iptPrefix}_${i}" | |
ipt -t mangle -X "${iptPrefix}_${i}" | |
done | |
} | |
cleanup_marking_chains() { | |
local i | |
for i in $(get_mark_nft_chains); do | |
nft flush chain inet "$nftTable" "$i" | |
nft delete chain inet "$nftTable" "$i" | |
done | |
for i in $(get_mark_ipt_chains); do | |
ipt -t mangle -F "$i" | |
ipt -t mangle -X "$i" | |
done | |
} | |
cleanup_sets() { | |
local i | |
for i in $(get_nft_sets); do | |
nft flush set inet "$nftTable" "$i" | |
nft delete set inet "$nftTable" "$i" | |
done | |
for i in $(get_ipsets); do | |
ipset -q -! flush "$i" >/dev/null 2>&1 | |
ipset -q -! destroy "$i" >/dev/null 2>&1 | |
done | |
} | |
traffic_killswitch() { | |
local s=0 | |
case "$1" in | |
insert) | |
local lan_subnet wan_device | |
[ "$secure_reload" -ne 0 ] || return 0 | |
output 3 'Activating traffic killswitch ' | |
network_get_subnet lan_subnet 'lan' | |
network_get_physdev wan_device 'wan' | |
if is_nft; then | |
nft add chain inet "$nftTable" "${nftPrefix}_killswitch" '{ type filter hook forward priority 0; policy accept; }' || s=1 | |
nft add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname "$wan_device" ip saddr "$lan_subnet" counter reject || s=1 | |
# nft add rule inet "$nftTable" "${nftPrefix}_killswitch" oifname '$wan_devices' ip saddr '$lan_subnet' counter reject || s=1 | |
else | |
ipt -N "${iptPrefix}_KILLSWITCH" || s=1 | |
ipt -A "${iptPrefix}_KILLSWITCH" -s "$lan_subnet" -o "$wan_device" -j REJECT || s=1 | |
ipt -I FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1 | |
fi | |
if [ "$s" -eq 0 ]; then | |
output_okn | |
else | |
output_failn | |
fi | |
;; | |
remove) | |
if [ "$secure_reload" -ne 0 ]; then | |
output 3 'Deactivating traffic killswitch ' | |
fi | |
if is_nft; then | |
nft flush chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1 | |
nft delete chain inet "$nftTable" "${nftPrefix}_killswitch" || s=1 | |
else | |
ipt -D FORWARD -j "${iptPrefix}_KILLSWITCH" || s=1 | |
ipt -F "${iptPrefix}_KILLSWITCH" || s=1 | |
ipt -X "${iptPrefix}_KILLSWITCH" || s=1 | |
fi | |
if [ "$secure_reload" -ne 0 ]; then | |
if [ "$s" -eq 0 ]; then | |
output_okn | |
else | |
output_failn | |
fi | |
fi | |
;; | |
esac | |
} | |
policy_routing_tor() { if is_nft; then policy_routing_tor_nft "$@"; else policy_routing_tor_iptables "$@"; fi; } | |
policy_routing_tor_iptables() { | |
local comment="$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_upper "$8")" | |
chain="${chain:-PREROUTING}" | |
if [ -n "${src_addr}${src_port}${dest_port}" ]; then | |
processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n" | |
fi | |
if [ -n "$proto" ] && [ "$proto" != "all" ]; then | |
processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n" | |
fi | |
if [ "$chain" != "PREROUTING" ]; then | |
processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '$comment'\\n" | |
fi | |
ips 'add_resolver' "$iface" 'dst' 'ip' '' "${comment}: $dest_addr" "$dest_addr" || processPolicyError="${processPolicyError}${_ERROR_}: ips 'add_resolver' '$iface' 'dst' 'ip' '${comment}: $dest_addr' '$dest_addr'\\n" | |
return 0 | |
} | |
policy_routing_tor_nft() { | |
local comment="$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}" | |
if [ -n "${src_addr}${src_port}${dest_port}" ]; then | |
processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n" | |
fi | |
if [ -n "$proto" ] && [ "$proto" != "all" ]; then | |
processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n" | |
fi | |
if [ "$chain" != "prerouting" ]; then | |
processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'prerouting' for policy '$comment'\\n" | |
fi | |
nftset 'add_resolver' "$iface" 'dst' 'ip' '' "${comment}: $dest_addr" "$dest_addr" || processPolicyError="${processPolicyError}${_ERROR_}: nftset 'add_resolver' '$iface' 'dst' 'ip' '${comment}: $dest_addr' '$dest_addr'\\n" | |
return 0 | |
} | |
policy_routing() { if is_nft; then policy_routing_nft "$@"; else policy_routing_iptables "$@"; fi; } | |
policy_routing_iptables() { | |
local mark param4 param6 i negation value dest ipInsertOption="-A" | |
local ip4error='1' ip6error='1' | |
local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9" | |
proto="$(str_to_lower "$7")" | |
chain="$(str_to_upper "$8")" | |
chain="${chain:-PREROUTING}" | |
mark=$(eval echo "\$mark_${iface//-/_}") | |
if [ -n "$ipv6_enabled" ] && { is_ipv6 "$laddr" || is_ipv6 "$raddr"; }; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$name' as IPv6 support is disabled\\n" | |
return 1 | |
fi | |
if [ -n "$mark" ]; then | |
dest="-g ${iptPrefix}_MARK_${mark}" | |
elif [ "$iface" = "ignore" ]; then | |
dest="-j RETURN" | |
else | |
processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}\\n" | |
return 1 | |
fi | |
if [ -z "$proto" ]; then | |
if [ -n "$lport" ] || [ -n "$rport" ]; then | |
proto='tcp udp' | |
else | |
proto='all' | |
fi | |
fi | |
if is_family_mismatch "$laddr" "$raddr"; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$laddr' and '$raddr' in policy '$name'\\n" | |
return 1 | |
fi | |
for i in $proto; do | |
if [ "$i" = 'all' ]; then | |
param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest" | |
param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest" | |
elif ! is_supported_protocol "$i"; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$name'\\n" | |
return 1 | |
else | |
param4="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest -p $i" | |
param6="-t mangle ${ipInsertOption} ${iptPrefix}_${chain} $dest -p $i" | |
fi | |
if [ -n "$laddr" ]; then | |
if [ "${laddr:0:1}" = "!" ]; then | |
negation='!'; value="${laddr:1}" | |
else | |
unset negation; value="$laddr"; | |
fi | |
if is_phys_dev "$value"; then | |
param4="$param4 $negation -m physdev --physdev-in ${value:1}" | |
param6="$param6 $negation -m physdev --physdev-in ${value:1}" | |
elif is_netmask "$value"; then | |
local target='src' type='net' | |
if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \ | |
ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then | |
param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target" | |
param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target" | |
else | |
param4="$param4 $negation -s $value" | |
param6="$param6 $negation -s $value" | |
fi | |
elif is_mac_address "$value"; then | |
local target='src' type='mac' | |
if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \ | |
ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then | |
param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target" | |
param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target" | |
else | |
param4="$param4 -m mac $negation --mac-source $value" | |
param6="$param6 -m mac $negation --mac-source $value" | |
fi | |
else | |
local target='src' type='ip' | |
if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $laddr" && \ | |
ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $laddr" "$value"; then | |
param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target" | |
param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target" | |
else | |
param4="$param4 $negation -s $(resolveip_to_ipt -4 "$value")" | |
param6="$param6 $negation -s $(resolveip_to_ipt -6 "$value")" | |
fi | |
fi | |
fi | |
if [ -n "$lport" ]; then | |
if [ "${lport:0:1}" = "!" ]; then | |
negation='!'; value="${lport:1}" | |
else | |
unset negation; value="$lport"; | |
fi | |
param4="$param4 -m multiport $negation --sport ${value//-/:}" | |
param6="$param6 -m multiport $negation --sport ${value//-/:}" | |
fi | |
if [ -n "$raddr" ]; then | |
if [ "${raddr:0:1}" = "!" ]; then | |
negation='!'; value="${raddr:1}" | |
else | |
unset negation; value="$raddr"; | |
fi | |
if is_netmask "$value"; then | |
local target='dst' type='net' | |
if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \ | |
ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then | |
param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target" | |
param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target" | |
else | |
param4="$param4 $negation -d $value" | |
param6="$param6 $negation -d $value" | |
fi | |
elif is_domain "$value"; then | |
local target='dst' type='ip' | |
if ips 'create_resolver' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \ | |
ips 'add_resolver' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then | |
param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target" | |
param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target" | |
elif ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \ | |
ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then | |
param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target" | |
param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target" | |
else | |
param4="$param4 $negation -d $(resolveip_to_ipt -4 "$value")" | |
param6="$param6 $negation -d $(resolveip_to_ipt -6 "$value")" | |
fi | |
else | |
local target='dst' type='ip' | |
if ips 'create' "$iface" "$target" "$type" "$uid" "${name}: $raddr" && \ | |
ips 'add' "$iface" "$target" "$type" "$uid" "${name}: $raddr" "$value"; then | |
param4="$param4 -m set $negation --match-set ${ipsPrefix}_${iface}_4_${target}_${type}_${uid} $target" | |
param6="$param6 -m set $negation --match-set ${ipsPrefix}_${iface}_6_${target}_${type}_${uid} $target" | |
else | |
param4="$param4 $negation -d $value" | |
param6="$param6 $negation -d $value" | |
fi | |
fi | |
fi | |
if [ -n "$rport" ]; then | |
if [ "${rport:0:1}" = "!" ]; then | |
negation='!'; value="${rport:1}" | |
else | |
unset negation; value="$rport"; | |
fi | |
param4="$param4 -m multiport $negation --dport ${value//-/:}" | |
param6="$param6 -m multiport $negation --dport ${value//-/:}" | |
fi | |
if [ -n "$name" ]; then | |
param4="$param4 -m comment --comment $(str_extras_to_underscore "$name")" | |
param6="$param6 -m comment --comment $(str_extras_to_underscore "$name")" | |
fi | |
local ipv4_error='0' ipv6_error='0' | |
if [ "$param4" = "$param6" ]; then | |
ipt4 "$param4" || ipv4_error='1' | |
else | |
ipt4 "$param4" || ipv4_error='1' | |
ipt6 "$param6" || ipv6_error='1' | |
fi | |
# ipt6 returns true if IPv6 support is not enabled | |
[ -z "$ipv6_enabled" ] && ipv6_error='1' | |
if [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then | |
if [ -n "$ipv6_enabled" ]; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Policy insertion failed for both IPv4 and IPv6!\\n" | |
processPolicyError="${processPolicyError}${_ERROR_}: iptables $param4\\n" | |
processPolicyError="${processPolicyError}${_ERROR_}: iptables $param6\\n" | |
else | |
processPolicyError="${processPolicyError}${_ERROR_}: iptables $param4\\n" | |
fi | |
fi | |
done | |
} | |
policy_routing_nft() { | |
local mark param4 param6 i negation value dest nftInsertOption='add' | |
local ip4Flag='ip' ip6Flag='ip6' | |
local name="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain uid="$9" | |
proto="$(str_to_lower "$7")" | |
chain="$(str_to_lower "$8")" | |
chain="${chain:-prerouting}" | |
mark=$(eval echo "\$mark_${iface//-/_}") | |
if [ -z "$ipv6_enabled" ] && { is_ipv6 "$src_addr" || is_ipv6 "$dest_addr"; }; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$name' as IPv6 support is disabled\\n" | |
return 1 | |
fi | |
if [ -n "$mark" ]; then | |
dest="goto ${nftPrefix}_mark_${mark}" | |
elif [ "$iface" = "ignore" ]; then | |
dest="return" | |
else | |
processPolicyError="${processPolicyError}${_ERROR_}: Unknown packet mark for ${iface}\\n" | |
return 1 | |
fi | |
if is_family_mismatch "$src_addr" "$dest_addr"; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$src_addr' and '$dest_addr' in policy '$name'\\n" | |
return 1 | |
fi | |
if [ -n "$proto" ] && ! is_supported_protocol "$proto"; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$name'\\n" | |
return 1 | |
fi | |
if [ -n "$src_addr" ]; then | |
if [ "${src_addr:0:1}" = "!" ]; then | |
negation='!='; value="${src_addr:1}" | |
else | |
unset negation; value="$src_addr"; | |
fi | |
if is_phys_dev "$value"; then | |
param4="${param4:+$param4 }iifname ${negation:+$negation }${value:1}" | |
param6="${param6:+$param6 }iifname ${negation:+$negation }${value:1}" | |
elif is_mac_address "$value"; then | |
local target='src' type='mac' | |
if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \ | |
nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then | |
param4="${param4:+$param4 }ether ${negation:+$negation }saddr @${nftPrefix}_${iface}_4_${target}_${type}_${uid}" | |
param6="${param6:+$param6 }ether ${negation:+$negation }saddr @${nftPrefix}_${iface}_6_${target}_${type}_${uid}" | |
else | |
param4="${param4:+$param4 }ether saddr ${negation:+$negation }$value" | |
param6="${param6:+$param6 }ether saddr ${negation:+$negation }$value" | |
fi | |
else | |
local target='src' type='ip' | |
if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \ | |
nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then | |
param4="${param4:+$param4 }${ip4Flag:+$ip4Flag }${negation:+$negation }saddr @${nftPrefix}_${iface}_4_${target}_${type}_${uid}" | |
param6="${param6:+$param6 }${ip6Flag:+$ip6Flag }${negation:+$negation }saddr @${nftPrefix}_${iface}_6_${target}_${type}_${uid}" | |
else | |
param4="${param4:+$param4 }${ip4Flag:+$ip4Flag }saddr ${negation:+$negation }$value" | |
param6="${param6:+$param6 }${ip6Flag:+$ip6Flag }saddr ${negation:+$negation }$value" | |
fi | |
fi | |
fi | |
if [ -n "$dest_addr" ]; then | |
if [ "${dest_addr:0:1}" = "!" ]; then | |
negation='!='; value="${dest_addr:1}" | |
else | |
unset negation; value="$dest_addr"; | |
fi | |
if is_phys_dev "$value"; then | |
param4="${param4:+$param4 }oifname ${negation:+$negation }${value:1}" | |
param6="${param6:+$param6 }oifname ${negation:+$negation }${value:1}" | |
elif is_domain "$value"; then | |
local target='dst' type='ip' | |
if nftset 'create_resolver' "$iface" "$target" "$type" "$uid" "$name" && \ | |
nftset 'add_resolver' "$iface" "$target" "$type" "$uid" "$name" "$value"; then | |
param4="${param4:+$param4 }${ip4Flag:+$ip4Flag }${negation:+$negation }daddr @${nftPrefix}_${iface}_4_${target}_${type}_${uid}" | |
param6="${param6:+$param6 }${ip6Flag:+$ip6Flag }${negation:+$negation }daddr @${nftPrefix}_${iface}_6_${target}_${type}_${uid}" | |
elif nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \ | |
nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then | |
param4="${param4:+$param4 }${ip4Flag:+$ip4Flag }${negation:+$negation }daddr @${nftPrefix}_${iface}_4_${target}_${type}_${uid}" | |
param6="${param6:+$param6 }${ip6Flag:+$ip6Flag }${negation:+$negation }daddr @${nftPrefix}_${iface}_6_${target}_${type}_${uid}" | |
else | |
param4="${param4:+$param4 }${ip4Flag:+$ip4Flag }${negation:+$negation }daddr {$(resolveip_to_nftset4 "$value")}" | |
param6="${param6:+$param6 }${ip6Flag:+$ip6Flag }${negation:+$negation }daddr {$(resolveip_to_nftset6 "$value")}" | |
fi | |
else | |
local target='dst' type='ip' | |
if nftset 'create' "$iface" "$target" "$type" "$uid" "$name" && \ | |
nftset 'add' "$iface" "$target" "$type" "$uid" "$name" "$value"; then | |
param4="${param4:+$param4 }${ip4Flag:+$ip4Flag }${negation:+$negation }daddr @${nftPrefix}_${iface}_4_${target}_${type}_${uid}" | |
param6="${param6:+$param6 }${ip6Flag:+$ip6Flag }${negation:+$negation }daddr @${nftPrefix}_${iface}_6_${target}_${type}_${uid}" | |
else | |
param4="${param4:+$param4 }${ip4Flag:+$ip4Flag }${negation:+$negation }daddr $value" | |
param6="${param6:+$param6 }${ip6Flag:+$ip6Flag }${negation:+$negation }daddr $value" | |
fi | |
fi | |
fi | |
if [ -n "${src_port}${dest_port}" ]; then | |
proto="${proto:-tcp}" | |
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:+$proto }sport ${negation:+$negation }{$(ports_to_nftset "$value")}" | |
param6="${param6:+$param6 }${proto:+$proto }sport ${negation:+$negation }{$(ports_to_nftset "$value")}" | |
fi | |
if [ -n "$dest_port" ]; then | |
if [ "${dest_port:0:1}" = "!" ]; then | |
negation='!='; value="${dest_port:1}" | |
else | |
unset negation; value="$dest_port"; | |
fi | |
param4="${param4:+$param4 }${proto:+$proto }dport ${negation:+$negation }{$(ports_to_nftset "$value")}" | |
param6="${param6:+$param6 }${proto:+$proto }dport ${negation:+$negation }{$(ports_to_nftset "$value")}" | |
fi | |
param4="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain:-prerouting} $param4 $dest comment \"$name\"" | |
param6="$nftInsertOption rule inet $nftTable ${nftPrefix}_${chain:-prerouting} $param6 $dest comment \"$name\"" | |
local ipv4_error='0' ipv6_error='0' | |
if [ "$nftPrevParam4" != "$param4" ]; then | |
nft4 "$param4" || ipv4_error='1' | |
nftPrevParam4="$param4" | |
fi | |
if [ "$nftPrevParam6" != "$param6" ]; then | |
nft6 "$param6" || ipv6_error='1' | |
nftPrevParam6="$param6" | |
fi | |
# nft6 returns true if IPv6 support is not enabled | |
[ -z "$ipv6_enabled" ] && ipv6_error='1' | |
if [ "$ipv4_error" -eq '1' ] && [ "$ipv6_error" -eq '1' ]; then | |
if [ -n "$ipv6_enabled" ]; then | |
processPolicyError="${processPolicyError}${_ERROR_}: Policy insertion failed for both IPv4 and IPv6!\\n" | |
processPolicyError="${processPolicyError}${_ERROR_}: nft '$param4'\\n" | |
processPolicyError="${processPolicyError}${_ERROR_}: nft '$param6'\\n" | |
else | |
processPolicyError="${processPolicyError}${_ERROR_}: nft '$param4'\\n" | |
fi | |
fi | |
} | |
policy_process() { | |
local i j uid="$9" | |
if [ -z "$uid" ]; then # first non-recursive call | |
[ "$enabled" -gt 0 ] || return 0 | |
unset processPolicyWarning | |
unset processPolicyError | |
uid="$1" | |
if is_nft; then | |
chain="$(str_to_lower "$chain")" | |
else | |
chain="$(str_to_upper "$chain")" | |
fi | |
proto="$(str_to_lower "$proto")" | |
[ "$proto" = 'auto' ] && unset proto | |
[ "$proto" = 'all' ] && unset proto | |
output 2 "Routing '$name' via $interface " | |
if [ -z "${src_addr}${src_port}${dest_addr}${dest_port}" ]; then | |
errorSummary="${errorSummary}${_ERROR_}: Policy '$name' has no source/destination parameters\\n" | |
output_fail; return 1; | |
fi | |
if [ -z "$interface" ]; then | |
errorSummary="${errorSummary}${_ERROR_}: Policy '$name' has no assigned interface\\n" | |
output_fail; return 1; | |
fi | |
if ! is_supported_interface "$interface"; then | |
errorSummary="${errorSummary}${_ERROR_}: Policy '$name' has an unknown interface: '${interface}'\\n" | |
output_fail; return 1; | |
fi | |
src_port="${src_port// / }"; src_port="${src_port// /,}"; src_port="${src_port//,\!/ !}"; | |
dest_port="${dest_port// / }"; dest_port="${dest_port// /,}"; dest_port="${dest_port//,\!/ !}"; | |
if is_nft; then | |
nftset 'flush' "$interface" "dst" "ip" "$uid" | |
nftset 'flush' "$interface" "src" "ip" "$uid" | |
nftset 'flush' "$interface" "src" "mac" "$uid" | |
else | |
ips 'flush' "$interface" "dst" "ip" "$uid" | |
ips 'flush' "$interface" "src" "ip" "$uid" | |
ips 'flush' "$interface" "src" "mac" "$uid" | |
fi | |
policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid" | |
if [ -n "$processPolicyWarning" ]; then | |
warningSummary="${warningSummary}${processPolicyWarning}\\n" | |
fi | |
if [ -n "$processPolicyError" ]; then | |
output_fail | |
errorSummary="${errorSummary}${processPolicyError}\\n" | |
else | |
output_ok | |
fi | |
else # recursive call, get options from passed variables | |
local name="$1" interface="$2" src_addr="$3" src_port="$4" dest_addr="$5" dest_port="$6" proto="$7" chain="$8" | |
if str_contains "$src_addr" '[ ;\{\}]'; then | |
for i in $(str_extras_to_space "$src_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$i" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done | |
elif str_contains "$src_port" '[ ;\{\}]'; then | |
for i in $(str_extras_to_space "$src_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$i" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid"; done | |
elif str_contains "$dest_addr" '[ ;\{\}]'; then | |
for i in $(str_extras_to_space "$dest_addr"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$i" "$dest_port" "$proto" "$chain" "$uid"; done | |
elif str_contains "$dest_port" '[ ;\{\}]'; then | |
for i in $(str_extras_to_space "$dest_port"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$i" "$proto" "$chain" "$uid"; done | |
elif str_contains "$proto" '[ ;\{\}]'; then | |
for i in $(str_extras_to_space "$proto"); do [ -n "$i" ] && policy_process "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$i" "$chain" "$uid"; done | |
else | |
if is_tor "$interface"; then | |
policy_routing_tor "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid" | |
else | |
policy_routing "$name" "$interface" "$src_addr" "$src_port" "$dest_addr" "$dest_port" "$proto" "$chain" "$uid" | |
fi | |
fi | |
fi | |
} | |
interface_process_tor() { if is_nft; then interface_process_tor_nft "$@"; else interface_process_tor_iptables "$@"; fi; } | |
interface_process_tor_iptables() { | |
local s=0 iface="$1" action="$2" | |
local displayText set_name4 set_name6 | |
local dnsPort trafficPort | |
case "$action" in | |
reload) | |
displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}" | |
gatewaySummary="${gatewaySummary}${displayText}\\n" | |
;; | |
destroy) | |
for i in $chainsList; do | |
i="$(str_to_upper "$i")" | |
ipt -t nat -D "${i}" -m mark --mark "0x0/${fw_mask}" -j "${nftPrefix}_${i}" | |
ipt -t nat -F "${nftPrefix}_${i}"; ipt -t nat -X "${nftPrefix}_${i}"; | |
done | |
;; | |
create) | |
output 2 "Creating TOR redirects " | |
dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')" | |
trafficPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')" | |
dnsPort="${dnsPort:-9053}"; trafficPort="${trafficPort:-9040}"; | |
for i in $chainsList; do | |
ipt -t nat -N "${nftPrefix}_${i}" | |
ipt -t nat -A "$i" -m mark --mark "0x0/${fw_mask}" -j "${nftPrefix}_${i}" | |
done | |
if ips 'create_resolver' "$iface" 'dst' 'ip' && ips 'flush' "$iface" 'dst' 'ip'; then | |
set_name4="${ipsPrefix}_${iface}_4_dst_ip" | |
for i in $chainsList; do | |
i="$(str_to_lower "$i")" | |
ipt -t nat -I "${nftPrefix}_${i}" -p udp -m udp --dport 53 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$dnsPort" -m comment --comment "TorDNS-UDP" || s=1 | |
ipt -t nat -I "${nftPrefix}_${i}" -p tcp -m tcp --dport 80 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTP-TCP" || s=1 | |
ipt -t nat -I "${nftPrefix}_${i}" -p udp -m udp --dport 80 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTP-UDP" || s=1 | |
ipt -t nat -I "${nftPrefix}_${i}" -p tcp -m tcp --dport 443 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTPS-TCP" || s=1 | |
ipt -t nat -I "${nftPrefix}_${i}" -p udp -m udp --dport 443 -m set --match-set "${set_name4}" dst -j REDIRECT --to-ports "$trafficPort" -m comment --comment "TorHTTPS-UDP" || s=1 | |
done | |
else | |
s=1 | |
fi | |
displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}" | |
if [ "$s" -eq 0 ]; then | |
gatewaySummary="${gatewaySummary}${displayText}\\n" | |
output_ok | |
else | |
errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n" | |
output_fail | |
fi | |
;; | |
esac | |
return $s | |
} | |
interface_process_tor_nft() { | |
local s=0 iface="$1" action="$2" | |
local displayText set_name4 set_name6 | |
local dnsPort trafficPort | |
case "$action" in | |
reload) | |
displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}" | |
gatewaySummary="${gatewaySummary}${displayText}\\n" | |
;; | |
destroy) | |
;; | |
create) | |
output 2 "Creating TOR redirects " | |
dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')" | |
trafficPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')" | |
dnsPort="${dnsPort:-9053}"; trafficPort="${trafficPort:-9040}"; | |
if nftset 'create_resolver' "$iface" 'dst' 'ip' && nftset 'flush' "$iface" 'dst' 'ip'; then | |
set_name4="${nftPrefix}_${iface}_4_dst_ip" | |
set_name6="${nftPrefix}_${iface}_6_dst_ip" | |
nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 53 counter redirect to :"$dnsPort" comment "Tor-DNS-UDP-ipv4" || s=1 | |
nft meta nfproto ipv4 tcp daddr "@${set_name4}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-TCP-ipv4" || s=1 | |
nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-UDP-ipv4" || s=1 | |
nft meta nfproto ipv4 tcp daddr "@${set_name4}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-TCP-ipv4" || s=1 | |
nft meta nfproto ipv4 udp daddr "@${set_name4}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-UDP-ipv4" || s=1 | |
nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 53 counter redirect to :"$dnsPort" comment "Tor-DNS-UDP-ipv6" || s=1 | |
nft6 meta nfproto ipv6 tcp daddr "@${set_name6}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-TCP-ipv6" || s=1 | |
nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 80 counter redirect to :"$trafficPort" comment "Tor-HTTP-UDP-ipv6" || s=1 | |
nft6 meta nfproto ipv6 tcp daddr "@${set_name6}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-TCP-ipv6" || s=1 | |
nft6 meta nfproto ipv6 udp daddr "@${set_name6}" dport 443 counter redirect to :"$trafficPort" comment "Tor-HTTPS-UDP-ipv6" || s=1 | |
else | |
s=1 | |
fi | |
displayText="${iface}/53->${dnsPort}/80,443->${trafficPort}" | |
if [ "$s" -eq 0 ]; then | |
gatewaySummary="${gatewaySummary}${displayText}\\n" | |
output_ok | |
else | |
errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n" | |
output_fail | |
fi | |
;; | |
esac | |
return $s | |
} | |
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 | |
return 1 | |
fi | |
case "$action" in | |
create) | |
if is_netifd_table "$iface"; then | |
ipv4_error=0 | |
if [ -z "$(ip rule list fwmark "${mark}/${fw_mask}" table "$tid")" ]; then | |
ip rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1 | |
fi | |
if is_nft; then | |
nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1 | |
nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1 | |
nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1 | |
else | |
ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1 | |
ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1 | |
ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1 | |
fi | |
if [ -n "$ipv6_enabled" ]; then | |
ipv6_error=0 | |
if [ -z "$(ip -6 rule list fwmark "${mark}/${fw_mask}" table "$tid")" ]; then | |
ip -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1 | |
fi | |
fi | |
else | |
sed -i "/${ipTablePrefix}_${iface}/d" /etc/iproute2/rt_tables | |
ip route flush table "$tid" >/dev/null 2>&1 | |
if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then | |
ipv4_error=0 | |
echo "$tid ${ipTablePrefix}_${iface}" >> /etc/iproute2/rt_tables | |
if [ -z "$gw4" ]; then | |
ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1 | |
else | |
ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1 | |
fi | |
# shellcheck disable=SC2086 | |
while read -r i; do | |
idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')" | |
if ! is_supported_iface_dev "$idev"; then | |
ip -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1 | |
fi | |
done << EOF | |
$(ip -4 route list table main) | |
EOF | |
if [ -z "$(ip rule list fwmark "${mark}/${fw_mask}" table "$tid")" ]; then | |
ip rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1 | |
fi | |
if is_nft; then | |
nft add chain inet "$nftTable" "${nftPrefix}_mark_${mark}" || ipv4_error=1 | |
nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} counter mark set mark and ${fw_maskXor} xor ${mark}" || ipv4_error=1 | |
nft add rule inet "$nftTable" "${nftPrefix}_mark_${mark} return" || ipv4_error=1 | |
else | |
ipt -t mangle -N "${iptPrefix}_MARK_${mark}" || ipv4_error=1 | |
ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j MARK --set-xmark "${mark}/${fw_mask}" || ipv4_error=1 | |
ipt -t mangle -A "${iptPrefix}_MARK_${mark}" -j RETURN || ipv4_error=1 | |
fi | |
fi | |
if [ -n "$ipv6_enabled" ]; then | |
ipv6_error=0 | |
if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne 0 ]; then | |
if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then | |
ip -6 route add unreachable default table "$tid" || ipv6_error=1 | |
elif ip -6 route list table main | grep -q " dev $dev6 "; then | |
while read -r i; do | |
ip -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1 | |
done << EOF | |
$(ip -6 route list table main | grep " dev $dev6 ") | |
EOF | |
else | |
ip -6 route add "$(ip -6 -o a show "$dev6" | awk '{print $4}')" dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1 | |
ip -6 route add default dev "$dev6" table "$tid" >/dev/null 2>&1 || ipv6_error=1 | |
fi | |
fi | |
if [ -z "$(ip -6 rule list fwmark "${mark}/${fw_mask}" table "$tid")" ]; then | |
ip -6 rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv6_error=1 | |
fi | |
fi | |
fi | |
if [ "$ipv4_error" -eq 0 ] || [ "$ipv6_error" -eq 0 ]; then | |
dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)" | |
if is_nft; then | |
if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then | |
nft add rule inet "$nftTable" "${nftPrefix}_prerouting ip dscp ${dscp} goto ${nftPrefix}_mark_${mark}" || s=1 | |
fi | |
if [ "$iface" = "$icmp_interface" ]; then | |
nft add rule inet "$nftTable" "${nftPrefix}_output ip protocol icmp goto ${nftPrefix}_mark_${mark}" || s=1 | |
fi | |
else | |
if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then | |
ipt -t mangle -I "${iptPrefix}_PREROUTING" -m dscp --dscp "${dscp}" -g "${iptPrefix}_MARK_${mark}" || s=1 | |
fi | |
if [ "$iface" = "$icmp_interface" ]; then | |
ipt -t mangle -I "${iptPrefix}_OUTPUT" -p icmp -g "${iptPrefix}_MARK_${mark}" || s=1 | |
fi | |
fi | |
else | |
s=1 | |
fi | |
return "$s" | |
;; | |
create_user_set) | |
if is_nft; then | |
nftset 'create_user_set' "$iface" 'dst' 'ip' '' '' "$mark" || s=1 | |
nftset 'create_user_set' "$iface" 'src' 'ip' '' '' "$mark" || s=1 | |
nftset 'create_user_set' "$iface" 'src' 'mac' '' '' "$mark" || s=1 | |
else | |
ips 'create_user_set' "$iface" 'dst' 'ip' '' '' "$mark" || s=1 | |
ips 'create_user_set' "$iface" 'src' 'ip' '' '' "$mark" || s=1 | |
ips 'create_user_set' "$iface" 'dst' 'net' '' '' "$mark" || s=1 | |
ips 'create_user_set' "$iface" 'src' 'net' '' '' "$mark" || s=1 | |
ips 'create_user_set' "$iface" 'src' 'mac' '' '' "$mark" || s=1 | |
fi | |
return "$s" | |
;; | |
delete|destroy) | |
ip rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1 | |
if ! is_netifd_table "$iface"; then | |
ip route flush table "$tid" >/dev/null 2>&1 | |
sed -i "/${ipTablePrefix}_${iface}/d" /etc/iproute2/rt_tables | |
fi | |
return "$s" | |
;; | |
reload_interface) | |
is_netifd_table "$iface" && return 0; | |
ipv4_error=0 | |
ip rule del fwmark "${mark}/${fw_mask}" table "$tid" >/dev/null 2>&1 | |
ip route flush table "$tid" >/dev/null 2>&1 | |
ip -4 route del default table "$tid" >/dev/null 2>&1 | |
if [ -n "$gw4" ] || [ "$strict_enforcement" -ne 0 ]; then | |
if [ -z "$gw4" ]; then | |
ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1 | |
else | |
ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1 | |
fi | |
if [ -z "$(ip rule list fwmark "${mark}/${fw_mask}" table "$tid")" ]; then | |
ip rule add fwmark "${mark}/${fw_mask}" table "$tid" priority "$priority" || ipv4_error=1 | |
fi | |
fi | |
if [ -n "$ipv6_enabled" ]; then | |
ip -6 route del default table "$tid" >/dev/null 2>&1 | |
ipv6_error=0 | |
if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strict_enforcement" -ne 0 ]; then | |
if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then | |
ip -6 route add unreachable default table "$tid" || ipv6_error=1 | |
else | |
while read -r i; do | |
# shellcheck disable=SC2086 | |
ip -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1 | |
done << EOF | |
$(ip -6 route list default table main | grep " dev $dev6 ") | |
EOF | |
fi | |
fi | |
if [ -z "$(ip -6 rule list fwmark "${mark}/${fw_mask}" table "$tid")" ]; then | |
ip -6 rule add 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 '' | |
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 true | |
else | |
json_add_boolean default false | |
fi | |
json_add_string action "$action" | |
json_add_string table_id "$tid" | |
json_add_string mark "$mark" | |
json_add_string priority "$priority" | |
json_close_object | |
} | |
interface_process() { | |
local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" reloadedIface="$3" | |
local displayText dispDev dispGw4 dispGw6 dispStatus | |
if [ "$iface" = 'all' ] && [ "$action" = 'prepare' ]; then | |
config_load 'network' | |
ifaceMark="$(printf '0x%06x' "$wan_mark")" | |
ifacePriority="$wan_ip_rules_priority" | |
return 0 | |
fi | |
is_supported_interface "$iface" || return 0 | |
is_wan6 "$iface" && return 0 | |
[ $((ifaceMark)) -gt $((fw_mask)) ] && return 1 | |
network_get_device dev "$iface" | |
if is_wan "$iface" && [ -n "$wanIface6" ] && str_contains "$wanIface6" "$iface"; then | |
network_get_device dev6 "$wanIface6" | |
fi | |
[ -z "$dev6" ] && dev6="$dev" | |
[ -z "$ifaceMark" ] && ifaceMark="$(printf '0x%06x' "$wan_mark")" | |
[ -z "$ifacePriority" ] && ifacePriority="$wan_ip_rules_priority" | |
ifaceTableID="$(get_rt_tables_id "$iface")" | |
[ -z "$ifaceTableID" ] && ifaceTableID="$(get_rt_tables_next_id)" | |
eval "mark_${iface//-/_}"='$ifaceMark' | |
eval "tid_${iface//-/_}"='$ifaceTableID' | |
pbr_get_gateway gw4 "$iface" "$dev" | |
pbr_get_gateway6 gw6 "$iface" "$dev6" | |
dispGw4="${gw4:-0.0.0.0}" | |
dispGw6="${gw6:-::/0}" | |
[ "$iface" != "$dev" ] && dispDev="$dev" | |
is_default_dev "$dev" && dispStatus="${__OK__}" | |
displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}" | |
case "$action" in | |
create) | |
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" | |
output_ok | |
else | |
errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n" | |
output_fail | |
fi | |
;; | |
create_user_set) | |
interface_routing 'create_user_set' "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$ifacePriority" | |
;; | |
destroy) | |
displayText="${iface}/${dispDev:+$dispDev/}${dispGw4}${ipv6_enabled:+/$dispGw6}" | |
output 2 "Removing routing for '$displayText' " | |
interface_routing 'destroy' "${ifaceTableID}" "${ifaceMark}" "${iface}" | |
output_ok | |
;; | |
reload) | |
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n" | |
;; | |
reload_interface) | |
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" | |
output_ok | |
else | |
errorSummary="${errorSummary}${_ERROR_}: Failed to reload '$displayText'\\n" | |
output_fail | |
fi | |
else | |
gatewaySummary="${gatewaySummary}${displayText}${dispStatus:+ $dispStatus}\\n" | |
fi | |
;; | |
esac | |
# ifaceTableID="$((ifaceTableID + 1))" | |
ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))" | |
ifacePriority="$((ifacePriority - 1))" | |
return $s | |
} | |
user_file_process() { | |
local shellBin="${SHELL:-/bin/ash}" | |
[ "$enabled" -gt 0 ] || return 0 | |
if [ ! -s "$path" ]; then | |
errorSummary="${errorSummary}${_ERROR_}: Custom user file '$path' not found or empty!\\n" | |
output_fail | |
return 1 | |
fi | |
if ! $shellBin -n "$path"; then | |
errorSummary="${errorSummary}${_ERROR_}: Syntax error in custom user file '$path'!\\n" | |
output_fail | |
return 1 | |
fi | |
output 2 "Running $path " | |
# shellcheck disable=SC1090 | |
if ! . "$path"; then | |
errorSummary="${errorSummary}${_ERROR_}: Error running custom user file '$path'!\\n" | |
if grep -q -w 'curl' "$path" && ! is_present 'curl'; then | |
errorSummary="${errorSummary}${_ERROR_}: Use of 'curl' is detected in custom user file '$path', but 'curl' isn't installed!\\n" | |
errorSummary="${errorSummary}${_ERROR_}: If 'curl' is needed, install it with 'opkg update; opkg install curl;' command in CLI.\\n" | |
fi | |
output_fail | |
return 1 | |
else | |
output_ok | |
return 0 | |
fi | |
} | |
reload_firewall() { rc_procd start_service 'on_firewall_reload' "$1"; } | |
reload_interface() { rc_procd start_service 'on_interface_reload' "$1"; } | |
start_service() { | |
local resolverStoredHash resolverNewHash i reloadedIface param="$1" | |
load_environment 'on_start' "$(load_validate_config)" || return 1 | |
is_wan_up || return 1 | |
rm -f "$nftTempFile" | |
case "$param" in | |
on_boot) | |
serviceStartTrigger='on_start' | |
;; | |
on_firewall_reload) | |
if [ -z "$(ubus_get_status 'gateways')" ]; then # service is not running, do not start it on firewall reload | |
logger -t "$packageName" "Reload on firewall action aborted: service not running." | |
return 0; | |
else | |
serviceStartTrigger='on_start' | |
fi | |
;; | |
on_interface_reload) | |
serviceStartTrigger='on_interface_reload' | |
reloadedIface="$2" | |
;; | |
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 gateway)" ]; then | |
serviceStartTrigger='on_start' | |
unset reloadedIface | |
elif [ "$serviceStartTrigger" = 'on_interface_reload' ] && \ | |
[ -z "$(ubus_get_interface "$reloadedIface" 'gateway_4')" ] && \ | |
[ -z "$(ubus_get_interface "$reloadedIface" 'gateway_6')" ]; 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 | |
traffic_killswitch 'insert' | |
case $serviceStartTrigger in | |
on_interface_reload) | |
output 1 "Reloading Interface: $reloadedIface " | |
json_add_array 'gateways' | |
interface_process 'all' 'prepare' | |
config_foreach interface_process 'interface' 'reload_interface' "$reloadedIface" | |
json_close_array | |
output 1 '\n' | |
;; | |
on_reload) | |
cleanup_dnsmasq | |
cleanup_main_chains | |
cleanup_sets | |
if ! is_nft; then | |
for i in $chainsList; do | |
i="$(str_to_upper "$i")" | |
ipt -t mangle -N "${iptPrefix}_${i}" | |
ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}" | |
done | |
fi | |
json_add_array 'gateways' | |
interface_process 'all' 'prepare'; config_foreach interface_process 'interface' 'reload'; | |
interface_process_tor 'tor' 'destroy'; is_tor_running && interface_process_tor 'tor' 'reload'; | |
json_close_array | |
if is_config_enabled 'policy'; then | |
output 1 'Processing policies ' | |
config_load "$packageName" | |
config_foreach load_validate_policy 'policy' policy_process | |
output 1 '\n' | |
fi | |
if is_config_enabled 'include'; then | |
interface_process 'all' 'prepare' | |
config_foreach interface_process 'interface' 'create_user_set' | |
output 1 'Processing user file(s) ' | |
config_load "$packageName" | |
config_foreach load_validate_include 'include' user_file_process | |
output 1 '\n' | |
fi | |
if [ -s "$dnsmasqFile" ]; then | |
resolverNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')" | |
fi | |
[ "$resolverNewHash" != "$resolverStoredHash" ] && dnsmasq_restart | |
;; | |
on_start|*) | |
cleanup_dnsmasq | |
cleanup_main_chains | |
cleanup_sets | |
cleanup_marking_chains | |
if ! is_nft; then | |
for i in $chainsList; do | |
i="$(str_to_upper "$i")" | |
ipt -t mangle -N "${iptPrefix}_${i}" | |
ipt -t mangle "$rule_create_option" "$i" -m mark --mark "0x0/${fw_mask}" -j "${iptPrefix}_${i}" | |
done | |
fi | |
output 1 'Processing interfaces ' | |
json_add_array 'gateways' | |
interface_process 'all' 'prepare' | |
config_foreach interface_process 'interface' 'create' | |
interface_process_tor 'tor' 'destroy'; is_tor_running && interface_process_tor 'tor' 'create' | |
json_close_array | |
ip route flush cache | |
output 1 '\n' | |
if is_config_enabled 'policy'; then | |
output 1 'Processing policies ' | |
config_load "$packageName" | |
config_foreach load_validate_policy 'policy' policy_process | |
output 1 '\n' | |
fi | |
if is_config_enabled 'include'; then | |
interface_process 'all' 'prepare'; config_foreach interface_process 'interface' 'create_user_set'; | |
output 1 'Processing user file(s) ' | |
config_load "$packageName" | |
config_foreach load_validate_include 'include' user_file_process | |
output 1 '\n' | |
fi | |
if [ -s "$dnsmasqFile" ]; then | |
resolverNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')" | |
fi | |
[ "$resolverNewHash" != "$resolverStoredHash" ] && dnsmasq_restart | |
;; | |
esac | |
traffic_killswitch 'remove' | |
if [ -z "$gatewaySummary" ]; then | |
errorSummary="${errorSummary}${_ERROR_}: Failed to set up any gateway!\\n" | |
fi | |
json_add_object 'status' | |
[ -n "$gatewaySummary" ] && json_add_string 'gateways' "$gatewaySummary" | |
[ -n "$errorSummary" ] && json_add_string 'errors' "$errorSummary" | |
[ -n "$warningSummary" ] && json_add_string 'warnings' "$warningSummary" | |
if [ "$strict_enforcement" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then | |
json_add_string 'mode' "strict" | |
fi | |
json_close_object | |
procd_close_data | |
procd_close_instance | |
} | |
service_started() { | |
if is_nft; then | |
[ -n "$gatewaySummary" ] && output "$serviceName (nft) started with gateways:\\n${gatewaySummary}" | |
else | |
[ -n "$gatewaySummary" ] && output "$serviceName (iptables) started with gateways:\\n${gatewaySummary}" | |
fi | |
[ -n "$errorSummary" ] && output "${errorSummary}" | |
[ -n "$warningSummary" ] && output "${warningSummary}" | |
if [ -n "$errorSummary" ]; then | |
return 2 | |
elif [ -n "$warningSummary" ]; then | |
return 1 | |
else | |
return 0 | |
fi | |
} | |
service_triggers() { | |
local n | |
load_environment 'on_triggers' | |
# shellcheck disable=SC2034 | |
PROCD_RELOAD_DELAY=$(( procd_reload_delay * 1000 )) | |
procd_open_validate | |
load_validate_config | |
load_validate_policy | |
load_validate_include | |
procd_close_validate | |
procd_open_trigger | |
procd_add_reload_trigger 'openvpn' | |
procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload | |
for n in $ifacesSupported; do | |
procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} reload_interface "$n" | |
done | |
procd_close_trigger | |
if [ "$serviceStartTrigger" = 'on_start' ]; then | |
output 3 "$serviceName monitoring interfaces: ${ifacesSupported}\\n" | |
fi | |
} | |
stop_service() { | |
local i | |
load_environment 'on_stop' | |
is_service_running || return 0 | |
traffic_killswitch 'insert' | |
cleanup_main_chains | |
cleanup_sets | |
cleanup_marking_chains | |
output 1 'Resetting interfaces ' | |
config_load 'network' | |
config_foreach interface_process 'interface' 'destroy' | |
interface_process_tor 'tor' 'destroy' | |
output 1 "\\n" | |
ip route flush cache | |
unset ifaceMark | |
unset ifaceTableID | |
cleanup_dnsmasq && dnsmasq_restart | |
traffic_killswitch 'remove' | |
if [ "$enabled" -ne 0 ]; then | |
if is_nft; then | |
output "$serviceName (nft) stopped "; output_okn; | |
else | |
output "$serviceName (iptables) stopped "; output_okn; | |
fi | |
fi | |
} | |
status_service() { | |
local _SEPARATOR_='============================================================' | |
load_environment 'on_status' | |
if is_nft; then | |
status_service_nft "$@" | |
else | |
status_service_iptables "$@" | |
fi | |
} | |
status_service_nft() { | |
local i dev dev6 wan_tid | |
json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version | |
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" | |
status="$serviceName running on $dist $vers." | |
[ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}." | |
[ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}." | |
echo "$_SEPARATOR_" | |
echo "$packageName - environment" | |
echo "$status" | |
echo "$_SEPARATOR_" | |
dnsmasq --version 2>/dev/null | sed '/^$/,$d' | |
echo "$_SEPARATOR_" | |
echo "$packageName chains - policies" | |
for i in forward input output prerouting postrouting; do | |
"$nft" 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" 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" list table inet "$nftTable" | sed -n "/set ${i} {/,/\t}/p" | |
done | |
if [ -s "$dnsmasqFile" ]; then | |
echo "$_SEPARATOR_" | |
echo "dnsmasq sets" | |
cat "$dnsmasqFile" | |
fi | |
# echo "$_SEPARATOR_" | |
# ip rule list | grep "${packageName}_" | |
echo "$_SEPARATOR_" | |
tableCount="$(grep -c "${packageName}_" /etc/iproute2/rt_tables)" || tableCount=0 | |
wan_tid=$(($(get_rt_tables_next_id)-tableCount)) | |
i=0; while [ $i -lt $tableCount ]; do | |
echo "IPv4 table $((wan_tid + i)) route: $(ip -4 route show table $((wan_tid + i)) | grep default)" | |
echo "IPv4 table $((wan_tid + i)) rule: $(ip -4 rule list table "$((wan_tid + i))")" | |
i=$((i + 1)) | |
done | |
} | |
status_service_iptables() { | |
local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j wan_tid | |
json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version | |
if [ -n "$wanIface4" ]; then | |
network_get_gateway wanGW4 "$wanIface4" | |
network_get_device dev "$wanIface4" | |
fi | |
if [ -n "$wanIface6" ]; then | |
network_get_device dev6 "$wanIface6" | |
wanGW6=$(ip -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" | |
status="$serviceName running on $dist $vers." | |
[ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}." | |
[ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}." | |
{ | |
echo "$status" | |
echo "$_SEPARATOR_" | |
dnsmasq --version 2>/dev/null | sed '/^$/,$d' | |
if [ -n "$1" ]; then | |
echo "$_SEPARATOR_" | |
echo "Resolving domains" | |
for i in $1; do | |
echo "$i: $(resolveip "$i" | tr '\n' ' ')" | |
done | |
fi | |
echo "$_SEPARATOR_" | |
echo "Routes/IP Rules" | |
tableCount="$(grep -c "${packageName}_" /etc/iproute2/rt_tables)" || tableCount=0 | |
if [ -n "$set_d" ]; then route; else route | grep '^default'; fi | |
if [ -n "$set_d" ]; then ip rule list; fi | |
wan_tid=$(($(get_rt_tables_next_id)-tableCount)) | |
i=0; while [ $i -lt $tableCount ]; do | |
echo "IPv4 table $((wan_tid + i)) route: $(ip -4 route show table $((wan_tid + i)) | grep default)" | |
echo "IPv4 table $((wan_tid + i)) rule: $(ip -4 rule list table "$((wan_tid + i))")" | |
i=$((i + 1)) | |
done | |
if [ -n "$ipv6_enabled" ]; then | |
i=0; while [ $i -lt $tableCount ]; do | |
ip -6 route show table $((wan_tid + i)) | while read -r param; do | |
echo "IPv6 Table $((wan_tid + i)): $param" | |
done | |
i=$((i + 1)) | |
done | |
fi | |
for j in Mangle NAT; do | |
if [ -z "$set_d" ]; then | |
for i in $chainsList; do | |
i="$(str_to_upper "$i")" | |
if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" >/dev/null 2>&1; then | |
echo "$_SEPARATOR_" | |
echo "$j IP Table: $i" | |
iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" | |
if [ -n "$ipv6_enabled" ]; then | |
echo "$_SEPARATOR_" | |
echo "$j IPv6 Table: $i" | |
iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_${i}" | |
fi | |
fi | |
done | |
else | |
echo "$_SEPARATOR_" | |
echo "$j IP Table" | |
iptables -L -t "$(str_to_lower $j)" | |
if [ -n "$ipv6_enabled" ]; then | |
echo "$_SEPARATOR_" | |
echo "$j IPv6 Table" | |
iptables -L -t "$(str_to_lower $j)" | |
fi | |
fi | |
i=0; ifaceMark="$wan_mark"; | |
while [ $i -lt $tableCount ]; do | |
if iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}" >/dev/null 2>&1; then | |
echo "$_SEPARATOR_" | |
echo "$j IP Table MARK Chain: ${iptPrefix}_MARK_${ifaceMark}" | |
iptables -v -t "$(str_to_lower $j)" -S "${iptPrefix}_MARK_${ifaceMark}" | |
ifaceMark="$(printf '0x%06x' $((ifaceMark + wan_mark)))"; | |
fi | |
i=$((i + 1)) | |
done | |
done | |
echo "$_SEPARATOR_" | |
echo "Current ipsets" | |
ipset save | |
if [ -s "$dnsmasqFile" ]; then | |
echo "$_SEPARATOR_" | |
echo "DNSMASQ sets" | |
cat "$dnsmasqFile" | |
fi | |
echo "$_SEPARATOR_" | |
} | tee -a /var/${packageName}-support | |
if [ -n "$set_p" ]; then | |
printf "%b" "Pasting to paste.ee... " | |
if is_present 'curl' && is_variant_installed 'libopenssl' && is_installed 'ca-bundle'; then | |
json_init; json_add_string "description" "${packageName}-support" | |
json_add_array "sections"; json_add_object '0' | |
json_add_string "name" "$(uci -q get system.@system[0].hostname)" | |
json_add_string "contents" "$(cat /var/${packageName}-support)" | |
json_close_object; json_close_array; payload=$(json_dump) | |
out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload") | |
json_load "$out"; json_get_var id id; json_get_var s success | |
[ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n" | |
[ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support" | |
else | |
printf "%b" "${__FAIL__}\\n" | |
printf "%b" "${_ERROR_}: The curl, libopenssl or ca-bundle packages were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n" | |
fi | |
else | |
printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n" | |
fi | |
} | |
# shellcheck disable=SC2120 | |
load_validate_config() { | |
uci_load_validate "$packageName" "$packageName" "$1" "${2}${3:+ $3}" \ | |
'enabled:bool:0' \ | |
'procd_boot_delay:integer:0' \ | |
'strict_enforcement:bool:1' \ | |
'secure_reload:bool:0' \ | |
'ipv6_enabled:bool:0' \ | |
'resolver_set:or("", "none", "dnsmasq.ipset", "dnsmasq.nftset"):dnsmasq.nftset' \ | |
'verbosity:range(0,2):1' \ | |
"wan_mark:regex('0x[A-Fa-f0-9]{8}'):0x010000" \ | |
"fw_mask:regex('0x[A-Fa-f0-9]{8}'):0xff0000" \ | |
'icmp_interface:or("","ignore", uci("network", "@interface"))' \ | |
'ignored_interface:list(uci("network", "@interface"))' \ | |
'supported_interface:list(uci("network", "@interface"))' \ | |
'boot_timeout:integer:30' \ | |
'wan_ip_rules_priority:uinteger:30000' \ | |
'rule_create_option:or("", "add", "insert"):add' \ | |
'procd_reload_delay:integer:0' \ | |
'webui_supported_protocol:list(string)' | |
} | |
# shellcheck disable=SC2120 | |
load_validate_policy() { | |
local name | |
local enabled | |
local interface | |
local proto | |
local chain | |
local src_addr | |
local src_port | |
local dest_addr | |
local dest_port | |
uci_load_validate "$packageName" 'policy' "$1" "${2}${3:+ $3}" \ | |
'name:string:Untitled' \ | |
'enabled:bool:1' \ | |
'interface:or(uci("network", "@interface"),"ignore"):wan' \ | |
'proto:or(string)' \ | |
'chain:or("", "forward", "input", "output", "prerouting", "postrouting", "FORWARD", "INPUT", "OUTPUT", "PREROUTING", "POSTROUTING"):prerouting' \ | |
'src_addr:list(neg(or(host,network,macaddr,string)))' \ | |
'src_port:list(neg(or(portrange,string)))' \ | |
'dest_addr:list(neg(or(host,network,string)))' \ | |
'dest_port:list(neg(or(portrange,string)))' | |
} | |
# shellcheck disable=SC2120 | |
load_validate_include() { | |
local path= | |
local enabled= | |
uci_load_validate "$packageName" 'include' "$1" "${2}${3:+ $3}" \ | |
'path:file' \ | |
'enabled:bool:0' | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment