#!/bin/bash

# Bryggenet QoS script
# -------------------------------------
# Author: Jesper Dangaard Brouer <hawk@diku.dk>

CEIL=390000

if [ -z "$IFACE" ]; then
    echo "WARN: This script should be run from /etc/network/interfaces"
    if [ -n "$1" ]; then
	DEV=$1
	echo " Using arg1 as device name: $DEV"
    else
	echo "ERROR: Bailing out!"
	exit 1
    fi
else
    DEV=$IFACE
fi

# Bryggenet
#  eth1 - world being NAT'ted
#  eth2 - LAN with 10.0.0.0/8
#
#  We need a different hash in each direction, to assign a
#  DRR queue to each user/customer behind the shaper.
#
if   [ "$DEV" = "eth1" ]; then
    FLOW_HASH=nfct-src
elif [ "$DEV" = "eth2" ]; then
    FLOW_HASH=dst
else
    FLOW_HASH=src
fi

# Determining the 'tc' command location, if not specified
#  and verify the location if the $tc variable is set.
#
# Have installed the latest iproute2 in /usr/local/stow/
tc=/usr/local/sbin/tc
if [ -z "$tc" ]; then
    which tc > /dev/null 2>&1
    if [ $? -eq 0 ]; then
	tc=tc
    elif [ -x /sbin/tc ]; then
	tc=/sbin/tc
    else
	echo ""
	echo "ERROR: Linux traffic control command 'tc' not found!"
	exit 1
    fi
else
    # Verify $tc exists and is executable
    if [ ! -x $tc ]; then
	echo ""
	echo "ERROR: Linux traffic control command '$tc' not found/executable!"
	exit 1
    fi
fi



##########################################
# Functions
##########################################
VERBOSE=yes

function fun_tc() {
    $tc $@
    result=$?
    if [ $result -gt 0 ]; then
	echo "WARNING -- Error ($result) when executing the tc command:"
	echo " \"$tc $@\""
    else
	if [ -n "$VERBOSE" ]; then
	    echo "$tc $@"
	fi
    fi
}

function drr_hierarchy()
{
    # Thanks to Patrick McHardy
    parent=$1
    handle=$2
    classes=$3

    fun_tc qdisc replace dev $DEV parent $parent handle $handle \
	drr

    for ((i = 1; i <= $classes; i++)); do

	classid=$handle$(printf %x $i)
	fun_tc class replace dev $DEV parent $handle classid $classid \
	    drr
	#fun_tc qdisc replace dev $DEV parent $classid pfifo limit 40
	fun_tc qdisc replace dev $DEV parent $classid \
	    sfq perturb 120 limit 1024
    done

    # Filter:
    # FLOW_HASH is set above
    #  We need a different hash in each direction, to assign a
    #  DRR queue to each user/customer behind the shaper.
    #
    fun_tc filter add dev $DEV protocol ip pref 1 parent $handle handle 1 \
	flow hash keys $FLOW_HASH divisor 256 perturb 60

    # Filter EXAMPLES:
    # ----------------
    # SRC matching has a problem due to NAT
    #fun_tc filter add dev $DEV protocol ip pref 1 parent 20: handle 1 \
    #    flow hash keys src divisor 256 perturb 60

    # NFCT-SRC should be the source before NAT
    #fun_tc filter add dev $DEV protocol ip pref 1 parent 20: handle 1 \
    #    flow hash keys nfct-src divisor 256 perturb 60

    # NFCT-SRC source before NAT and the dst
    #fun_tc filter add dev $DEV protocol ip pref 1 parent 20: handle 1 \
    #    flow hash keys nfct-src,dst divisor 256 perturb 60

    # Direct mapping
    #fun_tc filter replace dev $DEV protocol ip pref 1 parent 20: handle 1 \
    #    flow map key src and 0xfff divisor 256

    if [ -n "$VERBOSE" ]; then
	fun_tc filter show dev $DEV parent 20:
    fi
}
#drr_hierarchy 140:1 1400: 1024
#
#tc filter add dev $DEV protocol ip pref 1 parent 1400: handle 1 \
#    flow hash keys src divisor 1024 perturb 30

#drr_hierarchy 1:30 300: 1024
#tc filter add dev $DEV protocol ip pref 1 parent 300: handle 1 \
#    flow hash keys src divisor 1024 perturb 60


function classify_iptables_stop() {
    ### STOP Classification of subnets using iptables
    iptables -t mangle -F FORWARD
}

