qos.initd 13.1 KB
Newer Older
1
#!/sbin/openrc-run
2 3 4 5 6 7 8 9 10
#
###########################################################################################################
# Traffic Control startup script
#
# Copyright (c) 2009 iilluzion
#
# Distributed under GPL-2
###########################################################################################################

11 12
PROGRAM=$SVCNAME
CONFIG=/etc/conf.d/$SVCNAME
13 14
DEBUG=0 #1

15 16
extra_started_commands="describe"
extra_stopped_commands="compile"
17

18 19 20
###########################################################################################################
#
#
21 22 23
depend() 
{
	need net
24
	after firewall
25 26
}

27 28 29
###########################################################################################################
#
#
30 31 32 33 34 35 36
checkconfig() {
	if [ ! -e $CONFIG ] ; then
		eerror "You need to create $CONFIG first."
	return 1
	fi
}

37 38 39 40
###########################################################################################################
#
#
bits()
41
{
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
        RATE=0
        R_RATE=$1

        R_NUMBER=`echo "$R_RATE" | sed -e "s/[^0-9]//g"`
        R_UNIT=`echo "$R_RATE" | sed -e "s/[0-9]//g"`

        if [ "$R_UNIT" == "" ]; then
                R_UNIT="kbit"
        fi

        if [ "$R_UNIT" == "kbps" ]; then   R_RATE=$(($R_NUMBER * 1024 * 8))
        elif [ "$R_UNIT" == "mbps" ]; then R_RATE=$(($R_NUMBER * 1024 * 1024 * 8))
        elif [ "$R_UNIT" == "mbit" ]; then R_RATE=$(($R_NUMBER * 1024 * 1024))
        elif [ "$R_UNIT" == "kbit" ]; then R_RATE=$(($R_NUMBER * 1024))
        elif [ "$R_UNIT" == "bps" ]; then  R_RATE=$(($R_NUMBER * 8))
        else
                echo "Unknown unit '$R_UNIT' (mbps, mbit, kbit, kbps, bps)"
        fi

        echo "$R_RATE"
62 63
}

64 65 66 67 68 69
###########################################################################################################
#
#
expand_leaf_qdisc()
{
	case "$1" in
70 71
		pfifo)	echo "pfifo limit ${PFIFO_LIMIT-20480}";;
		sfq)	echo "sfq perturb ${SFQ_PERTURB-10}";;
72 73 74 75 76 77 78
		red)	echo "red min $RED_MIN max $RED_MAX burst $RED_BURST limit $RED_LIMIT probability $RED_PROB avpkt $RED_AVPKT";;
	esac
}

