#!/bin/sh
# Copyright (C) 2009 Ubicom, Inc.

#
# (re)start StreamEngine
#

. /etc/rgw_config

#
# qos_rule_get(rule_num)
#	Read a qos rule from nvram config
# 
MANUAL_QOS_RULES_EXIST=0
qos_rule_set()
{
	RULE_NAME=qos_rule_$1
	RULE_SPEC=$(nvram get $RULE_NAME | cut -c15- -)
	QOS_RULE_ENABLED=$(echo $RULE_SPEC | cut -d/ -f1 -)
	QOS_RULE_NAME=$(echo $RULE_SPEC | cut -d/ -f2 -)
	QOS_RULE_PRIORITY=$(echo $RULE_SPEC | cut -d/ -f3 -)
	QOS_RULE_PROTOCOL=$(echo $RULE_SPEC | cut -d/ -f6 -)
	QOS_RULE_LOCAL_IP_FROM=$(echo $RULE_SPEC | cut -d/ -f7 -)
	QOS_RULE_LOCAL_IP_TO=$(echo $RULE_SPEC | cut -d/ -f8 -)
	QOS_RULE_LOCAL_PORT_FROM=$(echo $RULE_SPEC | cut -d/ -f9 -)
	QOS_RULE_LOCAL_PORT_TO=$(echo $RULE_SPEC | cut -d/ -f10 -)
	QOS_RULE_REMOTE_IP_FROM=$(echo $RULE_SPEC | cut -d/ -f11 -)
	QOS_RULE_REMOTE_IP_TO=$(echo $RULE_SPEC | cut -d/ -f12 -)
	QOS_RULE_REMOTE_PORT_FROM=$(echo $RULE_SPEC | cut -d/ -f13 -)
	QOS_RULE_REMOTE_PORT_TO=$(echo $RULE_SPEC | cut -d/ -f14 -)

	echo Setting QoS rule $RULE_NAME
	echo QOS_RULE_ENABLED=$QOS_RULE_ENABLED
	echo QOS_RULE_NAME=$QOS_RULE_NAME
	echo QOS_RULE_PRIORITY=$QOS_RULE_PRIORITY
	echo QOS_RULE_PROTOCOL=$QOS_RULE_PROTOCOL
	echo QOS_RULE_LOCAL_IP_FROM=$QOS_RULE_LOCAL_IP_FROM
	echo QOS_RULE_LOCAL_IP_TO=$QOS_RULE_LOCAL_IP_TO
	echo QOS_RULE_LOCAL_PORT_FROM=$QOS_RULE_LOCAL_PORT_FROM
	echo QOS_RULE_LOCAL_PORT_TO=$QOS_RULE_LOCAL_PORT_TO
	echo QOS_RULE_REMOTE_IP_FROM=$QOS_RULE_REMOTE_IP_FROM
	echo QOS_RULE_REMOTE_IP_TO=$QOS_RULE_REMOTE_IP_TO
	echo QOS_RULE_REMOTE_PORT_FROM=$QOS_RULE_REMOTE_PORT_FROM
	echo QOS_RULE_REMOTE_PORT_TO=$QOS_RULE_REMOTE_PORT_TO

	if [ $QOS_RULE_ENABLED -ne "1" ] ; then
		echo Rule not enabled
		return 0
	fi

	#
	# Some manual qos rules exist
	#
	MANUAL_QOS_RULES_EXIST=1

	#
	# Transform the priority into a priority mark that will match a priority filter rule.
	# 65280 = 0xFF00 + rule_priority
	#
	QOS_RULE_PRIORITY=`expr \( 65280 + $QOS_RULE_PRIORITY \)`
	echo Priority mark = $QOS_RULE_PRIORITY

	#
	# If the protocol number is 257 this is "Both" in which case a tcp and udp protocol rule needs to be added
	#
	if [ $QOS_RULE_PROTOCOL -eq "257" ] ; then
		QOS_RULE_SPEC="-t mangle -A se_manual_rules_table -p udp --sport $QOS_RULE_LOCAL_PORT_FROM:$QOS_RULE_LOCAL_PORT_TO --dport $QOS_RULE_REMOTE_PORT_FROM:$QOS_RULE_REMOTE_PORT_TO -m iprange --src-range $QOS_RULE_LOCAL_IP_FROM-$QOS_RULE_LOCAL_IP_TO --dst-range $QOS_RULE_REMOTE_IP_FROM-$QOS_RULE_REMOTE_IP_TO -j MARK --set-mark $QOS_RULE_PRIORITY"
		echo BOTH SPEC UDP: $QOS_RULE_SPEC
		iptables $QOS_RULE_SPEC

		QOS_RULE_SPEC="-t mangle -A se_manual_rules_table -p tcp --sport $QOS_RULE_LOCAL_PORT_FROM:$QOS_RULE_LOCAL_PORT_TO --dport $QOS_RULE_REMOTE_PORT_FROM:$QOS_RULE_REMOTE_PORT_TO -m iprange --src-range $QOS_RULE_LOCAL_IP_FROM-$QOS_RULE_LOCAL_IP_TO --dst-range $QOS_RULE_REMOTE_IP_FROM-$QOS_RULE_REMOTE_IP_TO -j MARK --set-mark $QOS_RULE_PRIORITY"
		echo BOTH SPEC TCP: $QOS_RULE_SPEC
		iptables $QOS_RULE_SPEC
		return 0
	fi

	#
	# Fixed protocol number.
	# Is this this UDP and therefore requires a port spec?
	#
	if [ $QOS_RULE_PROTOCOL -eq "17" ] ; then
		QOS_RULE_SPEC="-t mangle -A se_manual_rules_table -p udp --sport $QOS_RULE_LOCAL_PORT_FROM:$QOS_RULE_LOCAL_PORT_TO --dport $QOS_RULE_REMOTE_PORT_FROM:$QOS_RULE_REMOTE_PORT_TO -m iprange --src-range $QOS_RULE_LOCAL_IP_FROM-$QOS_RULE_LOCAL_IP_TO --dst-range $QOS_RULE_REMOTE_IP_FROM-$QOS_RULE_REMOTE_IP_TO -j MARK --set-mark $QOS_RULE_PRIORITY"
		echo $QOS_RULE_SPEC
		iptables $QOS_RULE_SPEC
		return 0
	fi

	#
	# tcp?
	#
	if [ $QOS_RULE_PROTOCOL -eq "6" ] ; then
		QOS_RULE_SPEC="-t mangle -A se_manual_rules_table -p tcp --sport $QOS_RULE_LOCAL_PORT_FROM:$QOS_RULE_LOCAL_PORT_TO --dport $QOS_RULE_REMOTE_PORT_FROM:$QOS_RULE_REMOTE_PORT_TO -m iprange --src-range $QOS_RULE_LOCAL_IP_FROM-$QOS_RULE_LOCAL_IP_TO --dst-range $QOS_RULE_REMOTE_IP_FROM-$QOS_RULE_REMOTE_IP_TO -j MARK --set-mark $QOS_RULE_PRIORITY"
		echo $QOS_RULE_SPEC
		iptables $QOS_RULE_SPEC
		return 0
	fi

	#
	# Non-port based protocols now.
	# Any protocol?
	#
	if [ $QOS_RULE_PROTOCOL -eq "256" ] ; then
		QOS_RULE_SPEC="-t mangle -A se_manual_rules_table -m iprange --src-range $QOS_RULE_LOCAL_IP_FROM-$QOS_RULE_LOCAL_IP_TO --dst-range $QOS_RULE_REMOTE_IP_FROM-$QOS_RULE_REMOTE_IP_TO -j MARK --set-mark $QOS_RULE_PRIORITY"
		echo $QOS_RULE_SPEC
		iptables $QOS_RULE_SPEC
		return 0
	fi

	#
	# protocol code
	#
	QOS_RULE_SPEC="-t mangle -A se_manual_rules_table -p $QOS_RULE_PROTOCOL -m iprange --src-range $QOS_RULE_LOCAL_IP_FROM-$QOS_RULE_LOCAL_IP_TO --dst-range $QOS_RULE_REMOTE_IP_FROM-$QOS_RULE_REMOTE_IP_TO -j MARK --set-mark $QOS_RULE_PRIORITY"
	echo $QOS_RULE_SPEC
	iptables $QOS_RULE_SPEC
}


