source: branches/backfire/package/qos-scripts/files/usr/lib/qos/generate.sh

Last change on this file was 28679, checked in by jow, 5 years ago

[backfire] qos-scripts: prefix was missing in some rules after backport, add it (#10315)

  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 12.5 KB
Line 
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
11add_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
38parse_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";;
57                default) pkt=1; append "$var" "-m mark --mark 0";;
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                        *:layer7)
72                                add_insmod ipt_layer7
73                                add_insmod xt_layer7
74                                append "$var" "-m layer7 --l7proto $value${pkt:+ --l7pkt}"
75                        ;;
76                        *:ports|*:srcports|*:dstports)
77                                value="$(echo "$value" | sed -e 's,-,:,g')"
78                                lproto=${lproto:-tcp}
79                                case "$proto" in
80                                        ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} -m multiport";;
81                                        *) unset "$var"; return 0;;
82                                esac
83                                case "$option" in
84                                        ports)
85                                                config_set "$section" srcports ""
86                                                config_set "$section" dstports ""
87                                                config_set "$section" portrange ""
88                                                append "$var" "--ports $value"
89                                        ;;
90                                        srcports)
91                                                config_set "$section" ports ""
92                                                config_set "$section" dstports ""
93                                                config_set "$section" portrange ""
94                                                append "$var" "--sports $value"
95                                        ;;
96                                        dstports)
97                                                config_set "$section" ports ""
98                                                config_set "$section" srcports ""
99                                                config_set "$section" portrange ""
100                                                append "$var" "--dports $value"
101                                        ;;
102                                esac
103                                ports=1
104                        ;;
105                        *:portrange)
106                                config_set "$section" ports ""
107                                config_set "$section" srcports ""
108                                config_set "$section" dstports ""
109                                value="$(echo "$value" | sed -e 's,-,:,g')"
110                                case "$proto" in
111                                        ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} --sport $value --dport $value";;
112                                        *) unset "$var"; return 0;;
113                                esac
114                                ports=1
115                        ;;
116                        *:connbytes)
117                                value="$(echo "$value" | sed -e 's,-,:,g')"
118                                add_insmod ipt_connbytes
119                                append "$var" "-m connbytes --connbytes $value --connbytes-dir both --connbytes-mode bytes"
120                        ;;
121                        *:tos)
122                                add_insmod ipt_tos
123                                case "$value" in
124                                        !*) append "$var" "-m tos ! --tos $value";;
125                                        *) append "$var" "-m tos --tos $value"
126                                esac
127                        ;;
128                        *:dscp)
129                                add_insmod ipt_dscp
130                                dscp_option="--dscp"
131                                [ -z "${value%%[EBCA]*}" ] && dscp_option="--dscp-class"
132                                case "$value" in
133                                        !*) append "$var" "-m dscp ! $dscp_option $value";;
134                                        *) append "$var" "-m dscp $dscp_option $value"
135                                esac
136                        ;;
137                        *:direction)
138                                value="$(echo "$value" | sed -e 's,-,:,g')"
139                                if [ "$value" = "out" ]; then
140                                        append "$var" "-o $device"
141                                elif [ "$value" = "in" ]; then
142                                        append "$var" "-i $device"
143                                fi
144                        ;;
145                        1:pktsize)
146                                value="$(echo "$value" | sed -e 's,-,:,g')"
147                                add_insmod ipt_length
148                                append "$var" "-m length --length $value"
149                        ;;
150                        1:limit)
151                                add_insmod ipt_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";;
165                                        *) append "$var" "-m mark --mark $class";;
166                                esac
167                        ;;
168                        1:TOS)
169                                add_insmod ipt_TOS
170                                config_get TOS "$rule" 'TOS'
171                                suffix="-j TOS --set-tos "${TOS:-"Normal-Service"}
172                        ;;
173                        1:DSCP)
174                                add_insmod ipt_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
188config_cb() {
189        option_cb() {
190                return 0
191        }
192
193        # Section start
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 options "$1"
202                        }
203                ;;
204        esac
205
206    # Section end
207        config_get TYPE "$CONFIG_SECTION" TYPE
208        case "$TYPE" in
209                interface)
210                        config_get_bool enabled "$CONFIG_SECTION" enabled 1
211                        [ 1 -eq "$enabled" ] || return 0
212                        config_get classgroup "$CONFIG_SECTION" classgroup
213                        config_set "$CONFIG_SECTION" imqdev "$C"
214                        C=$(($C+1))
215                        append INTERFACES "$CONFIG_SECTION"
216                        config_set "$classgroup" enabled 1
217                        config_get device "$CONFIG_SECTION" device
218                        [ -z "$device" ] && {
219                                device="$(find_ifname ${CONFIG_SECTION})"
220                                config_set "$CONFIG_SECTION" device "${device:-eth0}"
221                        }
222                ;;
223                classgroup) append CG "$CONFIG_SECTION";;
224                classify|default|reclassify)
225                        case "$TYPE" in
226                                classify) var="ctrules";;
227                                *) var="rules";;
228                        esac
229                        config_get target "$CONFIG_SECTION" target
230                        config_set "$CONFIG_SECTION" options "$options"
231                        append "$var" "$CONFIG_SECTION"
232                        unset options
233                ;;
234        esac
235}
236
237
238enum_classes() {
239        local c="0"
240        config_get classes "$1" classes
241        config_get default "$1" default
242        for class in $classes; do
243                c="$(($c + 1))"
244                config_set "${class}" classnr $c
245                case "$class" in
246                        $default) class_default=$c;;
247                esac
248        done
249        class_default="${class_default:-$c}"
250}
251
252cls_var() {
253        local varname="$1"
254        local class="$2"
255        local name="$3"
256        local type="$4"
257        local default="$5"
258        local tmp tmp1 tmp2
259        config_get tmp1 "$class" "$name"
260        config_get tmp2 "${class}_${type}" "$name"
261        tmp="${tmp2:-$tmp1}"
262        tmp="${tmp:-$tmp2}"
263        export ${varname}="${tmp:-$default}"
264}
265
266tcrules() {
267        dir=/usr/lib/qos
268        [ -e $dir/tcrules.awk ] || dir=.
269        echo "$cstr" | awk \
270                -v device="$dev" \
271                -v linespeed="$rate" \
272                -f $dir/tcrules.awk
273}
274
275start_interface() {
276        local iface="$1"
277        local num_imq="$2"
278        config_get device "$iface" device
279        config_get_bool enabled "$iface" enabled 1
280        [ -z "$device" -o 1 -ne "$enabled" ] && {
281                return 1
282        }
283        config_get upload "$iface" upload
284        config_get_bool halfduplex "$iface" halfduplex
285        config_get download "$iface" download
286        config_get classgroup "$iface" classgroup
287        config_get_bool overhead "$iface" overhead 0
288       
289        download="${download:-${halfduplex:+$upload}}"
290        enum_classes "$classgroup"
291        for dir in ${halfduplex:-up} ${download:+down}; do
292                case "$dir" in
293                        up)
294                                [ "$overhead" = 1 ] && upload=$(($upload * 98 / 100 - (15 * 128 / $upload)))
295                                dev="$device"
296                                rate="$upload"
297                                dl_mode=""
298                                prefix="cls"
299                        ;;
300                        down)
301                                [ "$(ls -d /proc/sys/net/ipv4/conf/imq* 2>&- | wc -l)" -ne "$num_imq" ] && add_insmod imq numdevs="$num_imq"
302                                config_get imqdev "$iface" imqdev
303                                [ "$overhead" = 1 ] && download=$(($download * 98 / 100 - (80 * 1024 / $download)))
304                                dev="imq$imqdev"
305                                rate="$download"
306                                dl_mode=1
307                                prefix="d_cls"
308                        ;;
309                        *) continue;;
310                esac
311                cstr=
312                for class in $classes; do
313                        cls_var pktsize "$class" packetsize $dir 1500
314                        cls_var pktdelay "$class" packetdelay $dir 0
315                        cls_var maxrate "$class" limitrate $dir 100
316                        cls_var prio "$class" priority $dir 1
317                        cls_var avgrate "$class" avgrate $dir 0
318                        cls_var qdisc_esfq "$class" qdisc_esfq $dir ""
319                        [ "$qdisc_esfq" != "" ] && add_insmod sch_esfq
320                        config_get classnr "$class" classnr
321                        append cstr "$classnr:$prio:$avgrate:$pktsize:$pktdelay:$maxrate:$qdisc_esfq" "$N"
322                done
323                append ${prefix}q "$(tcrules)" "$N"
324                export dev_${dir}="ifconfig $dev up txqueuelen 5 >&- 2>&-
325tc qdisc del dev $dev root >&- 2>&-
326tc qdisc add dev $dev root handle 1: hfsc default ${class_default}0
327tc class add dev $dev parent 1: classid 1:1 hfsc sc rate ${rate}kbit ul rate ${rate}kbit"
328        done
329        add_insmod cls_fw
330        add_insmod sch_hfsc
331        add_insmod sch_sfq
332        add_insmod sch_red
333
334        cat <<EOF
335${INSMOD:+$INSMOD$N}${dev_up:+$dev_up
336$clsq
337}${imqdev:+$dev_down
338$d_clsq
339$d_clsl
340$d_clsf
341}
342EOF
343        unset INSMOD clsq clsf clsl d_clsq d_clsl d_clsf dev_up dev_down
344}
345
346start_interfaces() {
347        local C="$1"
348        for iface in $INTERFACES; do
349                start_interface "$iface" "$C"
350        done
351}
352
353add_rules() {
354        local var="$1"
355        local rules="$2"
356        local prefix="$3"
357       
358        for rule in $rules; do
359                unset iptrule
360                config_get target "$rule" target
361                config_get target "$target" classnr
362                config_get options "$rule" options
363
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 ipt_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
373                parse_matching_rule iptrule "$rule" "$options" "$prefix" "-j MARK --set-mark $target"
374                append "$var" "$iptrule" "$N"
375        done
376}
377
378start_cg() {
379        local cg="$1"
380        local iptrules
381        local pktrules
382        local sizerules
383        enum_classes "$cg"
384        add_rules iptrules "$ctrules" "iptables -t mangle -A qos_${cg}_ct"
385        config_get classes "$cg" classes
386        for class in $classes; do
387                config_get mark "$class" classnr
388                config_get maxsize "$class" maxsize
389                [ -z "$maxsize" -o -z "$mark" ] || {
390                        add_insmod ipt_length
391                        append pktrules "iptables -t mangle -A qos_${cg} -m mark --mark $mark -m length --length $maxsize: -j MARK --set-mark 0" "$N"
392                }
393        done
394        add_rules pktrules "$rules" "iptables -t mangle -A qos_${cg}"
395        for iface in $INTERFACES; do
396                config_get classgroup "$iface" classgroup
397                config_get device "$iface" device
398                config_get imqdev "$iface" imqdev
399                config_get upload "$iface" upload
400                config_get download "$iface" download
401                config_get halfduplex "$iface" halfduplex
402                download="${download:-${halfduplex:+$upload}}"
403                add_insmod ipt_IMQ
404                append up "iptables -t mangle -A OUTPUT -o $device -j qos_${cg}" "$N"
405                append up "iptables -t mangle -A FORWARD -o $device -j qos_${cg}" "$N"
406                [ -z "$download" ] || {
407                        append down "iptables -t mangle -A POSTROUTING -o $device -j qos_${cg}" "$N"
408                        [ -z "$halfduplex" ] || {
409                                append down "iptables -t mangle -A POSTROUTING -o $device -j IMQ --todev $imqdev" "$N"
410                        }
411                        append down "iptables -t mangle -A PREROUTING -i $device -j qos_${cg}" "$N"
412                        append down "iptables -t mangle -A PREROUTING -i $device -j IMQ --todev $imqdev" "$N"
413                }
414        done
415        cat <<EOF
416$INSMOD
417iptables -t mangle -N qos_${cg} >&- 2>&-
418iptables -t mangle -N qos_${cg}_ct >&- 2>&-
419${iptrules:+${iptrules}${N}iptables -t mangle -A qos_${cg}_ct -j CONNMARK --save-mark}
420iptables -t mangle -A qos_${cg} -j CONNMARK --restore-mark
421iptables -t mangle -A qos_${cg} -m mark --mark 0 -j qos_${cg}_ct
422$pktrules
423$up$N${down:+${down}$N}
424EOF
425        unset INSMOD
426}
427
428start_firewall() {
429        add_insmod ipt_multiport
430        add_insmod ipt_CONNMARK
431        stop_firewall
432        for group in $CG; do
433                start_cg $group
434        done
435}
436
437stop_firewall() {
438        # Builds up a list of iptables commands to flush the qos_* chains,
439        # remove rules referring to them, then delete them
440
441        # Print rules in the mangle table, like iptables-save
442        iptables -t mangle -S |
443                # Find rules for the qos_* chains
444                grep '^-N qos_\|-j qos_' |
445                # Exclude rules in qos_* chains (inter-qos_* refs)
446                grep -v '^-A qos_' |
447                # Replace -N with -X and hold, with -F and print
448                # Replace -A with -D
449                # Print held lines at the end (note leading newline)
450                sed -e '/^-N/{s/^-N/-X/;H;s/^-X/-F/}' \
451                        -e 's/^-A/-D/' \
452                        -e '${p;g}' |
453                # Make into proper iptables calls
454                # Note:  awkward in previous call due to hold space usage
455                sed -n -e 's/^./iptables -t mangle &/p'
456}
457
458C="0"
459INTERFACES=""
460[ -e ./qos.conf ] && {
461        . ./qos.conf
462        config_cb
463} || config_load qos
464
465C="0"
466for iface in $INTERFACES; do
467        export C="$(($C + 1))"
468done
469
470case "$1" in
471        all)
472                start_interfaces "$C"
473                start_firewall
474        ;;
475        interface)
476                start_interface "$2" "$C"
477        ;;
478        interfaces)
479                start_interfaces
480        ;;
481        firewall)
482                case "$2" in
483                        stop)
484                                stop_firewall
485                        ;;
486                        start|"")
487                                start_firewall
488                        ;;
489                esac
490        ;;
491esac
Note: See TracBrowser for help on using the repository browser.