Browse Source

Import from ceropackages.

Toke Høiland-Jørgensen 8 years ago
commit
c8254efb07

+ 16 - 0
platform/openwrt/config/sqm

@@ -0,0 +1,16 @@
+
+config queue 'eth1'
+        option enabled '0'
+        option interface 'eth1'
+        option download '85000'
+        option upload '10000'
+        option qdisc 'fq_codel'
+        option script 'simple.qos'
+        option qdisc_advanced '0'
+        option ingress_ecn 'ECN'
+        option egress_ecn 'ECN'
+        option qdisc_really_really_advanced '0'
+        option itarget 'auto'
+        option etarget 'auto'
+        option linklayer 'none'
+

+ 3 - 0
platform/openwrt/hotplug.d/iface/11-sqm

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+[ "$ACTION" = ifup ] && /etc/init.d/sqm enabled && /usr/lib/sqm/run.sh start ${DEVICE}

+ 23 - 0
platform/openwrt/init.d/sqm

@@ -0,0 +1,23 @@
+#!/bin/sh /etc/rc.common
+
+START=50
+
+reload()
+{
+/usr/lib/sqm/run.sh start
+}
+
+restart()
+{
+reload
+}
+
+start()
+{
+reload
+}
+
+stop()
+{
+/usr/lib/sqm/run.sh stop
+}

+ 501 - 0
src/functions.sh