function classify_iptables_start() {
    ### Classification of subnets using iptables

    # Match all 10.0.0.0/8 will/SHOULD be rematched below
    iptables -t mangle -A FORWARD -s 10.0.0.0/8   -j CLASSIFY --set-class 1:77
    iptables -t mangle -A FORWARD -d 10.0.0.0/8   -j CLASSIFY --set-class 1:77

    # Lille
    iptables -t mangle -A FORWARD -s 10.16.0.0/15 -j CLASSIFY --set-class 1:30
    iptables -t mangle -A FORWARD -d 10.16.0.0/15 -j CLASSIFY --set-class 1:30

    # Stor
    iptables -t mangle -A FORWARD -s 10.20.0.0/15 -j CLASSIFY --set-class 1:20
    iptables -t mangle -A FORWARD -d 10.20.0.0/15 -j CLASSIFY --set-class 1:20

    iptables -t mangle -A FORWARD -s 10.12.0.0/16 -j CLASSIFY --set-class 1:20
    iptables -t mangle -A FORWARD -d 10.12.0.0/16 -j CLASSIFY --set-class 1:20

    iptables -t mangle -A FORWARD -s 194.255.87.0/24 -j CLASSIFY --set-class 1:20
    iptables -t mangle -A FORWARD -d 194.255.87.0/24 -j CLASSIFY --set-class 1:20

    iptables -t mangle -A FORWARD -s 194.255.112.0/24 -j CLASSIFY --set-class 1:20
    iptables -t mangle -A FORWARD -d 194.255.112.0/24 -j CLASSIFY --set-class 1:20
}




# Deletes previous classification (if any)
# ----------------------------------------
# Suppress output, because no classes/qdiscs result
#  in an error message, which is expected...
fun_tc qdisc del dev $DEV root > /dev/null 2>&1

# Stop the classification during building the queues
classify_iptables_stop

# Create the root of tree
# -----------------------
#  (default class: 1:50)
#
fun_tc qdisc add dev ${DEV} root        handle 1: htb default 50 r2q 1

fun_tc class add dev ${DEV} parent 1: classid 1:1 htb \
    rate ${CEIL}kbit ceil ${CEIL}kbit

##########################################
# Class:  1:50
# Mark :  50
# Description: Default fallthrough traffic
##########################################
procent=20
ceil_procent=95
fun_tc class add dev ${DEV} parent 1:1 classid 1:50 htb \
    rate $[${procent}*${CEIL}/100]kbit \
    ceil $[${ceil_procent}*${CEIL}/100]kbit \
    prio 4 \
    burst 10000 cburst 5000

fun_tc qdisc add dev ${DEV} parent 1:50 handle 4250: \
    sfq perturb 10 limit 256

fun_tc filter add dev ${DEV} parent 1:0 protocol ip \
    prio 50 handle 0x50 fw classid 1:50


##########################################
# Class:  1:20
# Mark :  20
# Description: Big Internet
##########################################
rate_stor=290000
ceil_stor=$CEIL
fun_tc class add dev ${DEV} parent 1:1 classid 1:20 htb \
    rate ${rate_stor}kbit \
    ceil ${ceil_stor}kbit \
    prio 1 \
    burst 10000 cburst 5000

# Can be replaced by DRR
#fun_tc qdisc add dev ${DEV} parent 1:20 handle 4220: \
#    sfq perturb 10 limit 256
#
drr_hierarchy 1:20 20: 256

fun_tc filter add dev ${DEV} parent 1:0 protocol ip \
    prio 20 handle 0x20 fw classid 1:20

##########################################
# Class:  1:30
# Mark :  30
# Description: Small Internet
##########################################
rate_lille=100000
ceil_lille=100000
fun_tc class add dev ${DEV} parent 1:1 classid 1:30 htb \
    rate ${rate_lille}kbit \
    ceil ${ceil_lille}kbit \
    prio 4 \
    burst 10000 cburst 5000

# Can be replaced by DRR
#fun_tc qdisc add dev ${DEV} parent 1:30 handle 4230: \
#    sfq perturb 10 limit 256
drr_hierarchy 1:30 30: 256


fun_tc filter add dev ${DEV} parent 1:0 protocol ip \
    prio 30 handle 0x30 fw classid 1:30


##########################################
# Class:  1:77
# Mark :  77
# Description: Should not occur
##########################################
rate_lille=100000
ceil_lille=100000
fun_tc class add dev ${DEV} parent 1:1 classid 1:77 htb \
    rate ${rate_lille}kbit \
    ceil ${ceil_lille}kbit \
    prio 6 \
    burst 10000 cburst 5000

fun_tc qdisc add dev ${DEV} parent 1:77 handle 4277: \
    sfq perturb 10 limit 128

fun_tc filter add dev ${DEV} parent 1:0 protocol ip \
    prio 77 handle 0x77 fw classid 1:77



############
# DRR setup
############
#drr_hierarchy 1:77 777: 1024
#fun_tc filter add dev $DEV protocol ip pref 1 parent 777: handle 1 \
#    flow hash keys src divisor 1024 perturb 60

#fun_tc filter show dev $DEV parent 777:
##fun_tc filter del dev $DEV protocol ip pref 1 parent 777:

####################
# DDR on Class 1:20
####################
# drr_hierarchy 1:20 20: 256






### Classification using iptables rules
classify_iptables_start

