#!/bin/bash
# Copyright (C) 2007 Krzysztof Kozlowski
#   License: GNU General Public License version 2
#            http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# Ostatnia zmiana: 20.12.2007
#
# Traffic shaper. Zalozenia i ogolne dzialanie:
#  - ruch dzielimy na 4 klasy: HP, MP, LP i default (High/Medium/Low Priority)
#  - priorytet ma ruch interaktywny, np. SSH
#  - aby upload nie blokowal downloadu do MP pakujemy ACK-i od polaczen download (dziala!)
#  - LP grupuje "bulk traffic", ale ten ceniony, czyli HTTP/POP3 itp.
#  - do reszty /default/ wpada p2p, pasywny FTP i reszta
#
# Mozliwe do poprawki:
#  - wysoki download (np. HTTP) potrafi jednak obnizyc interaktywnosc SSH

WAN="eth0"
LAN="eth1"

# Czy kontrolowac lacze w nasza strone?
SHAPE_DO=1
# Liczenie limitow lacza (6144/512 kbit). Limity u nas musza byc troszke nizsze,
# aby to u nas odbywalo sie kolejkowanie, a nie po stronie modemu ADSL lub u providera.
FULL_DO_RATE_NUM=$(expr 6144 \* 98 / 100)	# 98% lacza
FULL_DO_RATE="${FULL_DO_RATE_NUM}kbit"
FULL_UP_RATE_NUM=$(expr 512 \* 95 / 100)	# 95% lacza
FULL_UP_RATE="${FULL_UP_RATE_NUM}kbit"
# Przekraczanie limitow - nieistotne:
UP_BURST="10k"
DO_BURST="16k"

# Ruch priorytetowy, np.: SSH, DNS:
HP_UP_RATE_NUM=$(expr ${FULL_UP_RATE_NUM} \* 55 / 100)
HP_UP_RATE="${HP_UP_RATE_NUM}kbit"
HP_UP_CEIL=${FULL_UP_RATE}
HP_QUANTUM="6400"
HP_PRIO=1
HP_CLASS=10
# Ruch srednio-uprzywilejowany:
MP_UP_RATE_NUM=$(expr ${FULL_UP_RATE_NUM} \* 30 / 100)
MP_UP_RATE="${MP_UP_RATE_NUM}kbit"
MP_UP_CEIL=${FULL_UP_RATE}
MP_QUANTUM="3200"
MP_PRIO=2
MP_CLASS=11
# Niski priorytet - typowy ruch:
LP_UP_RATE_NUM=$(expr ${FULL_UP_RATE_NUM} \* 10 / 100)
LP_UP_RATE="${LP_UP_RATE_NUM}kbit"
LP_UP_CEIL=${FULL_UP_RATE}
LP_QUANTUM="1600"
LP_PRIO=3
LP_CLASS=12
# Ruch domyslny, czyli wszystko co nie wpada wczesniej:
DEF_UP_RATE_NUM=$(expr ${FULL_UP_RATE_NUM} \* 5 / 100)
DEF_UP_RATE="${DEF_UP_RATE_NUM}kbit"
# Nie chcemy dac tu pelnego pasma do wykorzystania (ceil), bo za duze ryzyko, ze wieloma
# polaczeniami FTP czy P2P lacze zostanie zapchane. Wobec tego ceil ustawiamy na:
#DEF_UP_CEIL=${FULL_UP_RATE}
DEF_UP_CEIL="$(expr ${FULL_UP_RATE_NUM} \* 60 / 100)kbit"
DEF_QUANTUM="1600"
DEF_PRIO=4
DEF_CLASS=20
# Wszystko sie powinno sumowac do naszego lacza FULL_UP_RATE_NUM:
SUM_RATE_NUM="$(expr ${HP_UP_RATE_NUM} + ${MP_UP_RATE_NUM} + ${LP_UP_RATE_NUM} + ${DEF_UP_RATE_NUM})"

echo "up-link:	${FULL_UP_RATE}"
if [ ${SHAPE_DO} -eq 1 ]; then
  echo "down-link:	${FULL_DO_RATE}"
fi
echo "HP up:		${HP_UP_RATE} (ceil: ${HP_UP_CEIL})"
echo "MP up:		${MP_UP_RATE} (ceil: ${MP_UP_CEIL})"
echo "LP up:		${LP_UP_RATE} (ceil: ${LP_UP_CEIL})"
echo "DEF up:		${DEF_UP_RATE} (ceil: ${DEF_UP_CEIL})"
echo "sum up:		${SUM_RATE_NUM}"
if [ ${SUM_RATE_NUM} -gt ${FULL_UP_RATE_NUM} ]; then
  echo "BLAD: suma transferow dla poszczegolnych podklas jest > niz lacze!"
fi

