| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | #!/bin/sh /etc/rc.common | 
|  | 2 | # Copyright 2017-2020 Stan Grishin (stangri@melmac.net) | 
|  | 3 | # shellcheck disable=SC2039,SC1091,SC2018,SC2019,SC3043,SC3057,SC3060 | 
|  | 4 | PKG_VERSION='dev-test' | 
|  | 5 |  | 
|  | 6 | # sysctl net.ipv4.conf.default.rp_filter=1 | 
|  | 7 | # sysctl net.ipv4.conf.all.rp_filter=1 | 
|  | 8 |  | 
|  | 9 | # shellcheck disable=SC2034 | 
|  | 10 | START=94 | 
|  | 11 | # shellcheck disable=SC2034 | 
|  | 12 | USE_PROCD=1 | 
|  | 13 |  | 
|  | 14 | if type extra_command 1>/dev/null 2>&1; then | 
|  | 15 | extra_command 'support' "Generates output required to troubleshoot routing issues | 
|  | 16 | Use '-d' option for more detailed output | 
|  | 17 | Use '-p' option to automatically upload data under VPR paste.ee account | 
|  | 18 | WARNING: while paste.ee uploads are unlisted, they are still publicly available | 
|  | 19 | List domain names after options to include their lookup in report" | 
|  | 20 | extra_command 'version' 'Show version information' | 
|  | 21 | extra_command 'reload_interface' 'Reload specific interface only' | 
|  | 22 | else | 
|  | 23 | # shellcheck disable=SC2034 | 
|  | 24 | EXTRA_COMMANDS='support version' | 
|  | 25 | # shellcheck disable=SC2034 | 
|  | 26 | EXTRA_HELP="	support	Generates output required to troubleshoot routing issues | 
|  | 27 | Use '-d' option for more detailed output | 
|  | 28 | Use '-p' option to automatically upload data under VPR paste.ee account | 
|  | 29 | WARNING: while paste.ee uploads are unlisted, they are still publicly available | 
|  | 30 | List domain names after options to include their lookup in report" | 
|  | 31 | fi | 
|  | 32 |  | 
|  | 33 | readonly packageName='vpn-policy-routing' | 
|  | 34 | readonly serviceName="$packageName $PKG_VERSION" | 
|  | 35 | readonly PIDFile="/var/run/${packageName}.pid" | 
|  | 36 | readonly jsonFile="/var/run/${packageName}.json" | 
|  | 37 | readonly dnsmasqFile="/var/dnsmasq.d/${packageName}" | 
|  | 38 | readonly sharedMemoryOutput="/dev/shm/$packageName-output" | 
|  | 39 | readonly _OK_='\033[0;32m\xe2\x9c\x93\033[0m' | 
|  | 40 | readonly _FAIL_='\033[0;31m\xe2\x9c\x97\033[0m' | 
|  | 41 | readonly __OK__='\033[0;32m[\xe2\x9c\x93]\033[0m' | 
|  | 42 | readonly __FAIL__='\033[0;31m[\xe2\x9c\x97]\033[0m' | 
|  | 43 | readonly _ERROR_='\033[0;31mERROR\033[0m' | 
|  | 44 | readonly _WARNING_='\033[0;33mWARNING\033[0m' | 
|  | 45 |  | 
|  | 46 | gatewaySummary=''; errorSummary=''; warningSummary=''; | 
|  | 47 | serviceEnabled=''; verbosity=''; strictMode=''; | 
|  | 48 | wanTableID=''; wanMark=''; fwMask=''; | 
|  | 49 | ipv6Enabled=''; srcIpset=''; destIpset=''; resolverIpset=''; | 
|  | 50 | wanIface4=''; wanIface6=''; ifaceMark=''; ifaceTableID=''; | 
|  | 51 | ifAll=''; ifSupported=''; ignoredIfaces=''; supportedIfaces=''; icmpIface=''; | 
|  | 52 | wanGW4=''; wanGW6=''; bootTimeout=''; insertOption=''; | 
|  | 53 | webuiChainColumn=''; webuiShowIgnore=''; dnsmasqIpsetSupported=''; | 
|  | 54 | procdReloadDelay=''; | 
|  | 55 | usedChainsList='PREROUTING' | 
|  | 56 | ipsetSupported='true' | 
|  | 57 | configLoaded='false' | 
|  | 58 |  | 
|  | 59 | version() { echo "$PKG_VERSION"; } | 
|  | 60 | output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; } | 
|  | 61 | output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; } | 
|  | 62 | output_fail() { s=1; output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; } | 
|  | 63 | output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; } | 
|  | 64 | str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; } | 
|  | 65 | str_replace() { echo "${1//$2/$3}"; } | 
|  | 66 | str_contains() { [ -n "$2" ] && [ "${1//$2}" != "$1" ]; } | 
|  | 67 | str_contains_word() { echo "$1" | grep -q -w "$2"; } | 
|  | 68 | str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; } | 
|  | 69 | str_extras_to_underscore() { echo "$1" | tr '[\. ~`!@#$%^&*()\+/,<>?//;:]' '_'; } | 
|  | 70 | str_extras_to_space() { echo "$1" | tr ';{}' ' '; } | 
|  | 71 |  | 
|  | 72 | output() { | 
|  | 73 | # Can take a single parameter (text) to be output at any verbosity | 
|  | 74 | # Or target verbosity level and text to be output at specifc verbosity | 
|  | 75 | local msg memmsg logmsg | 
|  | 76 | if [ $# -ne 1 ]; then | 
|  | 77 | if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; else return 0; fi | 
|  | 78 | fi | 
|  | 79 | [ -t 1 ] && printf "%b" "$1" | 
|  | 80 | msg="${1//$serviceName /service }"; | 
|  | 81 | if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then | 
|  | 82 | [ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")" | 
|  | 83 | logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')" | 
|  | 84 | logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")" | 
|  | 85 | rm -f "$sharedMemoryOutput" | 
|  | 86 | else | 
|  | 87 | printf "%b" "$msg" >> "$sharedMemoryOutput" | 
|  | 88 | fi | 
|  | 89 | } | 
|  | 90 | is_present() { command -v "$1" >/dev/null 2>&1; } | 
|  | 91 | is_installed() { [ -s "/usr/lib/opkg/info/${1}.control" ]; } | 
|  | 92 | is_variant_installed() { [ "$(echo /usr/lib/opkg/info/"${1}"*.control)" != "/usr/lib/opkg/info/${1}*.control" ]; } | 
|  | 93 |  | 
|  | 94 | build_ifAll() { ifAll="${ifAll}${1} "; } | 
|  | 95 | build_ifSupported() { is_supported_interface "$1" && ifSupported="${ifSupported}${1} "; } | 
|  | 96 | vpr_find_iface() { | 
|  | 97 | local iface i param="$2" | 
|  | 98 | [ "$param" = 'wan6' ] || param='wan' | 
|  | 99 | "network_find_${param}" iface | 
|  | 100 | is_tunnel "$iface" && unset iface | 
|  | 101 | if [ -z "$iface" ]; then | 
|  | 102 | for i in $ifAll; do | 
|  | 103 | if "is_${param}" "$i"; then break; else unset i; fi | 
|  | 104 | done | 
|  | 105 | fi | 
|  | 106 | eval "$1"='${iface:-$i}' | 
|  | 107 | } | 
|  | 108 | vpr_get_gateway() { | 
|  | 109 | local iface="$2" dev="$3" gw | 
|  | 110 | network_get_gateway gw "$iface" | 
|  | 111 | if [ -z "$gw" ] || [ "$gw" = '0.0.0.0' ]; then | 
|  | 112 | gw="$(ip -4 a list dev "$dev" 2>/dev/null | grep inet | awk '{print $2}' | awk -F "/" '{print $1}')" | 
|  | 113 | fi | 
|  | 114 | eval "$1"='$gw' | 
|  | 115 | } | 
|  | 116 | vpr_get_gateway6() { | 
|  | 117 | local iface="$2" dev="$3" gw | 
|  | 118 | network_get_gateway6 gw "$iface" | 
|  | 119 | if [ -z "$gw" ] || [ "$gw" = '::/0' ] || [ "$gw" = '::0/0' ] || [ "$gw" = '::' ]; then | 
|  | 120 | gw="$(ip -6 a list dev "$dev" 2>/dev/null | grep inet6 | awk '{print $2}')" | 
|  | 121 | fi | 
|  | 122 | eval "$1"='$gw' | 
|  | 123 | } | 
|  | 124 | is_l2tp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "l2tp" ]; } | 
|  | 125 | is_oc() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:11}" = "openconnect" ]; } | 
|  | 126 | is_ovpn() { local dev i; for i in ifname device; do [ -z "$dev" ] && dev="$(uci -q get "network.${1}.${i}")"; done; [ "${dev:0:3}" = "tun" ] || [ "${dev:0:3}" = "tap" ] || [ -f "/sys/devices/virtual/net/${dev}/tun_flags" ]; } | 
|  | 127 | is_pptp() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:4}" = "pptp" ]; } | 
|  | 128 | is_tor() { [ "$(str_to_lower "$1")" = "tor" ]; } | 
|  | 129 | is_tor_running() { | 
|  | 130 | local ret=0 | 
|  | 131 | if [ -s "/etc/tor/torrc" ]; then | 
|  | 132 | json_load "$(ubus call service list "{ 'name': 'tor' }")" | 
|  | 133 | json_select 'tor'; json_select 'instances'; json_select 'instance1'; | 
|  | 134 | json_get_var ret 'running'; json_cleanup | 
|  | 135 | fi | 
|  | 136 | if [ "$ret" = "0" ]; then return 1; else return 0; fi | 
|  | 137 | } | 
|  | 138 | is_wg() { local proto; proto=$(uci -q get network."$1".proto); [ "${proto:0:9}" = "wireguard" ]; } | 
|  | 139 | is_tunnel() { is_l2tp "$1" || is_oc "$1" || is_ovpn "$1" || is_pptp "$1" || is_tor "$1" || is_wg "$1"; } | 
|  | 140 | is_wan() { [ "$1" = "$wanIface4" ] || { [ "${1##wan}" != "$1" ] && [ "${1##wan6}" = "$1" ]; } || [ "${1%%wan}" != "$1" ]; } | 
|  | 141 | is_wan6() { [ -n "$wanIface6" ] && [ "$1" = "$wanIface6" ] || [ "${1/#wan6}" != "$1" ] || [ "${1/%wan6}" != "$1" ]; } | 
|  | 142 | is_ignored_interface() { str_contains_word "$ignoredIfaces" "$1"; } | 
|  | 143 | is_supported_interface() { str_contains_word "$supportedIfaces" "$1" || { ! is_ignored_interface "$1" && { is_wan "$1" || is_wan6 "$1" || is_tunnel "$1"; }; }; } | 
|  | 144 | 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; } | 
|  | 145 | is_ipv4() { expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; } | 
|  | 146 | is_ipv6() { ! is_mac_address "$1" && str_contains "$1" ":"; } | 
|  | 147 | is_family_mismatch() { ( is_netmask "${1//!}" && is_ipv6 "${2//!}" ) || ( is_ipv6 "${1//!}" && is_netmask "${2//!}" ); } | 
|  | 148 | is_ipv6_link_local() { [ "${1:0:4}" = "fe80" ]; } | 
|  | 149 | is_ipv6_unique_local() { [ "${1:0:2}" = "fc" ] || [ "${1:0:2}" = "fd" ]; } | 
|  | 150 | is_ipv6_global() { [ "${1:0:4}" = "2001" ]; } | 
|  | 151 | # is_ipv6_global() { is_ipv6 "$1" && ! is_ipv6_link_local "$1" && ! is_ipv6_link_local "$1"; } | 
|  | 152 | is_netmask() { local ip="${1%/*}"; [ "$ip" != "$1" ] && is_ipv4 "$ip"; } | 
|  | 153 | is_domain() { str_contains "$1" '[a-zA-Z]'; } | 
|  | 154 | is_phys_dev() { [ "${1:0:1}" = "@" ] && ip l show | grep -E -q "^\\d+\\W+${1:1}"; } | 
|  | 155 | is_turris() { /bin/ubus -S call system board | /bin/grep 'Turris' | /bin/grep -q '15.05'; } | 
|  | 156 | is_chaos_calmer() { ubus -S call system board | grep -q 'Chaos Calmer'; } | 
|  | 157 | dnsmasq_kill() { killall -q -s HUP dnsmasq; } | 
|  | 158 | dnsmasq_restart() { output 3 'Restarting DNSMASQ '; if /etc/init.d/dnsmasq restart >/dev/null 2>&1; then output_okn; else output_failn; fi; } | 
|  | 159 | is_default_dev() { [ "$1" = "$(ip -4 r | grep -m1 'dev' | grep -Eso 'dev [^ ]*' | awk '{print $2}')" ]; } | 
|  | 160 | is_supported_iface_dev() { | 
|  | 161 | for n in $ifSupported; do | 
|  | 162 | if [ "$1" = "$(uci -q get "network.${n}.ifname" || echo "$n")" ] || \ | 
|  | 163 | [ "$1" = "$(uci -q get "network.${n}.device" || echo "$n")" ] || \ | 
|  | 164 | [ "$1" = "$(uci -q get "network.${n}.proto")-${n}" ] ; then return 0; fi | 
|  | 165 | done | 
|  | 166 | return 1 | 
|  | 167 | } | 
|  | 168 | is_supported_protocol () { grep -o '^[^#]*' /etc/protocols | grep -w -v '0' | grep . | awk '{print $1}' | grep -q "$1"; } | 
|  | 169 | append_chains_targets() { | 
|  | 170 | local chain iface name | 
|  | 171 | config_get name "$1" 'name' 'blank' | 
|  | 172 | config_get chain "$1" 'chain' 'PREROUTING' | 
|  | 173 | config_get iface "$1" 'interface' | 
|  | 174 | if ! str_contains_word "$usedChainsList" "$chain"; then | 
|  | 175 | usedChainsList="$usedChainsList $chain" | 
|  | 176 | if [ "$chain" != 'PREROUTING' ] && [ "$webuiChainColumn" != '1' ]; then | 
|  | 177 | warningSummary="${warningSummary}$_WARNING_: Chain '$chain' is used by a policy '$name', but a WebUI setting to show chains column (webui_chain_column) is disabled!\\n" | 
|  | 178 | fi | 
|  | 179 | fi | 
|  | 180 | if [ "$iface" = 'ignore' ] && ! str_contains_word "$supportedIfaces" 'ignore'; then | 
|  | 181 | supportedIfaces="$supportedIfaces ignore" | 
|  | 182 | if [ "$webuiShowIgnore" != '1' ]; then | 
|  | 183 | warningSummary="${warningSummary}$_WARNING_: The 'ignore' target is used by a policy '$name', but a WebUI setting to show 'ignore' target (webui_show_ignore_target) is disabled!\\n" | 
|  | 184 | fi | 
|  | 185 | fi | 
|  | 186 | } | 
|  | 187 |  | 
|  | 188 | load_package_config() { | 
|  | 189 | [ "$configLoaded" = 'false' ] || return 0 | 
|  | 190 |  | 
|  | 191 | config_load "$packageName" | 
|  | 192 | config_get_bool serviceEnabled      'config' 'enabled' 0 | 
|  | 193 | config_get_bool strictMode          'config' 'strict_enforcement' 1 | 
|  | 194 | config_get_bool ipv6Enabled         'config' 'ipv6_enabled' 0 | 
|  | 195 | config_get_bool srcIpset            'config' 'src_ipset' 0 | 
|  | 196 | config_get_bool destIpset           'config' 'dest_ipset' 0 | 
|  | 197 | config_get resolverIpset            'config' 'resolver_ipset' 'dnsmasq.ipset' | 
|  | 198 | config_get verbosity                'config' 'verbosity' '2' | 
|  | 199 | config_get wanTableID               'config' 'wan_tid' '201' | 
|  | 200 | config_get wanMark                  'config' 'wan_mark' '0x010000' | 
|  | 201 | config_get fwMask                   'config' 'fw_mask' '0xff0000' | 
|  | 202 | config_get icmpIface                'config' 'icmp_interface' | 
|  | 203 | config_get ignoredIfaces            'config' 'ignored_interface' | 
|  | 204 | config_get supportedIfaces          'config' 'supported_interface' | 
|  | 205 | config_get bootTimeout              'config' 'boot_timeout' '30' | 
|  | 206 | config_get insertOption             'config' 'iptables_rule_option' 'append' | 
|  | 207 | config_get procdReloadDelay         'config' 'procd_reload_delay' '0' | 
|  | 208 | config_get_bool webuiChainColumn    'config' 'webui_chain_column' '0' | 
|  | 209 | config_get_bool webuiShowIgnore     'config' 'webui_show_ignore_target' '0' | 
|  | 210 | config_foreach append_chains_targets 'policy' | 
|  | 211 |  | 
|  | 212 | if [ -z "${verbosity##*[!0-9]*}" ] || [ "$verbosity" -lt 0 ] || [ "$verbosity" -gt 2 ]; then | 
|  | 213 | verbosity=2 | 
|  | 214 | fi | 
|  | 215 |  | 
|  | 216 | . /lib/functions/network.sh | 
|  | 217 | . /usr/share/libubox/jshn.sh | 
|  | 218 | mkdir -p "${PIDFile%/*}" | 
|  | 219 | mkdir -p "${jsonFile%/*}" | 
|  | 220 | mkdir -p "${dnsmasqFile%/*}" | 
|  | 221 |  | 
|  | 222 | if [ -n "$icmpIface" ] && ! str_contains_word "$usedChainsList" 'OUTPUT'; then | 
|  | 223 | usedChainsList="$usedChainsList OUTPUT" | 
|  | 224 | fi | 
|  | 225 |  | 
|  | 226 | case $insertOption in | 
|  | 227 | insert|-i|-I) insertOption='-I';; | 
|  | 228 | append|-a|-A|*) insertOption='-A';; | 
|  | 229 | esac | 
|  | 230 |  | 
|  | 231 | [ "$resolverIpset" = 'dnsmasq.ipset' ] && dnsmasqIpsetSupported='true' | 
|  | 232 | if dnsmasq -v 2>/dev/null | grep -q 'no-ipset' || ! dnsmasq -v 2>/dev/null | grep -q -w 'ipset'; then | 
|  | 233 | unset dnsmasqIpsetSupported | 
|  | 234 | if [ -n "$dnsmasqIpsetSupported" ]; then | 
|  | 235 | errorSummary="${errorSummary}${_ERROR_}: Resolver ipset support (dnsmasq.ipset) is enabled in $packageName, but DNSMASQ ipsets are not supported on this system!\\n" | 
|  | 236 | fi | 
|  | 237 | fi | 
|  | 238 | if ! ipset help hash:net >/dev/null 2>&1; then | 
|  | 239 | unset ipsetSupported | 
|  | 240 | if [ -n "$dnsmasqIpsetSupported" ]; then | 
|  | 241 | errorSummary="${errorSummary}${_ERROR_}: DNSMASQ ipsets are supported, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n" | 
|  | 242 | unset dnsmasqIpsetSupported | 
|  | 243 | fi | 
|  | 244 | if [ "$destIpset" -ne 0 ]; then | 
|  | 245 | errorSummary="${errorSummary}${_ERROR_}: Destination ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n" | 
|  | 246 | destIpset=0 | 
|  | 247 | fi | 
|  | 248 | if [ "$srcIpset" -ne 0 ]; then | 
|  | 249 | errorSummary="${errorSummary}${_ERROR_}: Source ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:net' type!\\n" | 
|  | 250 | srcIpset=0 | 
|  | 251 | fi | 
|  | 252 | fi | 
|  | 253 | if ! ipset help hash:mac >/dev/null 2>&1; then | 
|  | 254 | if [ "$srcIpset" -ne 0 ]; then | 
|  | 255 | errorSummary="${errorSummary}${_ERROR_}: Source ipset support is enabled in $packageName, but ipset is either not installed or installed ipset does not support 'hash:mac' type!\\n" | 
|  | 256 | srcIpset=0 | 
|  | 257 | fi | 
|  | 258 | fi | 
|  | 259 |  | 
|  | 260 | configLoaded='true' | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | is_enabled() { | 
|  | 264 | load_package_config | 
|  | 265 | if [ "$serviceEnabled" -eq 0 ]; then | 
|  | 266 | if [ "$1" = 'on_start' ]; then | 
|  | 267 | errorSummary="${errorSummary}${_ERROR_}: ${packageName} is currently disabled.\\n" | 
|  | 268 | errorSummary="${errorSummary}Enable ${packageName} from WebUI or run the following commands:\\n" | 
|  | 269 | errorSummary="${errorSummary}uci set $packageName.config.enabled='1'; uci commit $packageName;\\n" | 
|  | 270 | fi | 
|  | 271 | return 1 | 
|  | 272 | fi | 
|  | 273 | } | 
|  | 274 |  | 
|  | 275 | load_network() { | 
|  | 276 | if [ -z "$ifAll" ]; then | 
|  | 277 | config_load 'network' | 
|  | 278 | config_foreach build_ifAll 'interface' | 
|  | 279 | fi | 
|  | 280 | vpr_find_iface wanIface4 'wan' | 
|  | 281 | [ "$ipv6Enabled" -ne 0 ] && vpr_find_iface wanIface6 'wan6' | 
|  | 282 | [ -n "$wanIface4" ] && network_get_gateway wanGW4 "$wanIface4" | 
|  | 283 | [ -n "$wanIface6" ] && network_get_gateway6 wanGW6 "$wanIface6" | 
|  | 284 | wanGW="${wanGW4:-$wanGW6}" | 
|  | 285 | unset ifSupported | 
|  | 286 | config_load 'network' | 
|  | 287 | config_foreach build_ifSupported 'interface' | 
|  | 288 | } | 
|  | 289 |  | 
|  | 290 | is_wan_up() { | 
|  | 291 | local sleepCount=1 | 
|  | 292 | load_network | 
|  | 293 | while [ -z "$wanGW" ] ; do | 
|  | 294 | load_network | 
|  | 295 | if [ $((sleepCount)) -gt $((bootTimeout)) ] || [ -n "$wanGW" ]; then break; fi | 
|  | 296 | output "$serviceName waiting for wan gateway...\\n" | 
|  | 297 | sleep 1 | 
|  | 298 | network_flush_cache | 
|  | 299 | sleepCount=$((sleepCount+1)) | 
|  | 300 | done | 
|  | 301 | if [ -n "$wanGW" ]; then | 
|  | 302 | return 0 | 
|  | 303 | else | 
|  | 304 | errorSummary="${errorSummary}${_ERROR_}: ${serviceName} failed to discover WAN gateway!\\n" | 
|  | 305 | return 1 | 
|  | 306 | fi | 
|  | 307 | } | 
|  | 308 |  | 
|  | 309 | ipt_cleanup() { | 
|  | 310 | local i | 
|  | 311 | for i in PREROUTING FORWARD INPUT OUTPUT; do | 
|  | 312 | while iptables -t mangle -D $i -m mark --mark 0x0/0xff0000 -j VPR_${i} >/dev/null 2>&1; do : ; done | 
|  | 313 | done | 
|  | 314 | for i in PREROUTING FORWARD INPUT OUTPUT; do | 
|  | 315 | while iptables -t mangle -D $i -j VPR_${i} >/dev/null 2>&1; do : ; done | 
|  | 316 | done | 
|  | 317 | } | 
|  | 318 |  | 
|  | 319 | # shellcheck disable=SC2086 | 
|  | 320 | ipt() { | 
|  | 321 | local d failFlagIpv4=1 failFlagIpv6=1 | 
|  | 322 | for d in "${*//-A/-D}" "${*//-I/-D}" "${*//-N/-F}" "${*//-N/-X}"; do | 
|  | 323 | [ "$d" != "$*" ] && { iptables $d >/dev/null 2>&1; ip6tables $d >/dev/null 2>&1; } | 
|  | 324 | done | 
|  | 325 |  | 
|  | 326 | d="$*"; iptables $d >/dev/null 2>&1 && failFlagIpv4=0; | 
|  | 327 | if [ "$ipv6Enabled" -gt 0 ]; then ip6tables $d >/dev/null 2>&1 && failFlagIpv6=0; fi | 
|  | 328 |  | 
|  | 329 | [ "$failFlagIpv4" -eq 0 ] || [ "$failFlagIpv6" -eq 0 ] | 
|  | 330 | } | 
|  | 331 |  | 
|  | 332 | # shellcheck disable=SC2086 | 
|  | 333 | ips() { | 
|  | 334 | local command="$1" ipset="${2//-/_}" param="$3" comment="$4" appendix failFlag=0 | 
|  | 335 | if str_contains "$ipset" '_ip'; then | 
|  | 336 | ipset="${ipset//_ip}"; appendix='_ip'; | 
|  | 337 | elif str_contains "$ipset" '_mac'; then | 
|  | 338 | ipset="${ipset//_mac}"; appendix='_mac'; | 
|  | 339 | fi | 
|  | 340 |  | 
|  | 341 | case "$command" in | 
|  | 342 | add_dnsmasq) | 
|  | 343 | [ "$resolverIpset" = "dnsmasq.ipset" ] || return 1 | 
|  | 344 | if [ -z "$dnsmasqIpsetSupported" ]; then | 
|  | 345 | warningSummary="${warningSummary}${_WARNING_}: The 'resolver_ipset' is set to 'dnsmasq.ipset', but DNSMASQ ipsets are not supported on this system!\\n" | 
|  | 346 | failFlag=1 | 
|  | 347 | elif [ "$ipv6Enabled" -ne 0 ]; then | 
|  | 348 | echo "ipset=/${param}/${ipset},${ipset}6 # $comment" >> "$dnsmasqFile" || failFlag=1 | 
|  | 349 | else | 
|  | 350 | echo "ipset=/${param}/${ipset} # $comment" >> "$dnsmasqFile" || failFlag=1 | 
|  | 351 | fi | 
|  | 352 | ;; | 
|  | 353 | add) | 
|  | 354 | if [ -z "$appendix" ] && [ "$destIpset" -eq 0 ]; then return 1; fi | 
|  | 355 | if [ -n "$appendix" ] && [ "$srcIpset" -eq 0 ]; then return 1; fi | 
|  | 356 | if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then | 
|  | 357 | ipset -q -! $command "${ipset}6${appendix}" $param comment "$comment" || failFlag=1 | 
|  | 358 | fi | 
|  | 359 | ipset -q -! $command "${ipset}${appendix}" $param comment "$comment" || failFlag=1 | 
|  | 360 | ;; | 
|  | 361 | create) | 
|  | 362 | if [ "$ipv6Enabled" -ne 0 ] && [ "$appendix" != "_mac" ]; then | 
|  | 363 | ipset -q -! "$command" "${ipset}6${appendix}" $param family inet6 || failFlag=1 | 
|  | 364 | fi | 
|  | 365 | ipset -q -! "$command" "${ipset}${appendix}" $param || failFlag=1 | 
|  | 366 | ;; | 
|  | 367 | destroy|flush) | 
|  | 368 | ipset -q -! "$command" "${ipset}6${appendix}" 2>/dev/null || failFlag=1 | 
|  | 369 | ipset -q -! "$command" "${ipset}${appendix}" 2>/dev/null || failFlag=1 | 
|  | 370 | return 0 | 
|  | 371 | ;; | 
|  | 372 | esac | 
|  | 373 | return $failFlag | 
|  | 374 | } | 
|  | 375 |  | 
|  | 376 | insert_tor_policy() { | 
|  | 377 | local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain | 
|  | 378 | proto="$(str_to_lower "$7")" | 
|  | 379 | chain="${8:-PREROUTING}" | 
|  | 380 | if [ -n "${laddr}${lport}${rport}" ]; then | 
|  | 381 | processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'src_addr', 'src_port' and 'dest_port' for policy '$comment'\\n" | 
|  | 382 | fi | 
|  | 383 | if [ -n "$proto" ] && [ "$proto" != "all" ]; then | 
|  | 384 | processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy '$comment'\\n" | 
|  | 385 | fi | 
|  | 386 | if [ "$chain" != "PREROUTING" ]; then | 
|  | 387 | processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'chain' or set 'chain' to 'PREROUTING' for policy '$comment'\\n" | 
|  | 388 | fi | 
|  | 389 | ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || processPolicyError="${processPolicyError}${_ERROR_}: ipset 'add' $iface $raddr\\n" | 
|  | 390 | return 0 | 
|  | 391 | } | 
|  | 392 |  | 
|  | 393 | insert_policy() { | 
|  | 394 | local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto chain | 
|  | 395 | local mark param i valueNeg value dest ipInsertOption="-A" | 
|  | 396 | proto="$(str_to_lower "$7")" | 
|  | 397 | chain="${8:-PREROUTING}" | 
|  | 398 | mark=$(eval echo "\$mark_${iface//-/_}") | 
|  | 399 | if [ "$ipv6Enabled" -eq 0 ] && ( is_ipv6 "$laddr" || is_ipv6 "$raddr" ); then | 
|  | 400 | processPolicyError="${processPolicyError}${_ERROR_}: Skipping IPv6 policy '$comment' as IPv6 support is disabled\\n" | 
|  | 401 | return 1 | 
|  | 402 | fi | 
|  | 403 |  | 
|  | 404 | if [ -n "$mark" ]; then | 
|  | 405 | dest="-g VPR_MARK${mark}" | 
|  | 406 | elif [ "$iface" = "ignore" ]; then | 
|  | 407 | dest="-j RETURN" | 
|  | 408 | else | 
|  | 409 | processPolicyError="${processPolicyError}${_ERROR_}: Unknown fw_mark for ${iface}\\n" | 
|  | 410 | return 0 | 
|  | 411 | fi | 
|  | 412 |  | 
|  | 413 | if [ -z "$proto" ]; then | 
|  | 414 | if [ -n "$lport" ] || [ -n "$rport" ]; then | 
|  | 415 | proto='tcp udp' | 
|  | 416 | else | 
|  | 417 | proto='all' | 
|  | 418 | fi | 
|  | 419 | fi | 
|  | 420 |  | 
|  | 421 | if is_family_mismatch "$laddr" "$raddr"; then | 
|  | 422 | processPolicyError="${processPolicyError}${_ERROR_}: Mismatched IP family between '$laddr' and '$raddr' in policy '$comment'\\n" | 
|  | 423 | return 0 | 
|  | 424 | fi | 
|  | 425 |  | 
|  | 426 | for i in $proto; do | 
|  | 427 | if [ "$i" = 'all' ]; then | 
|  | 428 | param="-t mangle ${ipInsertOption} VPR_${chain} $dest" | 
|  | 429 | elif ! is_supported_protocol "$i"; then | 
|  | 430 | processPolicyError="${processPolicyError}${_ERROR_}: Unknown protocol '$i' in policy '$comment'\\n" | 
|  | 431 | return 0 | 
|  | 432 | else | 
|  | 433 | param="-t mangle ${ipInsertOption} VPR_${chain} $dest -p $i" | 
|  | 434 | fi | 
|  | 435 |  | 
|  | 436 | if [ -n "$laddr" ]; then | 
|  | 437 | if [ "${laddr:0:1}" = "!" ]; then | 
|  | 438 | valueNeg='!'; value="${laddr:1}" | 
|  | 439 | else | 
|  | 440 | unset valueNeg; value="$laddr"; | 
|  | 441 | fi | 
|  | 442 | if is_phys_dev "$value"; then | 
|  | 443 | param="$param $valueNeg -m physdev --physdev-in ${value:1}" | 
|  | 444 | elif is_mac_address "$value"; then | 
|  | 445 | param="$param -m mac $valueNeg --mac-source $value" | 
|  | 446 | else | 
|  | 447 | param="$param $valueNeg -s $value" | 
|  | 448 | fi | 
|  | 449 | fi | 
|  | 450 |  | 
|  | 451 | if [ -n "$lport" ]; then | 
|  | 452 | if [ "${lport:0:1}" = "!" ]; then | 
|  | 453 | valueNeg='!'; value="${lport:1}" | 
|  | 454 | else | 
|  | 455 | unset valueNeg; value="$lport"; | 
|  | 456 | fi | 
|  | 457 | param="$param -m multiport $valueNeg --sport ${value//-/:}" | 
|  | 458 | fi | 
|  | 459 |  | 
|  | 460 | if [ -n "$raddr" ]; then | 
|  | 461 | if [ "${raddr:0:1}" = "!" ]; then | 
|  | 462 | valueNeg='!'; value="${raddr:1}" | 
|  | 463 | else | 
|  | 464 | unset valueNeg; value="$raddr"; | 
|  | 465 | fi | 
|  | 466 | param="$param $valueNeg -d $value" | 
|  | 467 | fi | 
|  | 468 |  | 
|  | 469 | if [ -n "$rport" ]; then | 
|  | 470 | if [ "${rport:0:1}" = "!" ]; then | 
|  | 471 | valueNeg='!'; value="${rport:1}" | 
|  | 472 | else | 
|  | 473 | unset valueNeg; value="$rport"; | 
|  | 474 | fi | 
|  | 475 | param="$param -m multiport $valueNeg --dport ${value//-/:}" | 
|  | 476 | fi | 
|  | 477 |  | 
|  | 478 | [ -n "$comment" ] && param="$param -m comment --comment $(str_extras_to_underscore "$comment")" | 
|  | 479 | ipt "$param" || processPolicyError="${processPolicyError}${_ERROR_}: iptables $param\\n" | 
|  | 480 | done | 
|  | 481 | return 0 | 
|  | 482 | } | 
|  | 483 |  | 
|  | 484 | r_process_policy(){ | 
|  | 485 | local comment="$1" iface="$2" laddr="$3" lport="$4" raddr="$5" rport="$6" proto="$7" chain="$8" resolved_laddr resolved_raddr i ipsFailFlag | 
|  | 486 | if str_contains "$laddr" '[ ;\{\}]'; then | 
|  | 487 | for i in $(str_extras_to_space "$laddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done | 
|  | 488 | return 0 | 
|  | 489 | elif str_contains "$lport" '[ ;\{\}]'; then | 
|  | 490 | for i in $(str_extras_to_space "$lport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$i" "$raddr" "$rport" "$proto" "$chain"; done | 
|  | 491 | return 0 | 
|  | 492 | elif str_contains "$raddr" '[ ;\{\}]'; then | 
|  | 493 | for i in $(str_extras_to_space "$raddr"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done | 
|  | 494 | return 0 | 
|  | 495 | elif str_contains "$rport" '[ ;\{\}]'; then | 
|  | 496 | for i in $(str_extras_to_space "$rport"); do [ -n "$i" ] && r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$i" "$proto" "$chain"; done | 
|  | 497 | return 0 | 
|  | 498 | fi | 
|  | 499 |  | 
|  | 500 | # start non-recursive processing | 
|  | 501 | # process TOR, netmask, physical device and mac-address separately, so we don't send them to resolveip | 
|  | 502 | if is_tor "$iface"; then | 
|  | 503 | insert_tor_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain" | 
|  | 504 | elif is_phys_dev "$laddr"; then | 
|  | 505 | insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain" | 
|  | 506 | elif [ -n "$laddr" ] && [ -z "${lport}${raddr}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then | 
|  | 507 | if is_mac_address "$laddr"; then | 
|  | 508 | if [ -n "$proto" ] && [ "$proto" != 'all' ] && [ "$srcIpset" -ne 0 ]; then | 
|  | 509 | processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', mac-address: '$laddr'\\n" | 
|  | 510 | fi | 
|  | 511 | ips 'add' "${iface}_mac" "$laddr" "${comment}: $laddr" || ipsFailFlag=1 | 
|  | 512 | else | 
|  | 513 | if [ -n "$proto" ] && [ "$proto" != "all" ] && [ "$srcIpset" -ne 0 ]; then | 
|  | 514 | processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', source: '$laddr'\\n" | 
|  | 515 | fi | 
|  | 516 | ips 'add' "${iface}_ip" "$laddr" "${comment}: $laddr" || ipsFailFlag=1 | 
|  | 517 | fi | 
|  | 518 | elif [ -n "$raddr" ] && [ -z "${laddr}${lport}${rport}" ] && [ "$chain" = 'PREROUTING' ]; then | 
|  | 519 | if [ -n "$proto" ] && [ "$proto" != 'all' ]; then | 
|  | 520 | processPolicyWarning="${processPolicyWarning}${_WARNING_}: Please unset 'proto' or set 'proto' to 'all' for policy: '$comment', destination: '$raddr'\\n" | 
|  | 521 | fi | 
|  | 522 | if is_domain "$raddr"; then | 
|  | 523 | ips 'add_dnsmasq' "${iface}" "$raddr" "${comment}" || ipsFailFlag=1 | 
|  | 524 | else | 
|  | 525 | ips 'add' "${iface}" "$raddr" "${comment}: $raddr" || ipsFailFlag=1 | 
|  | 526 | fi | 
|  | 527 | else | 
|  | 528 | ipsFailFlag=1 | 
|  | 529 | fi | 
|  | 530 | [ -n "$ipsFailFlag" ] || return 0; | 
|  | 531 | if is_mac_address "$laddr"; then | 
|  | 532 | insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain" | 
|  | 533 | elif is_netmask "$laddr" || is_netmask "$raddr"; then | 
|  | 534 | insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain" | 
|  | 535 | else | 
|  | 536 | [ -n "$laddr" ] && resolved_laddr="$(resolveip "$laddr")" | 
|  | 537 | [ -n "$raddr" ] && resolved_raddr="$(resolveip "$raddr")" | 
|  | 538 | if [ -n "$resolved_laddr" ] && [ "$resolved_laddr" != "$laddr" ]; then | 
|  | 539 | for i in $resolved_laddr; do [ -n "$i" ] && r_process_policy "$comment $laddr" "$iface" "$i" "$lport" "$raddr" "$rport" "$proto" "$chain"; done | 
|  | 540 | elif [ -n "$resolved_raddr" ] && [ "$resolved_raddr" != "$raddr" ]; then | 
|  | 541 | for i in $resolved_raddr; do [ -n "$i" ] && r_process_policy "$comment $raddr" "$iface" "$laddr" "$lport" "$i" "$rport" "$proto" "$chain"; done | 
|  | 542 | else | 
|  | 543 | insert_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain" | 
|  | 544 | fi | 
|  | 545 | fi | 
|  | 546 | } | 
|  | 547 |  | 
|  | 548 | process_policy(){ | 
|  | 549 | local name comment iface laddr lport raddr rport param mark processPolicyError processPolicyWarning proto chain enabled | 
|  | 550 | config_get comment "$1" 'comment' | 
|  | 551 | config_get name    "$1" 'name' 'blank' | 
|  | 552 | config_get iface   "$1" 'interface' | 
|  | 553 | config_get laddr   "$1" 'src_addr' | 
|  | 554 | config_get lport   "$1" 'src_port' | 
|  | 555 | config_get raddr   "$1" 'dest_addr' | 
|  | 556 | config_get rport   "$1" 'dest_port' | 
|  | 557 | config_get proto   "$1" 'proto' | 
|  | 558 | config_get chain   "$1" 'chain' 'PREROUTING' | 
|  | 559 | config_get_bool enabled "$1" 'enabled' 1 | 
|  | 560 |  | 
|  | 561 | [ "$enabled" -gt 0 ] || return 0 | 
|  | 562 | proto="$(str_to_lower "$proto")" | 
|  | 563 | [ "$proto" = 'auto' ] && unset proto | 
|  | 564 |  | 
|  | 565 | comment="${comment:-$name}" | 
|  | 566 | output 2 "Routing '$comment' via $iface " | 
|  | 567 |  | 
|  | 568 | if [ -z "$comment" ]; then | 
|  | 569 | errorSummary="${errorSummary}${_ERROR_}: Policy name is empty\\n" | 
|  | 570 | output_fail; return 1; | 
|  | 571 | fi | 
|  | 572 | if [ -z "${laddr}${lport}${raddr}${rport}" ]; then | 
|  | 573 | errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' missing all IPs/ports\\n" | 
|  | 574 | output_fail; return 1; | 
|  | 575 | fi | 
|  | 576 | if [ -z "$iface" ]; then | 
|  | 577 | errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has no assigned interface\\n" | 
|  | 578 | output_fail; return 1; | 
|  | 579 | fi | 
|  | 580 | if ! is_supported_interface "$iface"; then | 
|  | 581 | errorSummary="${errorSummary}${_ERROR_}: Policy '$comment' has unknown interface: '${iface}'\\n" | 
|  | 582 | output_fail; return 1; | 
|  | 583 | fi | 
|  | 584 |  | 
|  | 585 | lport="${lport//  / }"; lport="${lport// /,}"; lport="${lport//,\!/ !}"; | 
|  | 586 | rport="${rport//  / }"; rport="${rport// /,}"; rport="${rport//,\!/ !}"; | 
|  | 587 | r_process_policy "$comment" "$iface" "$laddr" "$lport" "$raddr" "$rport" "$proto" "$chain" | 
|  | 588 | if [ -n "$processPolicyWarning" ]; then | 
|  | 589 | warningSummary="${warningSummary}${processPolicyWarning}\\n" | 
|  | 590 | fi | 
|  | 591 | if [ -n "$processPolicyError" ]; then | 
|  | 592 | output_fail | 
|  | 593 | errorSummary="${errorSummary}${processPolicyError}\\n" | 
|  | 594 | else | 
|  | 595 | output_ok | 
|  | 596 | fi | 
|  | 597 | } | 
|  | 598 |  | 
|  | 599 | table_destroy(){ | 
|  | 600 | local tid="$1" iface="$2" mark="$3" | 
|  | 601 | if [ -n "$tid" ] && [ -n "$iface" ] && [ -n "$mark" ]; then | 
|  | 602 | ipt -t mangle -F "VPR_MARK${mark}" | 
|  | 603 | ipt -t mangle -X "VPR_MARK${mark}" | 
|  | 604 | ip -4 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1 | 
|  | 605 | ip -6 rule del fwmark "$mark" table "$tid" >/dev/null 2>&1 | 
|  | 606 | ip -4 rule del table "$tid" >/dev/null 2>&1 | 
|  | 607 | ip -6 rule del table "$tid" >/dev/null 2>&1 | 
|  | 608 | ip -4 route flush table "$tid" >/dev/null 2>&1 | 
|  | 609 | ip -6 route flush table "$tid" >/dev/null 2>&1 | 
|  | 610 | ips 'flush' "${iface}"; ips 'destroy' "${iface}"; | 
|  | 611 | ips 'flush' "${iface}_ip"; ips 'destroy' "${iface}_ip"; | 
|  | 612 | ips 'flush' "${iface}_mac"; ips 'destroy' "${iface}_mac"; | 
|  | 613 | ip -4 route flush cache | 
|  | 614 | ip -6 route flush cache | 
|  | 615 | sed -i "/$iface/d" /etc/iproute2/rt_tables | 
|  | 616 | return 0 | 
|  | 617 | else | 
|  | 618 | return 1 | 
|  | 619 | fi | 
|  | 620 | } | 
|  | 621 |  | 
|  | 622 | # shellcheck disable=SC2086 | 
|  | 623 | table_create(){ | 
|  | 624 | local tid="$1" mark="$2" iface="$3" gw4="$4" dev="$5" gw6="$6" dev6="$7" match="$8" dscp s=0 i ipv4_error=0 ipv6_error=1 | 
|  | 625 |  | 
|  | 626 | if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then | 
|  | 627 | return 1 | 
|  | 628 | fi | 
|  | 629 |  | 
|  | 630 | table_destroy "$tid" "$iface" "$mark" | 
|  | 631 |  | 
|  | 632 | if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then | 
|  | 633 | echo "$tid" "$iface" >> /etc/iproute2/rt_tables | 
|  | 634 | if [ -z "$gw4" ]; then | 
|  | 635 | ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1 | 
|  | 636 | else | 
|  | 637 | ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1 | 
|  | 638 | fi | 
|  | 639 | #		ip -4 route list table main | grep -v 'br-lan' | while read -r i; do | 
|  | 640 | ip -4 route list table main | while read -r i; do | 
|  | 641 | idev="$(echo "$i" | grep -Eso 'dev [^ ]*' | awk '{print $2}')" | 
|  | 642 | if ! is_supported_iface_dev "$idev"; then | 
|  | 643 | ip -4 route add $i table "$tid" >/dev/null 2>&1 || ipv4_error=1 | 
|  | 644 | fi | 
|  | 645 | done | 
|  | 646 | ip -4 route flush cache || ipv4_error=1 | 
|  | 647 | ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1 | 
|  | 648 | ipt -t mangle -N "VPR_MARK${mark}" || ipv4_error=1 | 
|  | 649 | ipt -t mangle -A "VPR_MARK${mark}" -j MARK --set-xmark "${mark}/${fwMask}" || ipv4_error=1 | 
|  | 650 | ipt -t mangle -A "VPR_MARK${mark}" -j RETURN || ipv4_error=1 | 
|  | 651 | fi | 
|  | 652 |  | 
|  | 653 | if [ "$ipv6Enabled" -ne 0 ]; then | 
|  | 654 | ipv6_error=0 | 
|  | 655 | if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strictMode" -ne 0 ]; then | 
|  | 656 | if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then | 
|  | 657 | ip -6 route add unreachable default table "$tid" || ipv6_error=1 | 
|  | 658 | else | 
|  | 659 | ip -6 route list table main | grep " dev $dev6 " | while read -r i; do | 
|  | 660 | ip -6 route add $i table "$tid" >/dev/null 2>&1 || ipv6_error=1 | 
|  | 661 | done | 
|  | 662 | fi | 
|  | 663 | ip -6 route flush cache || ipv6_error=1 | 
|  | 664 | ip -6 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv6_error=1 | 
|  | 665 | fi | 
|  | 666 | fi | 
|  | 667 |  | 
|  | 668 | if [ $ipv4_error -eq 0 ] || [ $ipv6_error -eq 0 ]; then | 
|  | 669 | dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)" | 
|  | 670 | if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then | 
|  | 671 | ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -g "VPR_MARK${mark}" || s=1 | 
|  | 672 | fi | 
|  | 673 | if [ -n "$ipsetSupported" ] && { [ -n "$dnsmasqIpsetSupported" ] || [ "$destIpset" -ne 0 ]; }; then | 
|  | 674 | if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then | 
|  | 675 | for i in $usedChainsList; do | 
|  | 676 | ipt -t mangle -I VPR_${i} -m set --match-set "${iface}" dst -g "VPR_MARK${mark}" || s=1 | 
|  | 677 | if [ "$ipv6Enabled" -ne 0 ]; then ipt -t mangle -I VPR_${i} -m set --match-set "${iface}6" dst -g "VPR_MARK${mark}" || s=1; fi | 
|  | 678 | done | 
|  | 679 | else | 
|  | 680 | s=1 | 
|  | 681 | fi | 
|  | 682 | fi | 
|  | 683 | if [ -n "$ipsetSupported" ] && [ "$srcIpset" -ne 0 ]; then | 
|  | 684 | if ips 'create' "${iface}_ip" 'hash:net comment' && ips 'flush' "${iface}_ip"; then | 
|  | 685 | ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_ip" src -g "VPR_MARK${mark}" || s=1 | 
|  | 686 | if [ "$ipv6Enabled" -ne 0 ]; then ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}6_ip" src -g "VPR_MARK${mark}" || s=1; fi | 
|  | 687 | else | 
|  | 688 | s=1 | 
|  | 689 | fi | 
|  | 690 | if ips 'create' "${iface}_mac" 'hash:mac comment' && ips 'flush' "${iface}_mac"; then | 
|  | 691 | ipt -t mangle -I VPR_PREROUTING -m set --match-set "${iface}_mac" src -g "VPR_MARK${mark}" || s=1 | 
|  | 692 | else | 
|  | 693 | s=1 | 
|  | 694 | fi | 
|  | 695 | fi | 
|  | 696 | if [ "$iface" = "$icmpIface" ]; then | 
|  | 697 | ipt -t mangle -I VPR_OUTPUT -p icmp -g "VPR_MARK${mark}" || s=1 | 
|  | 698 | fi | 
|  | 699 | else | 
|  | 700 | s=1 | 
|  | 701 | fi | 
|  | 702 |  | 
|  | 703 | return $s | 
|  | 704 | } | 
|  | 705 |  | 
|  | 706 | table_reload() { | 
|  | 707 | local tid="$1" mark="$2" iface="$3" gw4="$4" dev="$5" gw6="$6" dev6="$7" match="$8" dscp s=0 i ipv4_error=0 ipv6_error=1 | 
|  | 708 |  | 
|  | 709 | if [ -z "$tid" ] || [ -z "$mark" ] || [ -z "$iface" ]; then | 
|  | 710 | return 1 | 
|  | 711 | fi | 
|  | 712 |  | 
|  | 713 | ip -4 route del default table "$tid" >/dev/null 2>&1 | 
|  | 714 | if [ -n "$gw4" ] || [ "$strictMode" -ne 0 ]; then | 
|  | 715 | if [ -z "$gw4" ]; then | 
|  | 716 | ip -4 route add unreachable default table "$tid" >/dev/null 2>&1 || ipv4_error=1 | 
|  | 717 | else | 
|  | 718 | ip -4 route add default via "$gw4" dev "$dev" table "$tid" >/dev/null 2>&1 || ipv4_error=1 | 
|  | 719 | fi | 
|  | 720 | ip -4 route flush cache || ipv4_error=1 | 
|  | 721 | ip -4 rule del fwmark "${mark}/${fwMask}" table "$tid" >/dev/null 2>&1 | 
|  | 722 | ip -4 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv4_error=1 | 
|  | 723 | fi | 
|  | 724 |  | 
|  | 725 | if [ "$ipv6Enabled" -ne 0 ]; then | 
|  | 726 | ip -6 route del default table "$tid" >/dev/null 2>&1 | 
|  | 727 | ipv6_error=0 | 
|  | 728 | if { [ -n "$gw6" ] && [ "$gw6" != "::/0" ]; } || [ "$strictMode" -ne 0 ]; then | 
|  | 729 | if [ -z "$gw6" ] || [ "$gw6" = "::/0" ]; then | 
|  | 730 | ip -6 route add unreachable default table "$tid" || ipv6_error=1 | 
|  | 731 | else | 
|  | 732 | ip -6 route list table main | grep " dev $dev6 " | while read -r i; do | 
|  | 733 | ip -6 route add "$i" table "$tid" >/dev/null 2>&1 || ipv6_error=1 | 
|  | 734 | done | 
|  | 735 | fi | 
|  | 736 | ip -6 route flush cache || ipv6_error=1 | 
|  | 737 | ip -6 rule del fwmark "${mark}/${fwMask}" table "$tid" >/dev/null 2>&1 | 
|  | 738 | ip -6 rule add fwmark "${mark}/${fwMask}" table "$tid" || ipv6_error=1 | 
|  | 739 | fi | 
|  | 740 | fi | 
|  | 741 |  | 
|  | 742 | if [ $ipv4_error -eq 0 ] || [ $ipv6_error -eq 0 ]; then | 
|  | 743 | dscp="$(uci -q get "${packageName}".config."${iface}"_dscp)" | 
|  | 744 | if [ "${dscp:-0}" -ge 1 ] && [ "${dscp:-0}" -le 63 ]; then | 
|  | 745 | ipt -t mangle -I VPR_PREROUTING -m dscp --dscp "${dscp}" -g "VPR_MARK${mark}" || s=1 | 
|  | 746 | fi | 
|  | 747 | if [ "$iface" = "$icmpIface" ]; then | 
|  | 748 | ipt -t mangle -I VPR_OUTPUT -p icmp -g "VPR_MARK${mark}" || s=1 | 
|  | 749 | fi | 
|  | 750 | else | 
|  | 751 | s=1 | 
|  | 752 | fi | 
|  | 753 |  | 
|  | 754 | return $s | 
|  | 755 | } | 
|  | 756 |  | 
|  | 757 | process_interface(){ | 
|  | 758 | local gw4 gw6 dev dev6 s=0 dscp iface="$1" action="$2" match="$3" displayText | 
|  | 759 |  | 
|  | 760 | is_supported_interface "$iface" || return 0 | 
|  | 761 | is_wan6 "$iface" && return 0 | 
|  | 762 | [ $((ifaceMark)) -gt $((fwMask)) ] && return 1 | 
|  | 763 |  | 
|  | 764 | network_get_device dev "$iface" | 
|  | 765 | [ -z "$dev" ] && config_get dev "$iface" 'ifname' | 
|  | 766 | [ -z "$dev" ] && config_get dev "$iface" 'device' | 
|  | 767 | if is_wan "$iface" && [ -n "$wanIface6" ]; then | 
|  | 768 | network_get_device dev6 "$wanIface6" | 
|  | 769 | [ -z "$dev6" ] && config_get dev6 "$wanIface6" 'ifname' | 
|  | 770 | [ -z "$dev6" ] && config_get dev6 "$wanIface6" 'device' | 
|  | 771 | fi | 
|  | 772 | [ -z "$dev6" ] && dev6="$dev" | 
|  | 773 |  | 
|  | 774 | [ -z "$ifaceTableID" ] && ifaceTableID="$wanTableID"; [ -z "$ifaceMark" ] && ifaceMark="$wanMark"; | 
|  | 775 |  | 
|  | 776 | case "$action" in | 
|  | 777 | destroy) | 
|  | 778 | table_destroy "${ifaceTableID}" "${iface}" "${ifaceMark}" | 
|  | 779 | ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))"; | 
|  | 780 | ;; | 
|  | 781 | create) | 
|  | 782 | eval "mark_${iface//-/_}"='$ifaceMark' | 
|  | 783 | eval "tid_${iface//-/_}"='$ifaceTableID' | 
|  | 784 | if [ -z "$match" ]; then | 
|  | 785 | table_destroy "$ifaceTableID" "$iface" | 
|  | 786 | fi | 
|  | 787 | vpr_get_gateway gw4 "$iface" "$dev" | 
|  | 788 | vpr_get_gateway6 gw6 "$iface" "$dev6" | 
|  | 789 | if [ "$iface" = "$dev" ]; then | 
|  | 790 | displayText="${iface}/${gw4:-0.0.0.0}" | 
|  | 791 | else | 
|  | 792 | displayText="${iface}/${dev}/${gw4:-0.0.0.0}" | 
|  | 793 | fi | 
|  | 794 | [ "$ipv6Enabled" -ne 0 ] && displayText="${displayText}/${gw6:-::/0}" | 
|  | 795 | if [ -z "$match" ]; then | 
|  | 796 | output 2 "Creating table '$displayText' " | 
|  | 797 | is_default_dev "$dev" && displayText="${displayText} ${__OK__}" | 
|  | 798 | if table_create "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$match"; then | 
|  | 799 | gatewaySummary="${gatewaySummary}${displayText}\\n" | 
|  | 800 | output_ok | 
|  | 801 | else | 
|  | 802 | errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n" | 
|  | 803 | output_fail | 
|  | 804 | fi | 
|  | 805 | elif [ "$iface" = "$match" ]; then | 
|  | 806 | output 2 "Reloading table '$displayText' " | 
|  | 807 | is_default_dev "$dev" && displayText="${displayText} ${__OK__}" | 
|  | 808 | if table_reload "$ifaceTableID" "$ifaceMark" "$iface" "$gw4" "$dev" "$gw6" "$dev6" "$match"; then | 
|  | 809 | gatewaySummary="${gatewaySummary}${displayText}\\n" | 
|  | 810 | output_ok | 
|  | 811 | else | 
|  | 812 | errorSummary="${errorSummary}${_ERROR_}: Failed to reload '$displayText'\\n" | 
|  | 813 | output_fail | 
|  | 814 | fi | 
|  | 815 | else | 
|  | 816 | is_default_dev "$dev" && displayText="${displayText} ${__OK__}" | 
|  | 817 | gatewaySummary="${gatewaySummary}${displayText}\\n" | 
|  | 818 | fi | 
|  | 819 | ifaceTableID="$((ifaceTableID + 1))"; ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))"; | 
|  | 820 | ;; | 
|  | 821 | esac | 
|  | 822 | return $s | 
|  | 823 | } | 
|  | 824 |  | 
|  | 825 | process_tor_interface(){ | 
|  | 826 | local s=0 iface="$1" action="$2" displayText | 
|  | 827 | case "$action" in | 
|  | 828 | destroy) | 
|  | 829 | for i in PREROUTING FORWARD INPUT OUTPUT; do | 
|  | 830 | ipt -t nat -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}" | 
|  | 831 | ipt -t nat -F "VPR_${i}"; ipt -t nat -X "VPR_${i}"; | 
|  | 832 | done | 
|  | 833 | ;; | 
|  | 834 | create) | 
|  | 835 | output 2 "Creating TOR redirects " | 
|  | 836 | dnsPort="$(grep -m1 DNSPort /etc/tor/torrc | awk -F: '{print $2}')" | 
|  | 837 | transPort="$(grep -m1 TransPort /etc/tor/torrc | awk -F: '{print $2}')" | 
|  | 838 | dnsPort="${dnsPort:-9053}"; transPort="${transPort:-9040}"; | 
|  | 839 | for i in $usedChainsList; do | 
|  | 840 | ipt -t nat -N "VPR_${i}" | 
|  | 841 | ipt -t nat "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}" | 
|  | 842 | done | 
|  | 843 | if ips 'create' "${iface}" 'hash:net comment' && ips 'flush' "${iface}"; then | 
|  | 844 | for i in $usedChainsList; do | 
|  | 845 | ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 53 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$dnsPort" -m comment --comment "TorDNS-UDP" || s=1 | 
|  | 846 | ipt -t nat -I "VPR_${i}" -p tcp -m tcp --dport 80 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTP-TCP" || s=1 | 
|  | 847 | ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 80 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTP-UDP" || s=1 | 
|  | 848 | ipt -t nat -I "VPR_${i}" -p tcp -m tcp --dport 443 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTPS-TCP" || s=1 | 
|  | 849 | ipt -t nat -I "VPR_${i}" -p udp -m udp --dport 443 -m set --match-set "${iface}" dst -j REDIRECT --to-ports "$transPort" -m comment --comment "TorHTTPS-UDP" || s=1 | 
|  | 850 | done | 
|  | 851 | else | 
|  | 852 | s=1 | 
|  | 853 | fi | 
|  | 854 | displayText="${iface}/53->${dnsPort}/80,443->${transPort}" | 
|  | 855 | if [ "$s" -eq "0" ]; then | 
|  | 856 | gatewaySummary="${gatewaySummary}${displayText}\\n" | 
|  | 857 | output_ok | 
|  | 858 | else | 
|  | 859 | errorSummary="${errorSummary}${_ERROR_}: Failed to set up '$displayText'\\n" | 
|  | 860 | output_fail | 
|  | 861 | fi | 
|  | 862 | ;; | 
|  | 863 | esac | 
|  | 864 | return $s | 
|  | 865 | } | 
|  | 866 |  | 
|  | 867 | convert_config(){ | 
|  | 868 | local i src_ipset dest_ipset resolver_ipset | 
|  | 869 | [ -s "/etc/config/${packageName}" ] || return 0 | 
|  | 870 | grep -q "ignored_interfaces" "/etc/config/${packageName}" && sed -i 's/ignored_interfaces/ignored_interface/g' "/etc/config/${packageName}" | 
|  | 871 | grep -q "supported_interfaces" "/etc/config/${packageName}" && sed -i 's/supported_interfaces/supported_interface/g' "/etc/config/${packageName}" | 
|  | 872 | grep -q "local_addresses" "/etc/config/${packageName}" && sed -i 's/local_addresses/local_address/g' "/etc/config/${packageName}" | 
|  | 873 | grep -q "local_ports" "/etc/config/${packageName}" && sed -i 's/local_ports/local_port/g' "/etc/config/${packageName}" | 
|  | 874 | grep -q "remote_addresses" "/etc/config/${packageName}" && sed -i 's/remote_addresses/remote_address/g' "/etc/config/${packageName}" | 
|  | 875 | grep -q "remote_ports" "/etc/config/${packageName}" && sed -i 's/remote_ports/remote_port/g' "/etc/config/${packageName}" | 
|  | 876 | grep -q "ipset_enabled" "/etc/config/${packageName}" && sed -i 's/ipset_enabled/dest_ipset/g' "/etc/config/${packageName}" | 
|  | 877 | grep -q "dnsmasq_enabled" "/etc/config/${packageName}" && sed -i 's/dnsmasq_enabled/resolver_ipset/g' "/etc/config/${packageName}" | 
|  | 878 | grep -q "enable_control" "/etc/config/${packageName}" && sed -i 's/enable_control/webui_enable_column/g' "/etc/config/${packageName}" | 
|  | 879 | grep -q "proto_control" "/etc/config/${packageName}" && sed -i 's/proto_control/webui_protocol_column/g' "/etc/config/${packageName}" | 
|  | 880 | grep -q "chain_control" "/etc/config/${packageName}" && sed -i 's/chain_control/webui_chain_column/g' "/etc/config/${packageName}" | 
|  | 881 | grep -q "sort_control" "/etc/config/${packageName}" && sed -i 's/sort_control/webui_sorting/g' "/etc/config/${packageName}" | 
|  | 882 | grep -q "local_address" "/etc/config/${packageName}" && sed -i 's/local_address/src_addr/g' "/etc/config/${packageName}" | 
|  | 883 | grep -q "local_port" "/etc/config/${packageName}" && sed -i 's/local_port/src_port/g' "/etc/config/${packageName}" | 
|  | 884 | grep -q "remote_address" "/etc/config/${packageName}" && sed -i 's/remote_address/dest_addr/g' "/etc/config/${packageName}" | 
|  | 885 | grep -q "remote_port" "/etc/config/${packageName}" && sed -i 's/remote_port/dest_port/g' "/etc/config/${packageName}" | 
|  | 886 | grep -q "local_ipset" "/etc/config/${packageName}" && sed -i 's/local_ipset/src_ipset/g' "/etc/config/${packageName}" | 
|  | 887 | grep -q "remote_ipset" "/etc/config/${packageName}" && sed -i 's/remote_ipset/dest_ipset/g' "/etc/config/${packageName}" | 
|  | 888 | dest_ipset="$(uci -q get $packageName.config.dest_ipset)" | 
|  | 889 | src_ipset="$(uci -q get $packageName.config.src_ipset)" | 
|  | 890 | resolver_ipset="$(uci -q get $packageName.config.resolver_ipset)" | 
|  | 891 |  | 
|  | 892 | if [ -n "$dest_ipset" ] && [ "$dest_ipset" != "0" ] && [ "$dest_ipset" != "1" ]; then | 
|  | 893 | uci set "$packageName".config.dest_ipset='0' | 
|  | 894 | if [ -z "$resolver_ipset" ]; then | 
|  | 895 | uci set "$packageName".config.resolver_ipset='dnsmasq.ipset' | 
|  | 896 | fi | 
|  | 897 | uci commit "$packageName" | 
|  | 898 | fi | 
|  | 899 | if [ -n "$src_ipset" ] && [ "$src_ipset" != "0" ] && [ "$src_ipset" != "1" ]; then | 
|  | 900 | uci set "$packageName".config.src_ipset='1' | 
|  | 901 | uci commit "$packageName" | 
|  | 902 | fi | 
|  | 903 | if [ -z "$(uci -q get $packageName.config.webui_supported_protocol)" ]; then | 
|  | 904 | uci add_list "$packageName".config.webui_supported_protocol='tcp' | 
|  | 905 | uci add_list "$packageName".config.webui_supported_protocol='udp' | 
|  | 906 | uci add_list "$packageName".config.webui_supported_protocol='tcp udp' | 
|  | 907 | uci add_list "$packageName".config.webui_supported_protocol='icmp' | 
|  | 908 | uci add_list "$packageName".config.webui_supported_protocol='all' | 
|  | 909 | uci commit "$packageName" | 
|  | 910 | fi | 
|  | 911 | for i in append_local_rules append_src_rules \ | 
|  | 912 | append_remote_rules append_dest_rules; do | 
|  | 913 | if [ -n "$(uci -q get $packageName.config.$i)" ]; then | 
|  | 914 | warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n" | 
|  | 915 | fi | 
|  | 916 | done | 
|  | 917 | for i in udp_proto_enabled forward_chain_enabled input_chain_enabled \ | 
|  | 918 | output_chain_enabled iprule_enabled; do | 
|  | 919 | if [ "$(uci -q get $packageName.config.$i)" = "1" ]; then | 
|  | 920 | warningSummary="${warningSummary}$_WARNING_: $i setting is not supported in ${serviceName}.\\n" | 
|  | 921 | fi | 
|  | 922 | done | 
|  | 923 | } | 
|  | 924 |  | 
|  | 925 | check_config(){ local en; config_get_bool en "$1" 'enabled' 1; [ "$en" -gt 0 ] && _cfg_enabled=0; } | 
|  | 926 | is_config_enabled(){ | 
|  | 927 | local cfg="$1" _cfg_enabled=1 | 
|  | 928 | [ -n "$1" ] || return 1 | 
|  | 929 | config_load "$packageName" | 
|  | 930 | config_foreach check_config "$cfg" | 
|  | 931 | return "$_cfg_enabled" | 
|  | 932 | } | 
|  | 933 |  | 
|  | 934 | process_user_file(){ | 
|  | 935 | local path enabled shellBin="${SHELL:-/bin/ash}" | 
|  | 936 | config_get_bool enabled "$1" 'enabled' 1 | 
|  | 937 | config_get      path    "$1" 'path' | 
|  | 938 | [ "$enabled" -gt 0 ] || return 0 | 
|  | 939 | if [ ! -s "$path" ]; then | 
|  | 940 | errorSummary="${errorSummary}${_ERROR_}: Custom user file '$path' not found or empty!\\n" | 
|  | 941 | output_fail | 
|  | 942 | return 1 | 
|  | 943 | fi | 
|  | 944 | if ! $shellBin -n "$path"; then | 
|  | 945 | errorSummary="${errorSummary}${_ERROR_}: Syntax error in custom user file '$path'!\\n" | 
|  | 946 | output_fail | 
|  | 947 | return 1 | 
|  | 948 | fi | 
|  | 949 | output 2 "Running $path " | 
|  | 950 | # shellcheck disable=SC1090 | 
|  | 951 | if ! . "$path"; then | 
|  | 952 | errorSummary="${errorSummary}${_ERROR_}: Error running custom user file '$path'!\\n" | 
|  | 953 | if grep -q -w 'curl' "$path" && ! is_present 'curl'; then | 
|  | 954 | errorSummary="${errorSummary}${_ERROR_}: Use of 'curl' is detected in custom user file '$path', but 'curl' isn't installed!\\n" | 
|  | 955 | errorSummary="${errorSummary}${_ERROR_}: If 'curl' is needed, install it with 'opkg update; opkg install curl;' command in CLI.\\n" | 
|  | 956 | fi | 
|  | 957 | output_fail | 
|  | 958 | return 1 | 
|  | 959 | else | 
|  | 960 | output_ok | 
|  | 961 | return 0 | 
|  | 962 | fi | 
|  | 963 | } | 
|  | 964 |  | 
|  | 965 | boot() { rc_procd start_service && rc_procd service_triggers; } | 
|  | 966 |  | 
|  | 967 | start_service() { | 
|  | 968 | local dnsmasqStoredHash dnsmasqNewHash i modprobeStatus=0 reloadedIface="$1" | 
|  | 969 | convert_config | 
|  | 970 | is_enabled 'on_start' || return 1 | 
|  | 971 | is_wan_up || return 1 | 
|  | 972 |  | 
|  | 973 | iptables -t 'mangle' --list 'VPR_PREROUTING' >/dev/null 2>&1 || unset reloadedIface | 
|  | 974 | [ -n "$(tmpfs get gateway)" ] || unset reloadedIface | 
|  | 975 |  | 
|  | 976 | if [ -s "$dnsmasqFile" ]; then | 
|  | 977 | dnsmasqStoredHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')" | 
|  | 978 | rm -f "$dnsmasqFile" | 
|  | 979 | fi | 
|  | 980 |  | 
|  | 981 | for i in xt_set ip_set ip_set_hash_ip; do | 
|  | 982 | modprobe "$i" >/dev/null 2>/dev/null || modprobeStatus=$((modprobeStatus + 1)) | 
|  | 983 | done | 
|  | 984 |  | 
|  | 985 | if [ "$modprobeStatus" -gt 0 ] && ! is_chaos_calmer; then | 
|  | 986 | errorSummary="${errorSummary}${_ERROR_}: Failed to load kernel modules\\n" | 
|  | 987 | fi | 
|  | 988 |  | 
|  | 989 | if [ -z "$reloadedIface" ]; then | 
|  | 990 | for i in $usedChainsList; do | 
|  | 991 | ipt -t mangle -N "VPR_${i}" | 
|  | 992 | ipt -t mangle "$insertOption" "$i" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}" | 
|  | 993 | done | 
|  | 994 | fi | 
|  | 995 |  | 
|  | 996 | if [ -z "$reloadedIface" ]; then | 
|  | 997 | output 1 'Processing Interfaces ' | 
|  | 998 | config_load 'network'; config_foreach process_interface 'interface' 'create'; | 
|  | 999 | process_tor_interface 'tor' 'destroy'; is_tor_running && process_tor_interface 'tor' 'create'; | 
|  | 1000 | output 1 '\n' | 
|  | 1001 | if is_config_enabled 'policy'; then | 
|  | 1002 | output 1 'Processing Policies ' | 
|  | 1003 | config_load "$packageName"; config_foreach process_policy 'policy' "$reloadedIface"; | 
|  | 1004 | output 1 '\n' | 
|  | 1005 | fi | 
|  | 1006 | if is_config_enabled 'include'; then | 
|  | 1007 | output 1 'Processing User File(s) ' | 
|  | 1008 | config_load "$packageName"; config_foreach process_user_file 'include'; | 
|  | 1009 | output 1 '\n' | 
|  | 1010 | fi | 
|  | 1011 | else | 
|  | 1012 | output 1 "Reloading Interface: $reloadedIface " | 
|  | 1013 | config_load 'network'; config_foreach process_interface 'interface' 'create' "$reloadedIface"; | 
|  | 1014 | output 1 '\n' | 
|  | 1015 | fi | 
|  | 1016 |  | 
|  | 1017 | if [ -s "$dnsmasqFile" ]; then | 
|  | 1018 | dnsmasqNewHash="$(md5sum $dnsmasqFile | awk '{ print $1; }')" | 
|  | 1019 | fi | 
|  | 1020 | [ "$dnsmasqNewHash" != "$dnsmasqStoredHash" ] && dnsmasq_restart | 
|  | 1021 |  | 
|  | 1022 | if [ -z "$gatewaySummary" ]; then | 
|  | 1023 | errorSummary="${errorSummary}${_ERROR_}: failed to set up any gateway!\\n" | 
|  | 1024 | fi | 
|  | 1025 | procd_open_instance "main" | 
|  | 1026 | procd_set_param command /bin/true | 
|  | 1027 | procd_set_param stdout 1 | 
|  | 1028 | procd_set_param stderr 1 | 
|  | 1029 | procd_open_data | 
|  | 1030 | json_add_array 'status' | 
|  | 1031 | json_add_object '' | 
|  | 1032 | [ -n "$gatewaySummary" ] && json_add_string gateway "$gatewaySummary" | 
|  | 1033 | [ -n "$errorSummary" ] && json_add_string error "$errorSummary" | 
|  | 1034 | [ -n "$warningSummary" ] && json_add_string warning "$warningSummary" | 
|  | 1035 | if [ "$strictMode" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then | 
|  | 1036 | json_add_string mode "strict" | 
|  | 1037 | fi | 
|  | 1038 | json_close_object | 
|  | 1039 | json_close_array | 
|  | 1040 | procd_close_data | 
|  | 1041 | procd_close_instance | 
|  | 1042 | } | 
|  | 1043 |  | 
|  | 1044 | tmpfs() { | 
|  | 1045 | local action="$1" param="$2" value="$3" | 
|  | 1046 | # shellcheck disable=SC2034 | 
|  | 1047 | local gateway error warning mode i | 
|  | 1048 | if [ -s "$jsonFile" ]; then | 
|  | 1049 | json_load_file "$jsonFile" 2>/dev/null | 
|  | 1050 | json_select 'status' 2>/dev/null | 
|  | 1051 | for i in gateway error warning mode; do | 
|  | 1052 | json_get_var $i "$i" 2>/dev/null | 
|  | 1053 | done | 
|  | 1054 | fi | 
|  | 1055 | case "$action" in | 
|  | 1056 | get) | 
|  | 1057 | printf "%b" "$(eval echo "\$$param")"; return;; | 
|  | 1058 | add) | 
|  | 1059 | eval "$param"='$(eval echo "\$$param")${value}';; | 
|  | 1060 | del) | 
|  | 1061 | case "$param" in | 
|  | 1062 | all) | 
|  | 1063 | unset gateway error warning mode;; | 
|  | 1064 | *) | 
|  | 1065 | unset "$param";; | 
|  | 1066 | esac | 
|  | 1067 | ;; | 
|  | 1068 | set) | 
|  | 1069 | eval "$param"='$value';; | 
|  | 1070 | esac | 
|  | 1071 | json_init | 
|  | 1072 | json_add_object 'status' | 
|  | 1073 | json_add_string version "$PKG_VERSION" | 
|  | 1074 | for i in gateway error warning mode; do | 
|  | 1075 | json_add_string "$i" "$(eval echo "\$$i")" | 
|  | 1076 | done | 
|  | 1077 | json_close_object | 
|  | 1078 | json_dump > "$jsonFile" | 
|  | 1079 | sync | 
|  | 1080 | } | 
|  | 1081 |  | 
|  | 1082 | service_started() { | 
|  | 1083 | tmpfs set 'gateway' "$gatewaySummary" | 
|  | 1084 | tmpfs set 'error' "$errorSummary" | 
|  | 1085 | tmpfs set 'warning' "$warningSummary" | 
|  | 1086 | if [ "$strictMode" -ne 0 ] && str_contains "$gatewaySummary" '0.0.0.0'; then | 
|  | 1087 | tmpfs set 'mode' 'strict' | 
|  | 1088 | fi | 
|  | 1089 | [ -n "$gatewaySummary" ] && output "$serviceName started with gateways:\\n${gatewaySummary}" | 
|  | 1090 | [ -n "$errorSummary" ] && output "${errorSummary}" | 
|  | 1091 | [ -n "$warningSummary" ] && output "${warningSummary}" | 
|  | 1092 | if [ -n "$errorSummary" ]; then | 
|  | 1093 | return 2 | 
|  | 1094 | elif [ -n "$warningSummary" ]; then | 
|  | 1095 | return 1 | 
|  | 1096 | else | 
|  | 1097 | return 0 | 
|  | 1098 | fi | 
|  | 1099 | } | 
|  | 1100 |  | 
|  | 1101 | stop_service() { | 
|  | 1102 | local i | 
|  | 1103 | iptables -t mangle -L | grep -q VPR_PREROUTING || return 0 | 
|  | 1104 | load_package_config | 
|  | 1105 | for i in PREROUTING FORWARD INPUT OUTPUT; do | 
|  | 1106 | ipt -t mangle -D "${i}" -m mark --mark "0x0/${fwMask}" -j "VPR_${i}" | 
|  | 1107 | ipt -t mangle -F "VPR_${i}"; ipt -t mangle -X "VPR_${i}"; | 
|  | 1108 | done | 
|  | 1109 | config_load 'network'; config_foreach process_interface 'interface' 'destroy'; | 
|  | 1110 | process_tor_interface 'tor' 'destroy' | 
|  | 1111 | unset ifaceTableID; unset ifaceMark; | 
|  | 1112 | if [ -s "$dnsmasqFile" ]; then | 
|  | 1113 | rm -f "$dnsmasqFile" | 
|  | 1114 | dnsmasq_restart | 
|  | 1115 | fi | 
|  | 1116 | if [ "$serviceEnabled" -ne 0 ]; then | 
|  | 1117 | output "$serviceName stopped "; output_okn; | 
|  | 1118 | fi | 
|  | 1119 | } | 
|  | 1120 |  | 
|  | 1121 | reload_interface() { rc_procd start_service "$1"; } | 
|  | 1122 |  | 
|  | 1123 | service_triggers() { | 
|  | 1124 | local n | 
|  | 1125 | is_enabled || return 1 | 
|  | 1126 |  | 
|  | 1127 | if [ "$procdReloadDelay" -gt 0 ] && [ "$procdReloadDelay" -lt 100 ]; then | 
|  | 1128 | # shellcheck disable=SC2034 | 
|  | 1129 | PROCD_RELOAD_DELAY=$(( procdReloadDelay * 1000  )) | 
|  | 1130 | fi | 
|  | 1131 |  | 
|  | 1132 | procd_open_validate | 
|  | 1133 | validate_config | 
|  | 1134 | validate_policy | 
|  | 1135 | validate_include | 
|  | 1136 | procd_close_validate | 
|  | 1137 |  | 
|  | 1138 | procd_open_trigger | 
|  | 1139 | procd_add_reload_trigger 'openvpn' | 
|  | 1140 | if type procd_add_service_trigger 1>/dev/null 2>&1; then | 
|  | 1141 | procd_add_service_trigger "service.restart" "firewall" /etc/init.d/${packageName} reload | 
|  | 1142 | fi | 
|  | 1143 | procd_add_config_trigger "config.change" "${packageName}" /etc/init.d/${packageName} reload | 
|  | 1144 | for n in $ifSupported; do | 
|  | 1145 | procd_add_interface_trigger "interface.*" "$n" /etc/init.d/${packageName} reload_interface "$n" | 
|  | 1146 | done | 
|  | 1147 | procd_close_trigger | 
|  | 1148 |  | 
|  | 1149 | output 3 "$serviceName monitoring interfaces: $ifSupported"; output_okn; | 
|  | 1150 | } | 
|  | 1151 |  | 
|  | 1152 | status_service() { support "$@"; } | 
|  | 1153 | support() { | 
|  | 1154 | local dist vers out id s param status set_d set_p tableCount i=0 dev dev6 j | 
|  | 1155 | readonly _SEPARATOR_='============================================================' | 
|  | 1156 | is_enabled | 
|  | 1157 |  | 
|  | 1158 | json_load "$(ubus call system board)"; json_select release; json_get_var dist distribution; json_get_var vers version | 
|  | 1159 | if [ -n "$wanIface4" ]; then | 
|  | 1160 | network_get_gateway wanGW4 "$wanIface4" | 
|  | 1161 | [ -z "$dev" ] && dev="$(uci -q get network."${wanIface4}".ifname)" | 
|  | 1162 | [ -z "$dev" ] && dev="$(uci -q get network."${wanIface4}".device)" | 
|  | 1163 | fi | 
|  | 1164 | if [ -n "$wanIface6" ]; then | 
|  | 1165 | [ -z "$dev6" ] && dev6="$(uci -q get network."${wanIface6}".ifname)" | 
|  | 1166 | [ -z "$dev6" ] && dev6="$(uci -q get network."${wanIface6}".device)" | 
|  | 1167 | wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $1}') | 
|  | 1168 | [ "$wanGW6" = "default" ] && wanGW6=$(ip -6 route show | grep -m1 " dev $dev6 " | awk '{print $3}') | 
|  | 1169 | fi | 
|  | 1170 | while [ "${1:0:1}" = "-" ]; do param="${1//-/}"; eval "set_$param=1"; shift; done | 
|  | 1171 | [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support" | 
|  | 1172 | status="$serviceName running on $dist $vers." | 
|  | 1173 | [ -n "$wanIface4" ] && status="$status WAN (IPv4): ${wanIface4}/${dev}/${wanGW4:-0.0.0.0}." | 
|  | 1174 | [ -n "$wanIface6" ] && status="$status WAN (IPv6): ${wanIface6}/${dev6}/${wanGW6:-::/0}." | 
|  | 1175 | { | 
|  | 1176 | echo "$status" | 
|  | 1177 | echo "$_SEPARATOR_" | 
|  | 1178 | dnsmasq --version 2>/dev/null | sed '/^$/,$d' | 
|  | 1179 | if [ -n "$1" ]; then | 
|  | 1180 | echo "$_SEPARATOR_" | 
|  | 1181 | echo "Resolving domains" | 
|  | 1182 | for i in $1; do | 
|  | 1183 | echo "$i: $(resolveip "$i" | tr '\n' ' ')" | 
|  | 1184 | done | 
|  | 1185 | fi | 
|  | 1186 |  | 
|  | 1187 | echo "$_SEPARATOR_" | 
|  | 1188 | echo "Routes/IP Rules" | 
|  | 1189 | tableCount=$(ip rule list | grep -c 'fwmark') || tableCount=0 | 
|  | 1190 | if [ -n "$set_d" ]; then route; else route | grep '^default'; fi | 
|  | 1191 | if [ -n "$set_d" ]; then ip rule list; fi | 
|  | 1192 | i=0; while [ $i -lt $tableCount ]; do | 
|  | 1193 | echo "" | 
|  | 1194 | echo "IPv4 Table $((wanTableID + i)): $(ip -4 route show table $((wanTableID + i)))" | 
|  | 1195 | echo "IPv4 Table $((wanTableID + i)) Rules:" | 
|  | 1196 | ip -4 rule list table "$((wanTableID + i))" | 
|  | 1197 | i=$((i + 1)) | 
|  | 1198 | done | 
|  | 1199 |  | 
|  | 1200 | if [ "$ipv6Enabled" -ne 0 ]; then | 
|  | 1201 | i=0; while [ $i -lt $tableCount ]; do | 
|  | 1202 | ip -6 route show table $((wanTableID + i)) | while read -r param; do | 
|  | 1203 | echo "IPv6 Table $((wanTableID + i)): $param" | 
|  | 1204 | done | 
|  | 1205 | i=$((i + 1)) | 
|  | 1206 | done | 
|  | 1207 | fi | 
|  | 1208 |  | 
|  | 1209 | for j in Mangle NAT; do | 
|  | 1210 | if [ -z "$set_d" ]; then | 
|  | 1211 | for i in $usedChainsList; do | 
|  | 1212 | if iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}" 1>/dev/null 2>&1; then | 
|  | 1213 | echo "$_SEPARATOR_" | 
|  | 1214 | echo "$j IP Table: $i" | 
|  | 1215 | iptables -v -t "$(str_to_lower $j)" -S "VPR_${i}" | 
|  | 1216 | if [ "$ipv6Enabled" -ne 0 ]; then | 
|  | 1217 | echo "$_SEPARATOR_" | 
|  | 1218 | echo "$j IPv6 Table: $i" | 
|  | 1219 | ip6tables -v -t "$(str_to_lower $j)" -S "VPR_${i}" | 
|  | 1220 | fi | 
|  | 1221 | fi | 
|  | 1222 | done | 
|  | 1223 | else | 
|  | 1224 | echo "$_SEPARATOR_" | 
|  | 1225 | echo "$j IP Table" | 
|  | 1226 | iptables -L -t "$(str_to_lower $j)" | 
|  | 1227 | if [ "$ipv6Enabled" -ne 0 ]; then | 
|  | 1228 | echo "$_SEPARATOR_" | 
|  | 1229 | echo "$j IPv6 Table" | 
|  | 1230 | ip6tables -L -t "$(str_to_lower $j)" | 
|  | 1231 | fi | 
|  | 1232 | fi | 
|  | 1233 | i=0; ifaceMark="$wanMark"; | 
|  | 1234 | while [ $i -lt $tableCount ]; do | 
|  | 1235 | if iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}" 1>/dev/null 2>&1; then | 
|  | 1236 | echo "$_SEPARATOR_" | 
|  | 1237 | echo "$j IP Table MARK Chain: VPR_MARK${ifaceMark}" | 
|  | 1238 | iptables -v -t "$(str_to_lower $j)" -S "VPR_MARK${ifaceMark}" | 
|  | 1239 | ifaceMark="$(printf '0x%06x' $((ifaceMark + wanMark)))"; | 
|  | 1240 | fi | 
|  | 1241 | i=$((i + 1)) | 
|  | 1242 | done | 
|  | 1243 | done | 
|  | 1244 |  | 
|  | 1245 | echo "$_SEPARATOR_" | 
|  | 1246 | echo "Current ipsets" | 
|  | 1247 | ipset save | 
|  | 1248 | if [ -s "$dnsmasqFile" ]; then | 
|  | 1249 | echo "$_SEPARATOR_" | 
|  | 1250 | echo "DNSMASQ ipsets" | 
|  | 1251 | cat "$dnsmasqFile" | 
|  | 1252 | fi | 
|  | 1253 | echo "$_SEPARATOR_" | 
|  | 1254 | } | tee -a /var/${packageName}-support | 
|  | 1255 | if [ -n "$set_p" ]; then | 
|  | 1256 | printf "%b" "Pasting to paste.ee... " | 
|  | 1257 | if is_present 'curl' && is_variant_installed 'libopenssl' && is_installed 'ca-bundle'; then | 
|  | 1258 | json_init; json_add_string "description" "${packageName}-support" | 
|  | 1259 | json_add_array "sections"; json_add_object '0' | 
|  | 1260 | json_add_string "name" "$(uci -q get system.@system[0].hostname)" | 
|  | 1261 | json_add_string "contents" "$(cat /var/${packageName}-support)" | 
|  | 1262 | json_close_object; json_close_array; payload=$(json_dump) | 
|  | 1263 | out=$(curl -s -k "https://api.paste.ee/v1/pastes" -X "POST" -H "Content-Type: application/json" -H "X-Auth-Token:uVOJt6pNqjcEWu7qiuUuuxWQafpHhwMvNEBviRV2B" -d "$payload") | 
|  | 1264 | json_load "$out"; json_get_var id id; json_get_var s success | 
|  | 1265 | [ "$s" = "1" ] && printf "%b" "https://paste.ee/p/$id $__OK__\\n" || printf "%b" "$__FAIL__\\n" | 
|  | 1266 | [ -e "/var/${packageName}-support" ] && rm -f "/var/${packageName}-support" | 
|  | 1267 | else | 
|  | 1268 | printf "%b" "$__FAIL__\\n" | 
|  | 1269 | printf "%b" "$_ERROR_: curl, libopenssl or ca-bundle were not found!\\nRun 'opkg update; opkg install curl libopenssl ca-bundle' to install them.\\n" | 
|  | 1270 | fi | 
|  | 1271 | else | 
|  | 1272 | printf "%b" "Your support details have been logged to '/var/${packageName}-support'. $__OK__\\n" | 
|  | 1273 | fi | 
|  | 1274 | } | 
|  | 1275 |  | 
|  | 1276 | # shellcheck disable=SC2120 | 
|  | 1277 | validate_config() { | 
|  | 1278 | uci_validate_section "${packageName}" config "${1}" \ | 
|  | 1279 | 'enabled:bool:0' \ | 
|  | 1280 | 'strict_enforcement:bool:1' \ | 
|  | 1281 | 'ipv6_enabled:bool:0' \ | 
|  | 1282 | 'src_ipset:bool:0' \ | 
|  | 1283 | 'dest_ipset:bool:0' \ | 
|  | 1284 | 'resolver_ipset::or("", "none", "dnsmasq.ipset")' \ | 
|  | 1285 | 'verbosity:range(0,2):1' \ | 
|  | 1286 | 'wan_tid:integer:201' \ | 
|  | 1287 | 'wan_fw_mark:hex(8)' \ | 
|  | 1288 | 'fw_mask:hex(8)' \ | 
|  | 1289 | 'icmp_interface:string' \ | 
|  | 1290 | 'ignored_interface:list(string)' \ | 
|  | 1291 | 'supported_interface:list(string)' \ | 
|  | 1292 | 'boot_timeout:integer:30' \ | 
|  | 1293 | 'iptables_rule_option:or("", "append", "insert")' \ | 
|  | 1294 | 'procd_reload_delay:integer:0' \ | 
|  | 1295 | 'webui_enable_column:bool:0' \ | 
|  | 1296 | 'webui_protocol_column:bool:0' \ | 
|  | 1297 | 'webui_supported_protocol:list(string)' \ | 
|  | 1298 | 'webui_chain_column:bool:0' \ | 
|  | 1299 | 'webui_sorting:bool:1' \ | 
|  | 1300 | 'webui_show_ignore_target:bool:0' | 
|  | 1301 | } | 
|  | 1302 |  | 
|  | 1303 | # shellcheck disable=SC2120 | 
|  | 1304 | validate_policy() { | 
|  | 1305 | uci_validate_section "${packageName}" policy "${1}" \ | 
|  | 1306 | 'name:string' \ | 
|  | 1307 | 'enabled:bool:0' \ | 
|  | 1308 | 'interface:network' \ | 
|  | 1309 | 'proto:or(string)' \ | 
|  | 1310 | 'chain:or("", "PREROUTING", "FORWARD", "INPUT", "OUTPUT")' \ | 
|  | 1311 | 'src_addr:list(neg(or(host,network,macaddr)))' \ | 
|  | 1312 | 'src_port:list(neg(or(portrange, string)))' \ | 
|  | 1313 | 'dest_addr:list(neg(host))' \ | 
|  | 1314 | 'dest_port:list(neg(or(portrange, string)))' | 
|  | 1315 | } | 
|  | 1316 |  | 
|  | 1317 | # shellcheck disable=SC2120 | 
|  | 1318 | validate_include() { | 
|  | 1319 | uci_validate_section "${packageName}" include "${1}" \ | 
|  | 1320 | 'path:string' \ | 
|  | 1321 | 'enabled:bool:0' | 
|  | 1322 | } |