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