# clear
tc qdisc del dev ${WAN} root 2> /dev/null
tc qdisc del dev ${WAN} ingress 2> /dev/null

# up-link
tc qdisc add dev ${WAN} root handle 1: htb default ${DEF_CLASS} r2q 1
tc class add dev ${WAN} parent 1: classid 1:1 htb rate ${FULL_UP_RATE} burst ${UP_BURST}
# Dolaczamy do glownego wezla HTB klasy:
tc class add dev ${WAN} parent 1:1 classid 1:${DEF_CLASS} htb rate ${DEF_UP_RATE} ceil ${DEF_UP_CEIL} prio ${DEF_PRIO} quantum ${DEF_QUANTUM}
tc class add dev ${WAN} parent 1:1 classid 1:${HP_CLASS} htb rate ${HP_UP_RATE} ceil ${HP_UP_CEIL} prio ${HP_PRIO} quantum ${HP_QUANTUM}
tc class add dev ${WAN} parent 1:1 classid 1:${MP_CLASS} htb rate ${MP_UP_RATE} ceil ${MP_UP_CEIL} prio ${MP_PRIO} quantum ${MP_QUANTUM}
tc class add dev ${WAN} parent 1:1 classid 1:${LP_CLASS} htb rate ${LP_UP_RATE} ceil ${LP_UP_CEIL} prio ${LP_PRIO} quantum ${LP_QUANTUM}
# Kazda klasa ma SFQ dla sprawiedliwego podzialu sesji:
tc qdisc add dev ${WAN} parent 1:${DEF_CLASS} sfq perturb 10
tc qdisc add dev ${WAN} parent 1:${HP_CLASS} sfq perturb 10
tc qdisc add dev ${WAN} parent 1:${MP_CLASS} sfq perturb 10
tc qdisc add dev ${WAN} parent 1:${LP_CLASS} sfq perturb 10
# Filtry, czyli przypisanie ruchu do poszczegolnych klas
# High priority - DNS,SSH:
tc filter add dev ${WAN} parent 1:0 protocol ip prio 1 u32 match ip sport 53 0xffff flowid 1:${HP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 1 u32 match ip dport 53 0xffff flowid 1:${HP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 1 u32 match ip sport 22 0xffff flowid 1:${HP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 1 u32 match ip dport 22 0xffff flowid 1:${HP_CLASS}
# Medium priority:
# ACK-i (z nawiazanych polaczen) z HTTP, HTTPS, POP3 i POP3S leca powyzej def, aby wysoki upload nie blokowal downloadu:
# ip protocol 6 = TCP
# u16 0x0000 0xffc0 at 2 = dlugosc calego pakietu <= 32+16+8+4+2+1=63 bajty
# match u8 0x10 0xff at 33 = header TCP, ustawiony tylko bit ACK
tc filter add dev ${WAN} parent 1: protocol ip prio 1 u32 \
  match ip dport 80 0xffff \
  match ip protocol 6 0xff \
  match u16 0x0000 0xffc0 at 2 \
  match u8 0x10 0xff at 33 \
  flowid 1:${MP_CLASS}
tc filter add dev ${WAN} parent 1: protocol ip prio 1 u32 \
  match ip dport 443 0xffff \
  match ip protocol 6 0xff \
  match u16 0x0000 0xffc0 at 2 \
  match u8 0x10 0xff at 33 \
  flowid 1:${MP_CLASS}
tc filter add dev ${WAN} parent 1: protocol ip prio 1 u32 \
  match ip dport 110 0xffff \
  match ip protocol 6 0xff \
  match u16 0x0000 0xffc0 at 2 \
  match u8 0x10 0xff at 33 \
  flowid 1:${MP_CLASS}
tc filter add dev ${WAN} parent 1: protocol ip prio 1 u32 \
  match ip dport 995 0xffff \
  match ip protocol 6 0xff \
  match u16 0x0000 0xffc0 at 2 \
  match u8 0x10 0xff at 33 \
  flowid 1:${MP_CLASS}
# Low priority - SMTP, SMTPS, POP3, POP3S, HTTP, HTTPS, FTP:
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 25 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 465 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 110 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 995 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 80 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 443 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip sport 21 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip sport 20 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 21 0xffff flowid 1:${LP_CLASS}
tc filter add dev ${WAN} parent 1:0 protocol ip prio 2 u32 match ip dport 20 0xffff flowid 1:${LP_CLASS}

# down-link
if [ ${SHAPE_DO} -eq 1 ]; then
  tc qdisc add dev ${WAN} handle ffff: ingress
  tc filter add dev ${WAN} parent ffff: protocol ip prio 50 u32 match ip src \
    0.0.0.0/0 police rate ${FULL_DO_RATE} burst ${DO_BURST} drop flowid :1
fi

