generate.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. #!/bin/sh
  2. [ -e /etc/functions.sh ] && . /etc/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";;
  52. default) pkt=1; append "$var" "-m mark --mark 0";;
  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. *:layer7)
  66. add_insmod ipt_layer7
  67. add_insmod xt_layer7
  68. append "$var" "-m layer7 --l7proto $value${pkt:+ --l7pkt}"
  69. ;;
  70. *:ports|*:srcports|*:dstports)
  71. value="$(echo "$value" | sed -e 's,-,:,g')"
  72. lproto=${lproto:-tcp}
  73. case "$proto" in
  74. ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} -m multiport";;
  75. *) unset "$var"; return 0;;
  76. esac
  77. case "$option" in
  78. ports)
  79. config_set "$section" srcports ""
  80. config_set "$section" dstports ""
  81. config_set "$section" portrange ""
  82. append "$var" "--ports $value"
  83. ;;
  84. srcports)
  85. config_set "$section" ports ""
  86. config_set "$section" dstports ""
  87. config_set "$section" portrange ""
  88. append "$var" "--sports $value"
  89. ;;
  90. dstports)
  91. config_set "$section" ports ""
  92. config_set "$section" srcports ""
  93. config_set "$section" portrange ""
  94. append "$var" "--dports $value"
  95. ;;
  96. esac
  97. ports=1
  98. ;;
  99. *:portrange)
  100. config_set "$section" ports ""
  101. config_set "$section" srcports ""
  102. config_set "$section" dstports ""
  103. value="$(echo "$value" | sed -e 's,-,:,g')"
  104. case "$proto" in
  105. ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} --sport $value --dport $value";;
  106. *) unset "$var"; return 0;;
  107. esac
  108. ports=1
  109. ;;
  110. *:connbytes)
  111. value="$(echo "$value" | sed -e 's,-,:,g')"
  112. add_insmod ipt_connbytes
  113. append "$var" "-m connbytes --connbytes $value --connbytes-dir both --connbytes-mode bytes"
  114. ;;
  115. *:tos)
  116. add_insmod ipt_tos
  117. case "$value" in
  118. !*) append "$var" "-m tos ! --tos $value";;
  119. *) append "$var" "-m tos --tos $value"
  120. esac
  121. ;;
  122. *:dscp)
  123. add_insmod ipt_dscp
  124. dscp_option="--dscp"
  125. [ -z "${value%%[EBCA]*}" ] && dscp_option="--dscp-class"
  126. case "$value" in
  127. !*) append "$var" "-m dscp ! $dscp_option $value";;
  128. *) append "$var" "-m dscp $dscp_option $value"
  129. esac
  130. ;;
  131. *:direction)
  132. value="$(echo "$value" | sed -e 's,-,:,g')"
  133. if [ "$value" = "out" ]; then
  134. append "$var" "-o $device"
  135. elif [ "$value" = "in" ]; then
  136. append "$var" "-i $device"
  137. fi
  138. ;;
  139. 1:pktsize)
  140. value="$(echo "$value" | sed -e 's,-,:,g')"
  141. add_insmod ipt_length
  142. append "$var" "-m length --length $value"
  143. ;;
  144. 1:limit)
  145. add_insmod ipt_limit
  146. append "$var" "-m limit --limit $value"
  147. ;;
  148. 1:tcpflags)
  149. case "$proto" in
  150. tcp) append "$var" "-m tcp --tcp-flags ALL $value";;
  151. *) unset $var; return 0;;
  152. esac
  153. ;;
  154. 1:mark)
  155. config_get class "${value##!}" classnr
  156. [ -z "$class" ] && continue;
  157. case "$value" in
  158. !*) append "$var" "-m mark ! --mark $class";;
  159. *) append "$var" "-m mark --mark $class";;
  160. esac
  161. ;;
  162. 1:TOS)
  163. add_insmod ipt_TOS
  164. config_get TOS "$rule" 'TOS'
  165. suffix="-j TOS --set-tos "${TOS:-"Normal-Service"}
  166. ;;
  167. 1:DSCP)
  168. add_insmod ipt_DSCP
  169. config_get DSCP "$rule" 'DSCP'
  170. [ -z "${DSCP%%[EBCA]*}" ] && set_value="--set-dscp-class $DSCP" \
  171. || set_value="--set-dscp $DSCP"
  172. suffix="-j DSCP $set_value"
  173. ;;
  174. esac
  175. done
  176. append "$var" "$suffix"
  177. case "$ports:$proto" in
  178. 1:) parse_matching_rule "$var" "$section" "$options" "$prefix" "$suffix" "udp";;
  179. esac
  180. }
  181. config_cb() {
  182. option_cb() {
  183. return 0
  184. }
  185. # Section start
  186. case "$1" in
  187. interface)
  188. config_set "$2" "classgroup" "Default"
  189. config_set "$2" "upload" "128"
  190. ;;
  191. classify|default|reclassify)
  192. option_cb() {
  193. append options "$1"
  194. }
  195. ;;
  196. esac
  197. # Section end
  198. config_get TYPE "$CONFIG_SECTION" TYPE
  199. case "$TYPE" in
  200. interface)
  201. config_get_bool enabled "$CONFIG_SECTION" enabled 1
  202. [ 1 -eq "$enabled" ] || return 0
  203. config_get classgroup "$CONFIG_SECTION" classgroup
  204. config_set "$CONFIG_SECTION" imqdev "$C"
  205. C=$(($C+1))
  206. append INTERFACES "$CONFIG_SECTION"
  207. config_set "$classgroup" enabled 1
  208. config_get device "$CONFIG_SECTION" device
  209. [ -z "$device" ] && {
  210. device="$(find_ifname ${CONFIG_SECTION})"
  211. config_set "$CONFIG_SECTION" device "${device:-eth0}"
  212. }
  213. ;;
  214. classgroup) append CG "$CONFIG_SECTION";;
  215. classify|default|reclassify)
  216. case "$TYPE" in
  217. classify) var="ctrules";;
  218. *) var="rules";;
  219. esac
  220. config_get target "$CONFIG_SECTION" target
  221. config_set "$CONFIG_SECTION" options "$options"
  222. append "$var" "$CONFIG_SECTION"
  223. unset options
  224. ;;
  225. esac
  226. }
  227. enum_classes() {
  228. local c="0"
  229. config_get classes "$1" classes
  230. config_get default "$1" default
  231. for class in $classes; do
  232. c="$(($c + 1))"
  233. config_set "${class}" classnr $c
  234. case "$class" in
  235. $default) class_default=$c;;
  236. esac
  237. done
  238. class_default="${class_default:-$c}"
  239. }
  240. cls_var() {
  241. local varname="$1"
  242. local class="$2"
  243. local name="$3"
  244. local type="$4"
  245. local default="$5"
  246. local tmp tmp1 tmp2
  247. config_get tmp1 "$class" "$name"
  248. config_get tmp2 "${class}_${type}" "$name"
  249. tmp="${tmp2:-$tmp1}"
  250. tmp="${tmp:-$tmp2}"
  251. export ${varname}="${tmp:-$default}"
  252. }
  253. tcrules() {
  254. dir=/usr/lib/qos
  255. [ -e $dir/tcrules.awk ] || dir=.
  256. echo "$cstr" | awk \
  257. -v device="$dev" \
  258. -v linespeed="$rate" \
  259. -f $dir/tcrules.awk
  260. }
  261. start_interface() {
  262. local iface="$1"
  263. local num_imq="$2"
  264. config_get device "$iface" device
  265. config_get_bool enabled "$iface" enabled 1
  266. [ -z "$device" -o 1 -ne "$enabled" ] && {
  267. return 1
  268. }
  269. config_get upload "$iface" upload
  270. config_get_bool halfduplex "$iface" halfduplex
  271. config_get download "$iface" download
  272. config_get classgroup "$iface" classgroup
  273. config_get_bool overhead "$iface" overhead 0
  274. download="${download:-${halfduplex:+$upload}}"
  275. enum_classes "$classgroup"
  276. for dir in ${halfduplex:-up} ${download:+down}; do
  277. case "$dir" in
  278. up)
  279. [ "$overhead" = 1 ] && upload=$(($upload * 98 / 100 - (15 * 128 / $upload)))
  280. dev="$device"
  281. rate="$upload"
  282. dl_mode=""
  283. prefix="cls"
  284. ;;
  285. down)
  286. [ "$(ls -d /proc/sys/net/ipv4/conf/imq* 2>&- | wc -l)" -ne "$num_imq" ] && add_insmod imq numdevs="$num_imq"
  287. config_get imqdev "$iface" imqdev
  288. [ "$overhead" = 1 ] && download=$(($download * 98 / 100 - (80 * 1024 / $download)))
  289. dev="imq$imqdev"
  290. rate="$download"
  291. dl_mode=1
  292. prefix="d_cls"
  293. ;;
  294. *) continue;;
  295. esac
  296. cstr=
  297. for class in $classes; do
  298. cls_var pktsize "$class" packetsize $dir 1500
  299. cls_var pktdelay "$class" packetdelay $dir 0
  300. cls_var maxrate "$class" limitrate $dir 100
  301. cls_var prio "$class" priority $dir 1
  302. cls_var avgrate "$class" avgrate $dir 0
  303. cls_var qdisc_esfq "$class" qdisc_esfq $dir ""
  304. [ "$qdisc_esfq" != "" ] && add_insmod sch_esfq
  305. config_get classnr "$class" classnr
  306. append cstr "$classnr:$prio:$avgrate:$pktsize:$pktdelay:$maxrate:$qdisc_esfq" "$N"
  307. done
  308. append ${prefix}q "$(tcrules)" "$N"
  309. export dev_${dir}="ifconfig $dev up txqueuelen 5 >&- 2>&-
  310. tc qdisc del dev $dev root >&- 2>&-
  311. tc qdisc add dev $dev root handle 1: hfsc default ${class_default}0
  312. tc class add dev $dev parent 1: classid 1:1 hfsc sc rate ${rate}kbit ul rate ${rate}kbit"
  313. done
  314. add_insmod cls_fw
  315. add_insmod sch_hfsc
  316. add_insmod sch_sfq
  317. add_insmod sch_red
  318. cat <<EOF
  319. ${INSMOD:+$INSMOD$N}${dev_up:+$dev_up
  320. $clsq
  321. }${imqdev:+$dev_down
  322. $d_clsq
  323. $d_clsl
  324. $d_clsf
  325. }
  326. EOF
  327. unset INSMOD clsq clsf clsl d_clsq d_clsl d_clsf dev_up dev_down
  328. }
  329. start_interfaces() {
  330. local C="$1"
  331. for iface in $INTERFACES; do
  332. start_interface "$iface" "$C"
  333. done
  334. }
  335. add_rules() {
  336. local var="$1"
  337. local rules="$2"
  338. local prefix="$3"
  339. for rule in $rules; do
  340. unset iptrule
  341. config_get target "$rule" target
  342. config_get target "$target" classnr
  343. config_get options "$rule" options
  344. ## If we want to override the TOS field, let's clear the DSCP field first.
  345. [ ! -z "$(echo $options | grep 'TOS')" ] && {
  346. s_options=${options%%TOS}
  347. add_insmod ipt_DSCP
  348. parse_matching_rule iptrule "$rule" "$s_options" "$prefix" "-j DSCP --set-dscp 0"
  349. append "$var" "$iptrule" "$N"
  350. unset iptrule
  351. }
  352. parse_matching_rule iptrule "$rule" "$options" "$prefix" "-j MARK --set-mark $target"
  353. append "$var" "$iptrule" "$N"
  354. done
  355. }
  356. start_cg() {
  357. local cg="$1"
  358. local iptrules
  359. local pktrules
  360. local sizerules
  361. local download
  362. enum_classes "$cg"
  363. add_rules iptrules "$ctrules" "iptables -t mangle -A ${cg}_ct"
  364. config_get classes "$cg" classes
  365. for class in $classes; do
  366. config_get mark "$class" classnr
  367. config_get maxsize "$class" maxsize
  368. [ -z "$maxsize" -o -z "$mark" ] || {
  369. add_insmod ipt_length
  370. append pktrules "iptables -t mangle -A ${cg} -m mark --mark $mark -m length --length $maxsize: -j MARK --set-mark 0" "$N"
  371. }
  372. done
  373. add_rules pktrules "$rules" "iptables -t mangle -A ${cg}"
  374. for iface in $INTERFACES; do
  375. config_get classgroup "$iface" classgroup
  376. config_get device "$iface" device
  377. config_get imqdev "$iface" imqdev
  378. config_get dl "$iface" download
  379. config_get halfduplex "$iface" halfduplex
  380. add_insmod ipt_IMQ
  381. append up "iptables -t mangle -A OUTPUT -o $device -j ${cg}" "$N"
  382. append up "iptables -t mangle -A FORWARD -o $device -j ${cg}" "$N"
  383. [ -z "$dl" ] || {
  384. append down "iptables -t mangle -A POSTROUTING -o $device -j ${cg}" "$N"
  385. [ -z "$halfduplex" ] || {
  386. append down "iptables -t mangle -A POSTROUTING -o $device -j IMQ --todev $imqdev" "$N"
  387. }
  388. append down "iptables -t mangle -A PREROUTING -i $device -j ${cg}" "$N"
  389. append down "iptables -t mangle -A PREROUTING -i $device -j IMQ --todev $imqdev" "$N"
  390. }
  391. done
  392. cat <<EOF
  393. $INSMOD
  394. iptables -t mangle -N ${cg} >&- 2>&-
  395. iptables -t mangle -N ${cg}_ct >&- 2>&-
  396. ${iptrules:+${iptrules}${N}iptables -t mangle -A ${cg}_ct -j CONNMARK --save-mark}
  397. iptables -t mangle -A ${cg} -j CONNMARK --restore-mark
  398. iptables -t mangle -A ${cg} -m mark --mark 0 -j ${cg}_ct
  399. $pktrules
  400. $up$N${down:+${down}$N}
  401. EOF
  402. unset INSMOD
  403. }
  404. start_firewall() {
  405. add_insmod ipt_multiport
  406. add_insmod ipt_CONNMARK
  407. cat <<EOF
  408. iptables -t mangle -F
  409. iptables -t mangle -X
  410. EOF
  411. for group in $CG; do
  412. start_cg $group
  413. done
  414. }
  415. C="0"
  416. INTERFACES=""
  417. [ -e ./qos.conf ] && {
  418. . ./qos.conf
  419. config_cb
  420. } || config_load qos
  421. C="0"
  422. for iface in $INTERFACES; do
  423. export C="$(($C + 1))"
  424. done
  425. case "$1" in
  426. all)
  427. start_interfaces "$C"
  428. start_firewall
  429. ;;
  430. interface)
  431. start_interface "$2" "$C"
  432. ;;
  433. interfaces)
  434. start_interfaces
  435. ;;
  436. firewall)
  437. start_firewall
  438. ;;
  439. esac