1#!/bin/sh
2
3. ${STREAMBOOST_CFGDIR:-/etc/appflow}/rc.appflow
4. ${STREAMBOOST_CFGDIR:-/etc/appflow}/classids.sh
5
6# Note: EXTRA_COMMANDS isn't actually used by the rc.appflow environment, but
7# it's defined here in the hopes that one day it will be.  At that time,
8# the redefinition of action() and usage() in this file should be deleted.
9EXTRA_COMMANDS="start_qdiscs stop_qdiscs setup_iptables teardown_iptables"
10EXTRA_HELP="        start_qdiscs	create root qdisc structure
11        stop_qdiscs	delete root qdisc structure
12        setup_iptables	insert iptables rules
13        teardown_iptables	delete iptables rules"
14
15#
16# Environment config
17#
18NAME="qdiscman"
19DISPLAY_NAME=${NAME}
20
21# path to binary
22BINARY="${BINDIR}/${NAME}"
23
24# path to pid file
25PIDFILE="${RUNDIR}/${NAME}.pid"
26
27# Redis server port
28REDIS_PORT=6379
29
30# source the setup_iface, setup_iptables, and teardown_iptables functions,
31# switched by whether or not we're enabled for NSS mode.  to be enabled
32# for NSS mode, the kmod-qca-nss-qdisc package must be installed and the
33# nss_qdisc variable in streamboost.user.conf must be 'yes'.
34if nss_qdisc_is_installed; then
35	# we can potentially use NSS QDiscs, so see if we're configured for it.
36	# the user config file contains the nss_qdisc setting
37	. $STREAMBOOST_USER_CFG
38	# test the nss_qdisc value to see if we're in NSS mode
39	if [ "${nss_qdisc}" = "yes" ]; then
40		. ${STREAMBOOST_CFGDIR:-/etc/appflow}/qdiscman-nss.sh
41	else
42		. ${STREAMBOOST_CFGDIR:-/etc/appflow}/qdiscman.sh
43	fi
44else
45	. ${STREAMBOOST_CFGDIR:-/etc/appflow}/qdiscman.sh
46fi
47
48[ -f /etc/dhcp.guest.conf ] && . /etc/dhcp.guest.conf
49
50# Format the command line parameters
51CMDLINE_OPTS="\
52--daemon \
53--run-dir=${RUNDIR} \
54--pid-file=${PIDFILE} \
55--redis-port=${REDIS_PORT} \
56--redis-stat-prefix=flowdb:flows: \
57--ifname-up=${WAN_IFACE} \
58--ifname-down=${LAN_IFACE} \
59${EXTRA_CMD_ARGS} \
60"
61
62#
63# Functions
64#
65sb_get_max_zone_id() {
66	redis-cli get "settings:max_zone_id"
67}
68
69# prints 0 if a zone with the given ID is configured in redis, else 1
70# $1: zone id
71sb_zone_is_configured() {
72	local zone=$1
73	if [ $(redis-cli exists "settings:zone:${zone}") = "1" ] &&
74	   [ $(redis-cli hexists "settings:zone:${zone}" "weight:up") = "1" ] &&
75	   [ $(redis-cli hexists "settings:zone:${zone}" "weight:down") = "1" ]; then
76		echo 0
77	else
78		echo 1
79	fi
80}
81
82# echos the number of configured zones.  a zone is considered "configured"
83# if it has a hash entry in settings:zone:<id> where id is less than
84# settings:max_zone_id
85sb_get_zone_count() {
86	local maxzoneid=$(sb_get_max_zone_id)
87	local zone=0
88	local count=0
89
90	if [ -n "${maxzoneid}" ]; then
91		while [ "${zone}" -le "${maxzoneid}" ]; do
92			if [ $(sb_zone_is_configured ${zone}) = "0" ]; then
93				let count=count+1
94			fi
95			let zone=zone+1
96		done
97	fi
98
99	echo ${count}
100}
101
102# returns a zone config item from redis
103# $1: dev (e.g., eth0, br-lan, etc.)
104# $2: zone (integer zone identifier)
105# $3: key (e.g., bw, weight)
106# $4: default (returned if key doesn't exist)
107sb_get_zone_config() {
108	local dev=$1
109	local zone=$2
110	local key=$3
111	local default=$4
112
113	if [ "${dev}" = "${WAN_IFACE}" ]; then
114		redis-cli hget settings:zone:${zone} ${key}:up
115	elif [ "${dev}" = "${LAN_IFACE}" ]; then
116		redis-cli hget settings:zone:${zone} ${key}:down
117	else
118		echo ${default}
119		echo "error: ${dev} is not a supported interface" 1>&2
120	fi
121}
122
123# returns the "bw" key from the zone config for the given interface zone
124# $1: dev
125# $2: zone
126sb_get_zone_rate() {
127	sb_get_zone_config $1 $2 bw
128}
129
130# returns the "weight" key from the zone config for the given interface zone
131# $1: dev
132# $2: zone
133sb_get_zone_weight() {
134	sb_get_zone_config $1 $2 weight 10000
135}
136
137# generates a hex value in the CLASSID_ZONE_BASE range that can be
138# used as a classid or a qdisc handle
139# $1: zone id
140sb_gen_zone_classid() {
141	printf "%x" $((0x${CLASSID_ZONE_BASE} + ${1}))
142}
143
144# generates a hex value in the CLASSID_ZONE_TBL_BASE range that can be
145# used as a classid or a qdisc handle for NSS-based zone
146# $1: zone id
147sb_gen_zone_tbl_classid() {
148	printf "%x" $((0x${CLASSID_ZONE_TBL_BASE} + ${1}))
149}
150
151# generates a hex value in the CLASSID_ZONE_BG_BASE range that can be
152# used as a classid or a qdisc handle for NSS-based zone background qdisc
153# $1: zone id
154sb_gen_zone_bg_classid() {
155	printf "%x" $((0x${CLASSID_ZONE_BG_BASE} + ${1}))
156}
157
158# generates a hex value in the CLASSID_ZONE_CL_BASE range that can be
159# used as a classid or a qdisc handle for NSS-based zone classified qdisc
160# $1: zone id
161sb_gen_zone_cl_classid() {
162	printf "%x" $((0x${CLASSID_ZONE_CL_BASE} + ${1}))
163}
164
165# $1: dev
166# $2: parent
167# $3: handle
168# $4: qdisc type (default = fq_codel)
169# $5: extra opts
170add_interactive_qdisc() {
171	tc qdisc add dev $1 parent $2 handle $3 \
172		${4:-fq_codel} \
173			limit 100\
174			target 250000 interval 2500000 $5
175	[ $? = 0 ] || return $?
176}
177
178start_qdiscs() {
179	echo "Setting up qdiscs on interface ${WAN_IFACE}"
180	GUEST_BANDWIDTH_LIMIT="${GUEST_BANDWIDTH_LIMIT_UP}"
181	setup_iface ${WAN_IFACE}
182	[ $? = 0 ] || return $?
183
184	echo "Setting up qdiscs on interface ${LAN_IFACE}"
185	GUEST_BANDWIDTH_LIMIT="${GUEST_BANDWIDTH_LIMIT_DOWN}"
186	setup_iface ${LAN_IFACE}
187	[ $? = 0 ] || return $?
188}
189
190stop_qdiscs() {
191	tc qdisc del dev ${WAN_IFACE} root
192	tc qdisc del dev ${LAN_IFACE} root
193}
194
195start() {
196	[ -f "${PIDFILE}" ] && {
197		return 0
198	}
199
200	[ ! -f /var/log/setup_iptables ] && {
201		teardown_iptables 2>/dev/null
202		setup_iptables
203	}
204	touch /var/log/setup_iptables
205
206	for i in ${KERNEL_MODULES}; do
207		insmod $i 2>/dev/null
208	done
209
210	[ ! -d "${RUNDIR}" ] && {
211		mkdir ${RUNDIR}
212	}
213
214	[ -x ${BINARY} ] || {
215		echo "${NAME} not found: ${BINARY}"
216		exit 2
217	}
218
219	stop_qdiscs 2>/dev/null
220	start_qdiscs || exit 3
221
222	echo -n "Starting ${NAME}: "
223	${BINARY} ${CMDLINE_OPTS} "$@"
224	retval=$?
225	echo
226	return ${retval}
227}
228
229boot() {
230	start "$@"
231}
232
233stop() {
234	default_stop
235	local retval=$?
236
237	stop_qdiscs
238	teardown_iptables
239	rm -rf /var/log/setup_iptables
240
241	return ${retval}
242}
243
244usage() {
245	cat <<EOF
246Usage: $0 [command]
247
248Commands:
249	start
250	stop
251	restart
252	reload
253	boot
254	init
255	status
256${EXTRA_HELP}
257EOF
258}
259
260action() {
261	action=${1:-$DEFAULT_ACTION}
262	# this shift is required because the start() function accepts
263	# parameters from the command line and passes them through to the
264	# daemon
265	shift
266
267	BINARY=${BINARY:-$(echo $0 | sed 's/.*\///')}
268	DISPLAY_NAME=${DISPLAY_NAME:-${NAME:-$BINARY}}
269	PIDFILE=${PIDFILE:-$RUNDIR/$BINARY.pid}
270
271	case "${action}" in
272		boot|init)
273			boot "$@"
274			;;
275		start|stop|restart|reload|status|start_qdiscs|stop_qdiscs|setup_iptables|teardown_iptables)
276			${action} "$@"
277			;;
278		*)
279			usage
280			exit 3
281	esac
282}
283
284action "$@"
285exit $?
286