@@ -0,0 +1,501 @@
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#       Copyright (C) 2012-4 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+
+#improve the logread output
+sqm_logger() {
+    logger -t SQM -s "${1}"
+}
+
+insmod() {
+  lsmod | grep -q ^$1 || $INSMOD $1
+}
+
+ipt() {
+  d=`echo $* | sed s/-A/-D/g`
+  [ "$d" != "$*" ] && {
+	iptables $d > /dev/null 2>&1
+	ip6tables $d > /dev/null 2>&1
+  }
+  d=`echo $* | sed s/-I/-D/g`
+  [ "$d" != "$*" ] && {
+	iptables $d > /dev/null 2>&1
+	ip6tables $d > /dev/null 2>&1
+  }
+  iptables $* > /dev/null 2>&1
+  ip6tables $* > /dev/null 2>&1
+}
+
+do_modules() {
+#sm TODO: check first whether the modules exist and only load then
+	insmod act_ipt
+	insmod sch_$QDISC
+	insmod sch_ingress
+	insmod act_mirred
+	insmod cls_fw
+	insmod sch_htb
+}
+
+
+# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down
+
+[ -z "$UPLINK" ] && UPLINK=2302
+[ -z "$DOWNLINK" ] && DOWNLINK=14698
+[ -z "$IFACE" ] && IFACE=ge00
+[ -z "$QDISC" ] && QDISC=fq_codel
+[ -z "$LLAM" ] && LLAM="tc_stab"
+[ -z "$LINKLAYER" ] && LINKLAYER="none"
+[ -z "$OVERHEAD" ] && OVERHEAD=0
+[ -z "$STAB_MTU" ] && STAB_MTU=2047
+[ -z "$STAB_MPU" ] && STAB_MPU=0
+[ -z "$STAB_TSIZE" ] && STAB_TSIZE=512
+[ -z "$AUTOFLOW" ] && AUTOFLOW=0
+[ -z "$LIMIT" ] && LIMIT=1001	# sane global default for *LIMIT for fq_codel on a small memory device
+[ -z "$ILIMIT" ] && ILIMIT=
+[ -z "$ELIMIT" ] && ELIMIT=
+[ -z "$ITARGET" ] && ITARGET=
+[ -z "$ETARGET" ] && ETARGET=
+[ -z "$IECN" ] && IECN="ECN"
+[ -z "$EECN" ] && EECN="NOECN"
+[ -z "$SQUASH_DSCP" ] && SQUASH_DSCP="1"
+[ -z "$SQUASH_INGRESS" ] && SQUASH_INGRESS="1"
+[ -z "$IQDISC_OPTS" ] && IQDISC_OPTS=""
+[ -z "$EQDISC_OPTS" ] && EQDISC_OPTS=""
+[ -z "$TC" ] && TC=`which tc`
+#[ -z "$TC" ] && TC="sqm_logger tc"# this redirects all tc calls into the log
+[ -z "$IP" ] && IP=$( which ip )
+[ -z "$INSMOD" ] && INSMOD=`which insmod`
+[ -z "$TARGET" ] && TARGET="5ms"
+[ -z "$IPT_MASK" ] && IPT_MASK="0xff"
+[ -z "$IPT_MASK_STRING" ] && IPT_MASK_STRING="/${IPT_MASK}"	# for set-mark
+
+#sqm_logger "${0} IPT_MASK: ${IPT_MASK_STRING}"
+
+
+
+# find the ifb device associated with a specific interface, return nothing of no ifb is associated with IF
+get_ifb_associated_with_if() {
+    CUR_IF=$1
+    # CUR_IFB=$( tc -p filter show parent ffff: dev ${CUR_IF} | grep -o -e ifb'[[:digit:]]\+' )
+    CUR_IFB=$( tc -p filter show parent ffff: dev ${CUR_IF} | grep -o -e ifb'[^)]\+' )	# my editor's syntax coloration is limitied so I need a single quote in this line (between eiditor and s)
+    sqm_logger "ifb associated with interface ${CUR_IF}: ${CUR_IFB}"
+    echo ${CUR_IFB}
+}
+
+# ATTENTION, IFB names can only be 15 chararcters, so we chop of excessive characters at the start of the interface name
+# if required
+create_new_ifb_for_if() {
+    CUR_IF=$1
+    MAX_IF_NAME_LENGTH=15
+    IFB_PREFIX="ifb4"
+    NEW_IFB="${IFB_PREFIX}${CUR_IF}"
+    IFB_NAME_LENGTH=${#NEW_IFB}
+    if [ ${IFB_NAME_LENGTH} -gt ${MAX_IF_NAME_LENGTH} ];
+    then
+	sqm_logger "The requsted IFB name ${NEW_IFB} is longer than the allowed 15 characters, trying to make it shorter"
+	OVERLIMIT=$(( ${#NEW_IFB} - ${MAX_IF_NAME_LENGTH} ))
+	NEW_IFB=${IFB_PREFIX}${CUR_IF:${OVERLIMIT}:$(( ${MAX_IF_NAME_LENGTH} - ${#IFB_PREFIX} ))}
+    fi
+    sqm_logger "trying to create new IFB: ${NEW_IFB}"
+    $IP link add name ${NEW_IFB} type ifb #>/dev/null 2>&1	# better be verbose
+    echo ${NEW_IFB}
+}
+
+# the best match is either the IFB already associated with the current interface or a new named IFB
+get_ifb_for_if() {
+    CUR_IF=$1
+    # if an ifb is already associated return that
+    CUR_IFB=$( get_ifb_associated_with_if ${CUR_IF} )
+    [ -z "$CUR_IFB" ] && CUR_IFB=$( create_new_ifb_for_if ${CUR_IF} )
+    [ -z "$CUR_IFB" ] && sqm_logger "Could not find existing IFB for ${CUR_IF}, nor create a new IFB instead..."
+    echo ${CUR_IFB}
+}
+
+#sm: we need the functions above before trying to set the ingress IFB device
+[ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} )      # automagically get the right IFB device for the IFACE"
+
+
+get_htb_adsll_string() {
+	ADSLL=""
+	if [ "$LLAM" = "htb_private" -a "$LINKLAYER" != "none" ];
+	then
+		# HTB defaults to MTU 1600 and an implicit fixed TSIZE of 256, but HTB as of around 3.10.0
+		# does not actually use a table in the kernel
+		ADSLL="mpu ${STAB_MPU} linklayer ${LINKLAYER} overhead ${OVERHEAD} mtu ${STAB_MTU}"
+		sqm_logger "ADSLL: ${ADSLL}"
+	fi
+	echo ${ADSLL}
+}
+
+get_stab_string() {
+	STABSTRING=""
+	if [ "${LLAM}" = "tc_stab" -a "$LINKLAYER" != "none" ];
+	then
+		STABSTRING="stab mtu ${STAB_MTU} tsize ${STAB_TSIZE} mpu ${STAB_MPU} overhead ${OVERHEAD} linklayer ${LINKLAYER}"
+		sqm_logger "STAB: ${STABSTRING}"
+	fi
+	echo ${STABSTRING}
+}
+
+#sm: cake knows how to handle ATM and per packet overhead, so expose and use this...
+get_cake_lla_string() {
+	STABSTRING=""
+	if [ "${LLAM}" = "cake" -a "${LINKLAYER}" != "none" ];
+	then
+		if [ "${LINKLAYER}" = "atm" ];
+		then
+		    STABSTRING="atm"
+		fi
+
+		STABSTRING="${STABSTRING} overhead ${OVERHEAD}"
+		sqm_logger "cake link layer adjustments: ${STABSTRING}"
+	fi
+	echo ${STABSTRING}
+}
+
+
+sqm_stop() {
+	$TC qdisc del dev $IFACE ingress
+	$TC qdisc del dev $IFACE root
+	$TC qdisc del dev $DEV root
+}
+
+# Note this has side effects on the prio variable
+# and depends on the interface global too
+
+fc() {
+	$TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3
+	prio=$(($prio + 1))
+	$TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3
+	prio=$(($prio + 1))
+}
+
+fc_pppoe() {
+	PPPOE_SESSION_ETHERTYPE="0x8864"
+	PPPOE_DISCOVERY_ETHERTYPE="0x8863"
+	PPP_PROTO_IP4="0x0021"
+	PPP_PROTO_IP6="0x0057"
+	ARP_PROTO_IP4="0x0806"
+	$TC filter add dev $interface protocol ip parent $1 prio $prio u32 match ip tos $2 0xfc classid $3
+	$TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 400 + ${prio}  )) u32 \
+	    match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	    match u8 $2 0xfc at 9 \
+	    flowid $3
+
+	prio=$(($prio + 1))
+	$TC filter add dev $interface protocol ipv6 parent $1 prio $prio u32 match ip6 priority $2 0xfc classid $3
+	$TC filter add dev $interface parent $1 protocol ${PPPOE_SESSION_ETHERTYPE} prio $(( 600 + ${prio} )) u32 \
+	    match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	    match u16 0x0${2:2:2}0 0x0fc0 at 8 \
+	    flowid $3
+
+
+
+
+	prio=$(($prio + 1))
+
+
+}
+# FIXME: actually you need to get the underlying MTU on PPOE thing
+
+get_mtu() {
+	BW=$2
+	F=`cat /sys/class/net/$1/mtu`
+	if [ -z "$F" ]
+	then
+	F=1500
+	fi
+	if [ $BW -gt 20000 ]
+	then
+		F=$(($F * 2))
+	fi
+	if [ $BW -gt 30000 ]
+	then
+		F=$(($F * 2))
+	fi
+	if [ $BW -gt 40000 ]
+	then
+		F=$(($F * 2))
+	fi
+	if [ $BW -gt 50000 ]
+	then
+		F=$(($F * 2))
+	fi
+	if [ $BW -gt 60000 ]
+	then
+		F=$(($F * 2))
+	fi
+	if [ $BW -gt 80000 ]
+	then
+		F=$(($F * 2))
+	fi
+	echo $F
+}
+
+# FIXME should also calculate the limit
+# Frankly I think Xfq_codel can pretty much always run with high numbers of flows
+# now that it does fate sharing
+# But right now I'm trying to match the ns2 model behavior better
+# So SET the autoflow variable to 1 if you want the cablelabs behavior
+
+get_flows() {
+	if [ "$AUTOFLOW" -eq "1" ]
+	then
+		FLOWS=8
+		[ $1 -gt 999 ] && FLOWS=16
+		[ $1 -gt 2999 ] && FLOWS=32
+		[ $1 -gt 7999 ] && FLOWS=48
+		[ $1 -gt 9999 ] && FLOWS=64
+		[ $1 -gt 19999 ] && FLOWS=128
+		[ $1 -gt 39999 ] && FLOWS=256
+		[ $1 -gt 69999 ] && FLOWS=512
+		[ $1 -gt 99999 ] && FLOWS=1024
+		case $QDISC in
+			codel|ns2_codel|pie|*fifo|pfifo_fast) ;;
+			fq_codel|*fq_codel|sfq) echo flows $FLOWS ;;
+		esac
+	fi
+}
+
+# set the target parameter, also try to only take well formed inputs
+# Note, the link bandwidth in the current direction (ingress or egress)
+# is required to adjust the target for slow links
+get_target() {
+	local CUR_TARGET=${1}
+	local CUR_LINK_KBPS=${2}
+	[ ! -z "$CUR_TARGET" ] && sqm_logger "cur_target: ${CUR_TARGET} cur_bandwidth: ${CUR_LINK_KBPS}"
+	CUR_TARGET_STRING=
+	# either e.g. 100ms or auto
+	CUR_TARGET_VALUE=$( echo ${CUR_TARGET} | grep -o -e \^'[[:digit:]]\+' )
+	CUR_TARGET_UNIT=$( echo ${CUR_TARGET} | grep -o -e '[[:alpha:]]\+'\$ )
+	#[ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_VALUE: $CUR_TARGET_VALUE"
+	#[ ! -z "$CUR_TARGET" ] && sqm_logger "CUR_TARGET_UNIT: $CUR_TARGET_UNIT"
+
+	AUTO_TARGET=
+	UNIT_VALID=
+
+	case $QDISC in
+		*codel|*pie)
+			if [ ! -z "${CUR_TARGET_VALUE}" -a ! -z "${CUR_TARGET_UNIT}" ];
+    			then
+    			    case ${CUR_TARGET_UNIT} in
+    				    # permissible units taken from: tc_util.c get_time()
+				    s|sec|secs|ms|msec|msecs|us|usec|usecs)
+					CUR_TARGET_STRING="target ${CUR_TARGET_VALUE}${CUR_TARGET_UNIT}"
+					UNIT_VALID="1"
+					;;
+			    esac
+			fi
+			# empty field in GUI or undefined GUI variable now defaults to auto
+			if [ -z "${CUR_TARGET_VALUE}" -a -z "${CUR_TARGET_UNIT}" ];
+    			then
+				if [ ! -z "${CUR_LINK_KBPS}" ];
+				then
+				    TMP_TARGET_US=$( adapt_target_to_slow_link $CUR_LINK_KBPS )
+				    TMP_INTERVAL_STRING=$( adapt_interval_to_slow_link $TMP_TARGET_US )
+				    CUR_TARGET_STRING="target ${TMP_TARGET_US}us ${TMP_INTERVAL_STRING}"
+				    AUTO_TARGET="1"
+				    sqm_logger "get_target defaulting to auto."
+			    	else
+				    sqm_logger "required link bandwidth in kbps not passed to get_target()."
+				fi
+			fi
+			# but still allow explicit use of the keyword auto for backward compatibility
+                        case ${CUR_TARGET_UNIT} in
+                            auto|Auto|AUTO)
+                                if [ ! -z "${CUR_LINK_KBPS}" ];
+                                then
+                                    TMP_TARGET_US=$( adapt_target_to_slow_link $CUR_LINK_KBPS )
+                                    TMP_INTERVAL_STRING=$( adapt_interval_to_slow_link $TMP_TARGET_US )
+                                    CUR_TARGET_STRING="target ${TMP_TARGET_US}us ${TMP_INTERVAL_STRING}"
+                                    AUTO_TARGET="1"
+                                else
+                                    sqm_logger "required link bandwidth in kbps not passed to get_target()."
+                                fi
+                            ;;
+                        esac
+
+			case ${CUR_TARGET_UNIT} in
+			    default|Default|DEFAULT)
+				if [ ! -z "${CUR_LINK_KBPS}" ];
+				then
+				    CUR_TARGET_STRING=""	# return nothing so the default target is not over-ridden...
+				    AUTO_TARGET="1"
+				    #sqm_logger "get_target using qdisc default, no explicit target string passed."
+			    	else
+			    	    sqm_logger "required link bandwidth in kbps not passed to get_target()."
+			        fi
+			    ;;
+			esac
+			if [ ! -z "${CUR_TARGET}" ];
+			    then
+			    if [ -z "${CUR_TARGET_VALUE}" -o -z "${UNIT_VALID}" ];
+			    then
+				[ -z "$AUTO_TARGET" ] && sqm_logger "${CUR_TARGET} is not a well formed tc target specifier; e.g.: 5ms (or s, us), or one of the strings auto or default."
+			    fi
+			fi
+		    ;;
+	esac
+#	sqm_logger "target: ${CUR_TARGET_STRING}"
+	echo $CUR_TARGET_STRING
+}
+
+# for low bandwidth links fq_codels default target of 5ms does not work too well
+# so increase target for slow links (note below roughly 2500kbps a single packet will \
+# take more than 5 ms to be tansfered over the wire)
+adapt_target_to_slow_link() {
+    CUR_LINK_KBPS=$1
+    CUR_EXTENDED_TARGET_US=
+    MAX_PAKET_DELAY_IN_US_AT_1KBPS=$(( 1000 * 1000 *1540 * 8 / 1000 ))
+    CUR_EXTENDED_TARGET_US=$(( ${MAX_PAKET_DELAY_IN_US_AT_1KBPS} / ${CUR_LINK_KBPS} ))	# note this truncates the decimals
+    # do not change anything for fast links
+    [ "$CUR_EXTENDED_TARGET_US" -lt 5000 ] && CUR_EXTENDED_TARGET_US=5000
+    case ${QDISC} in
+        *codel|pie)
+	    echo "${CUR_EXTENDED_TARGET_US}"
+	    ;;
+    esac
+}
+
+# codel looks at a whole interval to figure out wether observed latency stayed below target
+# if target >= interval that will not work well, so increase interval by the same amonut that target got increased
+adapt_interval_to_slow_link() {
+    CUR_TARGET_US=$1
+    case ${QDISC} in
+        *codel)
+            CUR_EXTENDED_INTERVAL_US=$(( (100 - 5) * 1000 + ${CUR_TARGET_US} ))
+	    echo "interval ${CUR_EXTENDED_INTERVAL_US}us"
+	    ;;
+	pie)
+	    ## not sure if pie needs this, probably not
+	    #CUR_EXTENDED_TUPDATE_US=$(( (30 - 20) * 1000 + ${CUR_TARGET_US} ))
+	    #echo "tupdate ${CUR_EXTENDED_TUPDATE_US}us"
+	    ;;
+    esac
+}
+
+
+# set quantum parameter if available for this qdisc
+get_quantum() {
+    case $QDISC in
+	*fq_codel|fq_pie|drr) echo quantum $1 ;;
+	*) ;;
+    esac
+}
+
+# only show limits to qdiscs that can handle them...
+# Note that $LIMIT contains the default limit
+get_limit() {
+    CURLIMIT=$1
+    case $QDISC in
+    *codel|*pie|pfifo_fast|sfq|pfifo) [ -z ${CURLIMIT} ] && CURLIMIT=${LIMIT}	# use the global default limit
+        ;;
+    bfifo) [ -z "$CURLIMIT" ] && [ ! -z "$LIMIT" ] && CURLIMIT=$(( ${LIMIT} * $( cat /sys/class/net/${IFACE}/mtu ) ))	# bfifo defaults to txquelength * MTU,
+        ;;
+    *) sqm_logger "${QDISC} does not support a limit"
+        ;;
+    esac
+    sqm_logger "get_limit: $1 CURLIMIT: ${CURLIMIT}"
+
+    if [ ! -z "$CURLIMIT" ]
+    then
+    echo "limit ${CURLIMIT}"
+    fi
+}
+
+get_ecn() {
+    CURECN=$1
+    #sqm_logger CURECN: $CURECN
+	case ${CURECN} in
+		ECN)
+			case $QDISC in
+				*codel|*pie|*red)
+				    CURECN=ecn
+				    ;;
+				*)
+				    CURECN=""
+				    ;;
+			esac
+			;;
+		NOECN)
+			case $QDISC in
+				*codel|*pie|*red)
+				    CURECN=noecn
+				    ;;
+				*)
+				    CURECN=""
+				    ;;
+			esac
+			;;
+		*)
+		    sqm_logger "ecn value $1 not handled"
+		    ;;
+	esac
+	#sqm_logger "get_ECN: $1 CURECN: ${CURECN} IECN: ${IECN} EECN: ${EECN}"
+	echo ${CURECN}
+
+}
+
+# This could be a complete diffserv implementation
+
+diffserv() {
+
+interface=$1
+prio=1
+
+# Catchall
+
+$TC filter add dev $interface parent 1:0 protocol all prio 999 u32 \
+        match ip protocol 0 0x00 flowid 1:12
+
+# Find the most common matches fast
+#fc_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL
+
+fc 1:0 0x00 1:12 # BE
+fc 1:0 0x20 1:13 # CS1
+fc 1:0 0x10 1:11 # IMM
+fc 1:0 0xb8 1:11 # EF
+fc 1:0 0xc0 1:11 # CS3
+fc 1:0 0xe0 1:11 # CS6
+fc 1:0 0x90 1:11 # AF42 (mosh)
+
+# Arp traffic
+$TC filter add dev $interface protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11
+
+
+prio=$(($prio + 1))
+
+
+}
+
+diffserv_pppoe() {
+
+interface=$1
+prio=1
+
+# Catchall
+
+$TC filter add dev $interface parent 1:0 protocol all prio 999 u32 \
+        match ip protocol 0 0x00 flowid 1:12
+
+# Find the most common matches fast
+#fc_pppoe() instead of fc() with effectice ingress classification for pppoe is very expensive and destroys LUL
+
+fc_pppoe 1:0 0x00 1:12 # BE
+fc_pppoe 1:0 0x20 1:13 # CS1
+fc_pppoe 1:0 0x10 1:11 # IMM
+fc_pppoe 1:0 0xb8 1:11 # EF
+fc_pppoe 1:0 0xc0 1:11 # CS3
+fc_pppoe 1:0 0xe0 1:11 # CS6
+fc_pppoe 1:0 0x90 1:11 # AF42 (mosh)
+
+# Arp traffic
+$TC filter add dev $interface protocol arp parent 1:0 prio $prio handle 500 fw flowid 1:11
+
+
+prio=$(($prio + 1))
+
+
+}