###########################################################################################################
#
#
79 80
configure()
{
81 82 83 84 85 86 87
	if [ ! -z $1 ]; then
		eval WAN_RATE="\$$1"_RATE
			WAN_RATE=`bits $WAN_RATE`

		# Calculaton of WAN classes rates
		WAN_SUB_RATE=$((WAN_RATE - (RATE_SUB_PERCENT * WAN_RATE / 100)))
			INTERACTIVE_RATE=$((WAN_SUB_RATE / 5))
88 89 90 91 92
		if [ $INTERACTIVE_RATE -lt 75000 ]; then
			INTERACTIVE_RATE=75000
			PRIVILEGED_RATE=$(((WAN_SUB_RATE - 75000) / 2))
			BESTEFFORT_RATE=$(((WAN_SUB_RATE - 75000) / 2))
		else
93 94
			PRIVILEGED_RATE=$((WAN_SUB_RATE / 2))
			BESTEFFORT_RATE=$((WAN_SUB_RATE / 3))
95
		fi
96 97 98 99 100 101 102 103 104 105 106 107 108

		DEV_RATE=${DEV_RATE:-$EGRESS_RATE}
			DEV_RATE=`bits $DEV_RATE`
				if [ $DEV_RATE -lt $WAN_RATE ]; then
					DEV_RATE=$WAN_RATE
				fi
		OUT_OF_WAN_RATE=$((DEV_RATE - WAN_RATE))

		# Calculation of Queuing Discipline parameters
		INTERACTIVE_PRIO_LATENCY=50000
		INTERACTIVE_PRIO_BURST=$((INTERACTIVE_RATE / 100 / 8))

		INTERACTIVE_HFSC_DMAX=50000
109
		INTERACTIVE_HFSC_UMAX=1500
110 111

		PRIVILEGED_HFSC_DMAX=100000
112
		PRIVILEGED_HFSC_UMAX=1500
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

		# Random Early Detect (RED) parameters calculation:
		#  min         = maximum delay * rate (dalay ~ 200ms = 0.2sec) [b]
		#  max         = 3 * min [b]
		#  avpkt       = 1000 (MTU 1500)
		#  limit       = 8 * max [b]
		#  burst       = (min + min + max)/(3 * avpkt) [b]
		#  probability = 0.02

		RED_DELAY=200
		RED_MIN=$((RED_DELAY * BESTEFFORT_RATE / 1000 / 8)) # devided on 8 since rate given in bit/s so we get bytes
		RED_MAX=$((3 * RED_MIN))
		RED_AVPKT=1000
		RED_PROB=0.02
		RED_BURST=$(((RED_MIN + RED_MIN + RED_MAX) / (3 * RED_AVPKT)))
		RED_LIMIT=$((8 * RED_MAX))

		# Setting leaf Queuing Disciplines parameters
131 132 133 134 135 136 137 138 139
		INTERACTIVE_LEAF_QDISC=pfifo
			INTERACTIVE_LEAF_QDISC=`expand_leaf_qdisc $INTERACTIVE_LEAF_QDISC`
		PRIVILEGED_LEAF_QDISC=pfifo
			PRIVILEGED_LEAF_QDISC=`expand_leaf_qdisc $PRIVILEGED_LEAF_QDISC`
		
		# Force using SFQ in case rate is less than 2mbit	
		if [ $BESTEFFORT_RATE -lt 2097152 ]; then
			BESTEFFORT_LEAF_QDISC=sfq
		fi
140
		BESTEFFORT_LEAF_QDISC=`expand_leaf_qdisc $BESTEFFORT_LEAF_QDISC`
141 142

		LAN_LEAF_QDISC=sfq
143 144
		LAN_LEAF_QDISC=`expand_leaf_qdisc $LAN_LEAF_QDISC`
	fi
145 146
}

147 148 149
###########################################################################################################
#
#
150 151
reset()
{
152 153 154 155 156 157 158 159 160
	# Supported Queuing Disciplines
	QDISCS="prio|tbf|htb|hfsc|sfq|red|pfifo"

	tc qdisc show dev $DEV | grep -v "pfifo_fast" | egrep -q "$QDISCS" && $ECHO tc qdisc del dev $DEV root
	tc qdisc show dev $DEV | grep -v "pfifo_fast" | grep  -q ingress && $ECHO tc qdisc del dev $DEV ingress

	if [ "$INGRESS_ALG" = "ifb" ] && [ ! -z $IFB_DEV ]; then 
		$ECHO ip link set dev $IFB_DEV down
	fi
161 162
}

163 164 165
###########################################################################################################
#
#
166 167
set_leaf_qdisc()
{
168
	PARENT_CLASSID=$1
169 170 171 172 173 174 175 176 177
		PARENT_CLASSID=${PARENT_CLASSID:-1}

	if [ ! "$QDISC_CMD" = "prio" ]; then 
		$ECHO tc qdisc add dev $DEV parent $PARENT_CLASSID:40 handle 40 $INTERACTIVE_LEAF_QDISC
	fi

	$ECHO tc qdisc add dev $DEV parent $PARENT_CLASSID:50 handle 50 $PRIVILEGED_LEAF_QDISC
	$ECHO tc qdisc add dev $DEV parent $PARENT_CLASSID:60 handle 60 $BESTEFFORT_LEAF_QDISC

178
	if [ $OUT_OF_WAN_RATE -gt 0 ]; then
179 180 181 182 183 184
		$ECHO tc qdisc add dev $DEV parent $PARENT_CLASSID:70 handle 70 $LAN_LEAF_QDISC
	fi

	$ECHO
}

