update qos-scripts to v1.1 - rewritten hfsc rate calculation
authorFelix Fietkau <nbd@openwrt.org>
Fri, 6 Apr 2007 16:59:56 +0000 (16:59 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Fri, 6 Apr 2007 16:59:56 +0000 (16:59 +0000)
SVN-Revision: 6875

package/qos-scripts/Makefile
package/qos-scripts/files/etc/config/qos
package/qos-scripts/files/etc/hotplug.d/iface/10-qos
package/qos-scripts/files/etc/init.d/qos
package/qos-scripts/files/usr/bin/qos-start
package/qos-scripts/files/usr/lib/qos.sh [deleted file]
package/qos-scripts/files/usr/lib/qos/generate.sh [new file with mode: 0755]
package/qos-scripts/files/usr/lib/qos/tcrules.awk [new file with mode: 0644]

index 78a8cc2563e809076caaa5368bbc7c36a58903f6..43225f919fbb390824774a7a764116d4f704dc22 100644 (file)
@@ -9,7 +9,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=qos-scripts
-PKG_VERSION:=0.9.4
+PKG_VERSION:=1.1.0
 PKG_RELEASE:=1
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
index 1bf991766cce285cb2faef3ec391226de3ba192a..7d3501cc6edc208dc8b166aefc6b887e85b826fe 100644 (file)
@@ -60,35 +60,29 @@ config classgroup "Default"
 
 
 config class "Priority"
-       option packetsize  300
-       option packetdelay 10
+       option packetsize  400
        option maxsize     400
-       option avgrate     40
-       option linksharing 75
+       option avgrate     10
+       option priority    20
 config class "Priority_down"
-       option packetsize  1500
-       option avgrate     20
+       option packetsize  1000
+       option avgrate     10
 
 
 config class "Express"
-       option packetsize  1300
-       option packetdelay 15
+       option packetsize  1000
        option maxsize     800
-       option avgrate     30
-       option linksharing 80
-
+       option avgrate     50
+       option priority    10
 
 config class "Normal"
        option packetsize  1500
-       option packetdelay 150
-       option avgrate     20
-       option linksharing 30
+       option packetdelay 100
+       option avgrate     10
+       option priority    5
 config class "Normal_down"
-       option avgrate     30
-
+       option avgrate     20
 
 config class "Bulk"
-       option linksharing 10
-config class "Bulk_down"
-       option avgrate     15
-       option limitrate   85
+       option avgrate     1
+       option packetdelay 200
index 0682db145c5eb6d8ed765536ff2a77a3e78611a1..8537297f9f6fcbad7a9f10964981ffdb9224bb22 100755 (executable)
@@ -1,2 +1,2 @@
 #!/bin/sh
-[ "$ACTION" = ifup ] && /usr/lib/qos.sh interface "$INTERFACE" | sh
+[ "$ACTION" = ifup ] && /usr/lib/qos/generate.sh interface "$INTERFACE" | sh
index 4fa42d4a2483240617858024b0b85bb378428179..3f711155ee241ab0c61be8729e246a1f2151174a 100755 (executable)
@@ -4,7 +4,7 @@
 START=50
 
 boot() {
-       /usr/lib/qos.sh firewall | sh
+       /usr/lib/qos/generate.sh firewall | sh
 }
 
 start() {
index 347ca46b902ae2eb9518b902c4b081d0c4643798..261ffb42b095d048abd60786f10209c02c533b09 100755 (executable)
@@ -1,4 +1,4 @@
 #!/bin/sh
 qos-stop 
-/usr/lib/qos.sh all | sh
+/usr/lib/qos/generate.sh all | sh
 
diff --git a/package/qos-scripts/files/usr/lib/qos.sh b/package/qos-scripts/files/usr/lib/qos.sh
deleted file mode 100755 (executable)
index 724784e..0000000
+++ /dev/null
@@ -1,495 +0,0 @@
-#!/bin/sh
-. /etc/functions.sh
-
-insmod="insmod"
-[ -f /sbin/modprobe ] && insmod="modprobe"
-
-add_insmod() {
-       eval "export isset=\${insmod_$1}"
-       case "$isset" in
-               1) ;;
-               *) append INSMOD "$insmod $* >&- 2>&-" "$N"; export insmod_$1=1;;
-       esac
-}
-
-find_ifname() {(
-       include /lib/network
-       scan_interfaces
-       config_get "$1" ifname
-)}
-
-parse_matching_rule() {
-       local var="$1"
-       local section="$2"
-       local options="$3"
-       local prefix="$4"
-       local suffix="$5"
-       local proto="$6"
-       local mport=""
-       local ports=""
-
-       append "$var" "$prefix" "$N"
-       for option in $options; do
-               case "$option" in
-                       proto) config_get value "$section" proto; proto="${proto:-$value}";;
-               esac
-       done
-       config_get type "$section" TYPE
-       case "$type" in
-               classify) unset pkt; append "$var" "-m mark --mark 0";;
-               default) pkt=1; append "$var" "-m mark --mark 0";;
-               reclassify) pkt=1;;
-       esac
-       append "$var" "${proto:+-p $proto}"
-       for option in $options; do
-               config_get value "$section" "$option"
-               
-               case "$pkt:$option" in
-                       *:srchost)
-                               append "$var" "-s $value"
-                       ;;
-                       *:dsthost)
-                               append "$var" "-d $value"
-                       ;;
-                       *:ipp2p)
-                               add_insmod ipt_ipp2p
-                               append "$var" "-m ipp2p"
-                               case "$value" in
-                                       all) append "$var" "--edk --dc --kazaa --gnu --bit";;
-                                       *) append "$var" "--$value";;
-                               esac
-                       ;;
-                       *:layer7)
-                               add_insmod ipt_layer7
-                               append "$var" "-m layer7 --l7proto $value${pkt:+ --l7pkt}"
-                       ;;
-                       *:ports|*:srcports|*:dstports)
-                               value="$(echo "$value" | sed -e 's,-,:,g')"
-                               lproto=${lproto:-tcp}
-                               case "$proto" in
-                                       ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} -m multiport";;
-                                       *) unset "$var"; return 0;;
-                               esac
-                               case "$option" in
-                                       ports)
-                                               config_set "$section" srcports ""
-                                               config_set "$section" dstports ""
-                                               config_set "$section" portrange ""
-                                               append "$var" "--ports $value"
-                                       ;;
-                                       srcports)
-                                               config_set "$section" ports ""
-                                               config_set "$section" dstports ""
-                                               config_set "$section" portrange ""
-                                               append "$var" "--sports $value"
-                                       ;;
-                                       dstports)
-                                               config_set "$section" ports ""
-                                               config_set "$section" srcports ""
-                                               config_set "$section" portrange ""
-                                               append "$var" "--dports $value"
-                                       ;;
-                               esac
-                               ports=1
-                       ;;
-                       *:portrange)
-                               config_set "$section" ports ""
-                               config_set "$section" srcports ""
-                               config_set "$section" dstports ""
-                               value="$(echo "$value" | sed -e 's,-,:,g')"
-                               case "$proto" in
-                                       ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} --sport $value --dport $value";;
-                                       *) unset "$var"; return 0;;
-                               esac
-                               ports=1
-                       ;;
-                       1:pktsize)
-                               value="$(echo "$value" | sed -e 's,-,:,g')"
-                               add_insmod ipt_length
-                               append "$var" "-m length --length $value"
-                       ;;
-                       1:limit)
-                               add_insmod ipt_limit
-                               append "$var" "-m limit --limit $value"
-                       ;;
-                       1:tcpflags)
-                               case "$proto" in
-                                       tcp) append "$var" "-m tcp --tcp-flags ALL $value";;
-                                       *) unset $var; return 0;;
-                               esac
-                       ;;
-                       1:mark)
-                               config_get class "${value##!}" classnr
-                               [ -z "$class" ] && continue;
-                               case "$value" in
-                                       !*) append "$var" "-m mark ! --mark $class";;
-                                       *) append "$var" "-m mark --mark $class";;
-                               esac
-               esac
-       done
-       append "$var" "$suffix"
-       case "$ports:$proto" in
-               1:)     parse_matching_rule "$var" "$section" "$options" "$prefix" "$suffix" "udp";;
-       esac
-}
-
-config_cb() {
-       option_cb() {
-               return 0
-       }
-
-       # Section start
-       case "$1" in
-               interface)
-                       config_set "$1" "classgroup" "Default"
-                       config_set "$1" "upload" "128"
-               ;;
-               classify|default|reclassify)
-                       option_cb() {
-                               append options "$1"
-                       }
-               ;;
-       esac
-
-    # Section end
-       config_get TYPE "$CONFIG_SECTION" TYPE
-       case "$TYPE" in
-               interface)
-                       config_get enabled "$CONFIG_SECTION" enabled
-                       config_get download "$CONFIG_SECTION" download
-                       config_get classgroup "$CONFIG_SECTION" classgroup
-                       config_set "$CONFIG_SECTION" imqdev "$C"
-                       [ -z "$enabled" -o "$(($enabled))" -eq 0 ] || {
-                               C=$(($C+1))
-                               INTERFACES="$INTERFACES $CONFIG_SECTION"
-                               config_set "$classgroup" enabled 1
-                       }
-                       config_get device "$CONFIG_SECTION" device
-                       [ -z "$device" ] && device="$(find_ifname ${CONFIG_SECTION})"
-                       config_set "$CONFIG_SECTION" device "${device:-eth0}"
-               ;;
-               classgroup) append CG "$CONFIG_SECTION";;
-               classify|default|reclassify)
-                       case "$TYPE" in
-                               classify) var="ctrules";;
-                               *) var="rules";;
-                       esac
-                       config_get target "$CONFIG_SECTION" target
-                       config_set "$CONFIG_SECTION" options "$options"
-                       append "$var" "$CONFIG_SECTION"
-                       unset options
-               ;;
-       esac
-}
-
-class_main_qdisc() {
-       local device="$1"
-       awk -f - <<EOF
-BEGIN {
-       limit = int("$maxrate")
-       m2 = int("$m2")
-       dmax = int("$dmax")
-       umax = int("$umax")
-       share = int("$share")
-       
-       if (!(m2 > 0)) {
-               dmax = 500
-               umax = 1500
-               m2 = 10
-               rt = 0
-       } else {
-               rt = 1
-       }
-
-       cdata = ""
-       pdmax = int (dmax + (umax * 8 / limit))
-       if (rt == 1) {
-               if (share > 0) cdata = " rt"
-               else cdata = " ls"
-               if ((umax > 0) && (dmax > 0)) {
-                       cdata = cdata " umax " umax "b dmax " pdmax "ms"
-               }
-               cdata = cdata " rate " m2 "kbit"
-       }
-       if (share > 0) {
-               if ((m2 > 0) && (umax > 0) && (dmax > 0)) {
-                       cdata = cdata " ls umax " umax "b dmax " pdmax "ms rate " share "kbit"
-               } else {
-                       cdata = cdata " ls m1 " share "kbit d 500ms m2 " share "kbit"
-               }
-       }
-
-       print "tc class add dev $device parent 1:1 classid 1:${classnr}0 hfsc" cdata " ul rate " limit "kbit"
-}
-EOF
-}
-
-class_leaf_qdisc() {
-       local device="$1"
-       awk -f - <<EOF
-
-function qlen(rate, m2, umax, dmax,    qb, qr, qt, ql) {
-       qlen_min = 5 # minimum queue length
-       qlen_base = 1.7 # base value - queueing time in seconds
-       qlen_avgr = 0.7 # avgrate modifier
-       qlen_dmax = 0.0 # dmax modifier
-
-       # bits in a packet
-       qb = 1500
-       if ((m2 > 0) && (umax > 0)) qb -= int((1500 - umax) * qlen_pkt)
-       qb *= 8 
-       
-       # rate in bits/s
-       qr = rate
-       qr -= int((rate - m2) * qlen_avgr)
-       qr *= 1024
-       
-       # queue time
-       qt = qlen_base + qlen_dmax * (dmax / 1000)
-
-       # queue length
-       ql = int(qr * qt / qb)
-       if (ql < qlen_min) ql = qlen_min
-
-       return ql
-}
-
-BEGIN {
-       sfq_dthresh     = 25 # use sfq for download if pktdelay set to this or lower
-
-       limit = int("$maxrate")
-       m2 = int("$m2")
-       dmax = int("$dmax")
-       umax = int("$umax")
-       
-       if (!(m2 > 0)) {
-               dmax = 500
-               umax = 1500
-               m2 = 10
-       }
-       
-       cqlen = ${dl_mode:+2 * }qlen(limit, m2, umax, dmax)
-
-       printf "tc qdisc add dev $device parent 1:${classnr}0 handle ${classnr}00: "
-       if (("$dir" != "down") || ((dmax > 0) && (dmax <= sfq_dthresh))) {
-               print "sfq perturb 10 limit " cqlen 
-       } else {
-               avpkt = 1200
-               if (min < avpkt) min = avpkt
-               min = int(limit * 1024 / 8 * 0.1)
-               dqb = cqlen * 1500
-               max = int(min + (dqb - min) * 0.25)
-               burst = int((2 * min + max) / (3 * avpkt))
-               
-               print "red min " min " max " max " burst " burst " avpkt " avpkt " limit " dqb " probability 0.04 ecn"
-       }
-}
-EOF
-}
-
-enum_classes() {
-       local c="0"
-       config_get classes "$1" classes
-       config_get default "$1" default
-       for class in $classes; do
-               c="$(($c + 1))"
-               config_set "${class}" classnr $c
-               case "$class" in
-                       $default) class_default=$c;;
-               esac
-       done
-       class_default="${class_default:-$c}"
-}
-
-cls_var() {
-       local varname="$1"
-       local class="$2"
-       local name="$3"
-       local type="$4"
-       local default="$5"
-       local tmp tmp1 tmp2
-       config_get tmp1 "$class" "$name"
-       config_get tmp2 "${class}_${type}" "$name"
-       tmp="${tmp2:-$tmp1}"
-       tmp="${tmp:-$tmp2}"
-       export ${varname}="${tmp:-$default}"
-}
-
-start_interface() {
-       local iface="$1"
-       local num_imq="$2"
-       config_get device "$iface" device
-       config_get enabled "$iface" enabled
-       [ -z "$device" -o -z "$enabled" ] && exit
-       config_get upload "$iface" upload
-       config_get halfduplex "$iface" halfduplex
-       config_get download "$iface" download
-       config_get classgroup "$iface" classgroup
-       
-       download="${download:-${halfduplex:+$upload}}"
-       enum_classes "$classgroup"
-       for dir in up${halfduplex} ${download:+down}; do
-               case "$dir" in
-                       up)
-                               upload=$(($upload * 98 / 100 - 10))
-                               dev="$device"
-                               rate="$upload"
-                               dl_mode=""
-                               prefix="cls"
-                       ;;
-                       down)
-                               add_insmod imq numdevs="$num_imq"
-                               config_get imqdev "$iface" imqdev
-                               download=$(($download * 96 / 100 - 64))
-                               dev="imq$imqdev"
-                               rate="$download"
-                               dl_mode=1
-                               prefix="d_cls"
-                       ;;
-                       *) continue;;
-               esac
-               for class in $classes; do
-                       cls_var umax "$class" packetsize $dir 1500
-                       cls_var dmax "$class" packetdelay $dir 500
-                       
-                       cls_var maxrate "$class" limitrate $dir 100
-                       cls_var share "$class" linksharing $dir 0
-                       cls_var m2 "$class" avgrate $dir 0
-                       maxrate=$(($maxrate * $rate / 100))
-                       share=$(($share * $rate / 100))
-                       m2=$(($m2 * $rate / 100))
-               
-                       config_get classnr "$class" classnr
-                       append ${prefix}q "$(class_main_qdisc "$dev" "$iface")" "$N"
-                       append ${prefix}l "$(class_leaf_qdisc "$dev" "$iface")" "$N"
-                       append ${prefix}f "tc filter add dev $dev parent 1: prio $classnr protocol ip handle $classnr fw flowid 1:${classnr}0" "$N"
-               done
-               export dev_${dir}="ifconfig $dev up txqueuelen 5 >&- 2>&-
-tc qdisc del dev $dev root >&- 2>&-
-tc qdisc add dev $dev root handle 1: hfsc default ${class_default}0
-tc class add dev $dev parent 1: classid 1:1 hfsc sc rate ${rate}kbit ul rate ${rate}kbit"
-       done
-       add_insmod cls_fw
-       add_insmod sch_hfsc
-       add_insmod sch_sfq
-       add_insmod sch_red
-       cat <<EOF
-${INSMOD:+$INSMOD$N}${dev_up:+$dev_up
-$clsq
-$clsl
-$clsf
-}${imqdev:+$dev_down
-$d_clsq
-$d_clsl
-$d_clsf
-}
-EOF
-       unset INSMOD clsq clsf clsl d_clsq d_clsl d_clsf dev_up dev_down
-}
-
-start_interfaces() {
-       local C="$1"
-       for iface in $INTERFACES; do
-               start_interface "$iface" "$C"
-       done
-}
-
-add_rules() {
-       local var="$1"
-       local rules="$2"
-       local prefix="$3"
-       
-       for rule in $rules; do
-               unset iptrule
-               config_get target "$rule" target
-               config_get target "$target" classnr
-               config_get options "$rule" options
-               parse_matching_rule iptrule "$rule" "$options" "$prefix" "-j MARK --set-mark $target"
-               append "$var" "$iptrule" "$N"
-       done
-}
-
-start_cg() {
-       local cg="$1"
-       local iptrules
-       local pktrules
-       local sizerules
-       local download
-       enum_classes "$cg"
-       add_rules iptrules "$ctrules" "iptables -t mangle -A ${cg}_ct"
-       config_get classes "$cg" classes
-       for class in $classes; do
-               config_get mark "$class" classnr
-               config_get maxsize "$class" maxsize
-               [ -z "$maxsize" -o -z "$mark" ] || {
-                       add_insmod ipt_length
-                       append pktrules "iptables -t mangle -A ${cg} -m mark --mark $mark -m length --length $maxsize: -j MARK --set-mark 0" "$N"
-               }
-       done
-       add_rules pktrules "$rules" "iptables -t mangle -A ${cg}"
-       for iface in $INTERFACES; do
-               config_get classgroup "$iface" classgroup
-               config_get device "$iface" device
-               config_get imqdev "$iface" imqdev
-               config_get dl "$iface" download
-               config_get halfduplex "$iface" halfduplex
-               add_insmod ipt_IMQ
-               append up "iptables -t mangle -A OUTPUT -o $device -j ${cg}" "$N"
-               append up "iptables -t mangle -A FORWARD -o $device -j ${cg}" "$N"
-               [ -z "$dl" ] || {
-                       [ -z "$halfduplex" ] || {
-                               append down "iptables -t mangle -A POSTROUTING -o $device -j IMQ --todev $imqdev" "$N"
-                       }
-                       append down "iptables -t mangle -A PREROUTING -i $device -j ${cg}" "$N"
-                       append down "iptables -t mangle -A PREROUTING -i $device -j IMQ --todev $imqdev" "$N"
-               }
-       done
-       cat <<EOF
-$INSMOD
-iptables -t mangle -N ${cg} >&- 2>&-
-iptables -t mangle -N ${cg}_ct >&- 2>&-
-${iptrules:+${iptrules}${N}iptables -t mangle -A ${cg}_ct -j CONNMARK --save-mark}
-iptables -t mangle -A ${cg} -j CONNMARK --restore-mark
-iptables -t mangle -A ${cg} -m mark --mark 0 -j ${cg}_ct
-$pktrules
-$up$N${down:+${down}$N}
-EOF
-       unset INSMOD
-}
-
-start_firewall() {
-       add_insmod ipt_multiport
-       add_insmod ipt_CONNMARK
-       cat <<EOF
-iptables -t mangle -F
-iptables -t mangle -X
-EOF
-       for group in $CG; do
-               start_cg $group
-       done
-}
-
-C="0"
-INTERFACES=""
-config_load qos
-
-C="0"
-for iface in $INTERFACES; do
-       export C="$(($C + 1))"
-done
-
-case "$1" in
-       all)
-               start_interfaces "$C"
-               start_firewall
-       ;;
-       interface)
-               start_interface "$2" "$C"
-       ;;
-       interfaces)
-               start_interfaces
-       ;;
-       firewall)
-               start_firewall
-       ;;
-esac
diff --git a/package/qos-scripts/files/usr/lib/qos/generate.sh b/package/qos-scripts/files/usr/lib/qos/generate.sh
new file mode 100755 (executable)
index 0000000..7715a45
--- /dev/null
@@ -0,0 +1,404 @@
+#!/bin/sh
+[ -e /etc/functions.sh ] && . /etc/functions.sh || . ./functions.sh
+[ -x /sbin/modprobe ] && insmod="modprobe" || insmod="insmod"
+
+add_insmod() {
+       eval "export isset=\${insmod_$1}"
+       case "$isset" in
+               1) ;;
+               *) append INSMOD "$insmod $* >&- 2>&-" "$N"; export insmod_$1=1;;
+       esac
+}
+
+[ -e /etc/config/network ] && {
+       # only try to parse network config on openwrt
+
+       find_ifname() {(
+               include /lib/network
+               scan_interfaces
+               config_get "$1" ifname
+       )}
+} || {
+       find_ifname() {
+               echo "Interface not found."
+               exit 1
+       }
+}
+
+parse_matching_rule() {
+       local var="$1"
+       local section="$2"
+       local options="$3"
+       local prefix="$4"
+       local suffix="$5"
+       local proto="$6"
+       local mport=""
+       local ports=""
+
+       append "$var" "$prefix" "$N"
+       for option in $options; do
+               case "$option" in
+                       proto) config_get value "$section" proto; proto="${proto:-$value}";;
+               esac
+       done
+       config_get type "$section" TYPE
+       case "$type" in
+               classify) unset pkt; append "$var" "-m mark --mark 0";;
+               default) pkt=1; append "$var" "-m mark --mark 0";;
+               reclassify) pkt=1;;
+       esac
+       append "$var" "${proto:+-p $proto}"
+       for option in $options; do
+               config_get value "$section" "$option"
+               
+               case "$pkt:$option" in
+                       *:srchost)
+                               append "$var" "-s $value"
+                       ;;
+                       *:dsthost)
+                               append "$var" "-d $value"
+                       ;;
+                       *:ipp2p)
+                               add_insmod ipt_ipp2p
+                               append "$var" "-m ipp2p"
+                               case "$value" in
+                                       all) append "$var" "--edk --dc --kazaa --gnu --bit";;
+                                       *) append "$var" "--$value";;
+                               esac
+                       ;;
+                       *:layer7)
+                               add_insmod ipt_layer7
+                               append "$var" "-m layer7 --l7proto $value${pkt:+ --l7pkt}"
+                       ;;
+                       *:ports|*:srcports|*:dstports)
+                               value="$(echo "$value" | sed -e 's,-,:,g')"
+                               lproto=${lproto:-tcp}
+                               case "$proto" in
+                                       ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} -m multiport";;
+                                       *) unset "$var"; return 0;;
+                               esac
+                               case "$option" in
+                                       ports)
+                                               config_set "$section" srcports ""
+                                               config_set "$section" dstports ""
+                                               config_set "$section" portrange ""
+                                               append "$var" "--ports $value"
+                                       ;;
+                                       srcports)
+                                               config_set "$section" ports ""
+                                               config_set "$section" dstports ""
+                                               config_set "$section" portrange ""
+                                               append "$var" "--sports $value"
+                                       ;;
+                                       dstports)
+                                               config_set "$section" ports ""
+                                               config_set "$section" srcports ""
+                                               config_set "$section" portrange ""
+                                               append "$var" "--dports $value"
+                                       ;;
+                               esac
+                               ports=1
+                       ;;
+                       *:portrange)
+                               config_set "$section" ports ""
+                               config_set "$section" srcports ""
+                               config_set "$section" dstports ""
+                               value="$(echo "$value" | sed -e 's,-,:,g')"
+                               case "$proto" in
+                                       ""|tcp|udp) append "$var" "-m ${proto:-tcp -p tcp} --sport $value --dport $value";;
+                                       *) unset "$var"; return 0;;
+                               esac
+                               ports=1
+                       ;;
+                       1:pktsize)
+                               value="$(echo "$value" | sed -e 's,-,:,g')"
+                               add_insmod ipt_length
+                               append "$var" "-m length --length $value"
+                       ;;
+                       1:limit)
+                               add_insmod ipt_limit
+                               append "$var" "-m limit --limit $value"
+                       ;;
+                       1:tcpflags)
+                               case "$proto" in
+                                       tcp) append "$var" "-m tcp --tcp-flags ALL $value";;
+                                       *) unset $var; return 0;;
+                               esac
+                       ;;
+                       1:mark)
+                               config_get class "${value##!}" classnr
+                               [ -z "$class" ] && continue;
+                               case "$value" in
+                                       !*) append "$var" "-m mark ! --mark $class";;
+                                       *) append "$var" "-m mark --mark $class";;
+                               esac
+               esac
+       done
+       append "$var" "$suffix"
+       case "$ports:$proto" in
+               1:)     parse_matching_rule "$var" "$section" "$options" "$prefix" "$suffix" "udp";;
+       esac
+}
+
+config_cb() {
+       option_cb() {
+               return 0
+       }
+
+       # Section start
+       case "$1" in
+               interface)
+                       config_set "$1" "classgroup" "Default"
+                       config_set "$1" "upload" "128"
+               ;;
+               classify|default|reclassify)
+                       option_cb() {
+                               append options "$1"
+                       }
+               ;;
+       esac
+
+    # Section end
+       config_get TYPE "$CONFIG_SECTION" TYPE
+       case "$TYPE" in
+               interface)
+                       config_get enabled "$CONFIG_SECTION" enabled
+                       config_get download "$CONFIG_SECTION" download
+                       config_get classgroup "$CONFIG_SECTION" classgroup
+                       config_set "$CONFIG_SECTION" imqdev "$C"
+                       [ -z "$enabled" -o "$(($enabled))" -eq 0 ] || {
+                               C=$(($C+1))
+                               INTERFACES="$INTERFACES $CONFIG_SECTION"
+                               config_set "$classgroup" enabled 1
+                       }
+                       config_get device "$CONFIG_SECTION" device
+                       [ -z "$device" ] && device="$(find_ifname ${CONFIG_SECTION})"
+                       config_set "$CONFIG_SECTION" device "${device:-eth0}"
+               ;;
+               classgroup) append CG "$CONFIG_SECTION";;
+               classify|default|reclassify)
+                       case "$TYPE" in
+                               classify) var="ctrules";;
+                               *) var="rules";;
+                       esac
+                       config_get target "$CONFIG_SECTION" target
+                       config_set "$CONFIG_SECTION" options "$options"
+                       append "$var" "$CONFIG_SECTION"
+                       unset options
+               ;;
+       esac
+}
+
+
+enum_classes() {
+       local c="0"
+       config_get classes "$1" classes
+       config_get default "$1" default
+       for class in $classes; do
+               c="$(($c + 1))"
+               config_set "${class}" classnr $c
+               case "$class" in
+                       $default) class_default=$c;;
+               esac
+       done
+       class_default="${class_default:-$c}"
+}
+
+cls_var() {
+       local varname="$1"
+       local class="$2"
+       local name="$3"
+       local type="$4"
+       local default="$5"
+       local tmp tmp1 tmp2
+       config_get tmp1 "$class" "$name"
+       config_get tmp2 "${class}_${type}" "$name"
+       tmp="${tmp2:-$tmp1}"
+       tmp="${tmp:-$tmp2}"
+       export ${varname}="${tmp:-$default}"
+}
+
+tcrules() {
+       dir=/usr/lib/qos
+       [ -e $dir/tcrules.awk ] || dir=.
+       echo "$cstr" | awk \
+               -v device="$dev" \
+               -v linespeed="$rate" \
+               -f $dir/tcrules.awk
+}
+
+start_interface() {
+       local iface="$1"
+       local num_imq="$2"
+       config_get device "$iface" device
+       config_get enabled "$iface" enabled
+       [ -z "$device" -o -z "$enabled" ] && exit
+       config_get upload "$iface" upload
+       config_get halfduplex "$iface" halfduplex
+       config_get download "$iface" download
+       config_get classgroup "$iface" classgroup
+       
+       download="${download:-${halfduplex:+$upload}}"
+       enum_classes "$classgroup"
+       for dir in up${halfduplex} ${download:+down}; do
+               case "$dir" in
+                       up)
+                               upload=$(($upload * 98 / 100 - (32 * 128 / $upload)))
+                               dev="$device"
+                               rate="$upload"
+                               dl_mode=""
+                               prefix="cls"
+                       ;;
+                       down)
+                               add_insmod imq numdevs="$num_imq"
+                               config_get imqdev "$iface" imqdev
+                               download=$(($download * 98 / 100 - (100 * 1024 / $download)))
+                               dev="imq$imqdev"
+                               rate="$download"
+                               dl_mode=1
+                               prefix="d_cls"
+                       ;;
+                       *) continue;;
+               esac
+               cstr=
+               for class in $classes; do
+                       cls_var pktsize "$class" packetsize $dir 1500
+                       cls_var pktdelay "$class" packetdelay $dir 0
+                       cls_var maxrate "$class" limitrate $dir 100
+                       cls_var prio "$class" priority $dir 1
+                       cls_var avgrate "$class" avgrate $dir 0
+                       config_get classnr "$class" classnr
+                       append cstr "$classnr:$prio:$avgrate:$pktsize:$pktdelay:$maxrate" "$N"
+               done
+               append ${prefix}q "$(tcrules)" "$N"
+               export dev_${dir}="ifconfig $dev up txqueuelen 5 >&- 2>&-
+tc qdisc del dev $dev root >&- 2>&-
+tc qdisc add dev $dev root handle 1: hfsc default ${class_default}0
+tc class add dev $dev parent 1: classid 1:1 hfsc sc rate ${rate}kbit ul rate ${rate}kbit"
+       done
+       add_insmod cls_fw
+       add_insmod sch_hfsc
+       add_insmod sch_sfq
+       add_insmod sch_red
+
+       cat <<EOF
+${INSMOD:+$INSMOD$N}${dev_up:+$dev_up
+$clsq
+}${imqdev:+$dev_down
+$d_clsq
+$d_clsl
+$d_clsf
+}
+EOF
+       unset INSMOD clsq clsf clsl d_clsq d_clsl d_clsf dev_up dev_down
+}
+
+start_interfaces() {
+       local C="$1"
+       for iface in $INTERFACES; do
+               start_interface "$iface" "$C"
+       done
+}
+
+add_rules() {
+       local var="$1"
+       local rules="$2"
+       local prefix="$3"
+       
+       for rule in $rules; do
+               unset iptrule
+               config_get target "$rule" target
+               config_get target "$target" classnr
+               config_get options "$rule" options
+               parse_matching_rule iptrule "$rule" "$options" "$prefix" "-j MARK --set-mark $target"
+               append "$var" "$iptrule" "$N"
+       done
+}
+
+start_cg() {
+       local cg="$1"
+       local iptrules
+       local pktrules
+       local sizerules
+       local download
+       enum_classes "$cg"
+       add_rules iptrules "$ctrules" "iptables -t mangle -A ${cg}_ct"
+       config_get classes "$cg" classes
+       for class in $classes; do
+               config_get mark "$class" classnr
+               config_get maxsize "$class" maxsize
+               [ -z "$maxsize" -o -z "$mark" ] || {
+                       add_insmod ipt_length
+                       append pktrules "iptables -t mangle -A ${cg} -m mark --mark $mark -m length --length $maxsize: -j MARK --set-mark 0" "$N"
+               }
+       done
+       add_rules pktrules "$rules" "iptables -t mangle -A ${cg}"
+       for iface in $INTERFACES; do
+               config_get classgroup "$iface" classgroup
+               config_get device "$iface" device
+               config_get imqdev "$iface" imqdev
+               config_get dl "$iface" download
+               config_get halfduplex "$iface" halfduplex
+               add_insmod ipt_IMQ
+               append up "iptables -t mangle -A OUTPUT -o $device -j ${cg}" "$N"
+               append up "iptables -t mangle -A FORWARD -o $device -j ${cg}" "$N"
+               [ -z "$dl" ] || {
+                       [ -z "$halfduplex" ] || {
+                               append down "iptables -t mangle -A POSTROUTING -o $device -j IMQ --todev $imqdev" "$N"
+                       }
+                       append down "iptables -t mangle -A PREROUTING -i $device -j ${cg}" "$N"
+                       append down "iptables -t mangle -A PREROUTING -i $device -j IMQ --todev $imqdev" "$N"
+               }
+       done
+       cat <<EOF
+$INSMOD
+iptables -t mangle -N ${cg} >&- 2>&-
+iptables -t mangle -N ${cg}_ct >&- 2>&-
+${iptrules:+${iptrules}${N}iptables -t mangle -A ${cg}_ct -j CONNMARK --save-mark}
+iptables -t mangle -A ${cg} -j CONNMARK --restore-mark
+iptables -t mangle -A ${cg} -m mark --mark 0 -j ${cg}_ct
+$pktrules
+$up$N${down:+${down}$N}
+EOF
+       unset INSMOD
+}
+
+start_firewall() {
+       add_insmod ipt_multiport
+       add_insmod ipt_CONNMARK
+       cat <<EOF
+iptables -t mangle -F
+iptables -t mangle -X
+EOF
+       for group in $CG; do
+               start_cg $group
+       done
+}
+
+C="0"
+INTERFACES=""
+[ -e ./qos.conf ] && {
+       . ./qos.conf
+       config_cb
+} || config_load qos
+
+C="0"
+for iface in $INTERFACES; do
+       export C="$(($C + 1))"
+done
+
+case "$1" in
+       all)
+               start_interfaces "$C"
+               start_firewall
+       ;;
+       interface)
+               start_interface "$2" "$C"
+       ;;
+       interfaces)
+               start_interfaces
+       ;;
+       firewall)
+               start_firewall
+       ;;
+esac
diff --git a/package/qos-scripts/files/usr/lib/qos/tcrules.awk b/package/qos-scripts/files/usr/lib/qos/tcrules.awk
new file mode 100644 (file)
index 0000000..8220d99
--- /dev/null
@@ -0,0 +1,101 @@
+BEGIN {
+       dmax=100
+       if (!(linespeed > 0)) linespeed = 128
+       FS=":"
+       n = 0
+}
+
+($1 != "") {
+       n++
+       class[n] = $1
+       prio[n] = $2
+       avgrate[n] = ($3 * linespeed / 100)
+       pktsize[n] = $4
+       delay[n] = $5
+       maxrate[n] = ($6 * linespeed / 100)
+}
+
+END {
+       allocated = 0
+       maxdelay = 0
+
+       for (i = 1; i <= n; i++) {
+               # set defaults
+               if (!(pktsize[i] > 0)) pktsize[i] = 1500
+               if (!(prio[i] > 0)) prio[i] = 1
+
+               allocated += avgrate[i]
+               sum_prio += prio[i]
+               if ((avgrate[i] > 0) && !(delay[i] > 0)) {
+                       sum_rtprio += prio[i]
+               }
+       }
+       
+       # allocation of m1 in rt classes:
+       # sum(d * m1) must not exceed dmax * (linespeed - allocated)
+       dmax = 0
+       for (i = 1; i <= n; i++) {
+               if (avgrate[i] > 0) {
+                       rtm2[i] = avgrate[i]
+                       if (delay[i] > 0) {
+                               d[i] = delay[i]
+                       } else {
+                               d[i] = 2 * pktsize[i] * 1000 / (linespeed * 1024)
+                               if (d[i] > dmax) dmax = d[i]
+                       }
+               }
+       }       
+
+       ds_avail = dmax * (linespeed - allocated)
+       for (i = 1; i <= n; i++) {
+               lsm1[i] = 0
+               rtm1[i] = 0
+               lsm2[i] = linespeed * prio[i] / sum_prio
+               if ((avgrate[i] > 0) && (d[i] > 0)) {
+                       if (!(delay[i] > 0)) {
+                               ds = ds_avail * prio[i] / sum_rtprio
+                               ds_avail -= ds
+                               rtm1[i] = rtm2[i] + ds/d[i]
+                       }
+                       lsm1[i] = rtm1[i]
+               }
+               else {
+                       d[i] = 0
+               }
+       }
+
+       # main qdisc
+       for (i = 1; i <= n; i++) {
+               printf "tc class add dev "device" parent 1:1 classid 1:"class[i]"0 hfsc"
+               if (rtm1[i] > 0) {
+                       printf " rt m1 " int(rtm1[i]) "kbit d " int(d[i] * 1000) "us m2 " int(rtm2[i])"kbit"
+               }
+               printf " ls m1 " int(lsm1[i]) "kbit d " int(d[i] * 1000) "us m2 " int(lsm2[i]) "kbit"
+               print " ul rate " int(maxrate[i]) "kbit"
+       }
+
+       # leaf qdisc
+       avpkt = 1200
+       for (i = 1; i <= n; i++) {
+               ql = int((avgrate[i] + linespeed) * 1024 / (8 * pktsize[i]))
+               printf "tc qdisc add dev "device" parent 1:"class[i]"0 handle "class[i]"00: "
+               if (rtm1[i] > 0) {
+                       # rt class - use sfq
+                       print "sfq perturb 2 limit " ql
+               } else {
+                       # non-rt class - use red
+                       min = int(maxrate[i] * 1024 / 8 * 0.05)
+                       if (min < avpkt) min = avpkt
+                       dqb = 8 * min;
+                       max = int(2.1*min)
+                       rburst = int((1.5*min + max) / (3 * avpkt))
+                       print "red min " min " max " max " burst " rburst " avpkt " avpkt " limit " dqb " probability 0.04 ecn"
+               }
+       }
+       
+       # filter rule
+       for (i = 1; i <= n; i++) {
+               print "tc filter add dev "device" parent 1: prio "class[i]" protocol ip handle "class[i]" fw flowid 1:"class[i] "0" 
+       }
+}
+