#!/bin/sh 
#
# QoS setup for OpenWRT
#
# By Robert W. Brewer rwb123 at gmail dot com
#
# This script is placed in the public domain.
# 
# set up traffic control stuff
# use the heirarchical token bucket filter (HTB)
# with 3 priorities corresponding to low-latency, normal, and bulk
# traffic.  We mainly shape the outbound traffic.  Inbound traffic
# is harder to shape, but we try that to a minor extent also.
# We do our bandwidth classification based on the TOS flags in the 
# IP header.  The nice thing about this is that our TOS settings
# will stay with the packets as they go out onto the internet,
# and may be helpful somewhere along the way.  Probably not,
# but we can hope.
# For most packets, we assume that the creating app
# sets the TOS to something useful.  If it is at 
# the default "Normal-Service" we will set the TOS ourselves.
# For UDP and ICMP, we consider them low-latency.  Short
# TCP packets (less than 128 bytes) are low-latency.  This 
# covers TCP ack packets as well as most interactive stuff.
# Bulk TCP will generally try to optimize itself by sending
# larger packets up to 1500 bytes long.  Currently we just leave
# longer TCP packets as "Normal-Service" and hope that 
# the originating app will set it as bulk if needed.
# ssh is a complicating factor, since other bulk traffic is often
# forwarded over it.  Supposedly ssh doesn't handle this well, and
# still marks the packets as minimize-delay.  We use the connrate
# module to check the connection speed of traffic running to or from
# the ssh port, and if its current bandwidth usage exceeds a threshold,
# we re-mark the TOS as maximize-throughput.  Pretty slick, because
# when it is sending at a slower average rate, the packets will
# stay at minimize-delay.
#
# to check the status of the qos stuff:
#  iptables -t mangle -L
#  tc -s qdisc show dev eth1
#  tc -s class show dev eth1

. /etc/functions.sh

# interface to shape
#IFACE=$WAN
export IFACE=$(nvram get wan_ifname)
# uplink bandwidth
# specified in kbits (about 90% of actual max uplink rate)
UP_RATE=320
DOWN_RATE=4000

#insmod ipt_TOS
#insmod ipt_tos
#insmod ipt_length
#insmod sch_prio
#insmod sch_htb
#insmod sch_sfq
#insmod sch_ingress
#insmod cls_tcindex
#insmod cls_fw
#insmod cls_route
#insmod cls_u32


# clear traffic control to known state
tc qdisc del dev $IFACE root

# set up HTB with 3 bands
# class 1:20 is the default class for unclassified traffic
# the ceil command in each band allows it to suck up the entire bandwidth
# if any bandwidth is not being used by the other classes
tc qdisc add dev $IFACE root handle 1: htb default 20
tc class add dev $IFACE parent 1: classid 1:1 htb rate ${UP_RATE}kbit
# 70% bandwidth to band 0 (interactive, low-latency)
tc class add dev $IFACE parent 1:1 classid 1:10 htb rate 224kbit ceil ${UP_RATE}kbit prio 0
# 20% bandwidth to band 1 (normal stuff, web browsing, etc.)
tc class add dev $IFACE parent 1:1 classid 1:20 htb rate 64kbit ceil ${UP_RATE}kbit prio 1
# 10% bandwidth to band 2 (low priority, bulk stuff, file transfers, etc.)
tc class add dev $IFACE parent 1:1 classid 1:30 htb rate 32kbit ceil ${UP_RATE}kbit prio 2

# now use stochastic fairness queuing everywhere
tc qdisc add dev $IFACE parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev $IFACE parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev $IFACE parent 1:30 handle 30: sfq perturb 10

# check TOS and set that to something special
iptables -t mangle -N CHKTOS
# TOS already set, leave alone
iptables -t mangle -A CHKTOS -m tos --tos ! Normal-Service -j RETURN
# udp gets high priority
iptables -t mangle -A CHKTOS -p udp  -j TOS --set-tos Minimize-Delay
# small tcp packets get high priority
iptables -t mangle -A CHKTOS -p tcp -m length --length :128 -j TOS --set-tos Minimize-Delay
# ping gets high priority
iptables -t mangle -A CHKTOS -p icmp -j TOS --set-tos Minimize-Delay

# set up use of CHKTOS table, like a subroutine call
iptables -t mangle -A POSTROUTING -o $IFACE -j CHKTOS
# for fast-transferring ssh connections, let's change their TOS to 
# something else
#iptables -t mangle -A POSTROUTING -o $IFACE -p tcp --sport 22 -m tos --tos Minimize-Delay -m connrate --connrate 20000:inf -j TOS --set-tos Maximize-Throughput
#iptables -t mangle -A POSTROUTING -o $IFACE -p tcp --dport 22 -m tos --tos Minimize-Delay -m connrate --connrate 20000:inf -j TOS --set-tos Maximize-Throughput

# now classify our packets into the HTB bands based on the TOS flags
# The "handle" field in the tc line is the mark applied by iptables
iptables -t mangle -A POSTROUTING -o $IFACE -m tos --tos Minimize-Delay -j MARK --set-mark 1
tc filter add dev $IFACE protocol ip parent 1: prio 1 handle 1 fw classid 1:10

iptables -t mangle -A POSTROUTING -o $IFACE -m tos --tos Maximize-Reliability -j MARK --set-mark 2
iptables -t mangle -A POSTROUTING -o $IFACE -m tos --tos Normal-Service -j MARK --set-mark 2
tc filter add dev $IFACE protocol ip parent 1: prio 1 handle 2 fw classid 1:20

iptables -t mangle -A POSTROUTING -o $IFACE -m tos --tos Maximize-Throughput -j MARK --set-mark 3
iptables -t mangle -A POSTROUTING -o $IFACE -m tos --tos Minimize-Cost -j MARK --set-mark 3
tc filter add dev $IFACE protocol ip parent 1: prio 1 handle 3 fw classid 1:30


  
  
 
  
# police incoming traffic
tc qdisc del dev $IFACE ingress
tc qdisc add dev $IFACE ingress

# rate limit to 4 mbits... any TCP traffic over that limit is dropped
# on the floor.  Note that UDP traffic will not be dropped.
# This helps ensure that there is at least a little bandwidth left over for
# my VoIP calls.  And when congestion occurs, TCP will have to back off
# instead of UDP.
tc filter add dev $IFACE parent ffff: protocol ip prio 50 u32 match ip protocol 6 0xff police rate ${DOWN_RATE}kbit burst 150k drop flowid :1

