Skip to content

Commit 0b7f559

Browse files
committed
Large rework due to a fundamental misunderstanding of how HTB works.
Switch to using a DRR below each host HTB classes instead of HTB sub classes. I had made the bad assumption that HTB divides bandwidth along the class hierarchy. In reality what it does is only divide bandwidth at the leaves according to the class priority. This meant that in the previous version high or medium priority traffic from one host would affect low priority traffic for another. This may be useful to someone but isn't what I was aiming for. Also removed the limitations around the number of hosts by making full use of the major namespace. QDiscs are named and created in depth first order. Lots of small cleanups as well.
1 parent d695cf0 commit 0b7f559

File tree

2 files changed

+54
-59
lines changed

2 files changed

+54
-59
lines changed

src-3tos.sh

+54-59
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,26 @@
99
# value to something sane for your network.
1010
#
1111
# The hierarchy with 8 host buckets looks like:
12-
# http://git.coverfire.com/?p=linux-qos-scripts.git;a=blob;f=src-3tos_8hosts.png;hb=HEAD
1312
#
14-
# Or if you prefer ASCII:
13+
# ASCII:
1514
#
1615
# Interface
1716
# |
1817
# HTB 1:1
1918
# / \
2019
# Host Bucket 1 .. NUM_HOST_BUCKETS [Classes 1:10-1:(10+NUM_HOST_BUCKETS)]
20+
# |
21+
# DRR
2122
# / | \
22-
# High Normal Low [Priority classes are named 1:(HOST_BUCKET * 16 + 1)]
23+
# High Normal Low [DRR: With three classes]
2324
# |
24-
# Flow Bucket 1 .. NUM_FLOW_BUCKETS [Flow QDiscs are named (HOST_BUCKET * 16 + 1 + 1):0]
25+
# Leaf QDisc [Choose the type of the leaf QDisc below]
2526
#
26-
# The 0->NUM_FLOW_BUCKETS exist under every high, normal and low class. SFQ, SFB and FQ_CODEL
27-
# have embedded classes per flow and do not use NUM_FLOW_BUCKETS.
27+
# The tree is created and the QDiscs are named in depth first order.
2828
#
29-
# Yes, the class and QDisc naming is confusing and there are probably bugs
30-
# if you set the NUM_HOST_BUCKETS or NUM_FLOW_BUCKETS too high.
31-
#
32-
####
29+
######################
3330
# Config
34-
####
31+
######################
3532

3633
#TC="/usr/local/sbin/tc"
3734
TC=`which tc`
@@ -46,15 +43,6 @@ DEVICE="ppp0"
4643
# in your network.
4744
NUM_HOST_BUCKETS=16
4845

49-
if [ ${NUM_HOST_BUCKETS} -gt 150 ] ; then
50-
# Safety valve. Script won't work after ~160 today. This is because
51-
# the parent class for each of the per host classes is encoded in the
52-
# class minor number. This makes debugging easier but wastes the minor
53-
# number space.
54-
echo "Sorry, can't do more than 150 hosts right now."
55-
exit
56-
fi
57-
5846
# The number of flow buckets within each high, normal and low class.
5947
# If SFQ, SFB or FQ_CODEL are used this value is not used as these QDiscs
6048
# have many embedded queues.
@@ -65,12 +53,7 @@ NUM_FLOW_BUCKETS=32
6553
####
6654
# All rates are kbit/sec.
6755
# RATE should be set to just under your link rate.
68-
RATE="6400"
69-
# Below three rates should add up to RATE.
70-
# Priority of these classes is 1 (Higest) -> 3 (Lowest)
71-
RATE_1="2000"
72-
RATE_2="2400"
73-
RATE_3="2000"
56+
RATE="6200"
7457

7558
####
7659
# Queue size
@@ -136,7 +119,7 @@ HOST_KEYS="nfct-src"
136119

137120
####
138121
# Choose the type of queue for each of the three per host priority classes
139-
# Options:
122+
# Support options:
140123
# drr
141124
# sfq
142125
# fq_codel
@@ -179,7 +162,7 @@ if [ "${QUANTUM}" != "" ]; then
179162
fi
180163

181164
######################
182-
# Util functions
165+
# Utility functions
183166
######################
184167

185168
function DEBUG()
@@ -240,6 +223,16 @@ function tc_h {
240223
${OUTPUT}
241224
}
242225

