Guest User

Untitled

a guest
Jun 27th, 2024
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 75.83 KB | None | 0 0
  1. #!/bin/sh
  2. ###########################################################
  3. # ______ _ ____ _____ #
  4. # | ____|| | / __ \ / ____| #
  5. # | |__ | | ___ __ __| | | | ___ | (___ #
  6. # | __| | | / _ \\ \/ /| | | | / _ \ \___ \ #
  7. # | | | || __/ > < | |__| || (_) |____) | #
  8. # |_| |_| \___|/_/\_\ \___\_\ \___/|_____/ #
  9. # #
  10. ###########################################################
  11. # FlexQoS maintained by dave14305
  12. # Contributors: @maghuro
  13. # shellcheck disable=SC1090,SC1091,SC2039,SC2154,SC3043
  14. # amtm NoMD5check
  15. version=1.4.3
  16. release=2024-06-18
  17. # Forked from FreshJR_QOS v8.8, written by FreshJR07 https://github.com/FreshJR07/FreshJR_QOS
  18. # License
  19. # FlexQoS is free to use under the GNU General Public License, version 3 (GPL-3.0).
  20. # https://opensource.org/licenses/GPL-3.0
  21.  
  22. # initialize Merlin Addon API helper functions
  23. . /usr/sbin/helper.sh
  24.  
  25. # -x is a flag to show verbose script output for debugging purposes only
  26. if [ "${1}" = "-x" ]; then
  27. shift
  28. set -x
  29. fi
  30.  
  31. # Global variables
  32. readonly SCRIPTNAME_DISPLAY="FlexQoS"
  33. readonly SCRIPTNAME="flexqos"
  34. readonly GIT_URL="https://raw.githubusercontent.com/dave14305/${SCRIPTNAME_DISPLAY}/master"
  35.  
  36. readonly ADDON_DIR="/jffs/addons/${SCRIPTNAME}"
  37. readonly WEBUIPATH="${ADDON_DIR}/${SCRIPTNAME}.asp"
  38. readonly SCRIPTPATH="${ADDON_DIR}/${SCRIPTNAME}.sh"
  39. readonly LOCKFILE="/tmp/addonwebui.lock"
  40. IPv6_enabled="$(nvram get ipv6_service)"
  41.  
  42. # Update version number in custom_settings.txt for reading in WebUI
  43. if [ "$(am_settings_get "${SCRIPTNAME}"_ver)" != "${version}" ]; then
  44. am_settings_set "${SCRIPTNAME}"_ver "${version}"
  45. fi
  46.  
  47. # If Merlin fq_codel patch is active, use original tc binary for passing commands
  48. # Will be obsolete in 386.1 and higher.
  49. if [ -e "/usr/sbin/realtc" ]; then
  50. TC="/usr/sbin/realtc"
  51. else
  52. TC="/usr/sbin/tc"
  53. fi
  54.  
  55. # Detect if script is run from an SSH shell interactively or being invoked via cron or from the WebUI (unattended)
  56. if tty >/dev/null 2>&1; then
  57. mode="interactive"
  58. else
  59. mode="unattended"
  60. fi
  61.  
  62. DL="$(nvram get qos_ibw)"
  63. UL="$(nvram get qos_obw)"
  64. OH="overhead $(nvram get qos_overhead)"
  65. ATM="$(nvram get qos_atm)"
  66. case ${ATM} in
  67. 1) OH="${OH} atm" ;;
  68. 2) OH="${OH} ptm" ;;
  69. esac
  70. OH="${OH} mpu $(nvram get qos_mpu)"
  71. WAN=eth0
  72.  
  73. # marks for iptables rules
  74. # We use the ffff value to avoid conflicts with predefined apps in AppDB so there would be no conflict
  75. # with any user-defined AppDB rules.
  76. Net_mark="09"
  77. Work_mark="06"
  78. Gaming_mark="08"
  79. Others_mark="0a"
  80. Web_mark="18"
  81. Streaming_mark="04"
  82. Downloads_mark="03"
  83. Learn_mark="3f"
  84.  
  85. logmsg() {
  86. if [ "$#" = "0" ]; then
  87. return
  88. fi
  89. logger -t "${SCRIPTNAME_DISPLAY}" "$*"
  90. } # logmsg
  91.  
  92. Red() {
  93. printf -- '\033[1;31m%s\033[0m\n' "${1}"
  94. }
  95.  
  96. Green() {
  97. printf -- '\033[1;32m%s\033[0m\n' "${1}"
  98. }
  99.  
  100. Blue() {
  101. printf -- '\033[1;36m%s\033[0m\n' "${1}"
  102. }
  103.  
  104. Yellow() {
  105. printf -- '\033[1;33m%s\033[0m\n' "${1}"
  106. }
  107.  
  108. get_class_mark() {
  109. case "${1}" in
  110. 0) printf "%s\n" "${Net_mark}" ;;
  111. 1) printf "%s\n" "${Gaming_mark}" ;;
  112. 2) printf "%s\n" "${Streaming_mark}" ;;
  113. 3) printf "%s\n" "${Work_mark}" ;;
  114. 4) printf "%s\n" "${Web_mark}" ;;
  115. 5) printf "%s\n" "${Downloads_mark}" ;;
  116. 6) printf "%s\n" "${Others_mark}" ;;
  117. 7) printf "%s\n" "${Learn_mark}" ;;
  118. *) printf "%s\n" "" ;;
  119. esac
  120. }
  121.  
  122. iptables_static_rules()
  123. {
  124.  
  125. # Setup download
  126. tc qdisc del dev br0 root
  127. tc qdisc add dev br0 root handle 1: htb default 1
  128. tc class add dev br0 parent 1: classid 1:2 htb prio 0 rate 10Gbit ceil 10Gbit
  129. tc filter add dev br0 parent 1: protocol all prio 1 u32 match mark 0x0000 0xc0000000 flowid 1:2
  130. tc class add dev br0 parent 1: classid 1:1 htb prio 0 rate 10Gbit ceil 10Gbit
  131. tc qdisc add dev br0 parent 1:1 handle 110: cake bandwidth ${DL}kbit diffserv4 dual-dsthost ingress wash raw no-split-gso ${OH}
  132.  
  133. # Bulk :1
  134. tc filter add dev br0 parent 110: protocol all prio 1 u32 match mark 0x80010000 0xc03f0000 action skbedit priority 110:1 # P2P
  135. tc filter add dev br0 parent 110: protocol all prio 2 u32 match mark 0x80030000 0xc03f0000 action skbedit priority 110:1 # File Sharing
  136. tc filter add dev br0 parent 110: protocol all prio 3 u32 match mark 0x800e0000 0xc03f0000 action skbedit priority 110:1 # Update downloads
  137.  
  138. # Video :3
  139. tc filter add dev br0 parent 110: protocol all prio 4 u32 match mark 0x80160000 0xc03f0000 action skbedit priority 110:3 # Adult
  140. tc filter add dev br0 parent 110: protocol all prio 5 u32 match mark 0x80040000 0xc03f0000 action skbedit priority 110:3 # Media Streaming
  141. tc filter add dev br0 parent 110: protocol all prio 5 u32 match mark 0x801400ac 0xc03fffff action skbedit priority 110:3 # STUN
  142.  
  143. # Voice :4
  144. tc filter add dev br0 parent 110: protocol all prio 6 u32 match mark 0x80060000 0xc03f0000 action skbedit priority 110:4 # Video Chat
  145. tc filter add dev br0 parent 110: protocol all prio 7 u32 match mark 0x80080000 0xc03f0000 action skbedit priority 110:4 # Gaming
  146. tc filter add dev br0 parent 110: protocol all prio 8 u32 match mark 0x80110000 0xc03f0000 action skbedit priority 110:4 # Video Conferencing
  147.  
  148. # Besteffort :2
  149. tc filter add dev br0 parent 110: protocol all prio 99 matchall action skbedit priority 110:2
  150.  
  151. # Setup upload
  152. tc qdisc del dev ${WAN} root
  153. tc qdisc add dev ${WAN} root handle 120: cake bandwidth ${UL}kbit diffserv4 dual-srchost nat raw no-split-gso ${OH}
  154.  
  155. # Bulk :1
  156. tc filter add dev ${WAN} parent 120: protocol all prio 1 u32 match mark 0x40010000 0xc03f0000 action skbedit priority 120:1 # P2P
  157. tc filter add dev ${WAN} parent 120: protocol all prio 2 u32 match mark 0x40030000 0xc03f0000 action skbedit priority 120:1 # File Sharing
  158. tc filter add dev ${WAN} parent 120: protocol all prio 3 u32 match mark 0x400e0000 0xc03f0000 action skbedit priority 120:1 # Update downloads
  159.  
  160. # Video :3
  161. tc filter add dev ${WAN} parent 120: protocol all prio 4 u32 match mark 0x40160000 0xc03f0000 action skbedit priority 120:3 # Adult
  162. tc filter add dev ${WAN} parent 120: protocol all prio 5 u32 match mark 0x40040000 0xc03f0000 action skbedit priority 120:3 # Media Streaming
  163. tc filter add dev ${WAN} parent 120: protocol all prio 5 u32 match mark 0x401400ac 0xc03fffff action skbedit priority 120:3 # STUN
  164.  
  165. # Voice :4
  166. tc filter add dev ${WAN} parent 120: protocol all prio 6 u32 match mark 0x40060000 0xc03f0000 action skbedit priority 120:4 # Video Chat
  167. tc filter add dev ${WAN} parent 120: protocol all prio 7 u32 match mark 0x40080000 0xc03f0000 action skbedit priority 120:4 # Gaming
  168. tc filter add dev ${WAN} parent 120: protocol all prio 8 u32 match mark 0x40110000 0xc03f0000 action skbedit priority 120:4 # Video Conferencing
  169.  
  170. # Besteffort :2
  171. tc filter add dev ${WAN} parent 120: protocol all prio 99 matchall action skbedit priority 120:2
  172. }
  173.  
  174. get_static_filter() {
  175. local MARK
  176. local FLOWID
  177.  
  178. MARK="${1}"
  179. FLOWID="${2}"
  180.  
  181. printf "filter add dev %s protocol all prio 5 u32 match mark 0x80%sffff 0xc03fffff flowid %s\n" "${tclan}" "${MARK}" "${FLOWID}"
  182. printf "filter add dev %s protocol all prio 5 u32 match mark 0x40%sffff 0xc03fffff flowid %s\n" "${tcwan}" "${MARK}" "${FLOWID}"
  183. } # get_static_filter
  184.  
  185. write_appdb_static_rules() {
  186. # These rules define the flowid (priority level) of the Class destinations selected by users in iptables rules.
  187. # Previous versions of the script were susceptible to the chosen Class being overridden by the users AppDB rules.
  188. # Adding these filters ensures the Class you select in iptables rules is strictly observed.
  189. # prio 5 is used because the first default filter rule (mark 0x80030000 0xc03f0000) is found at prio 6 as of this writing,
  190. # so we want these filters to always take precedence over the built-in filters.
  191. # File is overwritten (>) if it exists and later appended by write_appdb_rules() and write_custom_rates().
  192. {
  193. get_static_filter "${Net_mark}" "${Net_flow}"
  194. get_static_filter "${Work_mark}" "${Work_flow}"
  195. get_static_filter "${Gaming_mark}" "${Gaming_flow}"
  196. get_static_filter "${Others_mark}" "${Others_flow}"
  197. get_static_filter "${Web_mark}" "${Web_flow}"
  198. get_static_filter "${Streaming_mark}" "${Streaming_flow}"
  199. get_static_filter "${Downloads_mark}" "${Downloads_flow}"
  200. get_static_filter "${Learn_mark}" "${Learn_flow}"
  201. } > "/tmp/${SCRIPTNAME}_tcrules"
  202. } # write_appdb_static_rules
  203.  
  204. get_burst() {
  205. local RATE
  206. local DURATION
  207. local BURST
  208. local MIN_BURST
  209.  
  210. RATE="${1}"
  211. DURATION="${2}" # acceptable added latency in microseconds (1ms)
  212.  
  213. # https://github.com/tohojo/sqm-scripts/blob/master/src/functions.sh
  214. # let's assume ATM/AAL5 to be the worst case encapsulation
  215. # and 48 Bytes a reasonable worst case per packet overhead
  216. MIN_BURST=$(( WANMTU + 48 )) # add 48 bytes to MTU for the ovehead
  217. MIN_BURST=$(( MIN_BURST + 47 )) # now do ceil(Min_BURST / 48) * 53 in shell integer arithmic
  218. MIN_BURST=$(( MIN_BURST / 48 ))
  219. MIN_BURST=$(( MIN_BURST * 53 )) # for MTU 1489 to 1536 this will result in MIN_BURST = 1749 Bytes
  220.  
  221. BURST=$((DURATION*RATE/8000))
  222.  
  223. # If the calculated burst is less than ASUS' minimum value of 3200, use 3200
  224. # to avoid problems with child and leaf classes outside of FlexQoS scope that use 3200.
  225. # If using fq_codel option, use 1600 as a minimum burst.
  226. if [ "$(am_settings_get "${SCRIPTNAME}"_qdisc)" = "0" ]; then
  227. if [ "${BURST}" -lt 3200 ]; then
  228. BURST=3200
  229. fi
  230. elif [ "${BURST}" -lt "${MIN_BURST}" ]; then
  231. BURST="${MIN_BURST}"
  232. fi
  233.  
  234. printf "%s" "${BURST}"
  235. } # get_burst
  236.  
  237. get_cburst() {
  238. local RATE
  239. local BURST
  240. local MIN_BURST
  241.  
  242. RATE="${1}"
  243.  
  244. # https://github.com/tohojo/sqm-scripts/blob/master/src/functions.sh
  245. # let's assume ATM/AAL5 to be the worst case encapsulation
  246. # and 48 Bytes a reasonable worst case per packet overhead
  247. MIN_BURST=$(( WANMTU + 48 )) # add 48 bytes to MTU for the ovehead
  248. MIN_BURST=$(( MIN_BURST + 47 )) # now do ceil(Min_BURST / 48) * 53 in shell integer arithmic
  249. MIN_BURST=$(( MIN_BURST / 48 ))
  250. MIN_BURST=$(( MIN_BURST * 53 )) # for MTU 1489 to 1536 this will result in MIN_BURST = 1749 Bytes
  251.  
  252. BURST=$((RATE*1000/1280000))
  253. BURST=$((BURST*1600))
  254.  
  255. # If the calculated burst is less than ASUS' minimum value of 3200, use 3200
  256. # to avoid problems with child and leaf classes outside of FlexQoS scope that use 3200.
  257. if [ "${BURST}" -lt 3200 ]; then
  258. if [ "$(am_settings_get "${SCRIPTNAME}"_qdisc)" = "0" ]; then
  259. BURST=3200
  260. else
  261. BURST="${MIN_BURST}"
  262. fi
  263. fi
  264.  
  265. printf "%s" "${BURST}"
  266. } # get_cburst
  267.  
  268. get_quantum() {
  269. local RATE
  270. local QUANTUM
  271. local MIN_QUANTUM
  272.  
  273. RATE="${1}"
  274.  
  275. # https://github.com/tohojo/sqm-scripts/blob/master/src/functions.sh
  276. # let's assume ATM/AAL5 to be the worst case encapsulation
  277. # and 48 Bytes a reasonable worst case per packet overhead
  278. MIN_QUANTUM=$(( WANMTU + 48 )) # add 48 bytes to MTU for the ovehead
  279. MIN_QUANTUM=$(( MIN_QUANTUM + 47 )) # now do ceil(Min_BURST / 48) * 53 in shell integer arithmic
  280. MIN_QUANTUM=$(( MIN_QUANTUM / 48 ))
  281. MIN_QUANTUM=$(( MIN_QUANTUM * 53 )) # for MTU 1489 to 1536 this will result in MIN_BURST = 1749 Bytes
  282.  
  283. QUANTUM=$((RATE*1000/8/10))
  284.  
  285. # If the calculated quantum is less than the MTU, use MTU+14 as the quantum
  286. if [ "${QUANTUM}" -lt "${MIN_QUANTUM}" ]; then
  287. QUANTUM="${MIN_QUANTUM}"
  288. fi
  289.  
  290. printf "%s" "${QUANTUM}"
  291. } # get_quantum
  292.  
  293. get_overhead() {
  294. local NVRAM_OVERHEAD
  295. local NVRAM_ATM
  296. local OVERHEAD
  297.  
  298. NVRAM_OVERHEAD="$(nvram get qos_overhead)"
  299.  
  300. if [ -n "${NVRAM_OVERHEAD}" ] && [ "${NVRAM_OVERHEAD}" -gt "0" ]; then
  301. OVERHEAD="overhead ${NVRAM_OVERHEAD}"
  302. NVRAM_ATM="$(nvram get qos_atm)"
  303. if [ "${NVRAM_ATM}" = "1" ]; then
  304. OVERHEAD="${OVERHEAD} linklayer atm"
  305. else
  306. OVERHEAD="${OVERHEAD} linklayer ethernet"
  307. fi
  308. fi
  309.  
  310. printf "%s" "${OVERHEAD}"
  311. } # get_overhead
  312.  
  313. get_custom_rate_rule() {
  314. local IFACE
  315. local PRIO
  316. local RATE
  317. local CEIL
  318. local DURATION
  319.  
  320. IFACE="${1}"
  321. PRIO="${2}"
  322. RATE="${3}"
  323. CEIL="${4}"
  324. DURATION=1000 # 1000 microseconds = 1 ms
  325.  
  326. printf "class change dev %s parent 1:1 classid 1:1%s htb %s prio %s rate %sKbit ceil %sKbit burst %sb cburst %sb quantum %s\n" \
  327. "${IFACE}" "${PRIO}" "$(get_overhead)" "${PRIO}" "${RATE}" "${CEIL}" "$(get_burst "${CEIL}" "${DURATION}")" "$(get_cburst "${CEIL}")" "$(get_quantum "${RATE}")"
  328. } # get_custom_rate_rule
  329.  
  330. write_custom_rates() {
  331. local i
  332. if [ "${DownCeil}" -gt "0" ] && [ "${UpCeil}" -gt "0" ]; then
  333. # For all 8 classes (0-7), write the tc commands needed to modify the bandwidth rates and related parameters
  334. # that get assigned in set_tc_variables().
  335. # File is appended (>>) because it is initially created in write_appdb_static_rules().
  336. {
  337. for i in 0 1 2 3 4 5 6 7
  338. do
  339. eval get_custom_rate_rule "${tclan}" "${i}" \$DownRate${i} \$DownCeil${i}
  340. eval get_custom_rate_rule "${tcwan}" "${i}" \$UpRate${i} \$UpCeil${i}
  341. done
  342. } >> "/tmp/${SCRIPTNAME}_tcrules"
  343. fi
  344. } # write_custom_rates
  345.  
  346. set_tc_variables() {
  347. # Read various settings from the router and construct the variables needed to implement the custom rules.
  348. local drp0 drp1 drp2 drp3 drp4 drp5 drp6 drp7
  349. local dcp0 dcp1 dcp2 dcp3 dcp4 dcp5 dcp6 dcp7
  350. local urp0 urp1 urp2 urp3 urp4 urp5 urp6 urp7
  351. local ucp0 ucp1 ucp2 ucp3 ucp4 ucp5 ucp6 ucp7
  352. # shellcheck disable=SC2034
  353. local Cat0DownBandPercent Cat1DownBandPercent Cat2DownBandPercent Cat3DownBandPercent Cat4DownBandPercent Cat5DownBandPercent Cat6DownBandPercent Cat7DownBandPercent
  354. # shellcheck disable=SC2034
  355. local Cat0DownCeilPercent Cat1DownCeilPercent Cat2DownCeilPercent Cat3DownCeilPercent Cat4DownCeilPercent Cat5DownCeilPercent Cat6DownCeilPercent Cat7DownCeilPercent
  356. # shellcheck disable=SC2034
  357. local Cat0UpBandPercent Cat1UpBandPercent Cat2UpBandPercent Cat3UpBandPercent Cat4UpBandPercent Cat5UpBandPercent Cat6UpBandPercent Cat7UpBandPercent
  358. # shellcheck disable=SC2034
  359. local Cat0UpCeilPercent Cat1UpCeilPercent Cat2UpCeilPercent Cat3UpCeilPercent Cat4UpCeilPercent Cat5UpCeilPercent Cat6UpCeilPercent Cat7UpCeilPercent
  360. local flowid
  361. local line
  362. local i
  363.  
  364. tclan="br0"
  365. if [ -f /sys/module/tdts_udb/parameters/qos_wan ]; then
  366. tcwan="$(cat /sys/module/tdts_udb/parameters/qos_wan)"
  367. else
  368. tcwan="$(nvram get wan_ifname)"
  369. fi
  370.  
  371. # Detect the default filter rule for Untracked traffic (Mark 000000) if it exists.
  372. # Newer 384 stock firmware dropped this rule, so Untracked traffic flows into the Work-From-Home priority by default.
  373. # First check for older ASUS default rule (0x80000000 0xc000ffff).
  374. # If not found, get the prio for the Work-From-Home Instant messengers category 00 (0x80000000 0xc03f0000) and subtract 1.
  375. undf_prio="$("${TC}" filter show dev br0 | /bin/grep -i -m1 -B1 "0x80000000 0xc000ffff" | sed -nE 's/.* pref ([0-9]+) .*/\1/p')"
  376. if [ -z "${undf_prio}" ]; then
  377. undf_prio="$("${TC}" filter show dev br0 | /bin/grep -i -m1 -B1 "0x80000000 0xc03f0000" | sed -nE 's/.* pref ([0-9]+) .*/\1/p')"
  378. undf_prio="$((undf_prio-1))"
  379. fi
  380.  
  381. read -r \
  382. drp0 drp1 drp2 drp3 drp4 drp5 drp6 drp7 \
  383. dcp0 dcp1 dcp2 dcp3 dcp4 dcp5 dcp6 dcp7 \
  384. urp0 urp1 urp2 urp3 urp4 urp5 urp6 urp7 \
  385. ucp0 ucp1 ucp2 ucp3 ucp4 ucp5 ucp6 ucp7 \
  386. <<EOF
  387. $(echo "${bwrates}" | sed 's/^<//g;s/[<>]/ /g')
  388. EOF
  389.  
  390. # read priority order of QoS categories as set by user on the QoS page of the GUI
  391. flowid=0
  392. while read -r line;
  393. do
  394. if [ "$(echo "${line}" | cut -c 1)" = '[' ]; then
  395. flowid="$(echo "${line}" | cut -c 2)"
  396. fi
  397. case "${line}" in
  398. '0')
  399. Work_flow="1:1${flowid}"
  400. eval "Cat${flowid}DownBandPercent=${drp3}"
  401. eval "Cat${flowid}DownCeilPercent=${dcp3}"
  402. eval "Cat${flowid}UpBandPercent=${urp3}"
  403. eval "Cat${flowid}UpCeilPercent=${ucp3}"
  404. ;;
  405. '1')
  406. Downloads_flow="1:1${flowid}"
  407. eval "Cat${flowid}DownBandPercent=${drp5}"
  408. eval "Cat${flowid}DownCeilPercent=${dcp5}"
  409. eval "Cat${flowid}UpBandPercent=${urp5}"
  410. eval "Cat${flowid}UpCeilPercent=${ucp5}"
  411. ;;
  412. '4')
  413. # Special handling for category 4 since it is duplicated between Streaming and Learn-From-Home.
  414. # We have to find the priority placement of Learn-From-Home versus Streaming in the QoS GUI to know
  415. # if the first time we encounter a 4 in the file if it is meant to be Streaming or Learn-From-Home.
  416. # The second time we encounter a 4, we know it is meant for the remaining option.
  417. if nvram get bwdpi_app_rulelist | /bin/grep -qE "<4,13(<.*)?<4<"; then
  418. # Learn-From-Home is higher priority than Streaming
  419. if [ -z "${Learn_flow}" ]; then
  420. Learn_flow="1:1${flowid}"
  421. eval "Cat${flowid}DownBandPercent=${drp7}"
  422. eval "Cat${flowid}DownCeilPercent=${dcp7}"
  423. eval "Cat${flowid}UpBandPercent=${urp7}"
  424. eval "Cat${flowid}UpCeilPercent=${ucp7}"
  425. else
  426. Streaming_flow="1:1${flowid}"
  427. eval "Cat${flowid}DownBandPercent=${drp2}"
  428. eval "Cat${flowid}DownCeilPercent=${dcp2}"
  429. eval "Cat${flowid}UpBandPercent=${urp2}"
  430. eval "Cat${flowid}UpCeilPercent=${ucp2}"
  431. fi
  432. else
  433. # Streaming is higher priority than Learn-From-Home
  434. if [ -z "${Streaming_flow}" ]; then
  435. Streaming_flow="1:1${flowid}"
  436. eval "Cat${flowid}DownBandPercent=${drp2}"
  437. eval "Cat${flowid}DownCeilPercent=${dcp2}"
  438. eval "Cat${flowid}UpBandPercent=${urp2}"
  439. eval "Cat${flowid}UpCeilPercent=${ucp2}"
  440. else
  441. Learn_flow="1:1${flowid}"
  442. eval "Cat${flowid}DownBandPercent=${drp7}"
  443. eval "Cat${flowid}DownCeilPercent=${dcp7}"
  444. eval "Cat${flowid}UpBandPercent=${urp7}"
  445. eval "Cat${flowid}UpCeilPercent=${ucp7}"
  446. fi
  447. fi # Check Learn-From-Home and Streaming priority order
  448. ;;
  449. '7')
  450. Others_flow="1:1${flowid}"
  451. eval "Cat${flowid}DownBandPercent=${drp6}"
  452. eval "Cat${flowid}DownCeilPercent=${dcp6}"
  453. eval "Cat${flowid}UpBandPercent=${urp6}"
  454. eval "Cat${flowid}UpCeilPercent=${ucp6}"
  455. ;;
  456. '8')
  457. Gaming_flow="1:1${flowid}"
  458. eval "Cat${flowid}DownBandPercent=${drp1}"
  459. eval "Cat${flowid}DownCeilPercent=${dcp1}"
  460. eval "Cat${flowid}UpBandPercent=${urp1}"
  461. eval "Cat${flowid}UpCeilPercent=${ucp1}"
  462. ;;
  463. '9')
  464. Net_flow="1:1${flowid}"
  465. eval "Cat${flowid}DownBandPercent=${drp0}"
  466. eval "Cat${flowid}DownCeilPercent=${dcp0}"
  467. eval "Cat${flowid}UpBandPercent=${urp0}"
  468. eval "Cat${flowid}UpCeilPercent=${ucp0}"
  469. ;;
  470. '24')
  471. Web_flow="1:1${flowid}"
  472. eval "Cat${flowid}DownBandPercent=${drp4}"
  473. eval "Cat${flowid}DownCeilPercent=${dcp4}"
  474. eval "Cat${flowid}UpBandPercent=${urp4}"
  475. eval "Cat${flowid}UpCeilPercent=${ucp4}"
  476. ;;
  477. 'na')
  478. # This is how the old ASUS default category would appear, but this option will soon be deprecated
  479. # when all supported models are using the new QoS Categories.
  480. Learn_flow="1:1${flowid}"
  481. eval "Cat${flowid}DownBandPercent=${drp7}"
  482. eval "Cat${flowid}DownCeilPercent=${dcp7}"
  483. eval "Cat${flowid}UpBandPercent=${urp7}"
  484. eval "Cat${flowid}UpCeilPercent=${ucp7}"
  485. ;;
  486. esac
  487. done <<EOF
  488. $(sed -E '/^ceil_/d;s/rule=//g;/\{/q' /tmp/bwdpi/qosd.conf | head -n -1)
  489. EOF
  490.  
  491. #calculate up/down rates based on user-provided bandwidth from GUI
  492. #GUI shows in Mb/s; nvram stores in Kb/s
  493. DownCeil="$(printf "%.0f" "$(nvram get qos_ibw)")"
  494. UpCeil="$(printf "%.0f" "$(nvram get qos_obw)")"
  495.  
  496. # Only apply custom rates if Manual Bandwidth mode set in QoS page
  497. if [ "${DownCeil}" -gt "0" ] && [ "${UpCeil}" -gt "0" ]; then
  498. # Automatic bandwidth mode incompatible with custom rates
  499. i=0
  500. while [ "${i}" -lt "8" ]
  501. do
  502. eval "DownRate${i}=\$((DownCeil\*Cat${i}DownBandPercent/100))"
  503. eval "UpRate${i}=\$((UpCeil\*Cat${i}UpBandPercent/100))"
  504. eval "DownCeil${i}=\$((DownCeil\*Cat${i}DownCeilPercent/100))"
  505. eval "UpCeil${i}=\$((UpCeil\*Cat${i}UpCeilPercent/100))"
  506. i="$((i+1))"
  507. done
  508. fi # Auto Bandwidth check
  509. } # set_tc_variables
  510.  
  511. appdb() {
  512. # Search TrendMicro appdb file for matches to user-specified string. Return up to 25 matches
  513. local line cat_decimal
  514. /bin/grep -m 25 -i "${1}" /tmp/bwdpi/bwdpi.app.db | while read -r line; do
  515. echo "${line}" | awk -F "," '{printf " Application: %s\n Mark: %02X%04X\nDefault Class: ", $4, $1, $2}'
  516. cat_decimal=$(echo "${line}" | cut -f 1 -d "," )
  517. case "${cat_decimal}" in
  518. '9'|'18'|'19'|'20')
  519. printf "Net Control Packets"
  520. ;;
  521. '0'|'5'|'6'|'15'|'17')
  522. printf "Work-From-Home"
  523. ;;
  524. '8')
  525. printf "Gaming"
  526. ;;
  527. '7'|'10'|'11'|'21'|'23')
  528. printf "Others"
  529. ;;
  530. '13'|'24')
  531. printf "Web Surfing"
  532. ;;
  533. '4')
  534. printf "Video and Audio Streaming"
  535. ;;
  536. '1'|'3'|'14')
  537. printf "File Transferring"
  538. ;;
  539. *)
  540. printf "Unknown"
  541. ;;
  542. esac
  543. printf "\n\n"
  544. done
  545. } # appdb
  546.  
  547. webconfigpage() {
  548. local urlpage urlproto urldomain urlport
  549.  
  550. # Eye candy function that will construct a URL to display after install or upgrade so a user knows where to
  551. # find the webUI page. In most cases though, they will go to the Adaptive QoS tab and find the FlexQoS sub-tab anyway.
  552. urlpage="$(sed -nE "/${SCRIPTNAME_DISPLAY}/ s/.*url\: \"(user[0-9]+\.asp)\".*/\1/p" /tmp/menuTree.js)"
  553. if [ "$(nvram get http_enable)" = "1" ]; then
  554. urlproto="https"
  555. else
  556. urlproto="http"
  557. fi
  558. if [ -n "$(nvram get lan_domain)" ]; then
  559. urldomain="$(nvram get lan_hostname).$(nvram get lan_domain)"
  560. else
  561. urldomain="$(nvram get lan_ipaddr)"
  562. fi
  563. if [ "$(nvram get ${urlproto}_lanport)" = "80" ] || [ "$(nvram get ${urlproto}_lanport)" = "443" ]; then
  564. urlport=""
  565. else
  566. urlport=":$(nvram get ${urlproto}_lanport)"
  567. fi
  568.  
  569. if echo "${urlpage}" | grep -qE "user[0-9]+\.asp"; then
  570. printf "Advanced configuration available via:\n"
  571. Blue " ${urlproto}://${urldomain}${urlport}/${urlpage}"
  572. fi
  573. } # webconfigpage
  574.  
  575. scriptinfo() {
  576. # Version header used in interactive sessions
  577. [ "${mode}" = "interactive" ] || return
  578. printf "\n"
  579. Green "${SCRIPTNAME_DISPLAY} v${version} released ${release}"
  580. printf "\n"
  581. } # scriptinfo
  582.  
  583. debug() {
  584. local RMODEL ipt_debug appdb_debug
  585. [ -z "$(nvram get odmpid)" ] && RMODEL="$(nvram get productid)" || RMODEL="$(nvram get odmpid)"
  586. Green "[SPOILER=\"${SCRIPTNAME_DISPLAY} Debug\"][CODE]"
  587. scriptinfo
  588. printf "Debug date : %s\n" "$(date +'%Y-%m-%d %H:%M:%S%z')"
  589. printf "Router Model : %s\n" "${RMODEL}"
  590. printf "Firmware Ver : %s_%s\n" "$(nvram get buildno)" "$(nvram get extendno)"
  591. printf "DPI/Sig Ver : %s / %s\n" "$(nvram get bwdpi_dpi_ver)" "$(nvram get bwdpi_sig_ver)"
  592. get_config
  593. set_tc_variables
  594.  
  595. printf "WAN iface : %s\n" "${wan}"
  596. printf "tc WAN iface : %s\n" "${tcwan}"
  597. printf "IPv6 : %s\n" "${IPv6_enabled}"
  598. printf "Undf Prio : %s\n" "${undf_prio}"
  599. printf "Down Band : %s\n" "${DownCeil}"
  600. printf "Up Band : %s\n" "${UpCeil}"
  601. printf "*****************\n"
  602. printf "Net Control : %s\n" "${Net_flow}"
  603. printf "Work-From-Home : %s\n" "${Work_flow}"
  604. printf "Gaming : %s\n" "${Gaming_flow}"
  605. printf "Others : %s\n" "${Others_flow}"
  606. printf "Web Surfing : %s\n" "${Web_flow}"
  607. printf "Streaming : %s\n" "${Streaming_flow}"
  608. printf "File Transfers : %s\n" "${Downloads_flow}"
  609. printf "Learn-From-Home : %s\n" "${Learn_flow}"
  610. printf "*****************\n"
  611. # Only print custom rates if Manual Bandwidth setting is enabled on QoS page
  612. if [ "${DownCeil}" -gt "0" ] && [ "${UpCeil}" -gt "0" ]; then
  613. printf "Downrates : %7s, %7s, %7s, %7s, %7s, %7s, %7s, %7s\n" "${DownRate0}" "${DownRate1}" "${DownRate2}" "${DownRate3}" "${DownRate4}" "${DownRate5}" "${DownRate6}" "${DownRate7}"
  614. printf "Downceils : %7s, %7s, %7s, %7s, %7s, %7s, %7s, %7s\n" "${DownCeil0}" "${DownCeil1}" "${DownCeil2}" "${DownCeil3}" "${DownCeil4}" "${DownCeil5}" "${DownCeil6}" "${DownCeil7}"
  615. printf "Uprates : %7s, %7s, %7s, %7s, %7s, %7s, %7s, %7s\n" "${UpRate0}" "${UpRate1}" "${UpRate2}" "${UpRate3}" "${UpRate4}" "${UpRate5}" "${UpRate6}" "${UpRate7}"
  616. printf "Upceils : %7s, %7s, %7s, %7s, %7s, %7s, %7s, %7s\n" "${UpCeil0}" "${UpCeil1}" "${UpCeil2}" "${UpCeil3}" "${UpCeil4}" "${UpCeil5}" "${UpCeil6}" "${UpCeil7}"
  617. printf "*****************\n"
  618. else
  619. printf "Custom rates disabled with Automatic Bandwidth mode!\n"
  620. printf "*****************\n"
  621. fi
  622. ipt_debug="$(am_settings_get "${SCRIPTNAME}"_iptables)"
  623. printf "iptables settings: %s\n" "${ipt_debug:-Defaults}"
  624. write_iptables_rules
  625. # Remove superfluous commands from the output in order to focus on the parsed details
  626. /bin/sed -E "/^ip[6]?tables -t mangle -[FD] /d; s/ip[6]?tables -t mangle //g; s/[[:space:]]{2,}/ /g" "/tmp/${SCRIPTNAME}_iprules"
  627. printf "*****************\n"
  628. appdb_debug="$(am_settings_get "${SCRIPTNAME}"_appdb)"
  629. printf "appdb rules: %s\n" "${appdb_debug:-Defaults}"
  630. true > "/tmp/${SCRIPTNAME}_tcrules"
  631. write_appdb_rules
  632. write_custom_rates
  633. cat "/tmp/${SCRIPTNAME}_tcrules"
  634. Green "[/CODE][/SPOILER]"
  635. # Since these tmp files aren't being used to apply rules, we delete them to avoid confusion about the last known ruleset
  636. rm "/tmp/${SCRIPTNAME}_iprules" "/tmp/${SCRIPTNAME}_tcrules"
  637. printf "\n"
  638. Yellow "Copy the text from [SPOILER] to [/SPOILER] and paste into a forum post at snbforums.com"
  639. } # debug
  640.  
  641. get_flowid() {
  642. # Map class destination field from webui settings to the established class/flowid based on user priorities
  643. # flowid will be one of 1:10 - 1:17, depending on the user priority sequencing in the QoS GUI
  644. # Input: numeric class destination from iptables rule
  645. local flowid
  646. case "${1}" in
  647. 0) flowid="${Net_flow}" ;;
  648. 1) flowid="${Gaming_flow}" ;;
  649. 2) flowid="${Streaming_flow}" ;;
  650. 3) flowid="${Work_flow}" ;;
  651. 4) flowid="${Web_flow}" ;;
  652. 5) flowid="${Downloads_flow}" ;;
  653. 6) flowid="${Others_flow}" ;;
  654. 7) flowid="${Learn_flow}" ;;
  655. # return empty if destination missing
  656. *) flowid="" ;;
  657. esac
  658. printf "%s\n" "${flowid}"
  659. } # get_flowid
  660.  
  661. Is_Valid_CIDR() {
  662. /bin/grep -qE '^[!]?([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$'
  663. } # Is_Valid_CIDR
  664.  
  665. Is_Valid_Port() {
  666. /bin/grep -qE '^[!]?([0-9]{1,5})((:[0-9]{1,5})?|(,[0-9]{1,5})*)$'
  667. } # Is_Valid_Port
  668.  
  669. Is_Valid_Mark() {
  670. /bin/grep -qE '^[!]?[A-Fa-f0-9]{2}([A-Fa-f0-9]{4}|[\*]{4})$'
  671. } # Is_Valid_Mark
  672.  
  673. parse_appdb_rule() {
  674. # Process an appdb custom rule into the appropriate tc filter syntax
  675. # Input: $1 = Mark from appdb rule XXYYYY XX=Category(hex) YYYY=ID(hex or ****)
  676. # $2 = Class destination
  677. # Output: stdout is written directly to the /tmp/flexqos_appdb_rules file via redirect in write_appdb_rules(),
  678. # so don't add unnecessary output in this function.
  679. local cat id
  680. local DOWN_mark UP_mark
  681. local flowid
  682. local currmask
  683. local prio currprio
  684. local currhandledown currhandleup
  685. # Only process if Mark is a valid format
  686. if echo "${1}" | Is_Valid_Mark; then
  687. # Extract category and appid from mark
  688. cat="$(echo "${1}" | cut -c 1-2)"
  689. id="$(echo "${1}" | cut -c 3-6)"
  690. # check if wildcard mark
  691. if [ "${id}" = "****" ]; then
  692. # Replace asterisks with zeros and use category mask
  693. # This mark and mask
  694. DOWN_mark="0x80${cat}0000 0xc03f0000"
  695. UP_mark="0x40${cat}0000 0xc03f0000"
  696. elif [ "${1}" = "000000" ]; then
  697. # unidentified traffic needs a special mask
  698. DOWN_mark="0x80${1} 0xc000ffff"
  699. UP_mark="0x40${1} 0xc000ffff"
  700. else
  701. # specific application mark
  702. DOWN_mark="0x80${1} 0xc03fffff"
  703. UP_mark="0x40${1} 0xc03fffff"
  704. fi
  705.  
  706. # get destination class
  707. flowid="$(get_flowid "${2}")"
  708.  
  709. # To override the default tc filters with our custom filter rules, we need to insert our rules
  710. # at a higher priority (lower number) than the built-in filter for each category.
  711. if [ "${1}" = "000000" ]; then
  712. # special mask for unidentified traffic
  713. currmask="0xc000ffff"
  714. else
  715. currmask="0xc03f0000"
  716. fi
  717. # search the tc filter temp file we made in write_appdb_rules() for the existing priority of the
  718. # category we are going to override with a custom appdb filter rule.
  719. # e.g. If we are going to make a rule for appdb mark 1400C5, we need to find the current priority of category 14.
  720. prio="$(/bin/grep -i -m 1 -B1 "0x80${cat}0000 ${currmask}" "/tmp/${SCRIPTNAME}_tmp_tcfilterdown" | sed -nE 's/.* pref ([0-9]+) .*/\1/p')"
  721. currprio="${prio}"
  722.  
  723. # If there is no existing filter for the category, use the undf_prio defined in set_tc_variables().
  724. # This is usually only necessary for Untracked traffic (mark 000000).
  725. # Otherwise, take the current priority and subtract 1 so that our rule will be processed earlier than the default rule.
  726. if [ -z "${prio}" ]; then
  727. prio="${undf_prio}"
  728. else
  729. prio="$((prio-1))"
  730. fi
  731.  
  732. # Build and echo the tc filter commands based on the possible actions required:
  733. # 1. Change an existing filter to point to a new flowid (mostly relevant for wildcard appdb rules).
  734. # 2. Insert a new filter at a higher priority than the existing filter that would otherwise match this mark.
  735. if { [ "${id}" = "****" ] || [ "${1}" = "000000" ]; } && [ -n "${currprio}" ]; then
  736. # change existing rule for wildcard marks and Untracked mark only if current priority already determined.
  737. # Need to get handle of existing filter for proper tc filter change syntax.
  738. currhandledown="$(/bin/grep -i -m 1 -B1 "0x80${cat}0000 ${currmask}" "/tmp/${SCRIPTNAME}_tmp_tcfilterdown" | sed -nE 's/.* fh ([0-9a-f:]+) .*/\1/p')"
  739. currhandleup="$(/bin/grep -i -m 1 -B1 "0x40${cat}0000 ${currmask}" "/tmp/${SCRIPTNAME}_tmp_tcfilterup" | sed -nE 's/.* fh ([0-9a-f:]+) .*/\1/p')"
  740. printf "filter change dev %s prio %s protocol all handle %s u32 flowid %s\n" "${tclan}" "${currprio}" "${currhandledown}" "${flowid}"
  741. printf "filter change dev %s prio %s protocol all handle %s u32 flowid %s\n" "${tcwan}" "${currprio}" "${currhandleup}" "${flowid}"
  742. else
  743. # add new rule for individual app one priority level higher (-1)
  744. printf "filter add dev %s protocol all prio %s u32 match mark %s flowid %s\n" "${tclan}" "${prio}" "${DOWN_mark}" "${flowid}"
  745. printf "filter add dev %s protocol all prio %s u32 match mark %s flowid %s\n" "${tcwan}" "${prio}" "${UP_mark}" "${flowid}"
  746. fi
  747. fi # Is_Valid_Mark
  748. } # parse_appdb_rule
  749.  
  750. create_ipset() {
  751. # To translate IPv4 iptables rules using local IPv4 addresses, create 2 ipsets and 2 iptables rules to track
  752. # corresponding IPv6 addresses for a given IPv4 local address
  753. # Input: $1 = local IP/CIDR (minus optional negation)
  754. # Output: stdout ipset and iptables commands
  755. local LOCALIP IPV6LIFETIME IPV6RASTATE
  756.  
  757. # If IPv6 is disabled, return early
  758. [ "${IPv6_enabled}" = "disabled" ] && return
  759.  
  760. # Strip optional negation if present
  761. LOCALIP="${1}"
  762. IPV6RASTATE="$(nvram get ipv6_autoconf_type)" # 0=Stateless, 1=Stateful
  763. ipset -! create "${LOCALIP}-mac" hash:mac timeout "$(nvram get dhcp_lease)" 2>/dev/null
  764. ipset -! flush "${LOCALIP}-mac" 2>/dev/null
  765.  
  766. case "${IPv6_enabled}" in
  767. dhcp6|other) #Native or Static
  768. if [ "${IPV6RASTATE}" = "1" ]; then
  769. # Stateful, get DHCP Lifetime
  770. IPV6LIFETIME="$(nvram get ipv6_dhcp_lifetime)"
  771. else
  772. # Stateless, use hard-coded value from firmware
  773. IPV6LIFETIME=600
  774. fi
  775. ;;
  776. *)
  777. IPV6LIFETIME=600
  778. ;;
  779. esac
  780.  
  781. ipset -! create "${LOCALIP}" hash:ip family inet6 timeout "${IPV6LIFETIME}" 2>/dev/null
  782. ipset -! flush "${LOCALIP}" 2>/dev/null
  783.  
  784. printf "iptables -t mangle -D PREROUTING -i %s -m conntrack --ctstate NEW -s %s -j SET --add-set %s-mac src --exist 2>/dev/null\n" "${lan}" "${LOCALIP}" "${LOCALIP}"
  785. printf "ip6tables -t mangle -D PREROUTING -i %s -m conntrack --ctstate NEW -m set --match-set %s-mac src -j SET --add-set %s src --exist 2>/dev/null\n" "${lan}" "${LOCALIP}" "${LOCALIP}"
  786. printf "iptables -t mangle -I PREROUTING -i %s -m conntrack --ctstate NEW -s %s -j SET --add-set %s-mac src --exist\n" "${lan}" "${LOCALIP}" "${LOCALIP}"
  787. printf "ip6tables -t mangle -I PREROUTING -i %s -m conntrack --ctstate NEW -m set --match-set %s-mac src -j SET --add-set %s src --exist\n" "${lan}" "${LOCALIP}" "${LOCALIP}"
  788. }
  789.  
  790. parse_iptablerule() {
  791. # Process an iptables custom rule into the appropriate iptables syntax
  792. # Input: $1 = local IP (e.g. 192.168.1.100 !192.168.1.100 192.168.1.100/31 !192.168.1.100/31)
  793. # $2 = remote IP (e.g. 9.9.9.9 !9.9.9.9 9.9.9.0/24 !9.9.9.0/24)
  794. # $3 = protocol (e.g. both, tcp, or udp)
  795. # $4 = local port (e.g. 443 !443 1234:5678 !1234:5678 53,123,853 !53,123,853)
  796. # $5 = remote port (e.g. 443 !443 1234:5678 !1234:5678 53,123,853 !53,123,853)
  797. # $6 = mark (e.g. XXYYYY !XXYYYY XX=Category(hex) YYYY=ID(hex or ****))
  798. # $7 = class destination (e.g. 0-7)
  799. # Output: stdout is written directly to the /tmp/flexqos_iprules file via redirect in write_iptables_rules(),
  800. # so don't add unnecessary output in this function.
  801. local DOWN_Lip UP_Lip CIDR
  802. local DOWN_Lip6 UP_Lip6
  803. local DOWN_Rip UP_Rip
  804. local PROTOS proto
  805. local DOWN_Lport UP_Lport
  806. local DOWN_Rport UP_Rport
  807. local tmpMark DOWN_mark UP_mark
  808. local DOWN_dst UP_dst Dst_mark
  809. # local IP
  810. # Check for acceptable IP format
  811. if echo "${1}" | Is_Valid_CIDR; then
  812. # print ! (if present) and remaining CIDR
  813. DOWN_Lip="$(echo "${1}" | sed -E 's/^([!])?/\1 -d /')"
  814. UP_Lip="$(echo "${1}" | sed -E 's/^([!])?/\1 -s /')"
  815. # Only create ipset if there is no remote IP/CIDR defined, since the IPv6 rule would not work with remote IPv4 CIDR
  816. if ! echo "${2}" | Is_Valid_CIDR; then
  817. # Alternate syntax for IPv6 ipset matching
  818. CIDR="$(echo "${1}" | sed -E 's/^!//')"
  819. create_ipset "${CIDR}" # 2>/dev/null
  820. DOWN_Lip6="$(echo "${1}" | sed -E 's/^([!])?(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)/-m set \1 --match-set \2 dst/')"
  821. UP_Lip6="$(echo "${1}" | sed -E 's/^([!])?(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)/-m set \1 --match-set \2 src/')"
  822. fi
  823. else
  824. DOWN_Lip=""
  825. UP_Lip=""
  826. DOWN_Lip6=""
  827. UP_Lip6=""
  828. fi
  829.  
  830. # remote IP
  831. # Check for acceptable IP format
  832. if echo "${2}" | Is_Valid_CIDR; then
  833. # print ! (if present) and remaining CIDR
  834. DOWN_Rip="$(echo "${2}" | sed -E 's/^([!])?/\1 -s /')"
  835. UP_Rip="$(echo "${2}" | sed -E 's/^([!])?/\1 -d /')"
  836. else
  837. DOWN_Rip=""
  838. UP_Rip=""
  839. fi
  840.  
  841. # protocol (required when port specified)
  842. if [ "${3}" = "tcp" ] || [ "${3}" = "udp" ]; then
  843. # print protocol directly
  844. PROTOS="${3}"
  845. elif [ "${#4}" -gt "1" ] || [ "${#5}" -gt "1" ]; then
  846. # proto=both & ports are defined
  847. PROTOS="tcp>udp" # separated by > because IFS will be temporarily set to '>' by calling function. TODO Fix Me
  848. else
  849. # neither proto nor ports defined
  850. PROTOS="all"
  851. fi
  852.  
  853. # local port
  854. if echo "${4}" | Is_Valid_Port; then
  855. # Use multiport to specify any port specification:
  856. # single port, multiple ports, port range
  857. DOWN_Lport="-m multiport $(echo "${4}" | sed -E 's/^([!])?/\1 --dports /')"
  858. UP_Lport="-m multiport $(echo "${4}" | sed -E 's/^([!])?/\1 --sports /')"
  859. else
  860. DOWN_Lport=""
  861. UP_Lport=""
  862. fi
  863.  
  864. # remote port
  865. if echo "${5}" | Is_Valid_Port; then
  866. # Use multiport to specify any port specification:
  867. # single port, multiple ports, port range
  868. DOWN_Rport="-m multiport $(echo "${5}" | sed -E 's/^([!])?/\1 --sports /')"
  869. UP_Rport="-m multiport $(echo "${5}" | sed -E 's/^([!])?/\1 --dports /')"
  870. else
  871. DOWN_Rport=""
  872. UP_Rport=""
  873. fi
  874.  
  875. # mark
  876. if echo "${6}" | Is_Valid_Mark; then
  877. tmpMark="${6}" # Use a tmp variable since we have to manipulate the contents for ! and ****
  878. DOWN_mark="-m mark"
  879. UP_mark="-m mark"
  880. if [ "$(echo "${tmpMark}" | cut -c 1)" = "!" ]; then # first char is !
  881. DOWN_mark="${DOWN_mark} !"
  882. UP_mark="${UP_mark} !"
  883. tmpMark="$(echo "${tmpMark}" | sed -E 's/^!//')" # strip the !
  884. fi
  885. # Extract category and appid from mark
  886. cat="$(echo "${tmpMark}" | cut -c 1-2)"
  887. id="$(echo "${tmpMark}" | cut -c 3-6)"
  888. # check if wildcard mark
  889. if [ "${id}" = "****" ]; then
  890. # replace **** with 0000 and use category mask
  891. DOWN_mark="${DOWN_mark} --mark 0x80${cat}0000/0xc03f0000"
  892. UP_mark="${UP_mark} --mark 0x40${cat}0000/0xc03f0000"
  893. else
  894. DOWN_mark="${DOWN_mark} --mark 0x80${tmpMark}/0xc03fffff"
  895. UP_mark="${UP_mark} --mark 0x40${tmpMark}/0xc03fffff"
  896. fi
  897. else
  898. DOWN_mark=""
  899. UP_mark=""
  900. fi
  901.  
  902. # if all parameters are empty stop processing the rule
  903. if [ -z "${DOWN_Lip}${DOWN_Rip}${DOWN_Lport}${DOWN_Rport}${DOWN_mark}" ]; then
  904. return
  905. fi
  906.  
  907. # destination mark
  908. # numbers come from webui select options for class field
  909. Dst_mark="$(get_class_mark "${7}")"
  910. if [ -z "${Dst_mark}" ]; then
  911. return
  912. fi
  913. DOWN_dst="-j MARK --set-mark 0x80${Dst_mark}ffff/0xc03fffff"
  914. UP_dst="-j MARK --set-mark 0x40${Dst_mark}ffff/0xc03fffff"
  915.  
  916. # This block is redirected to the /tmp/flexqos_iprules file, so no extraneous output, please
  917. # If proto=both we have to create 2 statements, one for tcp and one for udp.
  918. for proto in ${PROTOS}; do
  919. # download ipv4
  920. printf "iptables -t mangle -A %s %s %s -p %s %s %s %s %s\n" "${SCRIPTNAME_DISPLAY}_down" "${DOWN_Lip}" "${DOWN_Rip}" "${proto}" "${DOWN_Lport}" "${DOWN_Rport}" "${DOWN_mark}" "${DOWN_dst}"
  921. # upload ipv4
  922. printf "iptables -t mangle -A %s %s %s -p %s %s %s %s %s\n" "${SCRIPTNAME_DISPLAY}_up" "${UP_Lip}" "${UP_Rip}" "${proto}" "${UP_Lport}" "${UP_Rport}" "${UP_mark}" "${UP_dst}"
  923. # If rule contains no IPv4 remote addresses, and IPv6 is enabled, add a corresponding rule for IPv6
  924. if [ "${IPv6_enabled}" != "disabled" ] && [ -z "${DOWN_Rip}" ]; then
  925. # download ipv6
  926. printf "ip6tables -t mangle -A %s %s -p %s %s %s %s %s\n" "${SCRIPTNAME_DISPLAY}_down" "${DOWN_Lip6}" "${proto}" "${DOWN_Lport}" "${DOWN_Rport}" "${DOWN_mark}" "${DOWN_dst}"
  927. # upload ipv6
  928. printf "ip6tables -t mangle -A %s %s -p %s %s %s %s %s\n" "${SCRIPTNAME_DISPLAY}_up" "${UP_Lip6}" "${proto}" "${UP_Lport}" "${UP_Rport}" "${UP_mark}" "${UP_dst}"
  929. fi
  930. done
  931. } # parse_iptablerule
  932.  
  933. about() {
  934. scriptinfo
  935. cat <<EOF
  936. License
  937. ${SCRIPTNAME_DISPLAY} is free to use under the GNU General Public License, version 3 (GPL-3.0).
  938. https://opensource.org/licenses/GPL-3.0
  939.  
  940. For discussion visit this thread:
  941. https://www.snbforums.com/forums/asuswrt-merlin-addons.60/?prefix_id=8
  942. https://github.com/dave14305/FlexQoS (Source Code)
  943.  
  944. About
  945. Script Changes Unidentified traffic destination away from Work-From-Home into Others
  946. Script Changes HTTPS traffic destination away from Net Control into Web Surfing
  947. Script Changes Guaranteed Bandwidth per QoS category into logical percentages of upload and download.
  948. Script includes misc default rules
  949. (Wifi Calling) - UDP traffic on remote ports 500 & 4500 moved into Work-From-Home
  950. (Facetime) - UDP traffic on local ports 16384 - 16415 moved into Work-From-Home
  951. (Usenet) - TCP traffic on remote ports 119 & 563 moved into File Transfers
  952. (Gaming) - Gaming TCP traffic from remote ports 80 & 443 moved into File Transfers.
  953. (Snapchat) - Moved into Others
  954. (Speedtest.net) - Moved into File Transfers
  955. (Google Play) - Moved into File Transfers
  956. (Apple AppStore)- Moved into File Transfers
  957. (VPN Fix) - Router VPN Client upload traffic moved into File Transfers instead of whitelisted
  958. (Gaming Manual) - Unidentified traffic for specified devices, not originating from ports 80/443, moved into Gaming
  959.  
  960. Gaming Rule Note
  961. Gaming traffic originating from ports 80 & 443 is primarily downloads & patches (some lobby/login protocols mixed within)
  962. Manually configurable rule will take untracked traffic for specified devices, not originating from server ports 80/443, and place it into Gaming
  963. Use of this gaming rule REQUIRES devices to have a continuous static ip assignment & this range needs to be passed into the script
  964. EOF
  965. }
  966.  
  967. backup() {
  968. # Backup existing user rules in /jffs/addons/custom_settings.txt
  969. # Input: create [force]|restore|remove
  970. case "${1}" in
  971. 'create')
  972. if [ "${2}" != "force" ] && [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ]; then
  973. grep "# Backup date" "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"
  974. printf "A backup already exists. Do you want to overwrite this backup? [1=Yes 2=No]: "
  975. read -r yn
  976. if [ "${yn}" != "1" ]; then
  977. Yellow "Backup cancelled."
  978. return
  979. fi
  980. fi
  981. printf "Running backup...\n"
  982. {
  983. printf "#!/bin/sh\n"
  984. printf "# Backup date: %s\n" "$(date +'%Y-%m-%d %H:%M:%S%z')"
  985. printf ". /usr/sbin/helper.sh\n"
  986. [ -n "$(am_settings_get "${SCRIPTNAME}"_iptables)" ] && printf "am_settings_set %s_iptables \"%s\"\n" "${SCRIPTNAME}" "$(am_settings_get "${SCRIPTNAME}"_iptables)"
  987. [ -n "$(am_settings_get "${SCRIPTNAME}"_iptables_names)" ] && printf "am_settings_set %s_iptables_names \"%s\"\n" "${SCRIPTNAME}" "$(am_settings_get "${SCRIPTNAME}"_iptables_names)"
  988. [ -n "$(am_settings_get "${SCRIPTNAME}"_appdb)" ] && printf "am_settings_set %s_appdb \"%s\"\n" "${SCRIPTNAME}" "$(am_settings_get "${SCRIPTNAME}"_appdb)"
  989. [ -n "$(am_settings_get "${SCRIPTNAME}"_bwrates)" ] && printf "am_settings_set %s_bwrates \"%s\"\n" "${SCRIPTNAME}" "$(am_settings_get "${SCRIPTNAME}"_bwrates)"
  990. [ -n "$(am_settings_get "${SCRIPTNAME}"_qdisc)" ] && printf "am_settings_set %s_qdisc \"%s\"\n" "${SCRIPTNAME}" "$(am_settings_get "${SCRIPTNAME}"_qdisc)"
  991. } > "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"
  992. if /bin/grep -q "${SCRIPTNAME}_" "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"; then
  993. Green "Backup done to ${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"
  994. else
  995. rm "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"
  996. Yellow "Backup cancelled. All settings using default values."
  997. fi
  998. ;;
  999. 'restore')
  1000. if [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ]; then
  1001. Yellow "$(grep "# Backup date" "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh")"
  1002. printf "Do you want to restore this backup? [1=Yes 2=No]: "
  1003. read -r yn
  1004. if [ "${yn}" = "1" ]; then
  1005. sh "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"
  1006. Green "Backup restored!"
  1007. needrestart=1
  1008. else
  1009. Yellow "Restore cancelled."
  1010. fi
  1011. else
  1012. Red "No backup file exists!"
  1013. fi
  1014. ;;
  1015. 'remove')
  1016. [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ] && rm "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"
  1017. Green "Backup deleted."
  1018. ;;
  1019. esac
  1020. } # backup
  1021.  
  1022. download_file() {
  1023. # Download file from Github once to a temp location. If the same as the destination file, don't replace.
  1024. # Otherwise move it from the temp location to the destination.
  1025. if curl -fsL --retry 3 --connect-timeout 3 "${GIT_URL}/${1}" -o "/tmp/${1}"; then
  1026. if [ "$(md5sum "/tmp/${1}" | awk '{print $1}')" != "$(md5sum "${2}" 2>/dev/null | awk '{print $1}')" ]; then
  1027. mv -f "/tmp/${1}" "${2}"
  1028. logmsg "Updated $(basename "${1}")"
  1029. else
  1030. logmsg "File $(basename "${2}") is already up-to-date"
  1031. rm -f "/tmp/${1}" 2>/dev/null
  1032. fi
  1033. else
  1034. logmsg "Updating $(basename "${1}") failed"
  1035. fi
  1036. } # download_file
  1037.  
  1038. compare_remote_version() {
  1039. # Check version on Github and determine the difference with the installed version
  1040. # Outcomes: Version update, or no update
  1041. local remotever
  1042. # Fetch version of the shell script on Github
  1043. remotever="$(curl -fsN --retry 3 --connect-timeout 3 "${GIT_URL}/$(basename "${SCRIPTPATH}")" | /bin/grep "^version=" | sed -e 's/version=//')"
  1044. if [ "$( echo "${version}" | sed 's/\.//g' )" -lt "$( echo "${remotever}" | sed 's/\.//g' )" ]; then # strip the . from version string for numeric comparison
  1045. # version upgrade
  1046. echo "${remotever}"
  1047. else
  1048. printf "NoUpdate\n"
  1049. fi
  1050. } # compare_remote_version
  1051.  
  1052. update() {
  1053. # Check for, and optionally apply updates.
  1054. # Parameter options: check (do not update), silent (update without prompting)
  1055. local updatestatus yn
  1056. scriptinfo
  1057. printf "Checking for updates\n"
  1058. # Update the webui status thorugh detect_update.js ajax call.
  1059. printf "var verUpdateStatus = \"%s\";\n" "InProgress" > "/www/ext/${SCRIPTNAME}/detect_update.js"
  1060. updatestatus="$(compare_remote_version)"
  1061. # Check to make sure we got back a valid status from compare_remote_version(). If not, indicate Error.
  1062. case "${updatestatus}" in
  1063. 'NoUpdate'|[0-9].[0-9].[0-9]) ;;
  1064. *) updatestatus="Error"
  1065. esac
  1066. printf "var verUpdateStatus = \"%s\";\n" "${updatestatus}" > "/www/ext/${SCRIPTNAME}/detect_update.js"
  1067.  
  1068. if [ "${1}" = "check" ]; then
  1069. # Do not proceed with any updating if check function requested
  1070. return
  1071. fi
  1072. if [ "${mode}" = "interactive" ] && [ -z "${1}" ]; then
  1073. case "${updatestatus}" in
  1074. 'NoUpdate')
  1075. Green " You have the latest version installed"
  1076. printf " Would you like to overwrite your existing installation anyway? [1=Yes 2=No]: "
  1077. ;;
  1078. 'Error')
  1079. Red " Error determining remote version status!"
  1080. PressEnter
  1081. return
  1082. ;;
  1083. *)
  1084. # New Version Number
  1085. Green " ${SCRIPTNAME_DISPLAY} v${updatestatus} is now available!"
  1086. printf " Would you like to update now? [1=Yes 2=No]: "
  1087. ;;
  1088. esac
  1089. read -r yn
  1090. printf "\n"
  1091. if [ "${yn}" != "1" ]; then
  1092. Green " No Changes have been made"
  1093. return 0
  1094. fi
  1095. fi
  1096. printf "Installing: %s...\n\n" "${SCRIPTNAME_DISPLAY}"
  1097. download_file "$(basename "${SCRIPTPATH}")" "${SCRIPTPATH}"
  1098. exec sh "${SCRIPTPATH}" -install "${1}"
  1099. exit
  1100. } # update
  1101.  
  1102. prompt_restart() {
  1103. # Restart QoS so that FlexQoS changes can take effect.
  1104. # Possible values for $needrestart:
  1105. # 0: No restart needed (initialized in main)
  1106. # 1: Restart needed, but prompt user if interactive session
  1107. # 2: Restart needed, do not prompt (force)
  1108. local yn
  1109. if [ "${needrestart}" -gt "0" ]; then
  1110. if [ "${mode}" = "interactive" ]; then
  1111. if [ "${needrestart}" = "1" ]; then
  1112. printf "\nWould you like to restart QoS for modifications to take effect? [1=Yes 2=No]: "
  1113. read -r yn
  1114. if [ "${yn}" = "2" ]; then
  1115. needrestart=0
  1116. return
  1117. fi
  1118. fi
  1119. fi
  1120. printf "Restarting QoS and firewall...\n"
  1121. service "restart_qos;restart_firewall"
  1122. needrestart=0
  1123. fi
  1124. } # prompt_restart
  1125.  
  1126. menu() {
  1127. # Minimal interactive, menu-driven interface for basic maintenance functions.
  1128. local yn
  1129. [ "${mode}" = "interactive" ] || return
  1130. clear
  1131. sed -n '2,10p' "${0}" # display banner
  1132. scriptinfo
  1133. printf " (1) about explain functionality\n"
  1134. printf " (2) update check for updates\n"
  1135. printf " (3) debug traffic control parameters\n"
  1136. printf " (4) restart restart QoS\n"
  1137. printf " (5) backup create settings backup\n"
  1138. if [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ]; then
  1139. printf " (6) restore restore settings from backup\n"
  1140. printf " (7) delete remove backup\n"
  1141. fi
  1142. printf "\n (9) uninstall uninstall script\n"
  1143. printf " (e) exit\n"
  1144. printf "\nMake a selection: "
  1145. read -r input
  1146. case "${input}" in
  1147. '1')
  1148. about
  1149. ;;
  1150. '2')
  1151. update
  1152. ;;
  1153. '3')
  1154. debug
  1155. ;;
  1156. '4')
  1157. needrestart=1
  1158. prompt_restart
  1159. ;;
  1160. '5')
  1161. backup create
  1162. ;;
  1163. '6')
  1164. if [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ]; then
  1165. backup restore
  1166. else
  1167. Red "$input is not a valid option!"
  1168. fi
  1169. ;;
  1170. '7')
  1171. if [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ]; then
  1172. backup remove
  1173. else
  1174. Red "$input is not a valid option!"
  1175. fi
  1176. ;;
  1177. '9')
  1178. scriptinfo
  1179. printf " Do you want to uninstall %s [1=Yes 2=No]: " "${SCRIPTNAME_DISPLAY}"
  1180. read -r yn
  1181. if [ "${yn}" = "1" ]; then
  1182. printf "\n"
  1183. sh "${SCRIPTPATH}" -uninstall
  1184. printf "\n"
  1185. exit
  1186. fi
  1187. printf "\n"
  1188. Yellow "${SCRIPTNAME_DISPLAY} has NOT been uninstalled"
  1189. ;;
  1190. 'e'|'E'|'exit')
  1191. return
  1192. ;;
  1193. *)
  1194. Red "$input is not a valid option!"
  1195. ;;
  1196. esac
  1197. PressEnter
  1198. menu # stay in the menu loop until exit is chosen
  1199. } # menu
  1200.  
  1201. remove_webui() {
  1202. local prev_webui_page
  1203. local FD
  1204. printf "Removing WebUI...\n"
  1205. prev_webui_page="$(sed -nE "s/^\{url\: \"(user[0-9]+\.asp)\"\, tabName\: \"${SCRIPTNAME_DISPLAY}\"\}\,$/\1/p" /tmp/menuTree.js 2>/dev/null)"
  1206. if [ -n "${prev_webui_page}" ]; then
  1207. # Remove page from the UI menu system
  1208. FD=386
  1209. eval exec "${FD}>${LOCKFILE}"
  1210. /usr/bin/flock -x "${FD}"
  1211. umount /www/require/modules/menuTree.js 2>/dev/null
  1212. sed -i "\~tabName: \"${SCRIPTNAME_DISPLAY}\"},~d" /tmp/menuTree.js
  1213. if diff -q /tmp/menuTree.js /www/require/modules/menuTree.js >/dev/null 2>&1; then
  1214. # no more custom pages mounted, so remove the file
  1215. rm /tmp/menuTree.js
  1216. else
  1217. # Still some modifications from another script so remount
  1218. mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
  1219. fi
  1220. /usr/bin/flock -u "${FD}"
  1221. # Remove last mounted asp page
  1222. rm -f "/www/user/${prev_webui_page}" 2>/dev/null
  1223. # Look for previously mounted asp pages that are orphaned now and delete them
  1224. /bin/grep -l "${SCRIPTNAME_DISPLAY} maintained by dave14305" /www/user/user*.asp 2>/dev/null | while read -r oldfile
  1225. do
  1226. rm "${oldfile}"
  1227. done
  1228. fi
  1229. rm -rf "/www/ext/${SCRIPTNAME}" 2>/dev/null # remove js helper scripts
  1230. } # remove_webui
  1231.  
  1232. install_webui() {
  1233. local prev_webui_page
  1234. local FD
  1235. # if this is an install or update...otherwise it's a normal startup/mount
  1236. if [ -z "${1}" ]; then
  1237. printf "Downloading WebUI files...\n"
  1238. download_file "$(basename "${WEBUIPATH}")" "${WEBUIPATH}"
  1239. # cleanup obsolete files from previous versions
  1240. [ -L "/www/ext/${SCRIPTNAME}" ] && rm "/www/ext/${SCRIPTNAME}" 2>/dev/null
  1241. [ -d "${ADDON_DIR}/table" ] && rm -r "${ADDON_DIR}/table"
  1242. [ -f "${ADDON_DIR}/${SCRIPTNAME}_arrays.js" ] && rm "${ADDON_DIR}/${SCRIPTNAME}_arrays.js"
  1243. fi
  1244. FD=386
  1245. eval exec "${FD}>${LOCKFILE}"
  1246. /usr/bin/flock -x "${FD}"
  1247. # Check if the webpage is already mounted in the GUI and reuse that page
  1248. prev_webui_page="$(sed -nE "s/^\{url\: \"(user[0-9]+\.asp)\"\, tabName\: \"${SCRIPTNAME_DISPLAY}\"\}\,$/\1/p" /tmp/menuTree.js 2>/dev/null)"
  1249. if [ -n "${prev_webui_page}" ]; then
  1250. # use the same filename as before
  1251. am_webui_page="${prev_webui_page}"
  1252. else
  1253. # get a new mountpoint
  1254. am_get_webui_page "${WEBUIPATH}"
  1255. fi
  1256. if [ "${am_webui_page}" = "none" ]; then
  1257. logmsg "No API slots available to install web page"
  1258. else
  1259. cp -p "${WEBUIPATH}" "/www/user/${am_webui_page}"
  1260. if [ ! -f /tmp/menuTree.js ]; then
  1261. cp /www/require/modules/menuTree.js /tmp/
  1262. mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
  1263. fi
  1264. if ! /bin/grep -q "{url: \"$am_webui_page\", tabName: \"${SCRIPTNAME_DISPLAY}\"}," /tmp/menuTree.js; then
  1265. umount /www/require/modules/menuTree.js 2>/dev/null
  1266. sed -i "\~{url: \"$am_webui_page\"~d; \~tabName: \"${SCRIPTNAME_DISPLAY}\"},~d" /tmp/menuTree.js
  1267. sed -i "/url: \"QoS_Stats.asp\", tabName:/i {url: \"$am_webui_page\", tabName: \"${SCRIPTNAME_DISPLAY}\"}," /tmp/menuTree.js
  1268. mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
  1269. fi
  1270. fi
  1271. /usr/bin/flock -u "${FD}"
  1272. [ ! -d "/www/ext/${SCRIPTNAME}" ] && mkdir -p "/www/ext/${SCRIPTNAME}"
  1273. }
  1274.  
  1275. Init_UserScript() {
  1276. # Properly setup an empty Merlin user script
  1277. local userscript
  1278. if [ -z "${1}" ]; then
  1279. return
  1280. fi
  1281. userscript="/jffs/scripts/$1"
  1282. if [ ! -f "${userscript}" ]; then
  1283. # If script doesn't exist yet, create with shebang
  1284. printf "#!/bin/sh\n\n" > "${userscript}"
  1285. elif [ -f "${userscript}" ] && ! head -1 "${userscript}" | /bin/grep -qE "^#!/bin/sh"; then
  1286. # Script exists but no shebang, so insert it at line 1
  1287. sed -i '1s~^~#!/bin/sh\n~' "${userscript}"
  1288. elif [ "$(tail -c1 "${userscript}" | wc -l)" = "0" ]; then
  1289. # Script exists with shebang, but no linefeed before EOF; makes appending content unpredictable if missing
  1290. printf "\n" >> "${userscript}"
  1291. fi
  1292. if [ ! -x "${userscript}" ]; then
  1293. # Ensure script is executable by owner
  1294. chmod 755 "${userscript}"
  1295. fi
  1296. } # Init_UserScript
  1297.  
  1298. Auto_ServiceEventEnd() {
  1299. # Borrowed from Adamm00
  1300. # https://github.com/Adamm00/IPSet_ASUS/blob/master/firewall.sh
  1301. local cmdline
  1302. Init_UserScript "service-event-end"
  1303. # Delete existing lines related to this script
  1304. sed -i "\~${SCRIPTNAME_DISPLAY} Addition~d" /jffs/scripts/service-event-end
  1305. # Add line to handle wrs and sig_check events that require reapplying settings
  1306. cmdline="if [ \"\$2\" = \"wrs\" ] || [ \"\$2\" = \"sig_check\" ]; then { sh ${SCRIPTPATH} -start & } ; fi # ${SCRIPTNAME_DISPLAY} Addition"
  1307. echo "${cmdline}" >> /jffs/scripts/service-event-end
  1308. # Add line to handle other events triggered from webui
  1309. cmdline="if echo \"\$2\" | /bin/grep -q \"^${SCRIPTNAME}\"; then { sh ${SCRIPTPATH} \"\${2#${SCRIPTNAME}}\" & } ; fi # ${SCRIPTNAME_DISPLAY} Addition"
  1310. echo "${cmdline}" >> /jffs/scripts/service-event-end
  1311. } # Auto_ServiceEventEnd
  1312.  
  1313. Auto_FirewallStart() {
  1314. # Borrowed from Adamm00
  1315. # https://github.com/Adamm00/IPSet_ASUS/blob/master/firewall.sh
  1316. local cmdline
  1317. Init_UserScript "firewall-start"
  1318. # Delete existing lines related to this script
  1319. sed -i "\~${SCRIPTNAME_DISPLAY} Addition~d" /jffs/scripts/firewall-start
  1320. # Add line to trigger script on firewall startup
  1321. cmdline="sh ${SCRIPTPATH} -start & # ${SCRIPTNAME_DISPLAY} Addition"
  1322. if /bin/grep -vE "^#" /jffs/scripts/firewall-start | /bin/grep -q "Skynet"; then
  1323. # If Skynet also installed, insert this script before Skynet so it doesn't have to wait for Skynet to startup before applying QoS
  1324. # Won't delay Skynet startup since we fork into the background
  1325. sed -i "/Skynet/i ${cmdline}" /jffs/scripts/firewall-start
  1326. else
  1327. # Skynet not installed, so just append
  1328. echo "${cmdline}" >> /jffs/scripts/firewall-start
  1329. fi # is Skynet also installed?
  1330. } # Auto_FirewallStart
  1331.  
  1332. setup_aliases() {
  1333. # shortcuts to launching script
  1334. local cmdline
  1335. if [ -d /opt/bin ]; then
  1336. # Entware is installed, so setup link to /opt/bin
  1337. printf "Adding %s link in Entware /opt/bin...\n" "${SCRIPTNAME}"
  1338. ln -sf "${SCRIPTPATH}" "/opt/bin/${SCRIPTNAME}"
  1339. else
  1340. # Setup shell alias
  1341. printf "Adding %s alias in profile.add...\n" "${SCRIPTNAME}"
  1342. sed -i "/alias ${SCRIPTNAME}/d" /jffs/configs/profile.add 2>/dev/null
  1343. cmdline="alias ${SCRIPTNAME}=\"sh ${SCRIPTPATH}\" # ${SCRIPTNAME_DISPLAY} Addition"
  1344. echo "${cmdline}" >> /jffs/configs/profile.add
  1345. fi
  1346. } # setup_aliases
  1347.  
  1348. Firmware_Check() {
  1349. printf "Checking firmware support...\n"
  1350. if ! nvram get rc_support | grep -q am_addons; then
  1351. Red "${SCRIPTNAME_DISPLAY} requires ASUSWRT-Merlin Addon API support. Installation aborted."
  1352. printf "\nInstall FreshJR_QOS via amtm as an alternative for your firmware version.\n"
  1353. return 1
  1354. fi
  1355. if [ "$(nvram get qos_enable)" != "1" ] || [ "$(nvram get qos_type)" != "1" ]; then
  1356. Red "Adaptive QoS is not enabled. Please enable it in the GUI. Aborting installation."
  1357. return 1
  1358. fi
  1359. if [ "$(nvram get jffs2_scripts)" != "1" ]; then
  1360. Red "\"Enable JFFS custom scripts and configs\" is not enabled. Please enable it in the GUI. Aborting installation."
  1361. return 1
  1362. fi
  1363. } # Firmware_Check
  1364.  
  1365. install() {
  1366. # Install script and download webui file
  1367. # This is also called by the update process once a new script is downloaded by update() function
  1368. if [ "${mode}" = "interactive" ]; then
  1369. clear
  1370. scriptinfo
  1371. printf "Installing %s...\n" "${SCRIPTNAME_DISPLAY}"
  1372. if ! Firmware_Check; then
  1373. PressEnter
  1374. rm -f "${0}" 2>/dev/null
  1375. exit 5
  1376. fi
  1377. fi
  1378. if [ ! -d "${ADDON_DIR}" ]; then
  1379. printf "Creating directories...\n"
  1380. mkdir -p "${ADDON_DIR}"
  1381. chmod 755 "${ADDON_DIR}"
  1382. fi
  1383. if [ ! -f "${SCRIPTPATH}" ]; then
  1384. cp -p "${0}" "${SCRIPTPATH}"
  1385. fi
  1386. if [ ! -x "${SCRIPTPATH}" ]; then
  1387. chmod 755 "${SCRIPTPATH}"
  1388. fi
  1389. install_webui
  1390. generate_bwdpi_arrays force
  1391. printf "Adding %s entries to Merlin user scripts...\n" "${SCRIPTNAME_DISPLAY}"
  1392. Auto_FirewallStart
  1393. Auto_ServiceEventEnd
  1394. setup_aliases
  1395.  
  1396. if [ "${mode}" = "interactive" ]; then
  1397. Green "${SCRIPTNAME_DISPLAY} installation complete!"
  1398. scriptinfo
  1399. webconfigpage
  1400.  
  1401. if [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ] && ! /bin/grep -qE "^${SCRIPTNAME}_[^(ver )]" /jffs/addons/custom_settings.txt ; then
  1402. Green "Backup found!"
  1403. backup restore
  1404. fi
  1405. [ "$(nvram get qos_enable)" = "1" ] && needrestart=1
  1406. else
  1407. [ "$(nvram get qos_enable)" = "1" ] && needrestart=2
  1408. fi
  1409. # Remove setting if set to default value 1 (enabled)
  1410. sed -i "/^${SCRIPTNAME}_conntrack 1/d" /jffs/addons/custom_settings.txt
  1411. # Remove deprecated 3:30 AM cron job if exists
  1412. sed -i "\~${SCRIPTNAME_DISPLAY}~d" /jffs/scripts/services-start 2>/dev/null
  1413. cru d "${SCRIPTNAME}" 2>/dev/null
  1414. } # install
  1415.  
  1416. uninstall() {
  1417. local yn
  1418. printf "Removing entries from Merlin user scripts...\n"
  1419. sed -i "\~${SCRIPTNAME_DISPLAY}~d" /jffs/scripts/firewall-start 2>/dev/null
  1420. sed -i "\~${SCRIPTNAME_DISPLAY}~d" /jffs/scripts/service-event-end 2>/dev/null
  1421. printf "Removing aliases and shortcuts...\n"
  1422. sed -i "/alias ${SCRIPTNAME}/d" /jffs/configs/profile.add 2>/dev/null
  1423. rm -f "/opt/bin/${SCRIPTNAME}" 2>/dev/null
  1424. printf "Removing delayed cron job...\n"
  1425. cru d "${SCRIPTNAME}_5min" 2>/dev/null
  1426. remove_webui
  1427. printf "Removing %s settings...\n" "${SCRIPTNAME_DISPLAY}"
  1428. if [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ]; then
  1429. printf "Backup found! Would you like to keep it? [1=Yes 2=No]: "
  1430. read -r yn
  1431. if [ "${yn}" = "2" ]; then
  1432. printf "Deleting Backup...\n"
  1433. rm "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh"
  1434. fi
  1435. else
  1436. printf "Do you want to backup your settings before uninstall? [1=Yes 2=No]: "
  1437. read -r yn
  1438. if [ "${yn}" = "1" ]; then
  1439. printf "Backing up %s settings...\n" "${SCRIPTNAME_DISPLAY}"
  1440. backup create force
  1441. fi
  1442. fi
  1443. sed -i "/^${SCRIPTNAME}_/d" /jffs/addons/custom_settings.txt
  1444. if [ -f "${ADDON_DIR}/restore_${SCRIPTNAME}_settings.sh" ]; then
  1445. printf "Deleting %s folder contents except Backup file...\n" "${SCRIPTNAME_DISPLAY}"
  1446. /usr/bin/find "${ADDON_DIR}" ! -name restore_"${SCRIPTNAME}"_settings.sh ! -exec test -d {} \; -a -exec rm {} +
  1447. else
  1448. printf "Deleting %s directory...\n" "${SCRIPTNAME_DISPLAY}"
  1449. rm -rf "${ADDON_DIR}"
  1450. fi
  1451. Green "${SCRIPTNAME_DISPLAY} has been uninstalled"
  1452. needrestart=1
  1453. } # uninstall
  1454.  
  1455. get_config() {
  1456. local iptables_rules_defined
  1457. local drp0 drp1 drp2 drp3 drp4 drp5 drp6 drp7
  1458. local dcp0 dcp1 dcp2 dcp3 dcp4 dcp5 dcp6 dcp7
  1459. local urp0 urp1 urp2 urp3 urp4 urp5 urp6 urp7
  1460. local ucp0 ucp1 ucp2 ucp3 ucp4 ucp5 ucp6 ucp7
  1461.  
  1462. # Read settings from Addon API config file. If not defined, set default values
  1463. iptables_rules="$(am_settings_get "${SCRIPTNAME}"_iptables)"
  1464. if [ -z "${iptables_rules}" ]; then
  1465. iptables_rules="<>>udp>>500,4500>>3<>>udp>16384:16415>>>3<>>tcp>>119,563>>5<>>tcp>>80,443>08****>5"
  1466. elif [ "${iptables_rules}" = "0" ]; then
  1467. iptables_rules=""
  1468. fi
  1469. appdb_rules="$(am_settings_get "${SCRIPTNAME}"_appdb)"
  1470. if [ -z "${appdb_rules}" ]; then
  1471. appdb_rules="<000000>6<00006B>6<0D0007>5<0D0086>5<0D00A0>5<12003F>4<13****>4<14****>4"
  1472. fi
  1473. bwrates="$(am_settings_get "${SCRIPTNAME}"_bwrates)"
  1474. if [ -z "${bwrates}" ]; then
  1475. # New settings not set
  1476. if [ -z "$(am_settings_get "${SCRIPTNAME}"_bandwidth)" ]; then
  1477. # Old settings not set either, so set the defaults
  1478. bwrates="<5>15>30>20>10>5>10>5<100>100>100>100>100>100>100>100<5>15>10>20>10>5>30>5<100>100>100>100>100>100>100>100"
  1479. else
  1480. # Convert bandwidth to bwrates by reading existing values into the re-sorted order
  1481. read -r \
  1482. drp0 drp1 drp2 drp3 drp4 drp5 drp6 drp7 \
  1483. dcp0 dcp1 dcp2 dcp3 dcp4 dcp5 dcp6 dcp7 \
  1484. urp0 urp1 urp2 urp3 urp4 urp5 urp6 urp7 \
  1485. ucp0 ucp1 ucp2 ucp3 ucp4 ucp5 ucp6 ucp7 \
  1486. <<EOF
  1487. $(am_settings_get "${SCRIPTNAME}"_bandwidth | sed 's/^<//g;s/[<>]/ /g')
  1488. EOF
  1489. am_settings_set "${SCRIPTNAME}"_bwrates "<${drp0}>${drp2}>${drp5}>${drp1}>${drp4}>${drp7}>${drp3}>${drp6}<${dcp0}>${dcp2}>${dcp5}>${dcp1}>${dcp4}>${dcp7}>${dcp3}>${dcp6}<${urp0}>${urp2}>${urp5}>${urp1}>${urp4}>${urp7}>${urp3}>${urp6}<${ucp0}>${ucp2}>${ucp5}>${ucp1}>${ucp4}>${ucp7}>${ucp3}>${ucp6}"
  1490. bwrates="<${drp0}>${drp2}>${drp5}>${drp1}>${drp4}>${drp7}>${drp3}>${drp6}<${dcp0}>${dcp2}>${dcp5}>${dcp1}>${dcp4}>${dcp7}>${dcp3}>${dcp6}<${urp0}>${urp2}>${urp5}>${urp1}>${urp4}>${urp7}>${urp3}>${urp6}<${ucp0}>${ucp2}>${ucp5}>${ucp1}>${ucp4}>${ucp7}>${ucp3}>${ucp6}"
  1491. if [ "${bwrates}" != "<5>15>30>20>10>5>10>5<100>100>100>100>100>100>100>100<5>15>10>20>10>5>30>5<100>100>100>100>100>100>100>100" ]; then
  1492. am_settings_set "${SCRIPTNAME}"_bwrates "<${drp0}>${drp2}>${drp5}>${drp1}>${drp4}>${drp7}>${drp3}>${drp6}<${dcp0}>${dcp2}>${dcp5}>${dcp1}>${dcp4}>${dcp7}>${dcp3}>${dcp6}<${urp0}>${urp2}>${urp5}>${urp1}>${urp4}>${urp7}>${urp3}>${urp6}<${ucp0}>${ucp2}>${ucp5}>${ucp1}>${ucp4}>${ucp7}>${ucp3}>${ucp6}"
  1493. fi
  1494. sed -i "/^${SCRIPTNAME}_bandwidth /d" /jffs/addons/custom_settings.txt
  1495. fi
  1496. fi
  1497. fccontrol="$(am_settings_get "${SCRIPTNAME}"_fccontrol)"
  1498. if [ -z "${fccontrol}" ]; then
  1499. fccontrol="0" # default to Off from GUI
  1500. fi
  1501. # Delete obsolete setting
  1502. if [ -n "$(am_settings_get "${SCRIPTNAME}"_branch)" ]; then
  1503. sed -i "/^${SCRIPTNAME}_branch /d" /jffs/addons/custom_settings.txt
  1504. fi
  1505. } # get_config
  1506.  
  1507. validate_iptables_rules() {
  1508. # Basic check to ensure the number of rules present in the iptables chain matches the number of expected rules
  1509. # Does not verify that the rules present match the rules in the config, since the config hasn't been parsed at this point.
  1510. local iptables_rules_defined iptables_rules_expected iptables_rulespresent
  1511.  
  1512. if [ -z "${iptables_rules}" ]; then
  1513. return 0
  1514. fi
  1515. iptables_rules_defined="$(echo "${iptables_rules}" | sed 's/</\n/g' | /bin/grep -vc "^$")"
  1516. iptables_rules_expected=$((iptables_rules_defined+1)) # 1 download and upload rule per user rule, plus 1 for chain definition
  1517. iptables_rulespresent="$(iptables -t mangle -S ${SCRIPTNAME_DISPLAY}_down | wc -l)" # count rules in chain plus chain itself
  1518. if [ "${iptables_rulespresent}" -lt "${iptables_rules_expected}" ]; then
  1519. return 1
  1520. else
  1521. return 0
  1522. fi
  1523. } # validate_iptables_rules
  1524.  
  1525. write_iptables_rules() {
  1526. # loop through iptables rules and write an iptables command to a temporary file for later execution
  1527. local OLDIFS
  1528. local localip remoteip proto lport rport mark class
  1529. if [ -z "${iptables_rules}" ]; then
  1530. return 0
  1531. fi
  1532. {
  1533. printf "iptables -t mangle -F %s 2>/dev/null\n" "${SCRIPTNAME_DISPLAY}_down"
  1534. printf "iptables -t mangle -F %s 2>/dev/null\n" "${SCRIPTNAME_DISPLAY}_up"
  1535. if [ "${IPv6_enabled}" != "disabled" ]; then
  1536. printf "ip6tables -t mangle -F %s 2>/dev/null\n" "${SCRIPTNAME_DISPLAY}_down"
  1537. printf "ip6tables -t mangle -F %s 2>/dev/null\n" "${SCRIPTNAME_DISPLAY}_up"
  1538. fi
  1539. } > "/tmp/${SCRIPTNAME}_iprules"
  1540. OLDIFS="${IFS}" # Save existing field separator
  1541. IFS=">" # Set custom field separator to match rule format
  1542. # read the rules, 1 per line and break into separate fields
  1543. echo "${iptables_rules}" | sed 's/</\n/g' | while read -r localip remoteip proto lport rport mark class
  1544. do
  1545. # Ensure at least one criteria field is populated
  1546. if [ -n "${localip}${remoteip}${proto}${lport}${rport}${mark}" ]; then
  1547. # Process the rule and the stdout containing the resulting rule gets saved to the temporary script file
  1548. parse_iptablerule "${localip}" "${remoteip}" "${proto}" "${lport}" "${rport}" "${mark}" "${class}" >> "/tmp/${SCRIPTNAME}_iprules" 2>/dev/null
  1549. fi
  1550. done
  1551. IFS="${OLDIFS}" # Restore saved field separator
  1552. } # write_iptables_rules
  1553.  
  1554. write_appdb_rules() {
  1555. # Write the user appdb rules to the existing tcrules file created during write_appdb_static_rules()
  1556. local OLDIFS
  1557. local mark class
  1558. # Save the current filter rules once to avoid repeated calls in parse_appdb_rule() to determine existing prios
  1559. "${TC}" filter show dev "${tclan}" parent 1: > "/tmp/${SCRIPTNAME}_tmp_tcfilterdown"
  1560. "${TC}" filter show dev "${tcwan}" parent 1: > "/tmp/${SCRIPTNAME}_tmp_tcfilterup"
  1561.  
  1562. # loop through appdb rules and write a tc command to a temporary script file
  1563. OLDIFS="${IFS}" # Save existing field separator
  1564. IFS=">" # Set custom field separator to match rule format
  1565.  
  1566. # read the rules, 1 per line and break into separate fields
  1567. echo "${appdb_rules}" | sed 's/</\n/g' | while read -r mark class
  1568. do
  1569. # Ensure the appdb mark is populated
  1570. if [ -n "${mark}" ]; then
  1571. parse_appdb_rule "${mark}" "${class}" >> "/tmp/${SCRIPTNAME}_tcrules" 2>/dev/null
  1572. fi
  1573. done
  1574. IFS="${OLDIFS}" # Restore old field separator
  1575. } # write_appdb_rules
  1576.  
  1577. get_fq_quantum() {
  1578. local BANDWIDTH
  1579. BANDWIDTH="${1}"
  1580.  
  1581. if [ "${BANDWIDTH}" -lt "51200" ]; then
  1582. printf "quantum 300\n"
  1583. fi
  1584. } # get_fq_quantum
  1585.  
  1586. get_fq_target() {
  1587. # Adapted from sqm-scripts adapt_target_to_slow_link() and adapt_interval_to_slow_link().
  1588. # https://github.com/tohojo/sqm-scripts/blob/master/src/functions.sh
  1589. local BANDWIDTH
  1590. local TARGET INTERVAL
  1591. BANDWIDTH="${1}"
  1592.  
  1593. # for ATM the worst case expansion including overhead seems to be 33 cells of 53 bytes each
  1594. # MAX DELAY = 1000 * 1000 * 33 * 53 * 8 / 1000 max delay in microseconds at 1kbps
  1595. TARGET=$(/usr/bin/awk -vBANDWIDTH="${BANDWIDTH}" 'BEGIN { print int( 1000 * 1000 * 33 * 53 * 8 / 1000 / BANDWIDTH ) }')
  1596. if [ "${TARGET}" -gt "5000" ]; then
  1597. # Increase interval by the same amount that target got increased
  1598. INTERVAL=$(( (100 - 5) * 1000 + TARGET ))
  1599. printf "target %sus interval %sus\n" "${TARGET}" "${INTERVAL}"
  1600. fi
  1601. } # get_fq_target
  1602.  
  1603. write_custom_qdisc() {
  1604. local i
  1605. if [ "$(am_settings_get "${SCRIPTNAME}"_qdisc)" != "0" ]; then
  1606. {
  1607. printf "qdisc replace dev %s parent 1:2 handle 102: fq_codel noecn\n" "${tclan}"
  1608. printf "qdisc replace dev %s parent 1:2 handle 102: fq_codel noecn\n" "${tcwan}"
  1609. for i in 0 1 2 3 4 5 6 7
  1610. do
  1611. printf "qdisc replace dev %s parent 1:1%s handle 11%s: fq_codel %s %s\n" "${tclan}" "${i}" "${i}" "$(get_fq_quantum "${DownCeil}")" "$(get_fq_target "${DownCeil}")"
  1612. printf "qdisc replace dev %s parent 1:1%s handle 11%s: fq_codel %s %s noecn\n" "${tcwan}" "${i}" "${i}" "$(get_fq_quantum "${UpCeil}")" "$(get_fq_target "${UpCeil}")"
  1613. done
  1614. } >> "/tmp/${SCRIPTNAME}_tcrules" 2>/dev/null
  1615. fi
  1616. } # write_custom_qdisc
  1617.  
  1618. check_qos_tc() {
  1619. # Check the status of the existing tc class and filter setup by stock Adaptive QoS before custom settings applied.
  1620. # Only br0 interface is checked since we have not yet identified the tcwan interface name yet.
  1621. dlclasscnt="$("${TC}" class show dev br0 parent 1: | /bin/grep -c "parent")" # should be 8
  1622. dlfiltercnt="$("${TC}" filter show dev br0 parent 1: | /bin/grep -cE "flowid 1:1[0-7]")" # should be 39 or 40
  1623. dlqdisccnt="$("${TC}" qdisc show dev br0 | /bin/grep -c " parent 1:1[0-7] ")" # should be 8
  1624. # Check class count, filter count, qdisc count, and tcwan interface name defined with an htb qdisc
  1625. if [ "${dlclasscnt}" -lt "8" ] || [ "${dlfiltercnt}" -lt "39" ] || [ "${dlqdisccnt}" -lt "8" ] || [ -z "$("${TC}" qdisc ls | sed -nE '/dev br[0-9] root/d; s/^qdisc htb 1: dev ([a-z0-9]+) root.*$/\1/p')" ]; then
  1626. return 0
  1627. else
  1628. return 1
  1629. fi
  1630. } # check_qos_tc
  1631.  
  1632. validate_tc_rules() {
  1633. # Check the existing tc filter rules against the user configuration. If any rule missing, force creation of all rules
  1634. # Must run after set_tc_variables() to ensure flowid can be determined
  1635. local OLDIFS filtermissing
  1636. local mark class flowid
  1637. {
  1638. # print a list of existing filters in the format of an appdb rule for easy comparison. Write to tmp file
  1639. "${TC}" filter show dev "${tclan}" parent 1: | sed -nE '/flowid/ { N; s/\n//g; s/.*flowid (1:1[0-7]).*mark 0x[48]0([0-9a-fA-F]{6}).*/<\2>\1/p }'
  1640. "${TC}" filter show dev "${tcwan}" parent 1: | sed -nE '/flowid/ { N; s/\n//g; s/.*flowid (1:1[0-7]).*mark 0x[48]0([0-9a-fA-F]{6}).*/<\2>\1/p }'
  1641. } > "/tmp/${SCRIPTNAME}_checktcrules" 2>/dev/null
  1642. OLDIFS="${IFS}"
  1643. IFS=">"
  1644. filtermissing="0"
  1645. while read -r mark class
  1646. do
  1647. if [ -n "${mark}" ]; then
  1648. flowid="$(get_flowid "${class}")"
  1649. mark="$(echo "${mark}" | sed 's/\*/0/g')"
  1650. if [ "$(/bin/grep -ic "<${mark}>${flowid}" "/tmp/${SCRIPTNAME}_checktcrules")" -lt "2" ]; then
  1651. filtermissing="$((filtermissing+1))"
  1652. break # stop checking after the first missing rule is identified
  1653. fi
  1654. fi
  1655. done <<EOF
  1656. $(echo "${appdb_rules}" | sed 's/</\n/g')
  1657. EOF
  1658. IFS="${OLDIFS}"
  1659. if [ "${filtermissing}" -gt "0" ]; then
  1660. # reapply tc rules
  1661. return 1
  1662. else
  1663. rm "/tmp/${SCRIPTNAME}_checktcrules" 2>/dev/null
  1664. return 0
  1665. fi
  1666. } # validate_tc_rules
  1667.  
  1668. schedule_check_job() {
  1669. # Schedule check for 5 minutes after startup to ensure no qos tc resets
  1670. cru a "${SCRIPTNAME}"_5min "$(/bin/date -D '%s' +'%M %H %d %m %a' -d $(($(/bin/date +%s)+300))) ${SCRIPTPATH} -check"
  1671. } # schedule_check_job
  1672.  
  1673. startup() {
  1674. local sleepdelay
  1675. if [ "$(nvram get qos_enable)" != "1" ] || [ "$(nvram get qos_type)" != "1" ]; then
  1676. logmsg "Adaptive QoS is not enabled. Skipping ${SCRIPTNAME_DISPLAY} startup."
  1677. return 1
  1678. fi # adaptive qos not enabled
  1679.  
  1680. Check_Lock
  1681. install_webui mount
  1682. generate_bwdpi_arrays
  1683. get_config
  1684.  
  1685. case "$(uname -r)" in
  1686. 4.19.*)
  1687. if \
  1688. [ "$(nvram get qos_ibw)" -lt 409600 ] && \
  1689. [ "$(nvram get qos_obw)" -lt 409600 ] && \
  1690. [ "$(nvram get fc_disable)" = "0" ] && \
  1691. [ -n "${iptables_rules}" ] && \
  1692. [ "${fccontrol}" = "2" ]
  1693. then
  1694. logmsg "Auto-disabling flowcache"
  1695. fc disable
  1696. fc flush
  1697. elif \
  1698. [ -n "${iptables_rules}" ] && \
  1699. [ "${fccontrol}" = "1" ]
  1700. then
  1701. logmsg "Disabling flowcache"
  1702. fc disable
  1703. fc flush
  1704. fi
  1705. ;;
  1706. esac
  1707.  
  1708. if ! validate_iptables_rules; then
  1709. write_iptables_rules
  1710. iptables_static_rules 2>&1 | logger -t "${SCRIPTNAME_DISPLAY}"
  1711. if [ -s "/tmp/${SCRIPTNAME}_iprules" ]; then
  1712. logmsg "Applying iptables custom rules"
  1713. . "/tmp/${SCRIPTNAME}_iprules" 2>&1 | logger -t "${SCRIPTNAME_DISPLAY}"
  1714. if [ "$(am_settings_get "${SCRIPTNAME}"_conntrack)" != "0" ]; then
  1715. # Flush conntrack table so that existing connections will be processed by new iptables rules
  1716. logmsg "Flushing conntrack table"
  1717. /usr/sbin/conntrack -F conntrack >/dev/null 2>&1
  1718. fi
  1719. fi
  1720. fi
  1721.  
  1722. cru d "${SCRIPTNAME}"_5min 2>/dev/null
  1723. sleepdelay=0
  1724. while check_qos_tc;
  1725. do
  1726. [ "${sleepdelay}" = "0" ] && logmsg "TC Modification Delayed Start"
  1727. sleep 10s
  1728. if [ "${sleepdelay}" -ge "180" ]; then
  1729. logmsg "QoS state: Classes=${dlclasscnt} | Filters=${dlfiltercnt} | qdiscs=${dlqdisccnt}"
  1730. if [ ! -f "/tmp/${SCRIPTNAME}_restartonce" ]; then
  1731. touch "/tmp/${SCRIPTNAME}_restartonce"
  1732. logmsg "TC Modification Delay reached maximum 180 seconds. Restarting QoS."
  1733. service "restart_qos;restart_firewall"
  1734. else
  1735. logmsg "TC Modification Delay reached maximum 180 seconds again. Canceling startup!"
  1736. rm "/tmp/${SCRIPTNAME}_restartonce" 2>/dev/null
  1737. fi
  1738. return 1
  1739. else
  1740. sleepdelay=$((sleepdelay+10))
  1741. fi
  1742. done
  1743. [ "${sleepdelay}" -gt "0" ] && logmsg "TC Modification delayed for ${sleepdelay} seconds"
  1744. rm "/tmp/${SCRIPTNAME}_restartonce" 2>/dev/null
  1745.  
  1746. set_tc_variables
  1747.  
  1748. # if TC modifcations have not been applied then run modification script
  1749. if ! validate_tc_rules; then
  1750. write_appdb_static_rules
  1751. write_appdb_rules
  1752. write_custom_rates
  1753. write_custom_qdisc
  1754.  
  1755. if [ -s "/tmp/${SCRIPTNAME}_tcrules" ]; then
  1756. logmsg "Applying AppDB rules and TC rates"
  1757. if ! "${TC}" -force -batch "/tmp/${SCRIPTNAME}_tcrules" >"/tmp/${SCRIPTNAME}_tcrules.log" 2>&1; then
  1758. cp -f "/tmp/${SCRIPTNAME}_tcrules" "/tmp/${SCRIPTNAME}_tcrules.err"
  1759. logmsg "ERROR! Check /tmp/${SCRIPTNAME}_tcrules.log"
  1760. else
  1761. rm "/tmp/${SCRIPTNAME}_tmp_tcfilterdown" "/tmp/${SCRIPTNAME}_tmp_tcfilterup" "/tmp/${SCRIPTNAME}_tcrules.log" "/tmp/${SCRIPTNAME}_checktcrules" "/tmp/${SCRIPTNAME}_tcrules.err" 2>/dev/null
  1762. fi
  1763. fi
  1764.  
  1765. schedule_check_job
  1766. else
  1767. logmsg "No TC modifications necessary"
  1768. fi
  1769. } # startup
  1770.  
  1771. show_help() {
  1772. scriptinfo
  1773. Red "You have entered an invalid command"
  1774. cat <<EOF
  1775.  
  1776. Available commands:
  1777.  
  1778. ${SCRIPTNAME} -about explains functionality
  1779. ${SCRIPTNAME} -appdb string search appdb for application marks
  1780. ${SCRIPTNAME} -update checks for updates
  1781. ${SCRIPTNAME} -restart restart QoS and Firewall
  1782. ${SCRIPTNAME} -install install script
  1783. ${SCRIPTNAME} -uninstall uninstall script & delete from disk
  1784. ${SCRIPTNAME} -enable enable script
  1785. ${SCRIPTNAME} -disable disable script but do not delete from disk
  1786. ${SCRIPTNAME} -backup backup user settings
  1787. ${SCRIPTNAME} -debug print debug info
  1788. ${SCRIPTNAME} -menu interactive main menu
  1789.  
  1790. EOF
  1791. webconfigpage
  1792. } # show_help
  1793.  
  1794. generate_bwdpi_arrays() {
  1795. # generate if not exist, plus after wrs restart (signature update)
  1796. # generate if signature rule file is newer than js file
  1797. # generate if js file is smaller than source file (source not present yet during boot)
  1798. # prepend wc variables with zero in case file doesn't exist, to avoid bad number error
  1799. if [ ! -f "/www/user/${SCRIPTNAME}/${SCRIPTNAME}_arrays.js" ] || \
  1800. [ "$(/bin/date -r /jffs/signature/rule.trf +%s)" -gt "$(/bin/date -r "/www/user/${SCRIPTNAME}/${SCRIPTNAME}_arrays.js" +%s)" ] || \
  1801. [ "${1}" = "force" ] || \
  1802. [ "0$(wc -c < "/www/user/${SCRIPTNAME}/${SCRIPTNAME}_arrays.js")" -lt "0$(wc -c 2>/dev/null < /tmp/bwdpi/bwdpi.app.db)" ]; then
  1803. {
  1804. printf "var catdb_mark_array = [ \"000000\""
  1805. awk -F, '{ printf(", \"%02X****\"",$1) }' /tmp/bwdpi/bwdpi.cat.db 2>/dev/null
  1806. awk -F, '{ printf(", \"%02X%04X\"",$1,$2) }' /tmp/bwdpi/bwdpi.app.db 2>/dev/null
  1807. printf ", \"\" ];"
  1808. printf "var catdb_label_array = [ \"Untracked\""
  1809. awk -F, '{ printf(", \"%s (%02X)\"",$2, $1) }' /tmp/bwdpi/bwdpi.cat.db 2>/dev/null
  1810. awk -F, '{ printf(", \"%s\"",$4) }' /tmp/bwdpi/bwdpi.app.db 2>/dev/null
  1811. printf ", \"\" ];"
  1812. } > "/www/user/${SCRIPTNAME}/${SCRIPTNAME}_arrays.js"
  1813. fi
  1814. }
  1815.  
  1816. PressEnter(){
  1817. [ "${mode}" = "interactive" ] || return
  1818. printf "\n"
  1819. while true; do
  1820. printf "Press enter to continue..."
  1821. read -r "key"
  1822. case "${key}" in
  1823. *)
  1824. break
  1825. ;;
  1826. esac
  1827. done
  1828. return 0
  1829. }
  1830.  
  1831. Kill_Lock() {
  1832. if [ -f "/tmp/${SCRIPTNAME}.lock" ] && [ -d "/proc/$(sed -n '1p' "/tmp/${SCRIPTNAME}.lock")" ]; then
  1833. logmsg "[*] Killing Delayed Process (pid=$(sed -n '1p' "/tmp/${SCRIPTNAME}.lock"))"
  1834. logmsg "[*] $(ps | awk -v pid="$(sed -n '1p' "/tmp/${SCRIPTNAME}.lock")" '$1 == pid')"
  1835. kill "$(sed -n '1p' "/tmp/${SCRIPTNAME}.lock")"
  1836. fi
  1837. rm -rf "/tmp/${SCRIPTNAME}.lock"
  1838. } # Kill_Lock
  1839.  
  1840. Check_Lock() {
  1841. if [ -f "/tmp/${SCRIPTNAME}.lock" ] && [ -d "/proc/$(sed -n '1p' "/tmp/${SCRIPTNAME}.lock")" ] && [ "$(sed -n '1p' "/tmp/${SCRIPTNAME}.lock")" != "$$" ]; then
  1842. Kill_Lock
  1843. fi
  1844. printf "%s\n" "$$" > "/tmp/${SCRIPTNAME}.lock"
  1845. lock="true"
  1846. } # Check_Lock
  1847.  
  1848. get_wan_setting() {
  1849. local varname varval
  1850. varname="${1}"
  1851. prefixes="wan0_ wan1_"
  1852.  
  1853. if [ "$(nvram get wans_mode)" = "lb" ] ; then
  1854. for prefix in $prefixes; do
  1855. state="$(nvram get "${prefix}"state_t)"
  1856. sbstate="$(nvram get "${prefix}"sbstate_t)"
  1857. auxstate="$(nvram get "${prefix}"auxstate_t)"
  1858.  
  1859. # is_wan_connect()
  1860. [ "${state}" = "2" ] || continue
  1861. [ "${sbstate}" = "0" ] || continue
  1862. [ "${auxstate}" = "0" ] || [ "${auxstate}" = "2" ] || continue
  1863.  
  1864. # get_wan_ifname()
  1865. proto="$(nvram get "${prefix}"proto)"
  1866. if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then
  1867. varval="$(nvram get "${prefix}"pppoe_"${varname}")"
  1868. else
  1869. varval="$(nvram get "${prefix}""${varname}")"
  1870. fi
  1871. done
  1872. else
  1873. for prefix in $prefixes; do
  1874. primary="$(nvram get "${prefix}"primary)"
  1875. [ "${primary}" = "1" ] && break
  1876. done
  1877.  
  1878. proto="$(nvram get "${prefix}"proto)"
  1879. if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then
  1880. varval="$(nvram get "${prefix}"pppoe_"${varname}")"
  1881. else
  1882. varval="$(nvram get "${prefix}""${varname}")"
  1883. fi
  1884. fi
  1885. printf "%s" "${varval}"
  1886. } # get_wan_setting
  1887.  
  1888. arg1="${1#-}"
  1889. if [ -z "${arg1}" ] || [ "${arg1}" = "menu" ] && ! /bin/grep -qE "${SCRIPTPATH} .* # ${SCRIPTNAME_DISPLAY}" /jffs/scripts/firewall-start; then
  1890. arg1="install"
  1891. fi
  1892.  
  1893. wan="$(get_wan_setting 'ifname')"
  1894. WANMTU="$(get_wan_setting 'mtu')"
  1895. lan="$(nvram get lan_ifname)"
  1896. needrestart=0 # initialize variable used in prompt_restart()
  1897.  
  1898. case "${arg1}" in
  1899. 'start'|'check')
  1900. logmsg "$0 (pid=$$) called in ${mode} mode with $# args: $*"
  1901. startup
  1902. ;;
  1903. 'appdb')
  1904. appdb "${2}"
  1905. ;;
  1906. 'install'|'enable')
  1907. install "${2}"
  1908. ;;
  1909. 'uninstall')
  1910. uninstall
  1911. ;;
  1912. 'disable')
  1913. sed -i "/${SCRIPTNAME}/d" /jffs/scripts/firewall-start 2>/dev/null
  1914. sed -i "/${SCRIPTNAME}/d" /jffs/scripts/service-event-end 2>/dev/null
  1915. remove_webui
  1916. needrestart=2
  1917. ;;
  1918. 'backup')
  1919. backup create force
  1920. ;;
  1921. 'debug')
  1922. debug
  1923. ;;
  1924. 'about')
  1925. about
  1926. ;;
  1927. update*) # updatecheck, updatesilent, or plain update
  1928. update "${arg1#update}" # strip 'update' from arg1 to pass to update function
  1929. ;;
  1930. 'menu'|'')
  1931. menu
  1932. ;;
  1933. 'restart')
  1934. needrestart=2
  1935. ;;
  1936. 'flushct')
  1937. sed -i "/^${SCRIPTNAME}_conntrack /d" /jffs/addons/custom_settings.txt
  1938. Green "Enabled conntrack flushing."
  1939. ;;
  1940. 'noflushct')
  1941. am_settings_set "${SCRIPTNAME}_conntrack" "0"
  1942. Yellow "Disabled conntrack flushing."
  1943. ;;
  1944. *)
  1945. show_help
  1946. ;;
  1947. esac
  1948.  
  1949. prompt_restart
  1950. if [ "${lock}" = "true" ]; then rm -rf "/tmp/${SCRIPTNAME}.lock"; fi
  1951.  
Add Comment
Please, Sign In to add comment