+ 133 - 0
src/run.sh

@@ -0,0 +1,133 @@
+#!/bin/sh
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#       Copyright (C) 2012-4 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+
+
+. /lib/functions.sh
+
+STOP=
+ACTIVE_STATE_PREFIX="SQM_active_on_"
+ACTIVE_STATE_FILE_DIR="/var/run/SQM"
+mkdir -p ${ACTIVE_STATE_FILE_DIR}
+
+
+START_ON_IF=$2	# only process this interface
+# TODO if $2 is empty select all interfaces with running sqm instance
+if [ -z ${START_ON_IF} ] ;
+then
+    # find all interfaces with active sqm instance
+    logger -t SQM -s "Trying to start/stop SQM on all interfaces."
+    PROTO_STATE_FILE_LIST=$( ls ${ACTIVE_STATE_FILE_DIR}/${ACTIVE_STATE_PREFIX}* 2> /dev/null )
+else
+    # only try to restart the just hotplugged interface, so reduce the list of interfaces to stop to the specified one
+    logger -t SQM -s "Trying to start/stop SQM on interface ${START_ON_IF}"
+    PROTO_STATE_FILE_LIST=${ACTIVE_STATE_FILE_DIR}/${ACTIVE_STATE_PREFIX}${START_ON_IF}
+fi
+
+
+
+
+case ${1} in
+    start)
+	# just run through, same as passing no argument
+	;;
+    stop)
+        logger -t SQM -s "run.sh stop"
+	STOP=$1
+        ;;
+esac
+
+
+
+
+
+
+# the current uci config file does not necessarily contain sections for all interfaces with active
+# SQM instances, so use the ACTIVE_STATE_FILES to detect the interfaces on which to stop SQM.
+# Currently the .qos scripts start with stopping any existing traffic shaping so this should not
+# effectively change anything...
+for STATE_FILE in ${PROTO_STATE_FILE_LIST} ; do
+    if [ -f ${STATE_FILE} ] ;
+    then
+	STATE_FILE_BASE_NAME=$( basename ${STATE_FILE} )
+	CURRENT_INTERFACE=${STATE_FILE_BASE_NAME:${#ACTIVE_STATE_PREFIX}:$(( ${#STATE_FILE_BASE_NAME} - ${#ACTIVE_STATE_PREFIX} ))}
+	logger -t SQM -s "${0} Stopping SQM on interface: ${CURRENT_INTERFACE}"
+	/usr/lib/sqm/stop.sh ${CURRENT_INTERFACE}
+	rm ${STATE_FILE}	# well, we stop it so it is not running anymore and hence no active state file needed...
+    fi
+done
+
+config_load sqm
+
+run_simple_qos() {
+	local section="$1"
+	export IFACE=$(config_get "$section" interface)
+
+	# If called explicitly for one interface only , so ignore anything else
+	[ -n "${START_ON_IF}" -a "$START_ON_IF" != "$IFACE" ] && return
+	#logger -t SQM -s "marching on..."
+
+	ACTIVE_STATE_FILE_FQN="${ACTIVE_STATE_FILE_DIR}/${ACTIVE_STATE_PREFIX}${IFACE}"	# this marks interfaces as active with SQM
+	[ -f "${ACTIVE_STATE_FILE_FQN}" ] && logger -t SQM -s "Uh, oh, ${ACTIVE_STATE_FILE_FQN} should already be stopped."	# Not supposed to happen
+
+	if [ $(config_get "$section" enabled) -ne 1 ];
+	then
+	    if [ -f "${ACTIVE_STATE_FILE_FQN}" ];
+	    then
+		# this should not be possible, delete after testing
+		local SECTION_STOP="stop"	# it seems the user just de-selected enable, so stop the active SQM
+	    else
+		logger -t SQM -s "${0} SQM for interface ${IFACE} is not enabled, skipping over..."
+		return 0	# since SQM is not active on the current interface nothing to do here
+	    fi
+	fi
+
+	export UPLINK=$(config_get "$section" upload)
+	export DOWNLINK=$(config_get "$section" download)
+	export LLAM=$(config_get "$section" linklayer_adaptation_mechanism)
+	export LINKLAYER=$(config_get "$section" linklayer)
+	export OVERHEAD=$(config_get "$section" overhead)
+	export STAB_MTU=$(config_get "$section" tcMTU)
+	export STAB_TSIZE=$(config_get "$section" tcTSIZE)
+	export STAB_MPU=$(config_get "$section" tcMPU)
+	export ILIMIT=$(config_get "$section" ilimit)
+	export ELIMIT=$(config_get "$section" elimit)
+	export ITARGET=$(config_get "$section" itarget)
+	export ETARGET=$(config_get "$section" etarget)
+	export IECN=$(config_get "$section" ingress_ecn)
+	export EECN=$(config_get "$section" egress_ecn)
+	export IQDISC_OPTS=$(config_get "$section" iqdisc_opts)
+	export EQDISC_OPTS=$(config_get "$section" eqdisc_opts)
+	export TARGET=$(config_get "$section" target)
+	export SQUASH_DSCP=$(config_get "$section" squash_dscp)
+	export SQUASH_INGRESS=$(config_get "$section" squash_ingress)
+
+	export QDISC=$(config_get "$section" qdisc)
+	export SCRIPT=/usr/lib/sqm/$(config_get "$section" script)
+
+#	# there should be nothing left to stop, so just avoid calling the script
+	if [ "$STOP" == "stop" -o "$SECTION_STOP" == "stop" ];
+	then
+#	     /usr/lib/sqm/stop.sh
+#	     [ -f ${ACTIVE_STATE_FILE_FQN} ] && rm ${ACTIVE_STATE_FILE_FQN}	# conditional to avoid errors ACTIVE_STATE_FILE_FQN does not exist anymore
+#	     $(config_set "$section" enabled 0)	# this does not save to the config file only to the loaded memory representation
+	     logger -t SQM -s "${0} SQM qdiscs on ${IFACE} removed"
+	     return 0
+	fi
+	# in case of spurious hotplug events, try double check whether the interface is really up
+	if [ ! -d /sys/class/net/${IFACE} ] ;
+	then
+	    echo "${IFACE} does currently not exist, not even trying to start SQM on nothing." > /dev/kmsg
+	    logger -t SQM -s "${IFACE} does currently not exist, not even trying to start SQM on nothing."
+	    return 0
+	fi
+
+	logger -t SQM -s "${0} Queue Setup Script: ${SCRIPT}"
+	[ -x "$SCRIPT" ] && { $SCRIPT ; touch ${ACTIVE_STATE_FILE_FQN}; }
+}
+
+config_foreach run_simple_qos

+ 245 - 0
src/simple.qos

@@ -0,0 +1,245 @@
+#!/bin/sh
+# Cero3 Shaper
+# A 3 bin tc_codel and ipv6 enabled shaping script for
+# ethernet gateways
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#       Copyright (C) 2012-5 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+
+# Compared to the complexity that debloat had become
+# this cleanly shows a means of going from diffserv marking
+# to prioritization using the current tools (ip(6)tables
+# and tc. I note that the complexity of debloat exists for
+# a reason, and it is expected that script is run first
+# to setup various other parameters such as BQL and ethtool.
+# (And that the debloat script has setup the other interfaces)
+
+# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down
+
+. /usr/lib/sqm/functions.sh
+
+ipt_setup() {
+
+ipt -t mangle -N QOS_MARK_${IFACE}
+
+case $QDISC in
+	cake*)
+	    sqm_logger cake does all the diffserv work - no need for iptables rules
+	    ;;
+	*)
+	    ipt -t mangle -A QOS_MARK_${IFACE} -j MARK --set-mark 0x2${IPT_MASK_STRING}
+	    # You can go further with classification but...
+	    ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS1 -j MARK --set-mark 0x3${IPT_MASK_STRING}
+	    ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS6 -j MARK --set-mark 0x1${IPT_MASK_STRING}
+	    ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class EF -j MARK --set-mark 0x1${IPT_MASK_STRING}
+	    ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class AF42 -j MARK --set-mark 0x1${IPT_MASK_STRING}
+	    ipt -t mangle -A QOS_MARK_${IFACE} -m tos  --tos Minimize-Delay -j MARK --set-mark 0x1${IPT_MASK_STRING}
+	    ;;
+esac
+
+# and it might be a good idea to do it for udp tunnels too
+
+# Turn it on. Preserve classification if already performed
+
+#sm: is it correct to do this in $IFACE? Should ingress not be on $DEV? since HTB acts on $DEV?
+#	SQUASH also does not work on $DEV (that is the IFB will still see the incoming ToS bits whether we squash or not)
+#	SQUASH is still useful to protect internal machines...
+if [ "$SQUASH_DSCP" = "1" ]
+then
+sqm_logger "Squashing differentiated services code points (DSCP) from ingress."
+CAKE_OPTS=besteffort # someday squash
+ipt -t mangle -I PREROUTING -i $IFACE -m dscp ! --dscp 0 -j DSCP --set-dscp-class be
+else
+sqm_logger "Keeping differentiated services code points (DSCP) from ingress."
+CAKE_OPTS=""
+ipt -t mangle -A PREROUTING -i $IFACE -m mark --mark 0x00${IPT_MASK_STRING} -g QOS_MARK_${IFACE}
+fi
+
+ipt -t mangle -A POSTROUTING -o $IFACE -m mark --mark 0x00${IPT_MASK_STRING} -g QOS_MARK_${IFACE}
+
+# The Syn optimization was nice but fq_codel does it for us
+# ipt -t mangle -A PREROUTING -i s+ -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x01
+# Not sure if this will work. Encapsulation is a problem period
+
+ipt -t mangle -I PREROUTING -i vtun+ -p tcp -j MARK --set-mark 0x2${IPT_MASK_STRING} # tcp tunnels need ordering
+
+# Emanating from router, do a little more optimization
+# but don't bother with it too much.
+
+ipt -t mangle -A OUTPUT -p udp -m multiport --ports 123,53 -j DSCP --set-dscp-class AF42
+
+#Not clear if the second line is needed
+#ipt -t mangle -A OUTPUT -o $IFACE -g QOS_MARK_${IFACE}
+
+}
+
+
+# TC rules
+
+egress() {
+
+CEIL=${UPLINK}
+PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty
+BE_RATE=`expr $CEIL / 6`   # Min for best effort
+BK_RATE=`expr $CEIL / 6`   # Min for background
+BE_CEIL=`expr $CEIL - 16`  # A little slop at the top
+
+LQ="quantum `get_mtu $IFACE $CEIL`"
+
+$TC qdisc del dev $IFACE root 2> /dev/null
+case $QDISC in
+	cake*)
+	    $TC qdisc add dev $IFACE root `get_stab_string` $QDISC bandwidth ${CEIL}kbit `get_cake_lla_string` ${EQDISC_OPTS}
+	    ;;
+	*)
+	    $TC qdisc add dev $IFACE root handle 1: `get_stab_string` htb default 12
+	    $TC class add dev $IFACE parent 1: classid 1:1 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string`
+	    $TC class add dev $IFACE parent 1:1 classid 1:10 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string`
+	    $TC class add dev $IFACE parent 1:1 classid 1:11 htb $LQ rate 128kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string`
+	    $TC class add dev $IFACE parent 1:1 classid 1:12 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string`
+	    $TC class add dev $IFACE parent 1:1 classid 1:13 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string`
+
+	    $TC qdisc add dev $IFACE parent 1:11 handle 110: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${PRIO_RATE}` ${EQDISC_OPTS}
+	    $TC qdisc add dev $IFACE parent 1:12 handle 120: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BE_RATE}` ${EQDISC_OPTS}
+	    $TC qdisc add dev $IFACE parent 1:13 handle 130: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS}
+
+	    # Need a catchall rule
+
+	    $TC filter add dev $IFACE parent 1:0 protocol all prio 999 u32 \
+	        match ip protocol 0 0x00 flowid 1:12
+
+	    # FIXME should probably change the filter here to do pre-nat
+
+	    $TC filter add dev $IFACE parent 1:0 protocol ip prio 1 handle 1 fw classid 1:11
+	    $TC filter add dev $IFACE parent 1:0 protocol ip prio 2 handle 2 fw classid 1:12
+	    $TC filter add dev $IFACE parent 1:0 protocol ip prio 3 handle 3 fw classid 1:13
+
+	    # ipv6 support. Note that the handle indicates the fw mark bucket that is looked for
+
+	    $TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 4 handle 1 fw classid 1:11
+	    $TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 5 handle 2 fw classid 1:12
+	    $TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 6 handle 3 fw classid 1:13
+
+	    # Arp traffic
+
+	    $TC filter add dev $IFACE parent 1:0 protocol arp prio 7 handle 1 fw classid 1:11
+
+	    # ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods
+	    # better instead
+
+	    $TC filter add dev $IFACE parent 1:0 protocol ip prio 8 \
+		 u32 match ip protocol 1 0xff flowid 1:13
+
+	    $TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 9 \
+		 u32 match ip protocol 1 0xff flowid 1:13
+	    ;;
+esac
+
+#diffserv $IFACE
+
+}
+
+ingress() {
+
+CEIL=$DOWNLINK
+PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty
+BE_RATE=`expr $CEIL / 6`   # Min for best effort
+BK_RATE=`expr $CEIL / 6`   # Min for background
+BE_CEIL=`expr $CEIL - 16`  # A little slop at the top
+
+LQ="quantum `get_mtu $IFACE $CEIL`"
+
+$TC qdisc del dev $IFACE handle ffff: ingress 2> /dev/null
+$TC qdisc add dev $IFACE handle ffff: ingress
+
+$TC qdisc del dev $DEV root  2> /dev/null
+
+if [ "$SQUASH_INGRESS" = "1" ]
+then
+	sqm_logger "Do not perform DSCP based filtering on ingress. (1-tier classification)"
+	# Revert to no dscp based filtering
+	case $QDISC in
+		cake*)
+		    $TC qdisc add dev $DEV root `get_stab_string` $QDISC bandwidth ${DOWNLINK}kbit besteffort `get_cake_lla_string` ${IQDISC_OPTS}
+		    ;;
+		*)
+		    $TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 10
+		    $TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit `get_htb_adsll_string`
+		    $TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit prio 0 `get_htb_adsll_string`
+		    $TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS}
+		    ;;
+	esac
+
+else
+    sqm_logger "Perform DSCP based filtering on ingress. (3-tier classification)"
+    case $QDISC in
+    	    cake*)
+    		    $TC qdisc add dev $DEV root `get_stab_string` $QDISC bandwidth ${DOWNLINK}kbit `get_cake_lla_string` $CAKE_OPTS ${IQDISC_OPTS}
+    		    ;;
+	    *)
+		    $TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 12
+		    $TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string`
+		    $TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string`
+		    $TC class add dev $DEV parent 1:1 classid 1:11 htb $LQ rate 32kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string`
+		    $TC class add dev $DEV parent 1:1 classid 1:12 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string`
+		    $TC class add dev $DEV parent 1:1 classid 1:13 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string`
+
+		    # I'd prefer to use a pre-nat filter but that causes permutation...
+
+		    $TC qdisc add dev $DEV parent 1:11 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 500` `get_flows ${PRIO_RATE}` ${IQDISC_OPTS}
+		    $TC qdisc add dev $DEV parent 1:12 handle 120: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 1500` `get_flows ${BE_RATE}` ${IQDISC_OPTS}
+		    $TC qdisc add dev $DEV parent 1:13 handle 130: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS}
+
+		    diffserv $DEV
+		    ;;
+    esac
+fi
+
+ifconfig $DEV up
+
+# redirect all IP packets arriving in $IFACE to ifb0
+
+$TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+  match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+
+}
+
+do_modules
+ipt_setup
+
+if [ "$UPLINK" -ne 0 ];
+then
+	egress
+	sqm_logger "egress shaping activated"
+else
+	sqm_logger "egress shaping deactivated"
+	tc qdisc del dev $IFACE root 2> /dev/null
+fi
+if [ "$DOWNLINK" -ne 0 ];
+then
+	ingress
+	sqm_logger "ingress shaping activated"
+else
+	sqm_logger "ingress shaping deactivated"
+	tc qdisc del dev $DEV root 2> /dev/null
+	tc qdisc del dev $IFACE ingress 2> /dev/null
+fi
+
+
+
+# References:
+# This alternate shaper attempts to go for 1/u performance in a clever way
+# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD
+
+# Comments
+# This does the right thing with ipv6 traffic.
+# It also tries to leverage diffserv to some sane extent. In particular,
+# the 'priority' queue is limited to 33% of the total, so EF, and IMM traffic
+# cannot starve other types. The rfc suggested 30%. 30% is probably
+# a lot in today's world.
+
+# Flaws
+# Many!

