generate.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. #!/bin/sh
  2. [ -e /lib/functions.sh ] && . /lib/functions.sh || . ./functions.sh
  3. [ -x /sbin/modprobe ] && {
  4. insmod="modprobe"
  5. rmmod="$insmod -r"
  6. } || {
  7. insmod="insmod"
  8. rmmod="rmmod"
  9. }
  10. add_insmod() {
  11. eval "export isset=\${insmod_$1}"
  12. case "$isset" in
  13. 1) ;;
  14. *) {
  15. [ "$2" ] && append INSMOD "$rmmod $1 >&- 2>&-" "$N"
  16. append INSMOD "$insmod $* >&- 2>&-" "$N"; export insmod_$1=1
  17. };;
  18. esac
  19. }
  20. [ -e /etc/config/network ] && {
  21. # only try to parse network config on openwrt
  22. find_ifname() {(
  23. reset_cb
  24. include /lib/network
  25. scan_interfaces
  26. config_get "$1" ifname
  27. )}
  28. } || {
  29. find_ifname() {
  30. echo "Interface not found."
  31. exit 1
  32. }
  33. }
  34. parse_matching_rule() {
  35. local var="$1"
  36. local section="$2"
  37. local options="$3"
  38. local prefix="$4"
  39. local suffix="$5"
  40. local proto="$6"
  41. local mport=""
  42. local ports=""
  43. append "$var" "$prefix" "$N"
  44. for option in $options; do
  45. case "$option" in
  46. proto) config_get value "$section" proto; proto="${proto:-$value}";;
  47. esac
  48. done
  49. config_get type "$section" TYPE
  50. case "$type" in
  51. classify) unset pkt; append "$var" "-m mark --mark 0/0x0f";;
  52. default) pkt=1; append "$var" "-m mark --mark 0/0xf0";;
  53. reclassify) pkt=1;;
  54. esac
  55. append "$var" "${proto:+-p $proto}"
  56. for option in $options; do
  57. config_get value "$section" "$option"
  58. case "$pkt:$option" in
  59. *:srchost)
  60. append "$var" "-s $value"
  61. ;;
  62. *:dsthost)
  63. append "$var" "-d $value"
  64. ;;
  65. *:ports|*:srcports|*:dstports)
  66. value="$(echo "$value" | sed -e 's,-,:,g')"
  67. lproto=${lproto:-tcp}
  68. case "$proto" in
  69. ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} -m multiport";;
  70. *) unset "$var"; return 0;;
  71. esac
  72. case "$option" in
  73. ports)
  74. config_set "$section" srcports ""
  75. config_set "$section" dstports ""
  76. config_set "$section" portrange ""
  77. append "$var" "--ports $value"
  78. ;;
  79. srcports)
  80. config_set "$section" ports ""
  81. config_set "$section" dstports ""
  82. config_set "$section" portrange ""
  83. append "$var" "--sports $value"
  84. ;;
  85. dstports)
  86. config_set "$section" ports ""
  87. config_set "$section" srcports ""
  88. config_set "$section" portrange ""
  89. append "$var" "--dports $value"
  90. ;;
  91. esac
  92. ports=1
  93. ;;
  94. *:portrange)
  95. config_set "$section" ports ""
  96. config_set "$section" srcports ""
  97. config_set "$section" dstports ""
  98. value="$(echo "$value" | sed -e 's,-,:,g')"
  99. case "$proto" in
  100. ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} --sport $value --dport $value";;
  101. *) unset "$var"; return 0;;
  102. esac
  103. ports=1
  104. ;;
  105. *:connbytes)
  106. value="$(echo "$value" | sed -e 's,-,:,g')"
  107. add_insmod xt_connbytes
  108. append "$var" "-m connbytes --connbytes $value --connbytes-dir both --connbytes-mode bytes"
  109. ;;
  110. *:comment)
  111. add_insmod xt_comment
  112. append "$var" "-m comment --comment '$value'"
  113. ;;
  114. *:tos)
  115. add_insmod xt_dscp
  116. case "$value" in
  117. !*) append "$var" "-m tos ! --tos $value";;
  118. *) append "$var" "-m tos --tos $value"
  119. esac
  120. ;;
  121. *:dscp)
  122. add_insmod xt_dscp
  123. dscp_option="--dscp"
  124. [ -z "${value%%[EBCA]*}" ] && dscp_option="--dscp-class"
  125. case "$value" in
  126. !*) append "$var" "-m dscp ! $dscp_option $value";;
  127. *) append "$var" "-m dscp $dscp_option $value"
  128. esac
  129. ;;
  130. *:direction)
  131. value="$(echo "$value" | sed -e 's,-,:,g')"
  132. if [ "$value" = "out" ]; then
  133. append "$var" "-o $device"
  134. elif [ "$value" = "in" ]; then
  135. append "$var" "-i $device"
  136. fi
  137. ;;
  138. *:srciface)
  139. append "$var" "-i $value"
  140. ;;
  141. 1:pktsize)
  142. value="$(echo "$value" | sed -e 's,-,:,g')"
  143. add_insmod xt_length
  144. append "$var" "-m length --length $value"
  145. ;;
  146. 1:limit)
  147. add_insmod xt_limit
  148. append "$var" "-m limit --limit $value"
  149. ;;
  150. 1:tcpflags)
  151. case "$proto" in
  152. tcp) append "$var" "-m tcp --tcp-flags ALL $value";;
  153. *) unset $var; return 0;;
  154. esac
  155. ;;
  156. 1:mark)
  157. config_get class "${value##!}" classnr
  158. [ -z "$class" ] && continue;
  159. case "$value" in
  160. !*) append "$var" "-m mark ! --mark $class/0x0f";;
  161. *) append "$var" "-m mark --mark $class/0x0f";;
  162. esac
  163. ;;
  164. 1:TOS)
  165. add_insmod xt_DSCP
  166. config_get TOS "$rule" 'TOS'
  167. suffix="-j TOS --set-tos "${TOS:-"Normal-Service"}
  168. ;;
  169. 1:DSCP)
  170. add_insmod xt_DSCP
  171. config_get DSCP "$rule" 'DSCP'
  172. [ -z "${DSCP%%[EBCA]*}" ] && set_value="--set-dscp-class $DSCP" \
  173. || set_value="--set-dscp $DSCP"
  174. suffix="-j DSCP $set_value"
  175. ;;
  176. esac
  177. done
  178. append "$var" "$suffix"
  179. case "$ports:$proto" in
  180. 1:) parse_matching_rule "$var" "$section" "$options" "$prefix" "$suffix" "udp";;
  181. esac
  182. }
  183. config_cb() {
  184. option_cb() {
  185. return 0
  186. }
  187. # Section start
  188. case "$1" in
  189. interface)
  190. config_set "$2" "classgroup" "Default"
  191. config_set "$2" "upload" "128"
  192. ;;
  193. classify|default|reclassify)
  194. option_cb() {
  195. append options "$1"
  196. }
  197. ;;
  198. esac
  199. # Section end
  200. config_get TYPE "$CONFIG_SECTION" TYPE
  201. case "$TYPE" in
  202. interface)
  203. config_get_bool enabled "$CONFIG_SECTION" enabled 1
  204. [ 1 -eq "$enabled" ] || return 0
  205. config_get classgroup "$CONFIG_SECTION" classgroup
  206. config_set "$CONFIG_SECTION" ifbdev "$C"
  207. C=$(($C+1))
  208. append INTERFACES "$CONFIG_SECTION"
  209. config_set "$classgroup" enabled 1
  210. config_get device "$CONFIG_SECTION" device
  211. [ -z "$device" ] && {
  212. device="$(find_ifname ${CONFIG_SECTION})"
  213. config_set "$CONFIG_SECTION" device "$device"
  214. }
  215. ;;
  216. classgroup) append CG "$CONFIG_SECTION";;
  217. classify|default|reclassify)
  218. case "$TYPE" in
  219. classify) var="ctrules";;
  220. *) var="rules";;
  221. esac
  222. config_get target "$CONFIG_SECTION" target
  223. config_set "$CONFIG_SECTION" options "$options"
  224. append "$var" "$CONFIG_SECTION"
  225. unset options
  226. ;;
  227. esac
  228. }
  229. enum_classes() {
  230. local c="0"
  231. config_get classes "$1" classes
  232. config_get default "$1" default
  233. for class in $classes; do
  234. c="$(($c + 1))"
  235. config_set "${class}" classnr $c
  236. case "$class" in
  237. $default) class_default=$c;;
  238. esac
  239. done
  240. class_default="${class_default:-$c}"
  241. }
  242. cls_var() {
  243. local varname="$1"
  244. local class="$2"
  245. local name="$3"
  246. local type="$4"
  247. local default="$5"
  248. local tmp tmp1 tmp2
  249. config_get tmp1 "$class" "$name"
  250. config_get tmp2 "${class}_${type}" "$name"
  251. tmp="${tmp2:-$tmp1}"
  252. tmp="${tmp:-$tmp2}"
  253. export ${varname}="${tmp:-$default}"
  254. }
  255. tcrules() {
  256. _dir=/usr/lib/qos
  257. [ -e $_dir/tcrules.awk ] || _dir=.
  258. echo "$cstr" | awk \
  259. -v device="$dev" \
  260. -v linespeed="$rate" \
  261. -v direction="$dir" \
  262. -f $_dir/tcrules.awk
  263. }
  264. start_interface() {
  265. local iface="$1"
  266. local num_ifb="$2"
  267. config_get device "$iface" device
  268. config_get_bool enabled "$iface" enabled 1
  269. [ -z "$device" -o 1 -ne "$enabled" ] && {
  270. return 1
  271. }
  272. config_get upload "$iface" upload
  273. config_get_bool halfduplex "$iface" halfduplex
  274. config_get download "$iface" download
  275. config_get classgroup "$iface" classgroup
  276. config_get_bool overhead "$iface" overhead 0
  277. download="${download:-${halfduplex:+$upload}}"
  278. enum_classes "$classgroup"
  279. for dir in ${halfduplex:-up} ${download:+down}; do
  280. case "$dir" in
  281. up)
  282. [ "$overhead" = 1 ] && upload=$(($upload * 98 / 100 - (15 * 128 / $upload)))
  283. dev="$device"
  284. rate="$upload"
  285. dl_mode=""
  286. prefix="cls"
  287. ;;
  288. down)
  289. [ "$(ls -d /proc/sys/net/ipv4/conf/ifb* 2>&- | wc -l)" -ne "$num_ifb" ] && add_insmod ifb numifbs="$num_ifb"
  290. config_get ifbdev "$iface" ifbdev
  291. [ "$overhead" = 1 ] && download=$(($download * 98 / 100 - (80 * 1024 / $download)))
  292. dev="ifb$ifbdev"
  293. rate="$download"
  294. dl_mode=1
  295. prefix="d_cls"
  296. ;;
  297. *) continue;;
  298. esac
  299. cstr=
  300. for class in $classes; do
  301. cls_var pktsize "$class" packetsize $dir 1500
  302. cls_var pktdelay "$class" packetdelay $dir 0
  303. cls_var maxrate "$class" limitrate $dir 100
  304. cls_var prio "$class" priority $dir 1
  305. cls_var avgrate "$class" avgrate $dir 0
  306. cls_var qdisc "$class" qdisc $dir ""
  307. cls_var filter "$class" filter $dir ""
  308. config_get classnr "$class" classnr
  309. append cstr "$classnr:$prio:$avgrate:$pktsize:$pktdelay:$maxrate:$qdisc:$filter" "$N"
  310. done
  311. append ${prefix}q "$(tcrules)" "$N"
  312. export dev_${dir}="ifconfig $dev up >&- 2>&-
  313. tc qdisc del dev $dev root >&- 2>&-
  314. tc qdisc add dev $dev root handle 1: hfsc default ${class_default}0
  315. tc class add dev $dev parent 1: classid 1:1 hfsc sc rate ${rate}kbit ul rate ${rate}kbit"
  316. done
  317. [ -n "$download" ] && {
  318. add_insmod cls_u32
  319. add_insmod em_u32
  320. add_insmod act_connmark
  321. add_insmod act_mirred
  322. add_insmod sch_ingress
  323. }
  324. if [ -n "$halfduplex" ]; then
  325. export dev_up="tc qdisc del dev $device root >&- 2>&-
  326. tc qdisc add dev $device root handle 1: hfsc
  327. tc filter add dev $device parent 1: prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev ifb$ifbdev"
  328. elif [ -n "$download" ]; then
  329. append dev_${dir} "tc qdisc del dev $device ingress >&- 2>&-
  330. tc qdisc add dev $device ingress
  331. tc filter add dev $device parent ffff: prio 1 u32 match u32 0 0 flowid 1:1 action connmark action mirred egress redirect dev ifb$ifbdev" "$N"
  332. fi
  333. add_insmod cls_fw
  334. add_insmod sch_hfsc
  335. add_insmod sch_fq_codel
  336. cat <<EOF
  337. ${INSMOD:+$INSMOD$N}${dev_up:+$dev_up
  338. $clsq
  339. }${ifbdev:+$dev_down
  340. $d_clsq
  341. $d_clsl
  342. $d_clsf
  343. }
  344. EOF
  345. unset INSMOD clsq clsf clsl d_clsq d_clsl d_clsf dev_up dev_down
  346. }
  347. start_interfaces() {
  348. local C="$1"
  349. for iface in $INTERFACES; do
  350. start_interface "$iface" "$C"
  351. done
  352. }
  353. add_rules() {
  354. local var="$1"
  355. local rules="$2"
  356. local prefix="$3"
  357. for rule in $rules; do
  358. unset iptrule
  359. config_get target "$rule" target
  360. config_get target "$target" classnr
  361. config_get options "$rule" options
  362. ## If we want to override the TOS field, let's clear the DSCP field first.
  363. [ ! -z "$(echo $options | grep 'TOS')" ] && {
  364. s_options=${options%%TOS}
  365. add_insmod xt_DSCP
  366. parse_matching_rule iptrule "$rule" "$s_options" "$prefix" "-j DSCP --set-dscp 0"
  367. append "$var" "$iptrule" "$N"
  368. unset iptrule
  369. }
  370. target=$(($target | ($target << 4)))
  371. parse_matching_rule iptrule "$rule" "$options" "$prefix" "-j MARK --set-mark $target/0xff"
  372. append "$var" "$iptrule" "$N"
  373. done
  374. }
  375. start_cg() {
  376. local cg="$1"
  377. local iptrules
  378. local pktrules
  379. local sizerules
  380. enum_classes "$cg"
  381. for command in $iptables; do
  382. add_rules iptrules "$ctrules" "$command -w -t mangle -A qos_${cg}_ct"
  383. done
  384. config_get classes "$cg" classes
  385. for class in $classes; do
  386. config_get mark "$class" classnr
  387. config_get maxsize "$class" maxsize
  388. [ -z "$maxsize" -o -z "$mark" ] || {
  389. add_insmod xt_length
  390. for command in $iptables; do
  391. append pktrules "$command -w -t mangle -A qos_${cg} -m mark --mark $mark/0x0f -m length --length $maxsize: -j MARK --set-mark 0/0xff" "$N"
  392. done
  393. }
  394. done
  395. for command in $iptables; do
  396. add_rules pktrules "$rules" "$command -w -t mangle -A qos_${cg}"
  397. done
  398. for iface in $INTERFACES; do
  399. config_get classgroup "$iface" classgroup
  400. config_get device "$iface" device
  401. config_get ifbdev "$iface" ifbdev
  402. config_get upload "$iface" upload
  403. config_get download "$iface" download
  404. config_get halfduplex "$iface" halfduplex
  405. download="${download:-${halfduplex:+$upload}}"
  406. for command in $iptables; do
  407. append up "$command -w -t mangle -A OUTPUT -o $device -j qos_${cg}" "$N"
  408. append up "$command -w -t mangle -A FORWARD -o $device -j qos_${cg}" "$N"
  409. done
  410. done
  411. cat <<EOF
  412. $INSMOD
  413. EOF
  414. for command in $iptables; do
  415. cat <<EOF
  416. $command -w -t mangle -N qos_${cg}
  417. $command -w -t mangle -N qos_${cg}_ct
  418. EOF
  419. done
  420. cat <<EOF
  421. ${iptrules:+${iptrules}${N}}
  422. EOF
  423. for command in $iptables; do
  424. cat <<EOF
  425. $command -w -t mangle -A qos_${cg}_ct -j CONNMARK --save-mark --mask 0xff
  426. $command -w -t mangle -A qos_${cg} -j CONNMARK --restore-mark --mask 0x0f
  427. $command -w -t mangle -A qos_${cg} -m mark --mark 0/0x0f -j qos_${cg}_ct
  428. EOF
  429. done
  430. cat <<EOF
  431. $pktrules
  432. EOF
  433. for command in $iptables; do
  434. cat <<EOF
  435. $command -w -t mangle -A qos_${cg} -j CONNMARK --save-mark --mask 0xff
  436. EOF
  437. done
  438. cat <<EOF
  439. $up$N${down:+${down}$N}
  440. EOF
  441. unset INSMOD
  442. }
  443. start_firewall() {
  444. add_insmod xt_multiport
  445. add_insmod xt_CONNMARK
  446. stop_firewall
  447. for group in $CG; do
  448. start_cg $group
  449. done
  450. }
  451. stop_firewall() {
  452. # Builds up a list of iptables commands to flush the qos_* chains,
  453. # remove rules referring to them, then delete them
  454. # Print rules in the mangle table, like iptables-save
  455. for command in $iptables; do
  456. $command -w -t mangle -S |
  457. # Find rules for the qos_* chains
  458. grep -E '(^-N qos_|-j qos_)' |
  459. # Exclude rules in qos_* chains (inter-qos_* refs)
  460. grep -v '^-A qos_' |
  461. # Replace -N with -X and hold, with -F and print
  462. # Replace -A with -D
  463. # Print held lines at the end (note leading newline)
  464. sed -e '/^-N/{s/^-N/-X/;H;s/^-X/-F/}' \
  465. -e 's/^-A/-D/' \
  466. -e '${p;g}' |
  467. # Make into proper iptables calls
  468. # Note: awkward in previous call due to hold space usage
  469. sed -n -e "s/^./${command} -w -t mangle &/p"
  470. done
  471. }
  472. C="0"
  473. INTERFACES=""
  474. [ -e ./qos.conf ] && {
  475. . ./qos.conf
  476. config_cb
  477. } || config_load qos
  478. C="0"
  479. for iface in $INTERFACES; do
  480. export C="$(($C + 1))"
  481. done
  482. [ -x /usr/sbin/ip6tables ] && {
  483. iptables="ip6tables iptables"
  484. } || {
  485. iptables="iptables"
  486. }
  487. case "$1" in
  488. all)
  489. start_interfaces "$C"
  490. start_firewall
  491. ;;
  492. interface)
  493. start_interface "$2" "$C"
  494. ;;
  495. interfaces)
  496. start_interfaces
  497. ;;
  498. firewall)
  499. case "$2" in
  500. stop)
  501. stop_firewall
  502. ;;
  503. start|"")
  504. start_firewall
  505. ;;
  506. esac
  507. ;;
  508. esac