summaryrefslogtreecommitdiffstats
path: root/hfsc_shaper.sh
blob: 3504c6893af038301da560f1bab5c12544779c53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#!/bin/bash
#----------------------------------------------------
# File: hfsc_shaper.sh
# Version: 0.2
# Edited: Florian "Bluewind" Pritz <bluewind@xinu.at>
# Author: Maciej Bliziński, http://automatthias.wordpress.com/
#----------------------------------------------------
#
# Special Thanks to 
# Maciej Bliziński, http://automatthias.wordpress.com/
#
# References:
# http://www.voip-info.org/wiki/view/QoS+Linux+with+HFSC
# http://www.nslu2-linux.org/wiki/HowTo/EnableTrafficShaping
# http://www.cs.cmu.edu/~hzhang/HFSC/main.html

########################################################################
# CONFIGURATION
########################################################################

# Uplink and downlink speeds
# Normally use a bit lower values than your real speed, but
# you should experiment a bit
# downlink is unused
#DOWNLINK=75000
UPLINK=7400

# Device that connects you to the Internet
DEV="extern0"

# Traffic classes:
# 1:2 Interactive (SSH, DNS, ACK, Quake)
# 1:3 Low latency (VoIP)
# 1:4 Browsing (HTTP, HTTPs)
# 1:5 Default
# 1:6 Middle-low priority
# 1:7 Lowest priority

# Interactive class: SSH Terminal, DNS and gaming (Quake)
INTERACTIVEPORTS="22 23 53 3389 5900 22222 6667 7000 44400 7776 4949 11030 11239 143 445 25765"

# VoIP telephony
#VOIPPORTS="5060:5100 10000:11000 5000:5059 8000:8016 5004 1720 1731"
VOIPPORTS="7977 9987"
# IP addresses of the VoIP phones,
# if none, set VOIPIPS=""
VOIPIPS=""


# WWW, jabber and IRC
BROWSINGPORTS="80 443 8080 993"

# Everything unspecified will be here (inbetween Browsing and Data)

# FTP, Mail...
DATAPORTS="110 25 21 137:139 4662 4664 "

# The lowest priority traffic: eDonkey, Bittorrent, etc.
#P2PPORTS="6881:6999 36892 8333"
P2PPORTS=""

########################################################################
# CONFIGURATION ENDS HERE
########################################################################

if [ -z "$DEV" ] ; then
	echo "$0: device not set, aborting."
	exit -1
fi

function stop() {
	# Reset everything to a known state (cleared)
	tc qdisc del dev $DEV root	&> /dev/null
	tc qdisc del dev $DEV ingress &> /dev/null

	# Flush and delete tables
	iptables -t mangle --delete POSTROUTING -o $DEV -j THESHAPER &> /dev/null
	iptables -t mangle --flush		THESHAPER &> /dev/null
	iptables -t mangle --delete-chain THESHAPER &> /dev/null
}

