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.

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