#! /bin/sh
# Written by Christian Parpart <cparpart@surakware.net>
# I am using Linux/2.4.18 (patched), iptables/1.2.6a, patched tc for htb

# ===========================================================================
# NETWORK CONFIGURATION

#        768Kbit
#         ppp0____
#        /    \   \
#       /      \   \_C 1kbit (default)
#      A        B
#   512Kbit  256Kbit(bounded)

devINET=ppp0
devINET_in=768Kbit  # unused, yet (still the issue below !)
devINET_out=128Kbit

bwINET=768          # Kbit (ISSUE : but it is asyncronous, 768/128 !)

bwA=512             # incoming? big   (Games)
bwB=$[768 - 512]    # incoming? small (StdWeb: www,email,ftp)

bwC=96              # output big?
bwD=$[128-96]       # output small?

devLAN=eth1
devLAN_in=100Mbit
devLAN_out=100Mbit
netLAN=192.168.20.0/24

# ===========================================================================
# MARKS

m_Gaming=1          # HalfLife Mods
m_Web=2             # http, ftp, smtp, pop3

# ===========================================================================

IPTABLES=/usr/sbin/iptables
TC=/usr/sbin/tc

function init_tc() {
  echo "Initializing Traffic Control..."

  # deleting previous settings
  $TC qdisc del dev $devINET root &> /dev/null

  # attach cbq to device ($devINET) with handle 1: (at 1:0)
  $TC qdisc add dev $devINET root handle 1: htb default 99

  # create root class (1:1)
  $TC class add dev $devINET parent 1: classid 1:1 htb rate ${bwINET}Kbit

  # create class A (1:10) with SFQ
  $TC class add dev $devINET parent 1:1 classid 1:10 htb rate ${bwA}Kbit burst 15k
  $TC qdisc add dev $devINET parent 1:10 handle 10: sfq perturb 10

  # create class B (1:20) with SFQ
  $TC class add dev $devINET parent 1:1 classid 1:20 htb rate ${bwB}Kbit burst 15k
  $TC qdisc add dev $devINET parent 1:20 handle 20: sfq perturb 10

  # create PSEUDO class (1:99) with SFQ
  $TC class add dev $devINET parent 1:1 classid 1:99 htb rate 1Kbit burst 15k
  $TC qdisc add dev $devINET parent 1:99 handle 99: sfq perturb 10

  # -----------------------------------------------------------
  # initializing TC filters...
  $TC filter add dev $devINET \
      parent 1: protocol ip handle $m_Gaming fw classid 1:10

  $TC filter add dev $devINET \
      parent 1: protocol ip handle $m_Web fw classid 1:20

  return 0
}

function deinit_tc() {
  echo "Deinitializing Traffic Control..."
  $TC qdisc del dev $devINET root &> /dev/null
  return 0
}

function init_fwmarks() {
  echo "Initializing FireWall Marks..."

  $IPTABLES -t mangle -N tc.marks.incoming
  $IPTABLES -t mangle -N tc.marks.outgoing

  $IPTABLES -t mangle -A PREROUTING -j tc.marks.incoming
  $IPTABLES -t mangle -A POSTROUTING -j tc.marks.outgoing

  # mark Web traffic
  for service in http ftp smtp pop3 443 8080; do
    # mark sending packets (outgoing)
    $IPTABLES -t mangle -A tc.marks.outgoing -o $devINET \
              -p tcp --sport 1024: --dport $service \
              -j MARK --set-mark $m_Web

    # mark receiving packets (incoming)
    $IPTABLES -t mangle -A tc.marks.incoming -i $devINET \
              -p tcp --sport $service --dport 1024: \
              -j MARK --set-mark $m_Web
  done

  # mark Gaming traffic, HalfLife Mods
  $IPTABLES -t mangle -A tc.marks.outgoing -o $devINET \
            -p udp --sport 27005 --dport 27015:27055 \
            -j MARK --set-mark $m_Gaming

  $IPTABLES -t mangle -A tc.marks.incoming -i $devINET \
            -p udp --sport 27015:27055 --dport 27005 \
            -j MARK --set-mark $m_Gaming

  return 0
}

function deinit_fwmarks() {
  echo "Deinitializing FireWall Marks..."

  $IPTABLES -t mangle -D PREROUTING  -j tc.marks.incoming
  $IPTABLES -t mangle -D POSTROUTING -j tc.marks.outgoing

  $IPTABLES -t mangle -F tc.marks.incoming
  $IPTABLES -t mangle -F tc.marks.outgoing

  $IPTABLES -t mangle -X tc.marks.incoming
  $IPTABLES -t mangle -X tc.marks.outgoing

  return 0
}

############################################################################

function tc_start() {
  init_tc;
  init_fwmarks;
}

function tc_stop() {
  deinit_fwmarks;
  deinit_tc;
}

function tc_status() {
  ( tc -s class show dev $devINET
    #tc filter show dev $devINET
  ) #| less
}

case "$1" in
  start)    tc_start ;;
  stop)     tc_stop ;;
  restart)  tc_stop && tc_start ;;
  status)   tc_status ;;
  *)        echo "$0: syntax error: unkown parameter $1"
            echo "usage: $0 (start|stop|restart|status)"
            exit 1 ;;
esac
