3 # A 3 bin tc_codel and ipv6 enabled shaping script for
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License version 2 as
8 # published by the Free Software Foundation.
10 # Copyright (C) 2012-4 Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
12 # Compared to the complexity that debloat had become
13 # this cleanly shows a means of going from diffserv marking
14 # to prioritization using the current tools (ip(6)tables
15 # and tc. I note that the complexity of debloat exists for
16 # a reason, and it is expected that script is run first
17 # to setup various other parameters such as BQL and ethtool.
18 # (And that the debloat script has setup the other interfaces)
20 # You need to jiggle these parameters. Note limits are tuned towards a <10Mbit uplink <60Mbup down
22 #sm: Goal to create a set of tc filters that also apply on pppoe encapsulated packets
23 # but having multiple filters run in succession is slow, so look at tc filter hashing
24 # (this should help cut down the number of OPs per packet considerably)
27 .
/usr
/lib
/sqm
/functions.sh
28 #sqm_logger IPT_MASK: ${IPT_MASK_STRING}
31 ipt
-t mangle
-N QOS_MARK_
${IFACE}
33 ipt
-t mangle
-A QOS_MARK_
${IFACE} -j MARK
--set-mark 0x2${IPT_MASK_STRING}
34 # You can go further with classification but...
35 ipt
-t mangle
-A QOS_MARK_
${IFACE} -m dscp
--dscp-class CS1
-j MARK
--set-mark 0x3${IPT_MASK_STRING}
36 ipt
-t mangle
-A QOS_MARK_
${IFACE} -m dscp
--dscp-class CS6
-j MARK
--set-mark 0x1${IPT_MASK_STRING}
37 ipt
-t mangle
-A QOS_MARK_
${IFACE} -m dscp
--dscp-class EF
-j MARK
--set-mark 0x1${IPT_MASK_STRING}
38 ipt
-t mangle
-A QOS_MARK_
${IFACE} -m dscp
--dscp-class AF42
-j MARK
--set-mark 0x1${IPT_MASK_STRING}
39 ipt
-t mangle
-A QOS_MARK_
${IFACE} -m tos
--tos Minimize-Delay
-j MARK
--set-mark 0x1${IPT_MASK_STRING}
41 # and it might be a good idea to do it for udp tunnels too
43 # Turn it on. Preserve classification if already performed
45 if [ "$SQUASH_DSCP" = "1" ]
47 sqm_logger
"Squashing differentiated services code points (DSCP) from ingress."
48 ipt
-t mangle
-I PREROUTING
-i $IFACE -m dscp
! --dscp 0 -j DSCP
--set-dscp-class be
50 sqm_logger
"Keeping differentiad services code points (DSCP) from ingress."
51 ipt
-t mangle
-A PREROUTING
-i $IFACE -m mark
--mark 0x00${IPT_MASK_STRING} -g QOS_MARK_
${IFACE}
54 ipt
-t mangle
-A POSTROUTING
-o $IFACE -m mark
--mark 0x00${IPT_MASK_STRING} -g QOS_MARK_
${IFACE}
56 # The Syn optimization was nice but fq_codel does it for us
57 # ipt -t mangle -A PREROUTING -i s+ -p tcp -m tcp --tcp-flags SYN,RST,ACK SYN -j MARK --set-mark 0x01
58 # Not sure if this will work. Encapsulation is a problem period
60 ipt
-t mangle
-I PREROUTING
-i vtun
+ -p tcp
-j MARK
--set-mark 0x2${IPT_MASK_STRING} # tcp tunnels need ordering
62 # Emanating from router, do a little more optimization
63 # but don't bother with it too much.
65 ipt
-t mangle
-A OUTPUT
-p udp
-m multiport
--ports 123,53 -j DSCP
--set-dscp-class AF42
67 #Not clear if the second line is needed
68 #ipt -t mangle -A OUTPUT -o $IFACE -g QOS_MARK_${IFACE}
73 MYBURST
=1600 #sm: make burst and cburst as well as quantum configurable for ingress and egress in the GUI
79 PRIO_RATE
=`expr $CEIL / 3` # Ceiling for priority
80 BE_RATE
=`expr $CEIL / 6` # Min for best effort
81 BK_RATE
=`expr $CEIL / 6` # Min for background
82 BE_CEIL
=`expr $CEIL - 16` # A little slop at the top
84 LQ
="quantum `get_mtu $IFACE $CEIL`"
85 HTB_BURSTS
="burst ${MYBURST} cburst ${MYBURST}"
87 $TC qdisc del dev
$IFACE root
2> /dev
/null
88 $TC qdisc add dev
$IFACE root handle
1: `get_stab_string` htb default
12
89 $TC class add dev
$IFACE parent
1: classid
1:1 htb
$LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit
`get_htb_adsll_string`
90 $TC class add dev
$IFACE parent
1:1 classid
1:10 htb
$LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit prio
0 `get_htb_adsll_string`
91 $TC class add dev
$IFACE parent
1:1 classid
1:11 htb
$LQ ${HTB_BURSTS} rate
128kbit ceil
${PRIO_RATE}kbit prio
1 `get_htb_adsll_string`
92 $TC class add dev
$IFACE parent
1:1 classid
1:12 htb
$LQ ${HTB_BURSTS} rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio
2 `get_htb_adsll_string`
93 $TC class add dev
$IFACE parent
1:1 classid
1:13 htb
$LQ ${HTB_BURSTS} rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio
3 `get_htb_adsll_string`
95 $TC qdisc add dev
$IFACE parent
1:11 handle
110: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${PRIO_RATE}` ${EQDISC_OPTS}
96 $TC qdisc add dev $IFACE parent 1:12 handle 120: $QDISC `get_limit
${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BE_RATE}` ${EQDISC_OPTS}
97 $TC qdisc add dev
$IFACE parent
1:13 handle
130: $QDISC `get_limit ${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS}
100 #sm: for testing we need a band to collect PPPOEd packets
101 $TC class add dev $IFACE parent 1:1 classid 1:14 htb $LQ rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string
`
102 $TC qdisc add dev $IFACE parent 1:14 handle 140: $QDISC `get_limit
${ELIMIT}` `get_target "${ETARGET}" ${UPLINK}` `get_ecn ${EECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${EQDISC_OPTS}
105 # Need a catchall rule (should also match VLANs and PPPoE packets)
107 $TC filter add dev
$IFACE parent
1:0 protocol all prio
999 u32 \
108 match ip protocol
0 0x00 flowid
1:12
110 # FIXME should probably change the filter here to do pre-nat
112 $TC filter add dev
$IFACE parent
1:0 protocol ip prio
1 handle
1 fw classid
1:11
113 $TC filter add dev
$IFACE parent
1:0 protocol ip prio
2 handle
2 fw classid
1:12
114 $TC filter add dev
$IFACE parent
1:0 protocol ip prio
3 handle
3 fw classid
1:13
116 # ipv6 support. Note that the handle indicates the fw mark bucket that is looked for
118 $TC filter add dev
$IFACE parent
1:0 protocol ipv6 prio
4 handle
1 fw classid
1:11
119 $TC filter add dev
$IFACE parent
1:0 protocol ipv6 prio
5 handle
2 fw classid
1:12
120 $TC filter add dev
$IFACE parent
1:0 protocol ipv6 prio
6 handle
3 fw classid
1:13
124 $TC filter add dev
$IFACE parent
1:0 protocol arp prio
7 handle
1 fw classid
1:11
126 # ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods
129 $TC filter add dev
$IFACE parent
1:0 protocol ip prio
8 \
130 u32 match ip protocol
1 0xff flowid
1:13
132 $TC filter add dev
$IFACE parent
1:0 protocol ipv6 prio
9 \
133 u32 match ip protocol
1 0xff flowid
1:13
138 # PPPoE encapsulated packets traversing the router (e.g.: the router does PPPoE termination but we shape
139 # on the underlaying ethernet interface instead of the pppoe device)
141 PPPOE_SESSION_ETHERTYPE
="0x8864"
142 PPPOE_DISCOVERY_ETHERTYPE
="0x8863"
143 PPP_PROTO_IP4
="0x0021"
144 PPP_PROTO_IP6
="0x0057"
145 ARP_PROTO_IP4
="0x0806"
147 # NOTE it seems prio can not be reused?
148 #$TC filter add dev $IFACE protocol 0x8863 parent 1:0 prio 1 u32 flowid 1:14
149 # PPPoE can be selected for by ether_type, the encapsulated IP version from the PPP (0x0021 IPv4, 0x0057 IPv6)
150 #U32_PREFIX="$TC filter add dev $IFACE" parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE}"
152 #BE: 1:12 is the default anyway, but this will catch all non marked packets
153 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 400 u32 \
154 # match u16 ${PPP_PROTO_IP4} 0xffff at 6 \
155 # match u8 0x00 0xfb at 9 \
159 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
401 u32 \
160 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
161 match u8
0x90 0xfc at 9 \
164 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
402 u32 \
165 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
166 match u8
0xb8 0xfc at 9 \
169 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
403 u32 \
170 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
171 match u8
0x20 0xf0 at 9 \
174 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
404 u32 \
175 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
176 match u8
0x10 0xf0 at 9 \
179 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
405 u32 \
180 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
181 match u8
0xc0 0xf0 at 9 \
184 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
406 u32 \
185 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
186 match u8
0xe0 0xf0 at 9 \
191 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 409 u32 \
192 # match u16 ${ARP_PROTO_IP4} 0xffff at 6 \
195 # ICMP traffic - Don't impress your friends. Deoptimize to manage ping floods
196 # better instead; sm: really only deprio echo requestst and echo replies instead?
197 # ECHO request, the rest stays in best effort
198 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
410 u32 \
199 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
200 match u8
0x01 0xff at 17 \
201 match u8
0x08 0xff at 28 \
204 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
410 u32 \
205 match u16
${PPP_PROTO_IP4} 0xffff at 6 \
206 match u8
0x01 0xff at 17 \
207 match u8
0x00 0xff at 28 \
210 ## ICMPv6 133-137 (NDP) is equivalent to IPv4 ARP, so only push echo request and reply into the bulk class
212 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
213 # match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
214 # match u8 0x85 0xff at 48 \
215 # match u8 0x3a 0xff at 14 \
218 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
219 # match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
220 # match u8 0x86 0xff at 48 \
221 # match u8 0x3a 0xff at 14 \
224 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
225 # match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
226 # match u8 0x87 0xff at 48 \
227 # match u8 0x3a 0xff at 14 \
230 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
231 # match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
232 # match u8 0x88 0xff at 48 \
233 # match u8 0x3a 0xff at 14 \
236 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 610 u32 \
237 # match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
238 # match u8 0x89 0xff at 48 \
239 # match u8 0x3a 0xff at 14 \
242 # ICMPv6 echo request
243 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
610 u32 \
244 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
245 match u8
0x3a 0xff at 14 \
246 match u8
0x80 0xff at 48 \
249 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
610 u32 \
250 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
251 match u8
0x3a 0xff at 14 \
252 match u8
0x81 0xff at 48 \
259 #BE: careful, will override ICMP
260 #$TC filter add dev $IFACE parent 1:0 protocol ${PPPOE_SESSION_ETHERTYPE} prio 600 u32 \
261 # match u16 ${PPP_PROTO_IP6} 0xffff at 6 \
262 # match u16 0x0000 0x0fb0 at 8 \
265 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
601 u32 \
266 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
267 match u16
0x0900 0x0fc0 at 8 \
270 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
602 u32 \
271 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
272 match u16
0x0b80 0x0fc0 at 8 \
275 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
603 u32 \
276 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
277 match u16
0x0200 0x0fc0 at 8 \
280 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
604 u32 \
281 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
282 match u16
0x0100 0x0fc0 at 8 \
285 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
605 u32 \
286 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
287 match u16
0x0c00 0x0fc0 at 8 \
290 $TC filter add dev
$IFACE parent
1:0 protocol
${PPPOE_SESSION_ETHERTYPE} prio
606 u32 \
291 match u16
${PPP_PROTO_IP6} 0xffff at 6 \
292 match u16
0x0e00 0x0fc0 at 8 \
305 PRIO_RATE
=`expr $CEIL / 3` # Ceiling for prioirty
306 BE_RATE
=`expr $CEIL / 6` # Min for best effort
307 BK_RATE
=`expr $CEIL / 6` # Min for background
308 BE_CEIL
=`expr $CEIL - 16` # A little slop at the top
310 LQ
="quantum `get_mtu $IFACE $CEIL`"
311 HTB_BURSTS
="burst ${MYBURST} cburst ${MYBURST}"
313 $TC qdisc del dev
$IFACE handle ffff
: ingress
2> /dev
/null
314 $TC qdisc add dev
$IFACE handle ffff
: ingress
316 $TC qdisc del dev
$DEV root
2> /dev
/null
318 if [ "$SQUASH_INGRESS" = "1" ]
320 sqm_logger
"Do not perform DSCP based filtering on ingress. (1-tier classification)"
321 # Revert to no dscp based filtering
322 $TC qdisc del dev
$DEV root
2>/dev
/null
323 $TC qdisc add dev
$DEV root handle
1: `get_stab_string` htb default
10
324 $TC class add dev
$DEV parent
1: classid
1:1 htb
$LQ ${HTB_BURSTS} rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit
`get_htb_adsll_string`
325 $TC class add dev
$DEV parent
1:1 classid
1:10 htb
$LQ ${HTB_BURSTS} rate ${DOWNLINK}kbit ceil ${DOWNLINK}kbit prio
0 `get_htb_adsll_string`
326 $TC qdisc add dev
$DEV parent
1:10 handle
110: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_flows ${DOWNLINK}` ${IQDISC_OPTS}
329 sqm_logger "Perform DSCP based filtering on ingress. (3-tier classification)"
330 $TC qdisc add dev $DEV root handle 1: `get_stab_string
` htb default 12
331 $TC class add dev $DEV parent 1: classid 1:1 htb $LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit `get_htb_adsll_string
`
332 $TC class add dev $DEV parent 1:1 classid 1:10 htb $LQ ${HTB_BURSTS} rate ${CEIL}kbit ceil ${CEIL}kbit prio 0 `get_htb_adsll_string
`
333 $TC class add dev $DEV parent 1:1 classid 1:11 htb $LQ ${HTB_BURSTS} rate 32kbit ceil ${PRIO_RATE}kbit prio 1 `get_htb_adsll_string
`
334 $TC class add dev $DEV parent 1:1 classid 1:12 htb $LQ ${HTB_BURSTS} rate ${BE_RATE}kbit ceil ${BE_CEIL}kbit prio 2 `get_htb_adsll_string
`
335 $TC class add dev $DEV parent 1:1 classid 1:13 htb $LQ ${HTB_BURSTS} rate ${BK_RATE}kbit ceil ${BE_CEIL}kbit prio 3 `get_htb_adsll_string
`
337 # I'd prefer to use a pre-nat filter but that causes permutation...
339 $TC qdisc add dev $DEV parent 1:11 handle 110: $QDISC `get_limit
${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 500` `get_flows ${PRIO_RATE}` ${IQDISC_OPTS}
340 $TC qdisc add dev
$DEV parent
1:12 handle
120: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 1500` `get_flows ${BE_RATE}` ${IQDISC_OPTS}
341 $TC qdisc add dev $DEV parent 1:13 handle 130: $QDISC `get_limit
${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS}
343 #sm: for PPPoE packet testing
344 $TC class add dev
$DEV parent
1:1 classid
1:14 htb
$LQ rate
${BK_RATE}kbit ceil
${BE_CEIL}kbit prio
3 `get_htb_adsll_string`
345 $TC qdisc add dev
$DEV parent
1:14 handle
140: $QDISC `get_limit ${ILIMIT}` `get_target "${ITARGET}" ${DOWNLINK}` `get_ecn ${IECN}` `get_quantum 300` `get_flows ${BK_RATE}` ${IQDISC_OPTS}
356 # redirect all IP packets arriving in $IFACE to ifb0
358 $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
359 match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
366 if [ "$UPLINK" -ne 0 ];
369 sqm_logger "egress shaping activated"
371 sqm_logger "egress shaping deactivated"
372 tc qdisc del dev $IFACE root 2> /dev/null
374 if [ "$DOWNLINK" -ne 0 ];
377 sqm_logger "ingress shaping activated"
379 sqm_logger "ingress shaping deactivated"
380 tc qdisc del dev $DEV root 2> /dev/null
381 tc qdisc del dev $IFACE ingress 2> /dev/null
387 # This alternate shaper attempts to go for 1/u performance in a clever way
388 # http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos.sh;hb=HEAD
391 # This does the right thing with ipv6 traffic.
392 # It also tries to leverage diffserv to some sane extent. In particular,
393 # the 'priority' queue is limited to 33% of the total, so EF, and IMM traffic
394 # cannot starve other types. The rfc suggested 30%. 30% is probably
395 # a lot in today's world.