2
0

generate.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. #!/bin/sh
  2. [ -e /etc/functions.sh ] && . /etc/functions.sh || . ./functions.sh
  3. [ -x /sbin/modprobe ] && insmod="modprobe" || insmod="insmod"
  4. add_insmod() {
  5. eval "export isset=\${insmod_$1}"
  6. case "$isset" in
  7. 1) ;;
  8. *) append INSMOD "$insmod $* >&- 2>&-" "$N"; export insmod_$1=1;;
  9. esac
  10. }
  11. [ -e /etc/config/network ] && {
  12. # only try to parse network config on openwrt
  13. find_ifname() {(
  14. reset_cb
  15. include /lib/network
  16. scan_interfaces
  17. config_load /var/state/network
  18. config_get "$1" ifname
  19. )}
  20. } || {
  21. find_ifname() {
  22. echo "Interface not found."
  23. exit 1
  24. }
  25. }
  26. parse_matching_rule() {
  27. local var="$1"
  28. local section="$2"
  29. local options="$3"
  30. local prefix="$4"
  31. local suffix="$5"
  32. local proto="$6"
  33. local mport=""
  34. local ports=""
  35. append "$var" "$prefix" "$N"
  36. for option in $options; do
  37. case "$option" in
  38. proto) config_get value "$section" proto; proto="${proto:-$value}";;
  39. esac
  40. done
  41. config_get type "$section" TYPE
  42. case "$type" in
  43. classify) unset pkt; append "$var" "-m mark --mark 0";;
  44. default) pkt=1; append "$var" "-m mark --mark 0";;
  45. reclassify) pkt=1;;
  46. esac
  47. append "$var" "${proto:+-p $proto}"
  48. for option in $options; do
  49. config_get value "$section" "$option"
  50. case "$pkt:$option" in
  51. *:srchost)
  52. append "$var" "-s $value"
  53. ;;
  54. *:dsthost)
  55. append "$var" "-d $value"
  56. ;;
  57. *:ipp2p)
  58. add_insmod ipt_ipp2p
  59. append "$var" "-m ipp2p"
  60. case "$value" in
  61. all) append "$var" "--edk --dc --kazaa --gnu --bit";;
  62. *) append "$var" "--$value";;
  63. esac
  64. ;;
  65. *:layer7)
  66. add_insmod ipt_layer7
  67. append "$var" "-m layer7 --l7proto $value${pkt:+ --l7pkt}"
  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 ipt_connbytes
  112. append "$var" "-m connbytes --connbytes $value --connbytes-dir both --connbytes-mode bytes"
  113. ;;
  114. *:tos)
  115. add_insmod ipt_tos
  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 ipt_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. 1:pktsize)
  139. value="$(echo "$value" | sed -e 's,-,:,g')"
  140. add_insmod ipt_length
  141. append "$var" "-m length --length $value"
  142. ;;
  143. 1:limit)
  144. add_insmod ipt_limit
  145. append "$var" "-m limit --limit $value"
  146. ;;
  147. 1:tcpflags)
  148. case "$proto" in
  149. tcp) append "$var" "-m tcp --tcp-flags ALL $value";;
  150. *) unset $var; return 0;;
  151. esac
  152. ;;
  153. 1:mark)
  154. config_get class "${value##!}" classnr
  155. [ -z "$class" ] && continue;
  156. case "$value" in
  157. !*) append "$var" "-m mark ! --mark $class";;
  158. *) append "$var" "-m mark --mark $class";;
  159. esac
  160. ;;
  161. 1:TOS)
  162. add_insmod ipt_TOS
  163. config_get TOS "$rule" 'TOS'
  164. suffix="-j TOS --set-tos "${TOS:-"Normal-Service"}
  165. ;;
  166. 1:DSCP)
  167. add_insmod ipt_DSCP
  168. config_get DSCP "$rule" 'DSCP'
  169. [ -z "${DSCP%%[EBCA]*}" ] && set_value="--set-dscp-class $DSCP" \
  170. || set_value="--set-dscp $DSCP"
  171. suffix="-j DSCP $set_value"
  172. ;;
  173. esac
  174. done
  175. append "$var" "$suffix"
  176. case "$ports:$proto" in
  177. 1:) parse_matching_rule "$var" "$section" "$options" "$prefix" "$suffix" "udp";;
  178. esac
  179. }
  180. config_cb() {
  181. option_cb() {
  182. return 0
  183. }
  184. # Section start
  185. case "$1" in
  186. interface)
  187. config_set "$2" "classgroup" "Default"
  188. config_set "$2" "upload" "128"
  189. ;;
  190. classify|default|reclassify)
  191. option_cb() {
  192. append options "$1"
  193. }
  194. ;;
  195. esac
  196. # Section end
  197. config_get TYPE "$CONFIG_SECTION" TYPE
  198. case "$TYPE" in
  199. interface)
  200. config_get_bool enabled "$CONFIG_SECTION" enabled 1
  201. [ 1 -eq "$enabled" ] || return 0
  202. config_get classgroup "$CONFIG_SECTION" classgroup
  203. config_set "$CONFIG_SECTION" imqdev "$C"
  204. C=$(($C+1))
  205. append INTERFACES "$CONFIG_SECTION"
  206. config_set "$classgroup" enabled 1
  207. config_get device "$CONFIG_SECTION" device
  208. [ -z "$device" ] && {
  209. device="$(find_ifname ${CONFIG_SECTION})"
  210. config_set "$CONFIG_SECTION" device "${device:-eth0}"
  211. }
  212. ;;
  213. classgroup) append CG "$CONFIG_SECTION";;
  214. classify|default|reclassify)
  215. case "$TYPE" in
  216. classify) var="ctrules";;
  217. *) var="rules";;
  218. esac
  219. config_get target "$CONFIG_SECTION" target
  220. config_set "$CONFIG_SECTION" options "$options"
  221. append "$var" "$CONFIG_SECTION"
  222. unset options
  223. ;;
  224. esac
  225. }
  226. enum_classes() {
  227. local c="0"
  228. config_get classes "$1" classes
  229. config_get default "$1" default
  230. for class in $classes; do
  231. c="$(($c + 1))"
  232. config_set "${class}" classnr $c
  233. case "$class" in
  234. $default) class_default=$c;;
  235. esac
  236. done
  237. class_default="${class_default:-$c}"
  238. }
  239. cls_var() {
  240. local varname="$1"
  241. local class="$2"
  242. local name="$3"
  243. local type="$4"
  244. local default="$5"
  245. local tmp tmp1 tmp2
  246. config_get tmp1 "$class" "$name"
  247. config_get tmp2 "${class}_${type}" "$name"
  248. tmp="${tmp2:-$tmp1}"
  249. tmp="${tmp:-$tmp2}"
  250. export ${varname}="${tmp:-$default}"
  251. }
  252. tcrules() {
  253. dir=/usr/lib/qos
  254. [ -e $dir/tcrules.awk ] || dir=.
  255. echo "$cstr" | awk \
  256. -v device="$dev" \
  257. -v linespeed="$rate" \
  258. -f $dir/tcrules.awk
  259. }
  260. start_interface() {
  261. local iface="$1"
  262. local num_imq="$2"
  263. config_get device "$iface" device
  264. config_get_bool enabled "$iface" enabled 1
  265. [ -z "$device" -o 1 -ne "$enabled" ] && {
  266. return 1
  267. }
  268. config_get upload "$iface" upload
  269. config_get halfduplex "$iface" halfduplex
  270. config_get download "$iface" download
  271. config_get classgroup "$iface" classgroup
  272. config_get_bool overhead "$iface" overhead 0
  273. download="${download:-${halfduplex:+$upload}}"
  274. enum_classes "$classgroup"
  275. for dir in up${halfduplex} ${download:+down}; do
  276. case "$dir" in
  277. up)
  278. [ "$overhead" = 1 ] && upload=$(($upload * 98 / 100 - (15 * 128 / $upload)))
  279. dev="$device"
  280. rate="$upload"
  281. dl_mode=""
  282. prefix="cls"
  283. ;;
  284. down)
  285. add_insmod imq numdevs="$num_imq"
  286. config_get imqdev "$iface" imqdev
  287. [ "$overhead" = 1 ] && download=$(($download * 98 / 100 - (80 * 1024 / $download)))
  288. dev="imq$imqdev"
  289. rate="$download"
  290. dl_mode=1
  291. prefix="d_cls"
  292. ;;
  293. *) continue;;
  294. esac
  295. cstr=
  296. for class in $classes; do
  297. cls_var pktsize "$class" packetsize $dir 1500
  298. cls_var pktdelay "$class" packetdelay $dir 0
  299. cls_var maxrate "$class" limitrate $dir 100
  300. cls_var prio "$class" priority $dir 1
  301. cls_var avgrate "$class" avgrate $dir 0
  302. config_get classnr "$class" classnr
  303. append cstr "$classnr:$prio:$avgrate:$pktsize:$pktdelay:$maxrate" "$N"
  304. done
  305. append ${prefix}q "$(tcrules)" "$N"
  306. export dev_${dir}="ifconfig $dev up txqueuelen 5 >&- 2>&-
  307. tc qdisc del dev $dev root >&- 2>&-
  308. tc qdisc add dev $dev root handle 1: hfsc default ${class_default}0
  309. tc class add dev $dev parent 1: classid 1:1 hfsc sc rate ${rate}kbit ul rate ${rate}kbit"
  310. done
  311. add_insmod cls_fw
  312. add_insmod sch_hfsc
  313. add_insmod sch_sfq
  314. add_insmod sch_red
  315. cat <<EOF
  316. ${INSMOD:+$INSMOD$N}${dev_up:+$dev_up
  317. $clsq
  318. }${imqdev:+$dev_down
  319. $d_clsq
  320. $d_clsl
  321. $d_clsf
  322. }
  323. EOF
  324. unset INSMOD clsq clsf clsl d_clsq d_clsl d_clsf dev_up dev_down
  325. }
  326. start_interfaces() {
  327. local C="$1"
  328. for iface in $INTERFACES; do
  329. start_interface "$iface" "$C"
  330. done
  331. }
  332. add_rules() {
  333. local var="$1"
  334. local rules="$2"
  335. local prefix="$3"
  336. for rule in $rules; do
  337. unset iptrule
  338. config_get target "$rule" target
  339. config_get target "$target" classnr
  340. config_get options "$rule" options
  341. ## If we want to override the TOS field, let's clear the DSCP field first.
  342. [ ! -z "$(echo $options | grep 'TOS')" ] && {
  343. s_options=${options%%TOS}
  344. add_insmod ipt_DSCP
  345. parse_matching_rule iptrule "$rule" "$s_options" "$prefix" "-j DSCP --set-dscp 0"
  346. append "$var" "$iptrule" "$N"
  347. unset iptrule
  348. }
  349. parse_matching_rule iptrule "$rule" "$options" "$prefix" "-j MARK --set-mark $target"
  350. append "$var" "$iptrule" "$N"
  351. done
  352. }
  353. start_cg() {
  354. local cg="$1"
  355. local iptrules
  356. local pktrules
  357. local sizerules
  358. local download
  359. enum_classes "$cg"
  360. add_rules iptrules "$ctrules" "iptables -t mangle -A ${cg}_ct"
  361. config_get classes "$cg" classes
  362. for class in $classes; do
  363. config_get mark "$class" classnr
  364. config_get maxsize "$class" maxsize
  365. [ -z "$maxsize" -o -z "$mark" ] || {
  366. add_insmod ipt_length
  367. append pktrules "iptables -t mangle -A ${cg} -m mark --mark $mark -m length --length $maxsize: -j MARK --set-mark 0" "$N"
  368. }
  369. done
  370. add_rules pktrules "$rules" "iptables -t mangle -A ${cg}"
  371. for iface in $INTERFACES; do
  372. config_get classgroup "$iface" classgroup
  373. config_get device "$iface" device
  374. config_get imqdev "$iface" imqdev
  375. config_get dl "$iface" download
  376. config_get halfduplex "$iface" halfduplex
  377. add_insmod ipt_IMQ
  378. append up "iptables -t mangle -A OUTPUT -o $device -j ${cg}" "$N"
  379. append up "iptables -t mangle -A FORWARD -o $device -j ${cg}" "$N"
  380. [ -z "$dl" ] || {
  381. [ -z "$halfduplex" ] || {
  382. append down "iptables -t mangle -A POSTROUTING -o $device -j IMQ --todev $imqdev" "$N"
  383. }
  384. append down "iptables -t mangle -A PREROUTING -i $device -j ${cg}" "$N"
  385. append down "iptables -t mangle -A POSTROUTING -o $device -j ${cg}" "$N"
  386. append down "iptables -t mangle -A PREROUTING -i $device -j IMQ --todev $imqdev" "$N"
  387. }
  388. done
  389. cat <<EOF
  390. $INSMOD
  391. iptables -t mangle -N ${cg} >&- 2>&-
  392. iptables -t mangle -N ${cg}_ct >&- 2>&-
  393. ${iptrules:+${iptrules}${N}iptables -t mangle -A ${cg}_ct -j CONNMARK --save-mark}
  394. iptables -t mangle -A ${cg} -j CONNMARK --restore-mark
  395. iptables -t mangle -A ${cg} -m mark --mark 0 -j ${cg}_ct
  396. $pktrules
  397. $up$N${down:+${down}$N}
  398. EOF
  399. unset INSMOD
  400. }
  401. start_firewall() {
  402. add_insmod ipt_multiport
  403. add_insmod ipt_CONNMARK
  404. cat <<EOF
  405. iptables -t mangle -F
  406. iptables -t mangle -X
  407. EOF
  408. for group in $CG; do
  409. start_cg $group
  410. done
  411. }
  412. C="0"
  413. INTERFACES=""
  414. [ -e ./qos.conf ] && {
  415. . ./qos.conf
  416. config_cb
  417. } || config_load qos
  418. C="0"
  419. for iface in $INTERFACES; do
  420. export C="$(($C + 1))"
  421. done
  422. case "$1" in
  423. all)
  424. start_interfaces "$C"
  425. start_firewall
  426. ;;
  427. interface)
  428. start_interface "$2" "$C"
  429. ;;
  430. interfaces)
  431. start_interfaces
  432. ;;
  433. firewall)
  434. start_firewall
  435. ;;
  436. esac