#!/bin/bash
#Latenzoptimierung für Internetgateways
#Martin Stern, 2010 Lizenz: GPL
PATH=/usr/sbin:/usr/bin:/sbin:/bin

#IPTables Bibliotheksverzeichnis gegebenen-
#falls für tc überschreiben.
#export IPTABLES_LIB_DIR=/usr/local/lib/...

# Hilfe ausgeben
if [ $# -eq 0 -o -$# -gt 3 ]; then
  echo -e "Usage:"
  echo -e "$0 [device]"
  echo -e "  Statistiken anzeigen\n"
  echo -e "$0 [device] clear"
  echo -e "  Trafficcontrol deaktivieren\n"
  echo -e "$0 [device] [downlink] [uplink]"
  echo -e "  Trafficcontrol einrichten."
  echo -e "  Downlink/Uplink in Kilobit/s,"
  exit 1
fi

# Statistic ausgeben
if [ $# -eq 1 ]; then
  echo -e "\nQDisc Statistik:"
  echo -e "-------------------------"
  tc -s qdisc ls dev $1
  echo -e "\nClass Statistik:"
  echo -e "-------------------------"
  tc -s class ls dev $1
  echo -e "\nFilter Statistik:"
  echo -e "-------------------------"
  tc -s filter ls dev $1
  echo -e "\nIngress Filter Statistik:"
  echo -e "-------------------------"
  tc -s filter ls parent ffff: dev $1
  exit
fi

# Konfiguration zurücksetzten
if [ $# -eq 2 ]; then
  tc qdisc del dev $1 root
  tc qdisc del dev $1 ingress 
  exit
fi

###########################################
#Neue Konfiguration erstellen
DEV=$1
DOWN=$2
UP=$3

#Alte Konfiguration löschen
tc qdisc del dev $DEV root    &> /dev/null
tc qdisc del dev $DEV ingress &> /dev/null


###########################################
#Uplink konfigurieren (egress)

#Minimales Quantum festlegen
SmallQ=""; BigQ=""
z=$((${UP}/10*128/3))
if [ $z -lt 1500 ]; then
  SmallQ="quantum 1500"
fi
z=$((${UP}/10*128))
if [ $z -lt 1500 ]; then
  BigQ="quantum 1500"
fi

#Root HTB QDisc
tc qdisc add dev $DEV root handle 1: htb\
  default 11

#Root HTB Klasse
tcc="tc class add dev $DEV parent"
$tcc 1: classid 1:1 htb \
  rate ${UP}kbit ceil ${UP}kbit \
  linklay atm $BigQ

#HTB Klasse 1:10 "Low Latency"
$tcc 1:1 classid 1:10 htb \
  rate $(($UP/3))kbit ceil ${UP}kbit \
  prio 0 linklay atm $SmallQ

#HTB Klasse 1:11 "Bulk and Default"
$tcc 1:1 classid 1:11 htb \
  rate $(($UP/3))kbit ceil ${UP}kbit \
  prio 1 linklay atm $SmallQ

#HTB Klasse 1:12 "Second-Order Bandwidth"
$tcc 1:1 classid 1:12 htb \
  rate $(($UP/3))kbit ceil ${UP}kbit \
  prio 2 linklay atm $SmallQ

#Stochastic Fairness Qdisc für alle drei
tcq="tc qdisc add dev $DEV parent"
$tcq 1:10 handle 10: sfq perturb 10
$tcq 1:11 handle 11: sfq perturb 10
$tcq 1:12 handle 12: sfq perturb 10

#Pakete den HTB Klassen zuweisen.
#Der erste passende Filter gilt.
#ACK -> "Low Latency"
tcf="tc filter add dev $DEV parent"
$tcf 1: protocol ip prio 22 u32 \
  match ip protocol 6 0xff \
  match u8 0x05 0x0f at 0 \
  match u16 0x0000 0xffc0 at 2 \
  flowid 1:10

#TOS Minimum Delay -> "Low Latency"
$tcf 1: protocol ip prio 10 u32 \
  match ip tos 0x10 0xff flowid 1:10

#IPTables fwmark 10 -> "Low Latency"
$tcf 1: protocol ip prio 16 \
  handle 10 fw flowid 1:10

#IPTables fwmark 11 -> "Bulk and Default"
$tcf 1: protocol ip prio 18 \
  handle 11 fw flowid 1:11

#IPTables fwmark 12 -> "Second-Order B.w."
$tcf 1: protocol ip prio 20 \
  handle 12 fw flowid 1:12

#ICMP -> "Low Latency"
$tcf 1: protocol ip prio 24 u32 \
  match ip protocol 1 0xff flowid 1:10

#DEFAULT -> "Bulk and Default"
$tcf 1: protocol ip prio 50 u32 \
  match ip dst 0.0.0.0/0 flowid 1:11

###########################################
#Downlink konfigurieren (ingress)

#Ingress Root Qdisc anlegen.
tc qdisc add dev $DEV handle ffff: ingress

#Wenn über 98% Auslastung ...
$tcf ffff: protocol ip prio 110 u32 \
  match ip src 0.0.0.0/0 flowid :1 \
  police rate $((${DOWN}*98/100))kbit \
  burst 6k linklay atm \
  conform-exceed continue/ok

#... dann überzählige Pakete markieren.
$tcf ffff: protocol ip prio 111 u32 \
  match u32 0 0 flowid :1 \
  action ipt -j MARK --set-mark 20 \
  action continue > /dev/null

#Wenn mehr als 1 weiteres % Auslastung ...
$tcf ffff: protocol ip prio 120 u32 \
  match ip src 0.0.0.0/0 flowid :1 \
  police rate $((${DOWN}*1/100))kbit \
  burst 8k linklay atm \
  conform-exceed continue/ok

#... dann große Pakete verwerfen.
$tcf ffff: protocol ip prio 121 u32 \
  match u16 0x0400 0x0400 at 2 flowid :1 \
  police rate 0kbit burst 2k linklay atm \
  conform-exceed drop/drop
$tcf ffff: protocol ip prio 122 u32 \
  match u16 0x0800 0x0800 at 2 flowid :1 \
  police rate 0kbit burst 2k linklay atm \
  conform-exceed drop/drop

#Wenn noch 1% Auslastung => verwerfen.
$tcf ffff: protocol ip prio 130 u32 \
  match ip src 0.0.0.0/0 flowid :1\
  police rate $((${DOWN}*1/100))kbit \
  burst 10k linklay atm \
  conform-exceed drop/drop