You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1178 lines
47 KiB

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