generate.sh 13 KB

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