#!/bin/sh

. /lib/rcscripts/sh/error.sh

conf=/etc/conf.d/bandwidth
. $conf || error "Failed to source '$conf'!"

DEFAULT_QDISC=
DEFAULT_QDISC_OPTS=

read DEFAULT_QDISC </proc/sys/net/core/default_qdisc

IFACES=eth0
if ip link show eth1 >/dev/null 2>&; then
	IFACES="$IFACES eth1"
fi

for iface in $IFACES; do
	mtu=$(ip -o link show $iface)
	mtu=${mtu#*mtu[[:blank:]]}
	mtu=${mtu%%[[:blank:]]*}

	[ "$mtu" ] || error "Could not get mtu for $iface"

	eval "MTU_$iface=$mtu"
done

get_default_qdisc_opts() {
	local mtu roundup flow_limit

	[ $# -gt 0 ] || error "get_default_qdisc_opts: missing iface name argument"

	DEFAULT_QDISC_OPTS=

	[ "$DEFAULT_QDISC" = fq ] || return 0

	[ "$WMEM_MAX" ] || {
		WMEM_MAX=$(cat /proc/sys/net/core/wmem_max) ||
			error "Failed to read wmem_max"
		WMEM_MAX=$(($WMEM_MAX * 2))
	}

	eval mtu=\$MTU_$1

	roundup=10
	flow_limit=$(($WMEM_MAX / $mtu))
	flow_limit=$(((($flow_limit / $roundup) + 1) * $roundup))

	DEFAULT_QDISC_OPTS="flow_limit $flow_limit"
}

tc_default() {
	local iface

	[ "$DEFAULT_QDISC" ] || return 0

	for iface in $IFACES; do
		get_default_qdisc_opts $iface
		tc qdisc replace dev $iface root $DEFAULT_QDISC $DEFAULT_QDISC_OPTS || return 1
	done
}

tc_add_cmd() {
	local tc_burst=

	[ $# -gt 0 ] || error "tc_add_cmd: missing iface name argument"

	eval tc_burst=\$MTU_$1
	tc_burst=$(($tc_burst + 100))

	if [ $TC_BURST -gt $tc_burst ]; then
		tc_burst=$TC_BURST
	fi

	[ $tc_burst -gt 0 ] || error "No valid burst value"

	tc qdisc del dev $1 root 2>/dev/null || :

	tc qdisc add dev $1 root handle 1: htb default 10 || return 1

	tc class add dev $1 parent 1: classid 1:1 htb rate $TC_RATE || return 1

	tc class add dev $1 parent 1:1 classid 1:10 htb rate 1kbit ceil $TC_RATE\
		prio 0 || return 1
	tc qdisc add dev $1 parent 1:10 handle 100: tbf rate $TC_RATE burst $tc_burst\
		latency 1 || return 1
	case $DEFAULT_QDISC in
		fq|fq_codel|pfifo_fast)
			get_default_qdisc_opts $1
			tc qdisc add dev $1 parent 100:1 $DEFAULT_QDISC $DEFAULT_QDISC_OPTS || return 1
			;
	esac

	tc class add dev $1 parent 1:1 classid 1:20 htb rate 1kbit ceil $TC_RATE \
		prio 0 || return 1
	tc qdisc add dev $1 parent 1:20 tbf rate $TC_RATE burst $tc_burst latency 1s \
	 || return 1
	tc filter add dev $1 parent 1: protocol ip prio 0 u32 match ip protocol 17 \
		0xff flowid 1:20 || return 1

	return 0
}

bandwidth_start() {
	local rate rate_unit iface changed

	rate=${TC_RATE%%[[:alpha:]]*}
	rate_unit=${TC_RATE#$rate}
	changed=

	case $rate_unit in
		[Kk][Bb]it)
			if [ $rate -ge $LOWER_LIMIT_KBIT ] &&
			   [ $rate -le $UPPER_LIMIT_KBIT ]; then
				changed=y
				for iface in $IFACES; do
					tc_add_cmd $iface || {
						bandwidth_stop
						break
					}
				done
			fi
			;
		[Mm][Bb]it)
			if [ $rate -ge $LOWER_LIMIT_MBIT ] &&
			   [ $rate -le $UPPER_LIMIT_MBIT ]; then
				changed=y
				for iface in $IFACES; do
					tc_add_cmd $iface || {
						bandwidth_stop
						break
					}
				done
			fi
			;
	esac

	[ "$changed" ] || tc_default
}

bandwidth_stop() {
	local iface

	for iface in $IFACES; do
		tc qdisc del dev $iface root 2>/dev/null || :
	done

	tc_default
}

case "$1" in
	start)
		information "Setting traffic control settings"
		bandwidth_start
		;
	stop)
		information "Removing traffic control settings"
		bandwidth_stop
		;
	*)
		error "Usage: $0 start|stop"
		;
esac