#!/bin/bash

#default config
if [ -r /etc/default/qos ] ; then
    . /etc/default/qos
else
    echo "No /etc/default/qos ... exiting"
    exit 1
fi

# File that describes filters for system services
SYSSERVICE_FILTERS=$QOSDIR/services
# List of ips that list bans to be applied
BANFILES="$QOSDIR/ban_ip"

TC="`which tc`"
IP="`which ip`"
SFQ="sfq perturb 10"
ESFQ="esfq perturb 10 hash dst"
BANCLASS=1:666
QUANTUM=1514


load_filters() {
    parent=$1
    flowprefix=$2
    directory=$3
    subs=$4

    echo "    adding filters: parent=$parent, prefix=$flowprefix, dir=$directory"
    filter_parent=${parent%%:*}
    for x in `cd $directory ; ls ` ; do
	flowid=`echo $x | sed -r "s/([0-9]+)-.*/\1/"`
	cat $directory/$x | sed "s/#.*//p" | while read filter ; do
	    if [ -z "$filter" ] ; then
		continue;
	    fi
	    case $subs in
		0)
		    ;;
		1)	
    		    $TF $parent protocol ip $filter flowid ${filter_parent}:${flowprefix}${flowid}
		    ;;
		*)
    		    $TF $parent protocol ip $filter flowid 1:666
		    ;;
	    esac
	done
    done
}

load_bananas() {
    parent=$1
    cls=$2
    BANANI=`cat $BANFILES | sed -r s/#.*// 2> /dev/null`
    for BANAN in $BANANI ; do
	if [ -z $BANAN ] ; then
	    continue;
	fi
	echo "    !! Banning $BANAN"
	$TC filter add dev ${FACE} parent $parent protocol ip prio 2 u32 match ip dst $BANAN flowid $cls
	$TC filter add dev ${FACE} parent $parent protocol ip prio 2 u32 match ip src $BANAN flowid $cls
    done
}

set_qos_classes() {
	TQ="$TC qdisc add dev $FACE"
	TA="$TC class add dev $FACE parent"
	TF="$TC filter add dev $FACE parent "
	
	$TQ root handle 1: htb default 4
	#root class
        $TA 1:  classid 1:1 htb rate ${speed}kbit ceil ${speed}kbit burst 150k quantum $QUANTUM

	#service
	$TA 1:1 classid 1:2 htb rate $((${speed}/5))kbit ceil $((${speed}/5))kbit burst 10k prio 0 quantum $QUANTUM
	#small
	$TA 1:1 classid 1:3 htb rate $((${speed}/20))kbit ceil $((${speed}/20))kbit burst 10k prio 0 quantum $QUANTUM
	#user
	$TA 1:1 classid 1:4 htb rate $((${speed}*4/5))kbit ceil $((${speed}))kbit burst 10k prio 0 quantum $QUANTUM
	#ack
	$TA 1:1 classid 1:9 htb rate $((${speed}/20))kbit ceil $((${speed}/20))kbit burst 10k prio 0 quantum $QUANTUM	
	#ban
	$TA 1:1 classid $BANCLASS htb rate 32kbit ceil 128kbit burst 5k prio 7 quantum $QUANTUM
	#service subclasses
	#===========================
	#ssh
	$TA 1:2 classid 1:5 htb rate $((${speed}/20))kbit ceil $((${speed}/5))kbit burst 10k prio 0 quantum $QUANTUM
	#ping
	$TA 1:2 classid 1:6 htb rate $((${speed}/20))kbit ceil $((${speed}/5))kbit burst 5k prio 0 quantum $QUANTUM
	#dns
	$TA 1:2 classid 1:7 htb rate $((${speed}/20))kbit ceil $((${speed}/10))kbit burst 10k prio 0 quantum $QUANTUM
	#other
	$TA 1:2 classid 1:8 htb rate $((${speed}/20))kbit ceil $((${speed}/10))kbit burst 5k prio 0 quantum $QUANTUM

	$TQ parent 1:3 handle 3: $SFQ	# small
	$TQ parent 1:5 handle 5: $SFQ	# SSH sub-classes
	$TQ parent 1:6 handle 6: $SFQ	# ping
	$TQ parent 1:7 handle 7: $SFQ	# DNS
	$TQ parent 1:8 handle 8: $SFQ	# other
	$TQ parent 1:9 handle 9: $SFQ	# ACK
	$TQ parent $BANCLASS handle 666: $SFQ

	load_filters 1:0 "" $SYSSERVICE_FILTERS 1
	load_bananas 1:0 $BANCLASS

	#run specified script
	OPER=start $QOSDIR/scripts/$type $FACE $iface $speed $rate
}

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