#
# main()
#	Entry point
# Is StreamEngine currently running?  If so then stop it.
#
./streamengine_stop

#
# Start StreamEngine if required
#
STREAMENGINE_ENABLED=$(nvram get traffic_shaping | cut -c19- -)
if [ $STREAMENGINE_ENABLED -ne 1 ] ; then
	echo StreamEngine disabled
	exit 1
fi

./start_se 256

#
# Link type detection (for now we always do auto detect)
#
LINK_CMD=$(nvram get qos_connection_type | cut -c23- -)
echo Link command = $LINK_CMD

rm -rf /var/tmp/ubicom_streamengine_calculated_rate.tmp
#
# Manual or automatic speed?
#
PERFORM_RATE_ESTIMATION=$(nvram get auto_uplink | cut -c15- -)
if [ $PERFORM_RATE_ESTIMATION -eq 0 ] ; then
	#
	# Manual speed
	#
	SPEED_CMD=$(nvram get qos_uplink | cut -c14- -)
	SPEED_CMD=`expr \( $SPEED_CMD \* 1000 \)`

	echo Manual speed = $SPEED_CMD
	./start_rating $SPEED_CMD $LINK_CMD
else
	#
	# Auto speed
	#
	echo Auto speed
	./start_rating 0 $LINK_CMD