function start() {
	#if [ -z "$DOWNLINK" ] ; then
		#echo "$0: start requires a downlink speed, aborting."
		#exit -1
	#fi
	if [ -z "$UPLINK" ] ; then
		echo "$0: start requires an uplink speed, aborting."
		exit -1
	fi

	# add HFSC root qdisc
	tc qdisc add dev $DEV root handle 1: hfsc default 5

	# add main rate limit class
	tc class add dev $DEV parent 1: classid 1:1 hfsc \
	  sc rate ${UPLINK}kbit ul rate ${UPLINK}kbit

	# Interactive traffic: guarantee full uplink for 50ms, then
	# 5/10 of the uplink
	tc class add dev $DEV parent 1:1  classid 1:2 hfsc \
	  sc m1   ${UPLINK}kbit d  50ms m2 $((5*$UPLINK/10))kbit \
	  ul rate ${UPLINK}kbit

	# VoIP: guarantee full uplink for 200ms, then 3/10
	tc class add dev $DEV parent 1:1  classid 1:3 hfsc \
	  sc m1 ${UPLINK}kbit d 200ms m2 $((3*$UPLINK/10))kbit \
	  ul rate ${UPLINK}kbit

	# Browsing: guarantee 3/10 uplink for 200ms, then
	# guarantee 1/10
	tc class add dev $DEV parent 1:1  classid 1:4 hfsc \
	  sc m1 $((3*$UPLINK/10))kbit d 200ms m2 $((1*$UPLINK/10))kbit \
	  ul rate ${UPLINK}kbit

	# Default traffic: guarantee 1/10 uplink for 100ms,
	# then guarantee 3/20
	tc class add dev $DEV parent 1:1  classid 1:5 hfsc \
	  sc m1 $((1*$UPLINK/10))kbit d 100ms m2 $((3*$UPLINK/20))kbit \
	  ul rate ${UPLINK}kbit

	# Middle-low taffic: don't guarantee anything for the first 5 seconds,
	# then guarantee 1/10
	tc class add dev $DEV parent 1:1  classid 1:6 hfsc \
	  sc m1			0 d   5s m2 $((1*$UPLINK/10))kbit \
	  ul rate ${UPLINK}kbit

	# Lowest taffic: don't guarantee anything for the first 10 seconds,
	# then guarantee 1/20
	  #ls m2 $((1*$UPLINK/200))kbit \
	tc class add dev $DEV parent 1:1  classid 1:7 hfsc \
	  sc m1			0 d   5s m2 10kbit \
		  ul rate $((UPLINK-200))kbit

	# add THESHAPER chain to the mangle table in iptables
	iptables -t mangle --new-chain THESHAPER
	iptables -t mangle --insert POSTROUTING -o $DEV -j THESHAPER

	# Type of service filters (see /etc/iproute2/rt_dsfield)
	iptables -t mangle -A THESHAPER \
	  -m tos --tos 0x10 \
	  -j CLASSIFY --set-class 1:2

	iptables -t mangle -A THESHAPER \
	  -m tos --tos 0x08 \
	  -j CLASSIFY --set-class 1:7

	# To speed up downloads while an upload is going on, put short ACK
	# packets in the interactive class
	iptables -t mangle -A THESHAPER \
	  -p tcp \
	  -m tcp --tcp-flags FIN,SYN,RST,ACK ACK \
	  -m length --length :64 \
	  -j CLASSIFY --set-class 1:2

	# put large (512+) icmp packets in default category
	iptables -t mangle -A THESHAPER \
	  -p icmp \
	  -m length --length 512: \
	  -j CLASSIFY --set-class 1:5

	# small ICMP in the interactive class
	iptables -t mangle -A THESHAPER \
	  -p icmp  \
	  -m length --length :512 \
	  -j CLASSIFY --set-class 1:2

	setclassbyport() {
		port=$1
		CLASS=$2
		iptables -t mangle -A THESHAPER -p udp --sport $port -j CLASSIFY --set-class $CLASS
		iptables -t mangle -A THESHAPER -p udp --dport $port -j CLASSIFY --set-class $CLASS
		iptables -t mangle -A THESHAPER -p tcp --sport $port -j CLASSIFY --set-class $CLASS
		iptables -t mangle -A THESHAPER -p tcp --dport $port -j CLASSIFY --set-class $CLASS
	}

	for port in $INTERACTIVEPORTS;	do setclassbyport $port 1:2; done
	for port in $VOIPPORTS;			do setclassbyport $port 1:3; done
	for port in $BROWSINGPORTS;		do setclassbyport $port 1:4; done
	for port in $DATAPORTS;			do setclassbyport $port 1:6; done
	for port in $P2PPORTS;			do setclassbyport $port 1:7; done

	for VOIP in $VOIPIPS; do
		iptables -t mangle -A THESHAPER --src $VOIP -j CLASSIFY --set-class 1:3
		iptables -t mangle -A THESHAPER --dst $VOIP -j CLASSIFY --set-class 1:3
	done

	# put large (1024+) https packets in default category
	iptables -t mangle -A THESHAPER \
	  -p tcp --dport 443 \
	  -m length --length 1024: \
	  -j CLASSIFY --set-class 1:6

	# put large (1024+) http packets in default category
	iptables -t mangle -A THESHAPER \
	  -p tcp --dport 80 \
	  -m length --length 1024: \
	  -j CLASSIFY --set-class 1:6

	# put large (1024+) packets in default category
	#iptables -t mangle -A THESHAPER \
	  #-p tcp \
	  #-m length --length 1024: \
	  #-j CLASSIFY --set-class 1:6

	# put large (1024+) ssh packets in default category
	iptables -t mangle -A THESHAPER \
	  -p tcp --dport 22 \
	  -m length --length 1024: \
	  -j CLASSIFY --set-class 1:6

	# put all traffic from user torrent into p2p category (only works for the host this script runs on)
	#iptables -t mangle -A THESHAPER \
	  #--match owner --uid-owner 169 -j CLASSIFY --set-class 1:7

	# Try to control the incoming traffic as well.
	# Set up ingress qdisc
	#tc qdisc add dev $DEV handle ffff: ingress

	# Filter everything that is coming in too fast
	# It's mostly HTTP downloads that keep jamming the downlink, so try to restrict
	# them to 95/100 of the downlink.

	# FIXME: slows down too much
	  #tc filter add dev $DEV parent ffff: protocol ip prio 50 \
		 #u32 match ip src 0.0.0.0/0 \
		 #match ip protocol 6 0xff \
		 #match ip sport 80 0xffff \
		 #police rate $((95*${DOWNLINK}/100))kbit \
		 #burst 10k drop flowid :1

	  #tc filter add dev $DEV parent ffff: protocol ip prio 50 \
		 #u32 match ip src 0.0.0.0/0 \
		 #police rate $((95*${DOWNLINK}/100))kbit \
		 #burst $((95*${DOWNLINK}/100*2)) drop flowid :1
}

function status() {
	echo "[qdisc]"
	tc -s qdisc show dev $DEV

	echo ""
	echo "[class]"
	tc -s class show dev $DEV

	echo ""
	echo "[filter]"
	tc -s filter show dev $DEV

	echo ""
	echo "[iptables]"
	iptables -n -t mangle -L THESHAPER -v -x
}

case "$1" in
	status)
		status
		;;
	stop)
		stop
		;;
	start)
		start
		;;
	restart)
		stop
		start
		;;
	*)
		echo "$0 [ACTION] [device]"
		echo "ACTION := { start | stop | status | restart }"
		exit
		;;
esac