+ 1 - 0
src/simple.qos.help

@@ -0,0 +1 @@
+BW-limited three-tier prioritisation scheme with fq_codel on each queue. (default)

+ 398 - 0
src/simple_pppoe.qos

@@ -0,0 +1,398 @@
+#!/bin/sh
+# Cero3 Shaper
+# A 3 bin tc_codel and ipv6 enabled shaping script for
+# ethernet gateways
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#       Copyright (C) 2012-4 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+
+# Compared to the complexity that debloat had become
+# this cleanly shows a means of going from diffserv marking
+# to prioritization using the current tools (ip(6)tables
+# and tc. I note that the complexity of debloat exists for
+# a reason, and it is expected that script is run first
+# to setup various other parameters such as BQL and ethtool.
+# (And that the debloat script has setup the other interfaces)
+
+# You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down
+
+#sm: Goal to create a set of tc filters that also apply on pppoe encapsulated packets
+#	but having multiple filters run in succession is slow, so look at tc filter hashing
+#	(this should help cut down the number of OPs per packet considerably)
+
+
+. /usr/lib/sqm/functions.sh
+#sqm_logger IPT_MASK: ${IPT_MASK_STRING}
+ipt_setup() {
+
+ipt -t mangle -N QOS_MARK_${IFACE}
+
+ipt -t mangle -A QOS_MARK_${IFACE} -j MARK --set-mark 0x2${IPT_MASK_STRING}
+# You can go further with classification but...
+ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS1 -j MARK --set-mark 0x3${IPT_MASK_STRING}
+ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class CS6 -j MARK --set-mark 0x1${IPT_MASK_STRING}
+ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class EF -j MARK --set-mark 0x1${IPT_MASK_STRING}
+ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class AF42 -j MARK --set-mark 0x1${IPT_MASK_STRING}
+ipt -t mangle -A QOS_MARK_${IFACE} -m tos  --tos Minimize-Delay -j MARK --set-mark 0x1${IPT_MASK_STRING}
+
+# and it might be a good idea to do it for udp tunnels too
+
+# Turn it on. Preserve classification if already performed
+
+if [ "$SQUASH_DSCP" = "1" ]
+then
+sqm_logger "Squashing differentiated services code points (DSCP) from ingress."
+ipt -t mangle -I PREROUTING -i $IFACE -m dscp ! --dscp 0 -j DSCP --set-dscp-class be
+else
+sqm_logger "Keeping differentiad services code points (DSCP) from ingress."
+ipt -t mangle -A PREROUTING -i $IFACE -m mark --mark 0x00${IPT_MASK_STRING} -g QOS_MARK_${IFACE}
+fi
+
+ipt -t mangle -A POSTROUTING -o $IFACE -m mark --mark 0x00${IPT_MASK_STRING} -g QOS_MARK_${IFACE}
+
+# The Syn optimization was nice but fq_codel does it for us
+# ipt -t mangle -A PREROUTING -i s+ -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x01
+# Not sure if this will work. Encapsulation is a problem period
+
+ipt -t mangle -I PREROUTING -i vtun+ -p tcp -j MARK --set-mark 0x2${IPT_MASK_STRING} # tcp tunnels need ordering
+
+# Emanating from router, do a little more optimization
+# but don't bother with it too much.
+
+ipt -t mangle -A OUTPUT -p udp -m multiport --ports 123,53 -j DSCP --set-dscp-class AF42
+
+#Not clear if the second line is needed
+#ipt -t mangle -A OUTPUT -o $IFACE -g QOS_MARK_${IFACE}
+
+}
+
+
+MYBURST=1600	#sm: make burst and cburst as well as quantum configurable for ingress and egress in the GUI
+# TC rules
+
+egress() {
+
+CEIL=${UPLINK}
+PRIO_RATE=`expr $CEIL / 3` # Ceiling for priority
+BE_RATE=`expr $CEIL / 6`   # Min for best effort
+BK_RATE=`expr $CEIL / 6`   # Min for background
+BE_CEIL=`expr $CEIL - 16`  # A little slop at the top
+
+LQ="quantum `get_mtu $IFACE $CEIL`"
+HTB_BURSTS="burst ${MYBURST} cburst ${MYBURST}"
+
+$TC qdisc del dev $IFACE root 2> /dev/null
+$TC qdisc add dev $IFACE root handle 1: `get_stab_string` htb default 12
+$TC class add dev $IFACE parent 1: classid 1:1 htb $LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string`
+$TC class add dev $IFACE parent 1:1 classid 1:10 htb $LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string`
+$TC class add dev $IFACE parent 1:1 classid 1:11 htb $LQ ${HTB_BURSTS} rate 128kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string`
+$TC class add dev $IFACE parent 1:1 classid 1:12 htb $LQ ${HTB_BURSTS} rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string`
+$TC class add dev $IFACE parent 1:1 classid 1:13 htb $LQ ${HTB_BURSTS} rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string`
+
+$TC qdisc add dev $IFACE parent 1:11 handle 110: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${PRIO_RATE}` ${EQDISC_OPTS}
+$TC qdisc add dev $IFACE parent 1:12 handle 120: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BE_RATE}` ${EQDISC_OPTS}
+$TC qdisc add dev $IFACE parent 1:13 handle 130: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS}
+
+
+#sm: for testing we need a band to collect PPPOEd packets
+$TC class add dev $IFACE parent 1:1 classid 1:14 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string`
+$TC qdisc add dev $IFACE parent 1:14 handle 140: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS}
+
+
+# Need a catchall rule (should also match VLANs and PPPoE packets)
+
+$TC filter add dev $IFACE parent 1:0 protocol all prio 999 u32 \
+        match ip protocol 0 0x00 flowid 1:12
+
+# FIXME should probably change the filter here to do pre-nat
+
+$TC filter add dev $IFACE parent 1:0 protocol ip prio 1 handle 1 fw classid 1:11
+$TC filter add dev $IFACE parent 1:0 protocol ip prio 2 handle 2 fw classid 1:12
+$TC filter add dev $IFACE parent 1:0 protocol ip prio 3 handle 3 fw classid 1:13
+
+# ipv6 support. Note that the handle indicates the fw mark bucket that is looked for
+
+$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 4 handle 1 fw classid 1:11
+$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 5 handle 2 fw classid 1:12
+$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 6 handle 3 fw classid 1:13
+
+# Arp traffic
+
+$TC filter add dev $IFACE parent 1:0 protocol arp prio 7 handle 1 fw classid 1:11
+
+# ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods
+# better instead
+
+$TC filter add dev $IFACE parent 1:0 protocol ip prio 8 \
+	 u32 match ip protocol 1 0xff flowid 1:13
+
+$TC filter add dev $IFACE parent 1:0 protocol ipv6 prio 9 \
+	 u32 match ip protocol 1 0xff flowid 1:13
+
+
+
+
+# PPPoE encapsulated packets traversing the router (e.g.: the router does PPPoE termination but we shape
+# on the underlaying ethernet interface instead of the pppoe device)
+
+PPPOE_SESSION_ETHERTYPE="0x8864"
+PPPOE_DISCOVERY_ETHERTYPE="0x8863"
+PPP_PROTO_IP4="0x0021"
+PPP_PROTO_IP6="0x0057"
+ARP_PROTO_IP4="0x0806"
+
+# NOTE it seems prio can not be reused?
+#$TC filter add dev $IFACE protocol 0x8863 parent 1:0 prio 1 u32 flowid 1:14
+# PPPoE can be selected for by ether_type, the encapsulated IP version from the PPP (0x0021 IPv4, 0x0057 IPv6)
+#U32_PREFIX="$TC filter add dev $IFACE" parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE}"
+
+#BE: 1:12 is the default anyway, but this will catch all non marked packets
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 400 u32 \
+#	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+#	match u8 0x00 0xfb at 9 \
+#	flowid 1:12
+
+#AF42
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 401 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0x90 0xfc at 9 \
+	flowid 1:11
+#EF
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 402 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0xb8 0xfc at 9 \
+	flowid 1:11
+#CS1
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 403 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0x20 0xf0 at 9 \
+	flowid 1:13
+#IMM
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 404 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0x10 0xf0 at 9 \
+	flowid 1:11
+#CS3
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 405 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0xc0 0xf0 at 9 \
+	flowid 1:11
+#CS6
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 406 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0xe0 0xf0 at 9 \
+	flowid 1:11
+
+
+## Arp traffic
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 409 u32 \
+#	match u16 ${ARP_PROTO_IP4} 0xffff at 6 \
+#	flowid 1:14
+
+# ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods
+# better instead; sm: really only deprio echo requestst and echo replies instead?
+# ECHO request, the rest stays in best effort
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 410 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0x01 0xff at 17 \
+	match u8 0x08 0xff at 28 \
+	flowid 1:13
+# ECHO reply
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 410 u32 \
+	match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
+	match u8 0x01 0xff at 17 \
+	match u8 0x00 0xff at 28 \
+	flowid 1:13
+
+## ICMPv6 133-137 (NDP) is equivalent to IPv4 ARP, so only push echo request and reply into the bulk class
+## 133
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
+#	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+#	match u8 0x85 0xff at 48 \
+#	match u8 0x3a 0xff at 14 \
+#	flowid 1:14
+## 134
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
+#	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+#	match u8 0x86 0xff at 48 \
+#	match u8 0x3a 0xff at 14 \
+#	flowid 1:14
+## 135
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
+#	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+#	match u8 0x87 0xff at 48 \
+#	match u8 0x3a 0xff at 14 \
+#	flowid 1:14
+## 136
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
+#	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+#	match u8 0x88 0xff at 48 \
+#	match u8 0x3a 0xff at 14 \
+#	flowid 1:14
+## 137
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
+#	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+#	match u8 0x89 0xff at 48 \
+#	match u8 0x3a 0xff at 14 \
+#	flowid 1:14
+
+# ICMPv6 echo request
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u8 0x3a 0xff at 14 \
+	match u8 0x80 0xff at 48 \
+	flowid 1:13
+# ICMPv6 echo reply
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u8 0x3a 0xff at 14 \
+	match u8 0x81 0xff at 48 \
+	flowid 1:13
+
+
+
+
+#IPV6
+#BE: careful, will override ICMP
+#$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 600 u32 \
+#	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+#	match u16 0x0000 0x0fb0 at 8 \
+#	flowid 1:12
+#AF42
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 601 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u16 0x0900 0x0fc0 at 8 \
+	flowid 1:11
+#EF
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 602 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u16 0x0b80 0x0fc0 at 8 \
+	flowid 1:11
+#CS1
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 603 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u16 0x0200 0x0fc0 at 8 \
+	flowid 1:13
+#IMM
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 604 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u16 0x0100 0x0fc0 at 8 \
+	flowid 1:11
+#CS3
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 605 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u16 0x0c00 0x0fc0 at 8 \
+	flowid 1:11
+#CS6
+$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 606 u32 \
+	match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
+	match u16 0x0e00 0x0fc0 at 8 \
+	flowid 1:11
+
+
+
+
+#diffserv $IFACE
+
+}
+
+ingress() {
+
+CEIL=$DOWNLINK
+PRIO_RATE=`expr $CEIL / 3` # Ceiling for prioirty
+BE_RATE=`expr $CEIL / 6`   # Min for best effort
+BK_RATE=`expr $CEIL / 6`   # Min for background
+BE_CEIL=`expr $CEIL - 16`  # A little slop at the top
+
+LQ="quantum `get_mtu $IFACE $CEIL`"
+HTB_BURSTS="burst ${MYBURST} cburst ${MYBURST}"
+
+$TC qdisc del dev $IFACE handle ffff: ingress 2> /dev/null
+$TC qdisc add dev $IFACE handle ffff: ingress
+
+$TC qdisc del dev $DEV root  2> /dev/null
+
+if [ "$SQUASH_INGRESS" = "1" ]
+then
+sqm_logger "Do not perform DSCP based filtering on ingress. (1-tier classification)"
+# Revert to no dscp based filtering
+$TC qdisc del dev $DEV root 2>/dev/null
+$TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 10
+$TC class add dev $DEV parent 1: classid 1:1 htb $LQ ${HTB_BURSTS} rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit `get_htb_adsll_string`
+$TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ ${HTB_BURSTS} rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit prio 0 `get_htb_adsll_string`
+$TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS}
+
+else
+sqm_logger "Perform DSCP based filtering on ingress. (3-tier classification)"
+$TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 12
+$TC class add dev $DEV parent 1: classid 1:1 htb $LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string`
+$TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string`
+$TC class add dev $DEV parent 1:1 classid 1:11 htb $LQ ${HTB_BURSTS} rate 32kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string`
+$TC class add dev $DEV parent 1:1 classid 1:12 htb $LQ ${HTB_BURSTS} rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string`
+$TC class add dev $DEV parent 1:1 classid 1:13 htb $LQ ${HTB_BURSTS} rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string`
+
+# I'd prefer to use a pre-nat filter but that causes permutation...
+
+$TC qdisc add dev $DEV parent 1:11 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 500` `get_flows ${PRIO_RATE}` ${IQDISC_OPTS}
+$TC qdisc add dev $DEV parent 1:12 handle 120: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 1500` `get_flows ${BE_RATE}` ${IQDISC_OPTS}
+$TC qdisc add dev $DEV parent 1:13 handle 130: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS}
+
+#sm: for PPPoE packet testing
+$TC class add dev $DEV parent 1:1 classid 1:14 htb $LQ rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string`
+$TC qdisc add dev $DEV parent 1:14 handle 140: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS}
+
+
+
+#diffserv $DEV
+diffserv_pppoe $DEV
+
+fi
+
+ifconfig $DEV up
+
+# redirect all IP packets arriving in $IFACE to ifb0
+
+$TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+  match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+
+}
+
+do_modules
+ipt_setup
+
+if [ "$UPLINK" -ne 0 ];
+then
+	egress
+	sqm_logger "egress shaping activated"
+else
+	sqm_logger "egress shaping deactivated"
+	tc qdisc del dev $IFACE root 2> /dev/null
+fi
+if [ "$DOWNLINK" -ne 0 ];
+then
+	ingress
+	sqm_logger "ingress shaping activated"
+else
+	sqm_logger "ingress shaping deactivated"
+	tc qdisc del dev $DEV root 2> /dev/null
+	tc qdisc del dev $IFACE ingress 2> /dev/null
+fi
+
+
+
+# References:
+# This alternate shaper attempts to go for 1/u performance in a clever way
+# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD
+
+# Comments
+# This does the right thing with ipv6 traffic.
+# It also tries to leverage diffserv to some sane extent. In particular,
+# the 'priority' queue is limited to 33% of the total, so EF, and IMM traffic
+# cannot starve other types. The rfc suggested 30%. 30% is probably
+# a lot in today's world.
+
+# Flaws
+# Many!