fi

#
# If something went wrong with the rating then we stop streamengine
#
if [ $? -ne "0" ] ; then
	echo Rate estimation failed - streamengine will not start
	./stop_se
	exit 1
fi

#
# If the detected speed is > 2097152 (2048kbps) then we disable streamengine - we assume there is enough upstream bandwidth so as not to need a shaper
#
TX_RATE_BPS=$(cat /sys/devices/system/ubicom_streamengine/ubicom_streamengine0/ubicom_streamengine_calculated_rate)
if [ $TX_RATE_BPS -gt 2097152 ] ; then
	cp -rf /sys/devices/system/ubicom_streamengine/ubicom_streamengine0/ubicom_streamengine_calculated_rate /var/tmp/ubicom_streamengine_calculated_rate.tmp
	echo Streamengine shaper disabled: WAN speed greater than 2048Kbps detected
	./stop_se
	exit 0
fi

#
# Do we need to see if we should set up default classification, fragmentation or manual rules?
#
CLASSIFICATION_ENABLED=$(nvram get qos_enable | cut -c14- -)
if [ $CLASSIFICATION_ENABLED -ne 1 ] ; then
	echo No StreamEngine classification required.
	./setup_shaper 0 auto
	exit 0
fi

DYN_FRAG_ENABLED=$(nvram get qos_dyn_fragmentation | cut -c25- -)
./setup_shaper $DYN_FRAG_ENABLED auto

#
# Do we need to enable dynamic classification?
#
DYN_CLASSIFICATION_ENABLED=$(nvram get auto_classification | cut -c23- -)
if [ $DYN_CLASSIFICATION_ENABLED -eq 1 ] ; then
	./start_dc
fi

#
# Set up manual QoS rules
# Create a table into which we add our rules that direct packets to certain priorities.
# NOTE: We use the mangle table because we will be using MARK target
#
iptables -t mangle -N se_manual_rules_table

#
# Add rules into the table
# NVRAM rules look like:
# qos_rule_00 = <en dis>/<name>/<prio>/<unused>/<unused>/<proto>/<local ip from>/<local ip to>/<local port from>/<local port to>/<remote ip from>/<remote ip to>/<remote port from>/<remote port to> 
#
qos_rule_set 00
qos_rule_set 01
qos_rule_set 02
qos_rule_set 03
qos_rule_set 04
qos_rule_set 05
qos_rule_set 06
qos_rule_set 07
qos_rule_set 08
qos_rule_set 09

#
# Uncomment these TWO lines to give HTTP traffic a boot
# 65407 = 0xFF7F (priority 127)
#
#iptables -t mangle -A se_manual_rules_table -p tcp --dport 80:80 -j MARK --set-mark 65407
#MANUAL_QOS_RULES_EXIST=1

#
# Add the rules into netfilter if there are some manual rules.
#
if [ $MANUAL_QOS_RULES_EXIST -eq 1 ] ; then
	#
	# Every packet that goes to the internet (and through the streamengine manual user rules table) will be assigned a nonsense mark value 0xAA00.
	# This mark signals that the connection has iterated the rules table and so the connection now carries the assigned mark - so that we don't
	# have to iterate this table for every packet.
	# Packets that have been assigned a nonsense mark value are not assigned a priority unless the mark is overriden by an actual rule.
	#
	iptables -t mangle -I se_manual_rules_table 1 -j MARK --set-mark 43520

	#
	# The last rule to be added to se_manual_rules_table saves the assigned mark to the conntrack connection
	#
	iptables -t mangle -A se_manual_rules_table --j CONNMARK --save-mark

	#
	# Check rules table for packets that are going to the internet.
	# We only need to concern ourselves with packets going to the internet because our packet marks will be processed by 
	# the streamengine qdisc - for packets going TO the internet only.
	# NOTE: Use of WAN_MODE_INTERFACE ensures we see packets over sub protocols such as PPP.
	#
	
	#
	# Begin by checking of this connection already has a mark value - from a previous rules table match
	#
	iptables -t mangle -A POSTROUTING -o $WAN_MODE_INTERFACE -j CONNMARK --restore-mark

	#
	# If the packet does not have a mark then we need it to iterate the rules table
	#
	iptables -t mangle -A POSTROUTING -o $WAN_MODE_INTERFACE -m mark --mark 0 -j se_manual_rules_table

	#
	# Show the rules (debug use)
	#
	iptables -t mangle -L se_manual_rules_table
else
	#
	# Delete se_manual_rules_table as there are no rules in it
	#
	iptables -t mangle -X se_manual_rules_table 2> /dev/null
fi

exit 0