185 186 187
###########################################################################################################
#
#
188 189 190
set_filters()
{
	CLASS_TYPES="INTERACTIVE PRIVILEGED BESTEFFORT"
191
		if [ $OUT_OF_WAN_RATE -gt 0 ]; then
192
			CLASS_TYPES=$CLASS_TYPES" LAN"
193
		fi
194 195 196 197 198

		PRIVILEGED_FILTER_FLOWID=50
		BESTEFFORT_FILTER_FLOWID=60
		LAN_FILTER_FLOWID=70

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
	{
		for CLASS_TYPE in $CLASS_TYPES; do
			if [ "$QDISC_CMD" = "prio" -a "$CLASS_TYPE" = "INTERACTIVE" ]; then
				PARENT_CLASSID=1
				INTERACTIVE_FILTER_FLOWID=1
			else
				PARENT_CLASSID=$1
					PARENT_CLASSID=${PARENT_CLASSID:-1}
				INTERACTIVE_FILTER_FLOWID=40
			fi

			for FILTER_NUM in `seq 1 100`; do
				eval FILTER="\$$CLASS_TYPE"_FILTER_$FILTER_NUM
				if [ ! -z "$FILTER" ]; then
					eval FILTER_FLOWID="\$$CLASS_TYPE"_FILTER_FLOWID
						$ECHO tc filter add dev $DEV parent $PARENT_CLASSID:0 $FILTER flowid $PARENT_CLASSID:$FILTER_FLOWID
				fi
			done
217
		done
218
	} | sort -g
219 220

	$ECHO
221 222
}

223 224 225
###########################################################################################################
#
#
226 227
set_htb()
{
228
	$ECHO tc qdisc add dev $DEV root handle 1 htb default 60 r2q ${HTB_R2Q-100}
229 230 231 232 233
		$ECHO tc class add dev $DEV parent 1: classid 1:2 htb rate $DEV_RATE burst $(($DEV_RATE*5/4))
			$ECHO tc class add dev $DEV parent 1:2 classid 1:30 htb rate $WAN_SUB_RATE burst $(($WAN_SUB_RATE*5/4))
				$ECHO tc class add dev $DEV parent 1:30 classid 1:40 htb rate $INTERACTIVE_RATE ceil $WAN_SUB_RATE prio 1
				$ECHO tc class add dev $DEV parent 1:30 classid 1:50 htb rate $PRIVILEGED_RATE ceil $WAN_SUB_RATE prio 3 burst $(($WAN_SUB_RATE*5/4))
				$ECHO tc class add dev $DEV parent 1:30 classid 1:60 htb rate $BESTEFFORT_RATE ceil $WAN_SUB_RATE prio 6 burst $(($WAN_SUB_RATE*5/4))
234 235

				if [ $OUT_OF_WAN_RATE -gt 0 ]; then
236 237 238
					$ECHO tc class add dev $DEV parent 1:2 classid 1:70 htb rate $OUT_OF_WAN_RATE prio 7
				fi
	
239
	set_leaf_qdisc
240

241
	$ECHO
242 243 244 245

	set_filters
}