# Removes the IMQ rule from the iptables
stop_iptables() {
    device=$1
    if [[ $device =~ imq ]] && [ -n "$iface" ] ; then
	[[ $device =~ "imq([0-9])" ]]
	devnum="${BASH_REMATCH[1]}"
	if [ -z "$devnum" ] ; then
	    return
	fi
	
	if [[ `iptables -t mangle -L PREROUTING -n -v --line-num` =~ "$iface +\*.*IMQ: todev $devnum" ]] ; then
	    iptables -t mangle -D PREROUTING -i $iface -j IMQ --todev $devnum
	fi
    fi
}

stop_device() {
    device=$1
    
    phys="$device"
    log="$device"

    if ! ip link show dev $device &> /dev/null ; then
	echo "setup-qos: Unknown device: $device" >&2
	exit 1
    fi

    echo "Stopping QoS on $log"
    $TC qdisc del dev $phys root &> /dev/null

    cfg=$QOSDIR/qos-$log
    if [ -r $cfg ] ; then
	. $cfg
    fi
    stop_iptables $device
    # Stop any queue device
    for x in $queues ; do 
	iface=$device /usr/local/sbin/setup-qos stop $x
    done

    if [[ $device =~ imq ]] ; then
	ip link set $device down &> /dev/null
    fi

    rm -f $DATADIR/qos-$device.act &>/dev/null
    rm -f $DATADIR/faces/$device &>/dev/null
    
}

setup_ip_tables() {
    device=$1
    if [[ $device =~ imq ]] && [ -n "$iface" ] ; then
	[[ $device =~ "imq([0-9])" ]]
	devnum="${BASH_REMATCH[1]}"
	if [ -z "$devnum" ] ; then
	    return
	fi
	
	if [[ ! `iptables -t mangle -L PREROUTING -n -v` =~ "$iface +\*.*IMQ: todev $devnum" ]] ; then
	    iptables -t mangle -A PREROUTING -i $iface -j IMQ --todev $devnum
	fi
    fi
}

start_device() {
    device=$1
    
    # Special handling for IMQ devices: they need to be modprobed and iplinked up
    if [[ $device =~ imq ]] ; then
	if ! lsmod | grep imq &> /dev/null; then
	    modprobe imq numdevs=$QUEUES
	    echo "Q: $QUEUE"
	fi
	ip link set $device up
    fi
    
    if ! ip link show dev $device &> /dev/null ; then
	echo "setup-qos: Unknown device: $device" >&2
	exit 1
    fi
    
    if [ -r $QOSDIR/default-qos ] ; then
	. $QOSDIR/default-qos
    fi
    
    phys="$device"
    log="$device"
    FACE="$device"
    iface="$device"

    cfg=$QOSDIR/qos-$log
    if [ ! -r $cfg ] ; then
        cfg=$QOSDIR/qos-$phys
    fi

    #nacteni promennych z konfiguraku
    if [ -r $cfg ] ; then
	. $cfg
    else
        return
    fi
    
    if [ -z "$speed" ] ; then
	echo "setup-qos: $device: Speed undefined" >&2
	exit 1
    fi
    
    setup_ip_tables $device
    
    echo "Starting QoS on $log ($phys): type=$type"
    echo "  Quantum = $quantum, Speed=$speed, Rate=$rate"
    set_qos_classes

    # Start any queue device
    for x in $queues ; do 
	iface=$device /usr/local/sbin/setup-qos start $x
    done
    echo "Finished"
}

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

case $1 in
    start|stop|restart)
	;;
    *)
	echo "Usage: setup-qos start|stop|restart [device ...]"
	exit 1
	;;
esac

cmd=$1
shift

devicelist="$*"

if [ -z "$devicelist" ] ; then
    devicelist=`cd $QOSDIR ; ls qos-* | sed "s/qos-//" | grep -v imq`
    #don't touch imq devices - use queues instead
fi

for dev in $devicelist ; do
    case $cmd in
	start)
	    start_device $dev
	    ;;
	stop)
	    stop_device $dev
	    ;;
	restart)
	    stop_device $dev
	    start_device $dev
	    ;;
	*)
	    ;;
    esac	
done