226+
function get_next_free_major {
227+
if [ "${FREE_MAJOR}" == "" ]; then
228+
FREE_MAJOR=2 # Assumes 1 is used.
229+
230+
return
231+
fi
232+
233+
FREE_MAJOR=$(expr ${FREE_MAJOR} + 1)
234+
}
235+
243236
######################
244237
# Functions to create QDiscs at the leaves.
245238
######################
@@ -343,19 +336,13 @@ function priority_class_qdisc {
343336
# The real work starts here.
344337
######################
345338

346-
# Calculate the divided rate values for use later.
339+
# Calculate the divided rate value for use later.
347340
DIV_RATE=`expr ${RATE} / ${NUM_HOST_BUCKETS}`
348-
DIV_RATE_1=`expr ${RATE_1} / ${NUM_HOST_BUCKETS}`
349-
DIV_RATE_2=`expr ${RATE_2} / ${NUM_HOST_BUCKETS}`
350-
DIV_RATE_3=`expr ${RATE_3} / ${NUM_HOST_BUCKETS}`
351341

352342
echo "Number of host buckets: ${NUM_HOST_BUCKETS}"
353343
echo "Rate per host (DIV_RATE):" ${DIV_RATE}
354-
echo "High priority rate per host (DIV_RATE_1):" ${DIV_RATE_1}
355-
echo "Normal priority rate per host (DIV_RATE_2):" ${DIV_RATE_2}
356-
echo "Low priority rate per host (DIV_RATE_3):" ${DIV_RATE_3}
357344

358-
# Delete any existing qdiscs if they exist.
345+
# Delete any existing QDiscs if they exist.
359346
tc_h qdisc del dev ${DEVICE} root
360347

361348
# HTB QDisc at the root. Default all traffic into the prio qdisc.
@@ -366,7 +353,8 @@ tc_h class add dev ${DEVICE} parent 1: classid 1:1 htb rate ${RATE}kbit ${QUANTU
366353

367354
######
368355
# Create NUM_HOST_BUCKETS classes within the top-level class.
369-
# Within each of these create three priority classes.
356+
# Within each of these create a DRR with three classes which implement the three priorities.
357+
# Within each priority class create the configured leaf QDisc.
370358
######
371359
for HOST_NUM in `seq ${NUM_HOST_BUCKETS}`; do
372360
DEBUG printf "Create host class: %i\n" $HOST_NUM
@@ -375,48 +363,55 @@ for HOST_NUM in `seq ${NUM_HOST_BUCKETS}`; do
375363
DEBUG printf "\tQID: %i\n" ${QID}
376364
tc_h class add dev ${DEVICE} parent 1:1 classid 1:${QID} htb rate ${DIV_RATE}kbit ceil ${RATE}kbit ${QUANTUM} prio 0 ${LINKLAYER} ${OVERHEAD}
377365

366+
378367
######
379-
# Within each host bucket add three sub-classes, high, normal and low priority.
380-
# Priority classes are named 1:[HOST_BUCKET * 16 + 1]
381-
# Why 16? Because tc shows major:minor in hex so this makes it easy to match
382-
# the three sub class to the parent host bucket.
383-
#
384-
# Within each priority class uncomment one of the QDisc types.
368+
# Within each host class create a DRR QDisc within which we'll create the
369+
# high, normal and low priority classes.
385370
######
371+
get_next_free_major
372+
SUB_MAJOR=${FREE_MAJOR}
373+
tc_h qdisc add dev ${DEVICE} parent 1:${QID} handle ${SUB_MAJOR}: drr
374+
375+
# Filter from the host class to the DRR within it.
376+
tc_h filter add dev ${DEVICE} prio 2 protocol ip parent 1:${QID} u32 match ip dst 0.0.0.0/0 flowid ${SUB_MAJOR}:0
377+
386378

387379
###
388380
# High priority class
389381
###
390-
QID_1=`expr $QID '*' 16 + 0`
391382
DEBUG printf "\t\tHigh: %i\n" ${QID_1}
392-
tc_h class add dev ${DEVICE} parent 1:${QID} classid 1:${QID_1} htb rate ${DIV_RATE_1}kbit ceil ${RATE}kbit ${QUANTUM} prio 0 ${LINKLAYER} ${OVERHEAD}
383+
tc_h class add dev ${DEVICE} parent ${SUB_MAJOR}: classid ${SUB_MAJOR}:1 drr ${QUANTUM}
393384

394385
# Create the leaf QDisc for this priority class.
395-
priority_class_qdisc ${HIGH_PRIORITY_QDISC_TYPE} 1:${QID_1} ${QID_1}
386+
get_next_free_major
387+
SUB_PRIO_MAJOR=${FREE_MAJOR}
388+
priority_class_qdisc ${HIGH_PRIORITY_QDISC_TYPE} ${SUB_MAJOR}:1 ${SUB_PRIO_MAJOR}
396389

397390
###
398391
# Normal priority class
399392
###
400-
QID_2=`expr $QID '*' 16 + 1`
401393
DEBUG printf "\t\tNormal: %i\n" ${QID_2}
402-
tc_h class add dev ${DEVICE} parent 1:${QID} classid 1:${QID_2} htb rate ${DIV_RATE_2}kbit ceil ${RATE}kbit ${QUANTUM} prio 1 ${LINKLAYER} ${OVERHEAD}
394+
tc_h class add dev ${DEVICE} parent ${SUB_MAJOR}: classid ${SUB_MAJOR}:2 drr ${QUANTUM}
403395

404396
# Create the leaf QDisc for this priority class.
405-
priority_class_qdisc ${NORMAL_PRIORITY_QDISC_TYPE} 1:${QID_2} ${QID_2}
397+
get_next_free_major
398+
SUB_PRIO_MAJOR=${FREE_MAJOR}
399+
priority_class_qdisc ${NORMAL_PRIORITY_QDISC_TYPE} ${SUB_MAJOR}:2 ${SUB_PRIO_MAJOR}
406400

407401
###
408402
# Low priority class
409403
###
410-
QID_3=`expr $QID '*' 16 + 2`
411404
DEBUG printf "\t\tLow: %i\n" ${QID_3}
412-
tc_h class add dev ${DEVICE} parent 1:${QID} classid 1:${QID_3} htb rate ${DIV_RATE_3}kbit ceil ${RATE}kbit ${QUANTUM} prio 2 ${LINKLAYER} ${OVERHEAD}
405+
tc_h class add dev ${DEVICE} parent ${SUB_MAJOR}: classid ${SUB_MAJOR}:3 drr ${QUANTUM}
413406

414407
# Create the leaf QDisc for this priority class.
415-
priority_class_qdisc ${LOW_PRIORITY_QDISC_TYPE} 1:${QID_3} ${QID_3}
408+
get_next_free_major
409+
SUB_PRIO_MAJOR=${FREE_MAJOR}
410+
priority_class_qdisc ${LOW_PRIORITY_QDISC_TYPE} ${SUB_MAJOR}:3 ${SUB_PRIO_MAJOR}
416411

417412

418413
######
419-
# Add filters to classify based on the TOS bits.
414+
# Add filters to classify based on the TOS bits into the high, normal and low priority classes.
420415
# Only mask against the three (used) TOS bits. The final two bits are used for ECN.
421416
# TOS field is XXXDTRXX.
422417
# X= Not part of the TOS field.
@@ -427,7 +422,7 @@ for HOST_NUM in `seq ${NUM_HOST_BUCKETS}`; do
427422
# OpenSSH terminal sets D.
428423
# OpenSSH SCP/SFTP sets T.
429424
# It's easy to configure the Transmission Bittorrent client to set T (settings.json).
430-
# For home VoIP devices use an Iptables rule to set all of their traffic to have D.
425+
# For home VoIP devices I use an iptables rule to set all of their traffic to have D.
431426
#
432427
# The thinking behind the below rules is to use D as an indication of delay sensitive
433428
# and T as an indication of background (big transfer). All other combinations are put into
@@ -436,22 +431,22 @@ for HOST_NUM in `seq ${NUM_HOST_BUCKETS}`; do
436431
DEBUG printf "\t\tCreating filters\n"
437432

438433
# D bit set.
439-
tc_h filter add dev ${DEVICE} parent 1:${QID} protocol ip prio 10 u32 match ip tos 0x10 0x1c flowid 1:${QID_1}
434+
tc_h filter add dev ${DEVICE} parent ${SUB_MAJOR}: protocol ip prio 10 u32 match ip tos 0x10 0x1c flowid ${SUB_MAJOR}:1
440435

441436
# Diffserv expedited forwarding. Put this in the high priority class.
442437
# Some VoIP clients set this (ie Ekiga).
443438
# DSCP=b8
444-
tc_h filter add dev ${DEVICE} parent 1:${QID} protocol ip prio 10 u32 match ip tos 0xb8 0xfc flowid 1:${QID_1}
439+
tc_h filter add dev ${DEVICE} parent ${SUB_MAJOR}: protocol ip prio 10 u32 match ip tos 0xb8 0xfc flowid ${SUB_MAJOR}:1
445440

446441
# T bit set.
447-
tc_h filter add dev ${DEVICE} parent 1:${QID} protocol ip prio 10 u32 match ip tos 0x08 0x1c flowid 1:${QID_3}
442+
tc_h filter add dev ${DEVICE} parent ${SUB_MAJOR}: protocol ip prio 10 u32 match ip tos 0x08 0x1c flowid ${SUB_MAJOR}:3
448443

449444
# Everything else into default.
450-
tc_h filter add dev ${DEVICE} parent 1:${QID} protocol ip prio 10 u32 match ip tos 0x00 0x00 flowid 1:${QID_2}
445+
tc_h filter add dev ${DEVICE} parent ${SUB_MAJOR}: protocol ip prio 10 u32 match ip tos 0x00 0x00 flowid ${SUB_MAJOR}:2
451446
done
452447

453448
# Send everything that hits the top level QDisc to the top class.
454-
tc_h filter add dev ${DEVICE} prio 1 protocol ip parent 1:0 u32 match u32 0 0 flowid 1:1
449+
tc_h filter add dev ${DEVICE} prio 1 protocol ip parent 1:0 u32 match ip dst 0.0.0.0/0 flowid 1:1
455450

456451
# From the top level class hash into the host classes.
457452
tc_h filter add dev ${DEVICE} prio 1 protocol ip parent 1:1 handle 1 flow hash keys ${HOST_KEYS} divisor ${NUM_HOST_BUCKETS} ${PERTURB} baseclass 1:10

src-3tos_8hosts.png

-279 KB
Binary file not shown.

0 commit comments

Comments
 (0)