246 247 248
###########################################################################################################
#
#
249 250
set_hfsc()
{
251 252 253
	$ECHO tc qdisc add dev $DEV root handle 1 hfsc default 60
		$ECHO tc class add dev $DEV parent 1: classid 1:2 hfsc sc rate $DEV_RATE ul rate $DEV_RATE
			$ECHO tc class add dev $DEV parent 1:2 classid 1:30 hfsc sc rate $WAN_SUB_RATE ul rate $WAN_SUB_RATE
254
				$ECHO tc class add dev $DEV parent 1:30 classid 1:40 hfsc sc umax $INTERACTIVE_HFSC_UMAX dmax $INTERACTIVE_HFSC_DMAX rate $INTERACTIVE_RATE ul rate $WAN_SUB_RATE
255 256 257 258
				$ECHO tc class add dev $DEV parent 1:30 classid 1:50 hfsc sc umax $PRIVILEGED_HFSC_UMAX dmax $PRIVILEGED_HFSC_DMAX rate $PRIVILEGED_RATE ul rate $WAN_SUB_RATE
				$ECHO tc class add dev $DEV parent 1:30 classid 1:60 hfsc sc rate $BESTEFFORT_RATE ul rate $WAN_SUB_RATE

				if [ $OUT_OF_WAN_RATE -gt 0 ]; then
259 260 261
					$ECHO tc class add dev $DEV parent 1:2 classid 1:70 hfsc sc rate $OUT_OF_WAN_RATE ul rate $OUT_OF_WAN_RATE
				fi

262
	set_leaf_qdisc
263

264
	$ECHO
265 266 267 268

	set_filters
}

269 270 271
###########################################################################################################
#
#
272 273 274
set_prio()
{
	PARENT_CLASSID=10
275
	$ECHO tc qdisc add dev $DEV root handle 1 prio bands 2 priomap 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 # by default unclassified traffic goes to flowid 1:2
276
		$ECHO tc qdisc add dev $DEV parent 1:1 handle 40: tbf rate $INTERACTIVE_RATE burst $WAN_SUB_RATE latency $INTERACTIVE_PRIO_LATENCY
277 278 279 280 281 282
		$ECHO tc qdisc add dev $DEV parent 1:2 handle $PARENT_CLASSID: htb default 60
			$ECHO tc class add dev $DEV parent $PARENT_CLASSID: classid $PARENT_CLASSID:30 htb rate $WAN_SUB_RATE
				$ECHO tc class add dev $DEV parent $PARENT_CLASSID:30 classid $PARENT_CLASSID:50 htb rate $PRIVILEGED_RATE ceil $WAN_SUB_RATE prio 3
				$ECHO tc class add dev $DEV parent $PARENT_CLASSID:30 classid $PARENT_CLASSID:60 htb rate $BESTEFFORT_RATE ceil $WAN_SUB_RATE prio 6

				if [ $OUT_OF_WAN_RATE -gt 0 ]; then
283
					$ECHO tc class add dev $DEV parent 10:1 classid $PARENT_CLASSID:70 htb rate $OUT_OF_WAN_RATE prio 7
284
				fi
285

286
	set_leaf_qdisc $PARENT_CLASSID
287

288
	$ECHO
289 290 291 292

	set_filters $PARENT_CLASSID
}

293 294 295 296 297 298 299 300 301 302 303 304 305
###########################################################################################################
#
#
set_ifb()
{
	$ECHO ip link set dev $IFB_DEV up
		$ECHO tc qdisc add dev $DEV ingress handle ffff:
			$ECHO tc filter add dev $DEV parent ffff: protocol ip prio 10 u32 match u32 0 0 action mirred egress redirect dev $IFB_DEV > /dev/null 2>&1
}

###########################################################################################################
#
#
306 307
set_police()
{
308 309 310 311 312

# Calculation of policing bursts
#       burst = rate / 17 (taken basing on experemental results)

	POLICE_BURST_SCALE=17
313
	WAN_POLICE_BURST=$((WAN_RATE / POLICE_BURST_SCALE))
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332

	WAN_POLICE_FLOWID=1

	$ECHO tc qdisc add dev $DEV handle ffff: ingress
	$ECHO tc filter add dev $DEV parent ffff: protocol ip prio 999 u32 match ip src 0.0.0.0/0 police rate $WAN_RATE burst $WAN_POLICE_BURST drop flowid :$WAN_POLICE_FLOWID
 
	$ECHO
}

