#!/bin/bash

IFACE="eth0"
RATE_TOTAL="970mbit"
RATE_HIGH="900mbit"
RATE_MEDIUM="50mbit"
RATE_LOW="8mbit"

# please refer to tc-u32(8) for format, rule VAL_MASK_16
# to use a single port use <port, no leading 0> 0xffff
read -r -d "" PORT_PATTERNS_HIGH << EOF
22 0xffff
EOF

read -r -d "" PORT_PATTERNS_LOW << EOF
80 0xffff
443 0xffff
EOF

# clear all qdiscs
tc qdisc del dev $IFACE root 2> /dev/null > /dev/null

# create a qdisc using htb and send traffic to 1:5 by default
tc qdisc add dev $IFACE root handle 1: htb default 5

# define limits for 1: (all packets on $IFACE)
tc class add dev $IFACE parent 1: classid 1:1 htb rate $RATE_TOTAL burst 64k

# define limits for 1:3 (high prio)
tc class add dev $IFACE parent 1:1 classid 1:3 htb rate $RATE_HIGH ceil $RATE_TOTAL burst 64k prio 0
# define limits for 1:5 (medium prio)
tc class add dev $IFACE parent 1:1 classid 1:5 htb rate $RATE_MEDIUM ceil $RATE_TOTAL burst 64k prio 5
# define limits for 1:9 (low prio)
tc class add dev $IFACE parent 1:1 classid 1:9 htb rate $RATE_LOW ceil $RATE_TOTAL burst 64k prio 9

# add sfq for all subclasses
tc qdisc add dev $IFACE parent 1:3 handle 3: sfq perturb 10
tc qdisc add dev $IFACE parent 1:5 handle 5: sfq perturb 10
tc qdisc add dev $IFACE parent 1:9 handle 9: sfq perturb 10

# add filters for marked packets
tc filter add dev $IFACE protocol ip parent 1: prio 0 handle 3 fw flowid 1:3
tc filter add dev $IFACE protocol ip parent 1: prio 0 handle 5 fw flowid 1:5
tc filter add dev $IFACE protocol ip parent 1: prio 0 handle 9 fw flowid 1:9

tc filter add dev $IFACE protocol ipv6 parent 1: prio 0 handle 3 fw flowid 1:3
tc filter add dev $IFACE protocol ipv6 parent 1: prio 0 handle 5 fw flowid 1:5
tc filter add dev $IFACE protocol ipv6 parent 1: prio 0 handle 9 fw flowid 1:9

# filters for high prio (executed before low prio matching)
echo "$PORT_PATTERNS_HIGH" | while read PORT_PATTERN ; do
    echo "processing (high): $PORT_PATTERN"
    tc filter add dev $IFACE protocol ip   parent 1: prio 0 u32 match ip  sport $PORT_PATTERN flowid 1:3
    tc filter add dev $IFACE protocol ipv6 parent 1: prio 0 u32 match ip6 sport $PORT_PATTERN flowid 1:3
done

# filters for low prio (executes after high prio matching)
echo "$PORT_PATTERNS_LOW" | while read PORT_PATTERN ; do
    echo "processing (low):  $PORT_PATTERN"
    tc filter add dev $IFACE protocol ip   parent 1: prio 0 u32 match ip  sport $PORT_PATTERN flowid 1:9
    tc filter add dev $IFACE protocol ipv6 parent 1: prio 0 u32 match ip6 sport $PORT_PATTERN flowid 1:9
done

# remaining traffic is medium priority
