#!/bin/bash
#
# PRIO_shaper - the simplest possible traffic shaper
#
# Written by Gustavo Homem (10/07/2006)
#
# A PRIO qdisc is used on both traffic directions, wrapped 
# on a HTB qdisc which is used only for rate limiting
#
# This approach is almost purely TOS based, except that
# minimal shaping is needed to avoid queuing on the modem

# configuration options - edit them according to your link

RATEUP=256
RATEDN=7000
EXTIF=ppp0
INTIF=eth0

# end of configuration options

# nothing below this point should need changes

function clear
{
DEV=$1

# Reset everything to a known state (cleared)
tc qdisc del dev $DEV root    2> /dev/null > /dev/null

echo "Shaping removed on $DEV."
}

function status
{
DEV=$1

echo "[qdisc]"
tc -s qdisc show dev $DEV
echo "[class]"
tc -s class show dev $DEV
echo "[filter]"
tc -s filter show dev $DEV

}

function PRIO_shape
{
###########################################################
# Shapes the traffic of an interface, limiting the rate
#
# Apart from the rate limit, the interface behaviour is 
# similar to the default one (pfifo_fast)
#
# This is the easiest way to set the rate on an interface!
#
# Arguments are DEV,RATE

DEV=$1
RATE=$2

tc qdisc add dev $DEV root handle 1: htb default 1
tc class add dev $DEV parent 1: classid 1:1 htb rate ${RATE}kbit ceil ${RATE}kbit
tc qdisc add dev $DEV parent 1:1 prio

echo "Shaping added to $DEV.  Rate: ${RATE}Kbit/sec."

}

function TOS_mark
{
###########################################################
# Sets up iptables to fill the TOS field of the packets
# according to their priorities
#

# packets for local machine and forwarding go via PREROUTING
# locally generated packets fo via OUTPUT

# packets are distributed to the 3 prio bands as follows:
#
# Minimize-Delay           -> prio 0 (ICMP, small SSH (interactive), small TCP)
# Maximize-Throughput      -> prio 2 (all packets larger than 1024 bytes)
# Normal-Service (default) -> prio 1 (all the remaining ones)
#
# All unmodified packets are Normal-Service packets

CMD="-A"

if [ "$1" = "stop" ]; then
	CMD="-D"
fi

# ICMP Minimize-Delay
iptables $CMD PREROUTING -t mangle -p icmp -j TOS --set-tos Minimize-Delay
iptables $CMD OUTPUT     -t mangle -p icmp -j TOS --set-tos Minimize-Delay

# SSH Minimize-Delay (except large ones which will be caught by the last rule)
iptables $CMD PREROUTING -t mangle -p tcp --dport ssh   -j TOS --set-tos Minimize-Delay
iptables $CMD OUTPUT     -t mangle -p tcp --dport ssh   -j TOS --set-tos Minimize-Delay

# Small TCP packets are probably ACKs, Minimize-Delay
iptables $CMD PREROUTING -t mangle -p tcp -m length --length :64  -j TOS --set-tos Minimize-Delay
iptables $CMD OUTPUT     -t mangle -p tcp -m length --length :64  -j TOS --set-tos Minimize-Delay

# All large packets are marked with Maximize-Throughput
iptables $CMD PREROUTING -t mangle -p tcp -m length --length 1024:  -j TOS --set-tos Maximize-Throughput
iptables $CMD OUTPUT     -t mangle -p tcp -m length --length 1024:  -j TOS --set-tos Maximize-Throughput

# All other packets are prio 1, that is Normal-Service

}

case "$1" in
  start)
	TOS_mark start
	PRIO_shape $INTIF $RATEDN
	PRIO_shape $EXTIF $RATEUP
	;;
  stop)
	clear $INTIF
	clear $EXTIF
	TOS_mark stop
        exit
	;;

  restart)
        $0 stop
        $0 start
        RETVAL=$?
        ;;
  status)
        status $INTIF
	status $EXTIF
        RETVAL=$?
        ;;

  *)
        echo -e "Usage: PRIO_shaper {start|stop|restart|status}\n"
        exit 1
esac

exit $RETVAL