###########################################################################################################
#
#
set_cpolice()
{

	# Calculation of policing bursts
	#       burst = rate / 17 (taken basing on experemental results)

	POLICE_BURST_SCALE=17
333 334 335
		INTERACTIVE_POLICE_BURST=$((INTERACTIVE_RATE / POLICE_BURST_SCALE))
		PRIVILEGED_POLICE_BURST=$((PRIVILEGED_RATE / POLICE_BURST_SCALE))
		BESTEFFORT_POLICE_BURST=$((BESTEFFORT_RATE / POLICE_BURST_SCALE))
336

337 338 339 340
	CLASS_TYPES="INTERACTIVE PRIVILEGED"
		INTERACTIVE_POLICE_FLOWID=1
		PRIVILEGED_POLICE_FLOWID=2

341 342 343 344 345 346 347 348 349 350
	$ECHO tc qdisc add dev $DEV handle ffff: ingress

	for CLASS_TYPE in $CLASS_TYPES; do
		for FILTER_NUM in `seq 1 100`; do
			eval FILTER="\$$CLASS_TYPE"_FILTER_$FILTER_NUM
				[ -z "$FILTER" ] && break
			eval FILTER_FLOWID="\$$CLASS_TYPE"_POLICE_FLOWID
			eval FILTER_RATE="\$$CLASS_TYPE"_RATE
			eval FILTER_BURST="\$$CLASS_TYPE"_POLICE_BURST
				$ECHO tc filter add dev $DEV parent ffff: $FILTER police rate $FILTER_RATE burst $FILTER_BURST continue flowid :$FILTER_FLOWID
351
		done
352
	done
353

354
	$ECHO tc filter add dev $DEV parent ffff: protocol ip prio 999 u32 match ip src 0.0.0.0/0 police rate $BESTEFFORT_RATE burst $BESTEFFORT_POLICE_BURST drop flowid :3
355
 
356
	$ECHO
357 358
}

359 360 361
###########################################################################################################
#
#
362 363
get_stats()
{
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
	echo $DEV Statistics
	echo
	echo "   Classes:"
	echo "--------------------------"
		$ECHO tc -s class show dev $DEV

	echo
	echo "   Leaf Queuing Disciplines:"
	echo "--------------------------"
		$ECHO tc -s qdisc show dev $DEV

	echo
	echo "   EGRESS Filters:"
	echo "--------------------------"
		$ECHO tc -s filter show dev $DEV
379 380
			$ECHO tc -s filter show dev $DEV parent 10: # if PRIO qdisc is applied
		
381 382 383 384 385 386
	echo
	echo "   INGRESS Policing Filters:"
	echo "--------------------------"
		$ECHO tc -s filter show dev $DEV parent ffff:

	echo
387 388
}

389 390 391
###########################################################################################################
#
#
392 393 394 395 396 397 398
compile()
{
	DEBUG=1
	
	start
}

399 400 401
###########################################################################################################
#
#
402 403 404 405 406 407 408
start()
{
	checkconfig || return 1

	if [ $DEBUG -gt 0 ]; then 
		ECHO="echo"
	else
409
		ebegin "Starting QoS at $DEV"
410 411
	fi

412
	reset
413

414 415 416 417 418
	for LINK_DIRECTION in EGRESS INGRESS; do
		configure $LINK_DIRECTION
			eval ALG_CMD="\$$LINK_DIRECTION"_ALG
				if [ ! "$ALG_CMD" = "none" ]; then
					set_$ALG_CMD
419 420 421 422 423 424 425 426
				fi
	done

	if [ $DEBUG -eq 0 ]; then
	 	eend $?
	fi
}

427 428 429
###########################################################################################################
#
#
430 431 432 433 434 435 436
stop()
{
	checkconfig || return 1

	if [ $DEBUG -gt 0 ]; then
		ECHO="echo"
	else
437
		ebegin "Stopping QoS at $DEV"
438 439 440 441 442 443 444 445 446
	fi

	reset

	if [ $DEBUG -eq 0 ]; then
		eend $?
	fi
}

447 448 449
###########################################################################################################
#
#
450 451 452 453 454 455
restart()
{
	stop
	start
}

456 457 458 459
###########################################################################################################
#
#
describe()
460
{
461 462
	checkconfig || return 1

463
	get_stats
464
}