-
Notifications
You must be signed in to change notification settings - Fork 11
/
SimpleHFSCgamerscript.sh
executable file
·470 lines (333 loc) · 15.3 KB
/
SimpleHFSCgamerscript.sh
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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
#!/bin/sh
## "atm" for old-school DSL or change to "DOCSIS" for cable modem, or
## "other" or anything else, for everything else
LINKTYPE="ethernet"
USEVETHDOWN=no
LANBR=br-lan
WAN=eth0.2 # change this to your WAN device name
UPRATE=18000 #change this to your kbps upload speed
LAN=eth0.1 # change to your LAN device if you don't use veth/bridge,
# leave it alone if you use veth, it will get set in the
# script below
DOWNRATE=65000 #change this to about 80% of your download speed (in kbps)
OH=40 # number of bytes of Overhead on your line (37 is reasonable
# starting point, better to be too big than too small) probably
# likely values are between 20 and 50
PFIFOMIN=5 ## minimum number of packets in pfifo, 4 to 10 is good guess
PACKETSIZE=350 # bytes per game packet avg (guess, 250 to 500 is likely)
MAXDEL=25 # ms we try to keep max delay below for game packets after
# burst 10-25 is good 1 clock tick at 64Hz is ~16ms
BWMAXRATIO=20 ## prevent ack floods by limiting download to at most
## upload times this amount... ratio somewhere between
## 10 and 20 probably optimal. we down-prioritize
## certain ACKs to reduce the chance of a flood as well.
if [ $((DOWNRATE > UPRATE*BWMAXRATIO)) -eq 1 ]; then
echo "We limit the downrate to at most $BWMAXRATIO times the upstream rate to ensure no upstream ACK floods occur which can cause game packet drops"
DOWNRATE=$((BWMAXRATIO*UPRATE))
fi
## how many kbps of UDP upload and download do you need for your games
## across all gaming machines?
## you can tune these yourself, but a good starting point is this
## formula. this script will not work for UPRATE less than about
## 600kbps or downrate less than about 1000kbps
GAMEUP=$((UPRATE*15/100+400))
GAMEDOWN=$((DOWNRATE*15/100+400))
## you can try setting GAMEUP and GAMEDOWN manually, some report this
## works well for CoD
#GAMEUP=400
#GAMEDOWN=800
DSCPSCRIPT="/etc/dscptag.sh"
if [ ! -f $DSCPSCRIPT ]; then
workdir=$(pwd)
echo "You do not have the DSCP tagging script, downloading from github"
cd /etc/
wget https://raw.githubusercontent.com/dlakelan/routerperf/master/dscptag.sh
cd $workdir
fi
## Right now there are four possible leaf qdiscs: pfifo, red,
## fq_codel, or netem. If you use netem it's so you can intentionally
## add delay to your packets, set netemdelayms to the number of ms you
## want to add each direction. Our default is pfifo it is reported to
## be the best for use in the realtime queue
gameqdisc="pfifo"
#gameqdisc="netem"
netemdelayms="1"
netemjitterms="7"
netemdist="normal"
pktlossp="none" # set to "none" for no packet loss, or use a fraction
# like 0.015 for 1.5% packet loss in the realtime UDP
# streams
if [ $gameqdisc != "fq_codel" -a $gameqdisc != "red" -a $gameqdisc != "pfifo" -a $gameqdisc != "netem" ]; then
echo "Other qdiscs are not tested and do not work on OpenWrt yet anyway, reverting to red"
gameqdisc="red"
fi
## set up your ipsets here:
## get rid of any references to the ipsets
iptables -t mangle -F dscptag > /dev/null 2>&1
for set in realtimeset4 lowprioset4 ; do
ipset destroy $set > /dev/null 2>&1
ipset create $set hash:ip > /dev/null 2>&1
ipset flush $set > /dev/null 2>&1
done
for set in realtimeset6 lowprioset6 ; do
ipset destroy $set > /dev/null 2>&1
ipset create $set hash:ip family inet6 > /dev/null 2>&1
ipset flush $set > /dev/null 2>&1
done
## some examples to add your gaming devices to the realtime sets,
## allows you to have more than one console etc. Just add your ips
## into the list of ips in the for loop
for ip4 in 192.168.1.111 192.168.1.222; do
ipset add realtimeset4 "$ip4"
done
for ip6 in 2001:db8::1 2001:db8::2 ; do
ipset add realtimeset6 "$ip6"
done
### add ips of "low priority" machines, examples might include things
### that interfere with more important stuff, like gaming ;-). For
### example 4k TVs will typically buffer big chunks of data which can
### cause gaming stuttering but because they have buffers they don't
### really need super high priority
for ip4 in 192.168.1.111 192.168.1.222; do
ipset add lowprioset4 "$ip4"
done
for ip6 in 2001:db8::1 2001:db8::2 ; do
ipset add lowprioset6 "$ip6"
done
## Help the system prioritize your gaming by telling it what is bulk
## traffic ... define a list of udp and tcp ports used for bulk
## traffic such as torrents. By default we include the transmission
## torrent client default port 51413 and the default TCP ports for
## bittorrent. Use comma separated values or ranges A:B as shown. Set
## your torrent client to use a known port and include it here
UDPBULKPT="51413"
TCPBULKPT="51413,6881:6889"
WASHDSCPUP="yes"
WASHDSCPDOWN="yes"
######################### CUSTOMIZATIONS GO ABOVE THIS LINE ###########
if [ $USEVETHDOWN = "yes" ] ; then
ip link show lanveth || ip link add lanveth type veth peer name lanbrport
LAN=lanveth
ip link set lanveth up
ip link set lanbrport up
ip link set lanbrport master $LANBR
ip route flush table 100
ip route add default dev $LAN table 100
ip -6 route add default dev $LAN table 100
ip rule add iif $WAN priority 100 table 100
ip -6 rule add iif $WAN priority 100 table 100
fi
cat <<EOF
This script prioritizes the UDP packets from / to a set of gaming
machines into a real-time HFSC queue with guaranteed total bandwidth
Based on your settings:
Game upload guarantee = $GAMEUP kbps
Game download guarantee = $GAMEDOWN kbps
Download direction only works if you install this on a *wired* router
and there is a separate AP wired into your network, because otherwise
there are multiple parallel queues for traffic to leave your router
heading to the LAN.
Based on your link total bandwidth, the **minimum** amount of jitter
you should expect in your network is about:
UP = $(((1500*8)*3/UPRATE)) ms
DOWN = $(((1500*8)*3/DOWNRATE)) ms
In order to get lower minimum jitter you must upgrade the speed of
your link, no queuing system can help.
Please note for your display rate that:
at 30Hz, one on screen frame lasts: 33.3 ms
at 60Hz, one on screen frame lasts: 16.6 ms
at 144Hz, one on screen frame lasts: 6.9 ms
This means the typical gamer is sensitive to as little as on the order
of 5ms of jitter. To get 5ms minimum jitter you should have bandwidth
in each direction of at least:
$((1500*8*3/5)) kbps
The queue system can ONLY control bandwidth and jitter in the link
between your router and the VERY FIRST device in the ISP
network. Typically you will have 5 to 10 devices between your router
and your gaming server, any of those can have variable delay and ruin
your gaming, and there is NOTHING that your router can do about it.
EOF
ipt64 (){
iptables $*
ip6tables $*
}
setqdisc () {
DEV=$1
RATE=$2
MTU=1500
highrate=$((RATE*90/100))
lowrate=$((RATE*10/100))
gamerate=$3
useqdisc=$4
DIR=$5
tc qdisc del dev "$DEV" root > /dev/null 2>&1
case $LINKTYPE in
"atm")
tc qdisc replace dev "$DEV" handle 1: root stab mtu 2047 tsize 512 mpu 68 overhead ${OH} linklayer atm hfsc default 13
;;
"DOCSIS")
tc qdisc replace dev $DEV stab overhead 25 linklayer ethernet handle 1: root hfsc default 13
;;
*)
tc qdisc replace dev $DEV stab overhead 40 linklayer ethernet handle 1: root hfsc default 13
;;
esac
DUR=$((5*1500*8/RATE))
if [ $DUR -lt 25 ]; then
DUR=25
fi
# if we're on the LAN side, create a queue just for traffic from the
# router, like LUCI and DNS lookups
if [ $DIR = "lan" ]; then
tc class add dev "$DEV" parent 1: classid 1:2 hfsc ls m1 50000kbit d "${DUR}ms" m2 10000kbit
fi
#limit the link overall:
tc class add dev "$DEV" parent 1: classid 1:1 hfsc ls m2 "${RATE}kbit" ul m2 "${RATE}kbit"
gameburst=$((gamerate*10))
if [ $gameburst -gt $((RATE*97/100)) ] ; then
gameburst=$((RATE*97/100));
fi
# high prio realtime class
tc class add dev "$DEV" parent 1:1 classid 1:11 hfsc rt m1 "${gameburst}kbit" d "${DUR}ms" m2 "${gamerate}kbit"
# fast non-realtime
tc class add dev "$DEV" parent 1:1 classid 1:12 hfsc ls m1 "$((RATE*70/100))kbit" d "${DUR}ms" m2 "$((RATE*30/100))kbit"
# normal
tc class add dev "$DEV" parent 1:1 classid 1:13 hfsc ls m1 "$((RATE*20/100))kbit" d "${DUR}ms" m2 "$((RATE*45/100))kbit"
# low prio
tc class add dev "$DEV" parent 1:1 classid 1:14 hfsc ls m1 "$((RATE*7/100))kbit" d "${DUR}ms" m2 "$((RATE*15/100))kbit"
# bulk
tc class add dev "$DEV" parent 1:1 classid 1:15 hfsc ls m1 "$((RATE*3/100))kbit" d "${DUR}ms" m2 "$((RATE*10/100))kbit"
## set this to "drr" or "qfq" to differentiate between different game
## packets, or use "pfifo" to treat all game packets equally
## games often use a 1/64 s = 15.6ms tick rate +- if we're getting so
## many packets that it takes that long to drain at full RATE, we're
## in trouble, because then everything lags by a full tick... so we
## set our RED minimum to start dropping at 9ms of packets at full
## line rate, and then drop 100% by 3x that much, it's better to drop
## packets for a little while than play a whole game lagged by a full
## tick
REDMIN=$((RATE*MAXDEL/3/8))
REDMAX=$((RATE * MAXDEL/8))
# for fq_codel
INTVL=$((100+2*1500*8/RATE))
TARG=$((540*8/RATE+4))
case $useqdisc in
"drr")
tc qdisc add dev "$DEV" parent 1:11 handle 2:0 drr
tc class add dev "$DEV" parent 2:0 classid 2:1 drr quantum 8000
tc qdisc add dev "$DEV" parent 2:1 handle 10: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
tc class add dev "$DEV" parent 2:0 classid 2:2 drr quantum 4000
tc qdisc add dev "$DEV" parent 2:2 handle 20: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
tc class add dev "$DEV" parent 2:0 classid 2:3 drr quantum 1000
tc qdisc add dev "$DEV" parent 2:3 handle 30: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
## with this send high priority game packets to 10:, medium to 20:, normal to 30:
## games will not starve but be given relative importance based on the quantum parameter
;;
"qfq")
tc qdisc add dev "$DEV" parent 1:11 handle 2:0 qfq
tc class add dev "$DEV" parent 2:0 classid 2:1 qfq weight 8000
tc qdisc add dev "$DEV" parent 2:1 handle 10: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
tc class add dev "$DEV" parent 2:0 classid 2:2 qfq weight 4000
tc qdisc add dev "$DEV" parent 2:2 handle 20: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
tc class add dev "$DEV" parent 2:0 classid 2:3 qfq weight 1000
tc qdisc add dev "$DEV" parent 2:3 handle 30: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
## with this send high priority game packets to 10:, medium to 20:, normal to 30:
## games will not starve but be given relative importance based on the weight parameter
;;
"pfifo")
tc qdisc add dev "$DEV" parent 1:11 handle 10: pfifo limit $((PFIFOMIN+MAXDEL*RATE/8/PACKETSIZE))
;;
"red")
tc qdisc add dev "$DEV" parent 1:11 handle 10: red limit 150000 min $REDMIN max $REDMAX avpkt 500 bandwidth ${RATE}kbit probability 1.0
## send game packets to 10:, they're all treated the same
;;
"fq_codel")
tc qdisc add dev "$DEV" parent "1:11" fq_codel memory_limit $((RATE*200/8)) interval "${INTVL}ms" target "${TARG}ms" quantum $((MTU * 2))
;;
"netem")
tc qdisc add dev "$DEV" parent 1:11 handle 10: netem limit $((4+9*RATE/8/500)) delay "${netemdelayms}ms" "${netemjitterms}ms" distribution "$netemdist"
;;
esac
echo "adding fq_codel qdisc for non-game traffic"
for i in 12 13 14 15; do
tc qdisc add dev "$DEV" parent "1:$i" fq_codel memory_limit $((RATE*200/8)) interval "${INTVL}ms" target "${TARG}ms" quantum $((MTU * 2))
done
}
setqdisc $WAN $UPRATE $GAMEUP $gameqdisc wan
setqdisc $LAN $DOWNRATE $GAMEDOWN $gameqdisc lan
## we want to classify packets, so use these rules
cat <<EOF
We are going to add classification rules via iptables to the
FORWARD chain. You should actually read and ensure that these
rules make sense in your firewall before running this script.
Continue? (type y or n and then RETURN/ENTER)
EOF
read -r cont
if [ "$cont" = "y" ]; then
/etc/init.d/firewall restart
ipt64 -t mangle -N dscptag
ipt64 -t mangle -F dscptag
ipt64 -t mangle -F OUTPUT ## not sure why this is needed, but firewall restart doesn't flush OUTPUT
if [ "$WASHDSCPUP" = "yes" ]; then
if [ $USEVETHDOWN = "yes" ]; then
ipt64 -t mangle -A FORWARD -i $LANBR -j DSCP --set-dscp-class CS0
else
ipt64 -t mangle -A FORWARD -i $LAN -j DSCP --set-dscp-class CS0
fi
fi
if [ "$WASHDSCPDOWN" = "yes" ]; then
ipt64 -t mangle -A FORWARD -i $WAN -j DSCP --set-dscp-class CS0
fi
ipt64 -t mangle -A POSTROUTING -j dscptag
source $DSCPSCRIPT
ipt64 -t mangle -A FORWARD -j CLASSIFY --set-class 1:13 -m comment --comment "Default" # default everything to 1:13, the "normal" qdisc
# traffic from the router to the LAN bypasses the download queue
ipt64 -t mangle -A OUTPUT -o $LAN -j CLASSIFY --set-class 1:2
## these dscp values go to realtime: EF, CS5, CS6, CS7 #Change "PS4" to your console etc. "Xbox"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class EF -j CLASSIFY --set-class 1:11 -m comment --comment "High"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class CS5 -j CLASSIFY --set-class 1:11 -m comment --comment "PS4"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class CS6 -j CLASSIFY --set-class 1:11 -m comment --comment "High"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class CS7 -j CLASSIFY --set-class 1:11 -m comment --comment "High"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class CS4 -j CLASSIFY --set-class 1:12 -m comment --comment "Fast"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class AF41 -j CLASSIFY --set-class 1:12 -m comment --comment "Fast"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class AF42 -j CLASSIFY --set-class 1:12 -m comment --comment "Fast"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class CS2 -j CLASSIFY --set-class 1:14 -m comment --comment "Low"
ipt64 -t mangle -A POSTROUTING -m dscp --dscp-class CS1 -j CLASSIFY --set-class 1:15 -m comment --comment "Bulk"
## wash DSCP out to the ISP now that we used it for classifying
if [ "$WASHDSCPUP" = "yes" ]; then
ipt64 -t mangle -A FORWARD -o $WAN -j DSCP --set-dscp-class CS0
fi
case $gameqdisc in
"red")
;;
"pfifo")
;;
*)
echo "YOU MUST PLACE CLASSIFIERS FOR YOUR GAME TRAFFIC HERE"
echo "SEND GAME TRAFFIC TO 2:1 (high) or 2:2 (medium) or 2:3 (normal)"
echo "Requires use of tc filters! -j CLASSIFY won't work!"
;;
esac
if [ $UPRATE -lt 3000 -o $DOWNRATE -lt 3000 ]; then
ipt64 -t mangle -F FORWARD
fi
if [ $UPRATE -lt 3000 ]; then
ipt64 -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -o $LAN -j TCPMSS --set-mss 540
fi
if [ $DOWNRATE -lt 3000 ]; then
## need to clamp MSS to 540 bytes in both directions to reduce
## the latency increase caused by 1 packet ahead of us in the
## queue since rates are too low to send 1500 byte packets at acceptable delay
ipt64 -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -o $WAN -j TCPMSS --set-mss 540
fi
else
cat <<EOF
Check the rules and come back when you're ready.
EOF
fi
echo "DONE!"
if [ "$gameqdisc" = "red" ]; then
echo "Can not output tc -s qdisc because it crashes on OpenWrt when using RED qdisc, but things are working!"
else
tc -s qdisc
fi