#!/bin/sh

# Debug Option
# set -x

# Enable to use filters
#CLASSIFIER='filter'


# Define some constants
TC=/sbin/tc

EXT_IF='eth0'
INT_IF='eth1'

# Maximums in Kbit/s
MAX_UP='220'      # less than the actual maximum
MAX_DOWN='510'

tc_start() {

   ## root qdiscs ###

   EXT_ROOT_Q='1:'
   INT_ROOT_Q='2:'
   # the r2q formula: min speed (in KB) / MTU > r2q but as large as possible
   # e.g. for upload 5632 B / 1500 B > 3
   $TC qdisc add dev $EXT_IF root handle $EXT_ROOT_Q htb r2q 3 default 15
   $TC qdisc add dev $INT_IF root handle $INT_ROOT_Q htb r2q 8 default 15

   ### root classes ###
   
   EXT_ROOT_C='1:10'
   INT_ROOT_C='2:10'
   $TC class add dev $EXT_IF parent $EXT_ROOT_Q classid $EXT_ROOT_C \
      htb rate ${MAX_UP}Kbit ceil ${MAX_UP}Kbit
   $TC class add dev $INT_IF parent $INT_ROOT_Q classid $INT_ROOT_C \
      htb rate ${MAX_DOWN}Kbit ceil ${MAX_DOWN}Kbit
   
   ### upload and download classes ###
   
   # User 1
   $TC class add dev $EXT_IF parent $EXT_ROOT_C classid ${EXT_ROOT_Q}11 \
      htb rate $(($MAX_UP/5))Kbit ceil ${MAX_UP}Kbit
   $TC class add dev $INT_IF parent $INT_ROOT_C classid ${INT_ROOT_Q}11 \
      htb rate $((MAX_DOWN/5))Kbit ceil ${MAX_DOWN}Kbit
   
   # User 2
   $TC class add dev $EXT_IF parent $EXT_ROOT_C classid ${EXT_ROOT_Q}12 \
      htb rate $(($MAX_UP/5))Kbit ceil ${MAX_UP}Kbit
   $TC class add dev $INT_IF parent $INT_ROOT_C classid ${INT_ROOT_Q}12 \
      htb rate $((MAX_DOWN/5))Kbit ceil ${MAX_DOWN}Kbit
   
   # User 3
   $TC class add dev $EXT_IF parent $EXT_ROOT_C classid ${EXT_ROOT_Q}13 \
      htb rate $(($MAX_UP/5))Kbit ceil ${MAX_UP}Kbit
   $TC class add dev $INT_IF parent $INT_ROOT_C classid ${INT_ROOT_Q}13 \
      htb rate $((MAX_DOWN/5))Kbit ceil ${MAX_DOWN}Kbit
   
   # User 4
   $TC class add dev $EXT_IF parent $EXT_ROOT_C classid ${EXT_ROOT_Q}14 \
      htb rate $(($MAX_UP/5))Kbit ceil ${MAX_UP}Kbit
   $TC class add dev $INT_IF parent $INT_ROOT_C classid ${INT_ROOT_Q}14 \
      htb rate $((MAX_DOWN/5))Kbit ceil ${MAX_DOWN}Kbit
   
   # Default, for everyting else
   $TC class add dev $EXT_IF parent $EXT_ROOT_C classid ${EXT_ROOT_Q}15 \
      htb rate $(($MAX_UP/5))Kbit ceil ${MAX_UP}Kbit
   $TC class add dev $INT_IF parent $INT_ROOT_C classid ${INT_ROOT_Q}15 \
      htb rate $((MAX_DOWN/5))Kbit ceil ${MAX_DOWN}Kbit
   
   # SFQ (shared fair queueing) within each class
   
   # User 1
   $TC qdisc add dev $EXT_IF parent ${EXT_ROOT_Q}11 handle 111: sfq perturb 10
   $TC qdisc add dev $INT_IF parent ${INT_ROOT_Q}11 handle 211: sfq perturb 10
   
   # User 2
   $TC qdisc add dev $EXT_IF parent ${EXT_ROOT_Q}12 handle 112: sfq perturb 10
   $TC qdisc add dev $INT_IF parent ${INT_ROOT_Q}12 handle 212: sfq perturb 10
   
   # User 3
   $TC qdisc add dev $EXT_IF parent ${EXT_ROOT_Q}13 handle 113: sfq perturb 10
   $TC qdisc add dev $INT_IF parent ${INT_ROOT_Q}13 handle 213: sfq perturb 10
   
   # User 4
   $TC qdisc add dev $EXT_IF parent ${EXT_ROOT_Q}14 handle 114: sfq perturb 10
   $TC qdisc add dev $INT_IF parent ${INT_ROOT_Q}14 handle 214: sfq perturb 10
   
   # Default, for everyone else
   $TC qdisc add dev $EXT_IF parent ${EXT_ROOT_Q}15 handle 115: sfq perturb 10
   $TC qdisc add dev $INT_IF parent ${INT_ROOT_Q}15 handle 215: sfq perturb 10
   
   # Add filters, if using them. Handles must match firewall marks on packets
   if [[ -n $CLASSIFIER ]]; then
      # User 1
      $TC filter add dev $EXT_IF parent $EXT_ROOT_Q \
         protocol ip prio 1 handle 111 fw classid ${EXT_ROOT_Q}11
      $TC filter add dev $INT_IF parent $INT_ROOT_Q \
         protocol ip prio 1 handle 211 fw classid ${INT_ROOT_Q}11
   
      # User 2
      $TC filter add dev $EXT_IF parent $EXT_ROOT_Q \
         protocol ip prio 1 handle 112 fw classid ${EXT_ROOT_Q}12
      $TC filter add dev $INT_IF parent $INT_ROOT_Q \
         protocol ip prio 1 handle 212 fw classid ${INT_ROOT_Q}12
   
      # User 3
      $TC filter add dev $EXT_IF parent $EXT_ROOT_Q \
         protocol ip prio 1 handle 113 fw classid ${EXT_ROOT_Q}13
      $TC filter add dev $INT_IF parent $INT_ROOT_Q \
         protocol ip prio 1 handle 213 fw classid ${INT_ROOT_Q}13
   
      # User 4
      $TC filter add dev $EXT_IF parent $EXT_ROOT_Q \
         protocol ip prio 1 handle 114 fw classid ${EXT_ROOT_Q}14
      $TC filter add dev $INT_IF parent $INT_ROOT_Q \
         protocol ip prio 1 handle 214 fw classid ${INT_ROOT_Q}14
   fi

}

tc_stop() {

    $TC qdisc del dev $EXT_IF root
    $TC qdisc del dev $INT_IF root

}

tc_restart() {

    tc_stop
    sleep 1
    tc_start

}

tc_show() {

    echo ""
    echo "$EXT_IF:"
    $TC qdisc show dev $EXT_IF
    $TC class show dev $EXT_IF
    $TC filter show dev $EXT_IF
    echo ""
    tc -s qdisc ls dev $EXT_IF
    echo ""

    echo "$INT_IF:"
    $TC qdisc show dev $INT_IF
    $TC class show dev $INT_IF
    $TC filter show dev $INT_IF
    echo ""
    tc -s qdisc ls dev $INT_IF
    echo ""
}

case "$1" in

  start)

    echo -n "Starting bandwidth shaping: "
    tc_start
    echo "done"
    ;;

  stop)

    echo -n "Stopping bandwidth shaping: "
    tc_stop
    echo "done"
    ;;

  restart)

    echo -n "Restarting bandwidth shaping: "
    tc_restart
    echo "done"
    ;;

  show)
   
    tc_show
    ;;

  *)

    echo "Usage: /etc/init.d/bandwidth.sh {start|stop|restart|show}"
    ;;

esac

exit 0