+ 2 - 0
src/simple_pppoe.qos.help

@@ -0,0 +1,2 @@
+BW-limited three-tier prioritisation scheme with fq_codel on each queue. Temporary version to implement shaping
+of pass through PPPOE encapsulated packets.

+ 98 - 0
src/simplest.qos

@@ -0,0 +1,98 @@
+#!/bin/sh
+# Cero3 Simple Shaper
+# A 1 bin tc_codel and ipv6 enabled shaping script for
+# ethernet gateways. This is nearly the simplest possible
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#       Copyright (C) 2012-5 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+
+. /usr/lib/sqm/functions.sh
+sqm_logger "Starting simplest.qos"
+
+egress() {
+
+LQ="quantum `get_mtu $IFACE ${UPLINK}`"
+
+$TC qdisc del dev $IFACE root 2>/dev/null
+case $QDISC in
+	cake*)
+	    $TC qdisc add dev $IFACE root `get_stab_string` cake bandwidth ${UPLINK}kbit besteffort `get_cake_lla_string` ${EQDISC_OPTS}
+	    ;;
+	*)
+	    $TC qdisc add dev $IFACE root handle 1: `get_stab_string` htb default 10
+	    $TC class add dev $IFACE parent 1: classid 1:1 htb $LQ rate ${UPLINK}kbit ceil ${UPLINK}kbit `get_htb_adsll_string`
+	    $TC class add dev $IFACE parent 1:1 classid 1:10 htb $LQ rate ${UPLINK}kbit ceil ${UPLINK}kbit prio 0 `get_htb_adsll_string`
+	    $TC qdisc add dev $IFACE parent 1:10 handle 110: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_flows ${UPLINK}` ${EQDISC_OPTS}
+	    ;;
+esac
+
+}
+
+
+ingress() {
+sqm_logger "ingress"
+$TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null
+$TC qdisc add dev $IFACE handle ffff: ingress
+
+LQ="quantum `get_mtu $IFACE ${DOWNLINK}`"
+
+$TC qdisc del dev $DEV root 2>/dev/null
+case $QDISC in
+	cake*)
+	    $TC qdisc add dev $DEV root `get_stab_string` cake bandwidth ${DOWNLINK}kbit besteffort `get_cake_lla_string` ${IQDISC_OPTS}
+	    ;;
+	*)
+	    $TC qdisc add dev $DEV root handle 1: `get_stab_string` htb default 10
+	    $TC class add dev $DEV parent 1: classid 1:1 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit `get_htb_adsll_string`
+	    $TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit prio 0 `get_htb_adsll_string`
+
+	    # FIXME: I'd prefer to use a pre-nat filter but we need to detect if nat is on this interface
+	    # AND we need to permute by a random number which we can't do from userspace filters
+
+	    # Most high rate flows are REALLY close. This stomps on those harder, but hurts on high rate long distance
+	    #$TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC limit $LIMIT $ECN interval 20ms target 3ms `get_flows ${DOWNLINK}`
+	    $TC qdisc add dev $DEV parent 1:10 handle 110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS}
+	    ;;
+esac
+
+ifconfig $DEV up
+
+# redirect all IP packets arriving in $IFACE to ifb0
+
+$TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+  match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+
+}
+
+do_modules
+
+if [ "$UPLINK" -ne 0 ];
+then
+	egress
+	sqm_logger "egress shaping activated"
+else
+	sqm_logger "egress shaping deactivated"
+	tc qdisc del dev $IFACE root 2> /dev/null
+fi
+if [ "$DOWNLINK" -ne 0 ];
+then
+	ingress
+	sqm_logger "ingress shaping activated"
+else
+	sqm_logger "ingress shaping deactivated"
+	tc qdisc del dev $DEV root 2> /dev/null
+	tc qdisc del dev $IFACE ingress 2> /dev/null
+fi
+
+
+# References:
+# This alternate shaper attempts to go for 1/u performance in a clever way
+# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD
+
+# Comments
+# This does the right thing with ipv6 traffic.
+# Flaws
+# Many!

+ 1 - 0
src/simplest.qos.help

@@ -0,0 +1 @@
+Simplest possible configuration: HTB rate limiter with your qdisc attached.

+ 42 - 0
src/stop.sh

@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#       Copyright (C) 2012-4 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+
+# allow passing in the IFACE as first command line argument
+[ ! -z ${1} ] && IFACE=${1}
+# now IFACE is defined so we can source functions.sh without creating a spurious ifb4ge00
+. /usr/lib/sqm/functions.sh
+# sqm_logger is defined in functions.sh...
+sqm_logger "${0}: Stopping ${IFACE}"
+
+# make sure to only delete the ifb associated with the current interface
+CUR_IFB=$( get_ifb_associated_with_if ${IFACE} )
+
+sqm_stop() {
+	tc qdisc del dev $IFACE ingress 2> /dev/null
+	tc qdisc del dev $IFACE root 2> /dev/null
+	[ ! -z "$CUR_IFB" ] && tc qdisc del dev $CUR_IFB root 2> /dev/null
+        [ ! -z "$CUR_IFB" ] && sqm_logger "${0}: ${CUR_IFB} shaper deleted"
+}
+
+ipt_stop() {
+	[ ! -z "$CUR_IFB" ] && ipt -t mangle -D POSTROUTING -o $CUR_IFB -m mark --mark 0x00 -g QOS_MARK_${IFACE}
+	ipt -t mangle -D POSTROUTING -o $IFACE -m mark --mark 0x00 -g QOS_MARK_${IFACE}
+	ipt -t mangle -D PREROUTING -i vtun+ -p tcp -j MARK --set-mark 0x2
+	ipt -t mangle -D OUTPUT -p udp -m multiport --ports 123,53 -j DSCP --set-dscp-class AF42
+	ipt -t mangle -F QOS_MARK_${IFACE}
+	ipt -t mangle -X QOS_MARK_${IFACE}
+}
+
+
+sqm_stop
+ipt_stop
+[ ! -z "$CUR_IFB" ] && ifconfig ${CUR_IFB} down
+[ ! -z "$CUR_IFB" ] && ip link delete ${CUR_IFB} type ifb
+[ ! -z "$CUR_IFB" ] && sqm_logger "${0}: ${CUR_IFB} interface deleted"
+
+exit 0