Auch die Besitzer eines schnellen oder gar symmetrischen DSL-Anschlusses kennen den Effekt: Während die Bürokollegen dicke E-Mails verschicken, ISO-Images runterladen und Videos schauen, gerät das eigene VoIP-Telefonat zur von Aussetzern und akustischen Artefakten geprägten Tortour - dagegen hilft nur Traffic Shaping [1]. Damit lassen sich die Latenzzeiten zeitkritischer Übertragungen auf ein Drittel bis ein Sechstel verringern. Das Bandbreitenmanagement hilft auch die Reaktionszeit von interaktiven SSH-, Rdesktop und ähnlichen Verbindung deutlich zu optimieren.
Tabelle 1 vergleicht die Ping-Zeiten auf einer symmetrischen 2-MBit/s-Internetstandleitung zwischen einem Debian Lenny ohne Traffic Shaping und der später in diesem Artikel vorgestellten Shaping-Lösung Trafficcontrol-inetgw. Die Nutzdatenrate lag während der Messung jeweils bei knapp 1,9 MBit/s, Gegenstelle war [www.google.de].
Warteschlangen vermeiden
Der beliebte Wondershaper [2] dient hier funktionell und in Sachen Performance als Referenz. Er verwendet für den Paketversand eine CBQ-QDisc [3] mit drei Warteschlangen. Dies erfüllt zwei Aufgaben:
-
Es verhindert einen Stau im Modem, der normalerweise deshalb
entsteht, weil DSL-Router und Kabelmodems Datenpakete wesentlich
schneller aus dem LAN empfangen als diese sich ins Internet
versenden lassen. Der Router verwaltet daher eine manchmal mehrere
Sekunden lange Warteschlange, die sich der Kontrolle des
Linux-Kernels entzieht (Abbildung 1). Die CBQ-QDisc dagegen
gestattet den Paketversand in Richtung Router nur, solange dort
eine bestimmte Bandbreite unterschritten ist. Die hat der
Administrator so zu konfigurieren, dass sich gerade keine
Warteschlange aufbaut. (Der Kasten "Glossar" und [4]
helfen beim Navigieren durch die etwas komplizierte
Begriffswelt.)
-
Zum anderen kann der Kernel Pakete auf die drei Warteschlangen
den gewünschten Antwortzeiten entsprechend verteilen. Die
CBQ-QDisc arbeitet die Warteschlangen nach ihrer Priorität ab
und bevorzugt Latenz-empfindliche Pakete damit deutlich. Das hat
mehrere Vorteile. So kommen ACK-Pakete schneller auf den Weg,
wodurch ein Upload einen Download weniger ausbremst. Auch SSH- und
ICMP-Pakete erfahren bevorzugte Abfertigung, weitere Protokolle
lassen sich leicht ergänzen. Sinnvoll ist das natürlich
nur, solange keine zusätzliche Warteschlange im Modem
existiert.
Abbildung 1: Beim Up- und Download bilden sich an mehreren Stellen Warteschlangen. Wer die Reaktionszeit optimieren will, sollten ihr Entstehen beim Provider und im eigenen DSL-Modem oder -Router vermeiden. Der Linux-Kernel hilft mit Shaping und Policing dabei.
|
Ingress: Eingehender Datenstrom.
Egress: Ausgehende Datenstrom.
Classifying: Pakete mit Filtern priorisieren und einer Warteschlange zuweisen [7].
Shaping: Hält Pakete in einer Warteschlange zurück, sobald der ausgehende Datenstrom (Egress) eine bestimmte Bandbreite überschreitet. Läuft die Warteschlange voll, werden neue Pakete verworfen.
QDisc: Im Linux-Kernel gibt es verschiedene Queueing Disciplines, also Regeln, nach denen ausgehende Pakete Schlange stehen. Einige QDiscs sind hierarchisch aufgebaut und dürfen weitere Klassen und QDiscs enthalten. Man spricht dann von "classful".
CBQ: Das Class Based Queueing ist einer von mehreren Mechanismen, um priorisierte Pakete abzuarbeiten. Dazu gibt es mehrere unterschiedlich priorisierte Queues, die ein Weighted-Round-Robin-Prozess (WRR) abklappert. Zum Beispiel versendet er zehn Pakete von Queue 1, anschließend fünf Pakete von Queue 2, eines von Queue 3 und dann wieder zehn von Queue 1 und so weiter [3].
HTB: Die seit Kernel 2.4.20 fest in den Kernel integriert Discipline "Hierarchical Token Bucket" funktioniert ähnlich wie Class Based Queueing, aber mit einem Token-Bucket-Filter: Wie CBQ teilt HTB die verfügbare Bandbreite auf mehrere Klassen auf und garantiert jeder Klasse auch bei starker Auslastung eine gewisse Bandbreite. Überschüssige Bandbreiten verteilt HTB auf die anderen Klassen [10].
Policing: Verwirft bestimmte Ingress-Pakete.
|
Für eingehende Datenpakete bietet der Kernel leider deutlich weniger Möglichkeiten als für den Versand - warum auch, schließlich ergibt es keinen Sinn, ein gerade eingetroffenes Paket erst mal in einer Warteschlange zu deponieren. Eine Warteschlange bildet sich im Downstream trotzdem: beim Provider. Während eines Downloads treffen dort Pakete nämlich deutlich schneller ein, als sie sich an den Kunden weiterleiten lassen.
Um den Stau in der Warteschlange beim Provider zu minimieren, bleibt dem Kunden nur, diesen um einen langsameren Versand zu bitten. Dazu bietet der Linux-Kernel die Ingress-QDisc [5]. Sie erlaubt es, nur bestimmte Pakete zu verwerfen. Der Absender einer TCP-Verbindung schickt das Paket daraufhin erneut und drosselt die Transferrate.
Auch der Wondershaper benutzt die Ingress-QDisc, um Pakete zu verwerfen, sobald der eingehende Datenstrom eine vom Administrator gewählte maximale Bandbreite überschreitet. Leider verwirft die Ingress-QDiscs so gelegentlich auch ACK-Pakete eines Uploads oder andere empfindliche Pakete, was die Latenzen für interaktive Anwendungen erhöht.
Einige noch wenig bekannte Optionen im TC-Subsystem des Kernels versprechen jedoch Hilfe, um Pakete differenzierter zu behandeln, als Wondershaper und andere Traffic-Shaping-Software das tun. Das jetzt vorstellte Trafficcontrol-inetgw [6] ist als Komplettlösung zur Latenzoptimierung auf Internetgateways mit dynamischer Overhead-Berechnung und differenziertem Ingress-Policing ausgelegt.
Dynamischer Overhead
Um die Warteschlangen im lokalem Modem und beim Provider zu vermeiden, muss der Linux-Kernel die übertragenen Paketgrößen inklusive aller Overheads kennen. Daraus ergibt sich die Bandbreite, die wirklich über den Internetzugang läuft. Beim Versand eines TCP-Pakets über DSL kommen zu den Nutzdaten die Header für TCP, IP, PPP, PPPoE, Ethernet und ATM. Besonders trickreich ist, dass ein Ethernet-Paket mindestens 64 Bytes groß sein muss und eine ATM-Zelle immer genau 53 Bytes umfasst. Bei ATM gibt es noch eine Besonderheit. Die letzte für ein Ethernet-Paket verwendete Zelle fasst nur 40 Bytes Nutzdaten, alle vorher 48. Das Modem füllt die fehlenden Bytes mit Nullen auf. Tabelle 2 verdeutlicht die quantitativen Zusammenhänge.
Der Overhead eines Datenpakets ist also nicht konstant, sondern abhängig von der Nutzdatenmenge. Erst neuere Linux-Kernel zusammen mit neueren IProute-Versionen können die Rohdatenmenge im ATM-Netz korrekt berechnen ([8], [9]) - vorausgesetzt man verwendet eine QDisc, die das unterstützt, etwa Hierarchical Token Bucket (HTB, [10]). Dort gibt es den neuen Parameter »linklay«, der die ATM-Rohdatenmenge korrekt berechnet. Für die Root-HTB-Klasse zum Beispiel sieht das so aus:
tc class add dev ppp0 parent 1: classid 1:1 htb rate 205kbit ceil 205kbit linklay atm
(Siehe Listing 1, ab Zeile 77.) Auch beim Ingress-Policing (Empfang) ist der Parameter erlaubt:
tc filter add dev ppp0 parent ffff: protocol ip prio 110 u32 match ip src 0.0.0.0/0 flowid :1 police rate 1710kbit burst 6k linklay atm conform-exceed continue/ok
(Siehe Listing 1, ab Zeile 144.) Außer ATM unterstützt »linklay« zurzeit Ethernet als Übertragungsschicht.
001 #!/bin/bash
002 #Latenzoptimierung für Internetgateways
003 #Martin Stern, 2010 Lizenz: GPL
004 PATH=/usr/sbin:/usr/bin:/sbin:/bin
005
006 #IPTables Bibliotheksverzeichnis gegebenen-
007 #falls für tc überschreiben.
008 #export IPTABLES_LIB_DIR=/usr/local/lib/...
009
010 # Hilfe ausgeben
011 if [ $# -eq 0 -o -$# -gt 3 ]; then
012 echo -e "Usage:"
013 echo -e "$0 [device]"
014 echo -e " Statistiken anzeigenn"
015 echo -e "$0 [device] clear"
016 echo -e " Trafficcontrol deaktivierenn"
017 echo -e "$0 [device] [downlink] [uplink]"
018 echo -e " Trafficcontrol einrichten."
019 echo -e " Downlink/Uplink in Kilobit/s,"
020 exit 1
021 fi
022
023 # Statistic ausgeben
024 if [ $# -eq 1 ]; then
025 echo -e "nQDisc Statistik:"
026 echo -e "-------------------------"
027 tc -s qdisc ls dev $1
028 echo -e "nClass Statistik:"
029 echo -e "-------------------------"
030 tc -s class ls dev $1
031 echo -e "nFilter Statistik:"
032 echo -e "-------------------------"
033 tc -s filter ls dev $1
034 echo -e "nIngress Filter Statistik:"
035 echo -e "-------------------------"
036 tc -s filter ls parent ffff: dev $1
037 exit
038 fi
039
040 # Konfiguration zurücksetzten
041 if [ $# -eq 2 ]; then
042 tc qdisc del dev $1 root
043 tc qdisc del dev $1 ingress
044 exit
045 fi
046
047 ###########################################
048 #Neue Konfiguration erstellen
049 DEV=$1
050 DOWN=$2
051 UP=$3
052
053 #Alte Konfiguration löschen
054 tc qdisc del dev $DEV root &> /dev/null
055 tc qdisc del dev $DEV ingress &> /dev/null
056
057
058 ###########################################
059 #Uplink konfigurieren (egress)
060
061 #Minimales Quantum festlegen
062 SmallQ=""; BigQ=""
063 z=$((${UP}/10*128/3))
064 if [ $z -lt 1500 ]; then
065 SmallQ="quantum 1500"
066 fi
067 z=$((${UP}/10*128))
068 if [ $z -lt 1500 ]; then
069 BigQ="quantum 1500"
070 fi
071
072 #Root HTB QDisc
073 tc qdisc add dev $DEV root handle 1: htb
074 default 11
075
076 #Root HTB Klasse
077 tcc="tc class add dev $DEV parent"
078 $tcc 1: classid 1:1 htb
079 rate ${UP}kbit ceil ${UP}kbit
080 linklay atm $BigQ
081
082 #HTB Klasse 1:10 "Low Latency"
083 $tcc 1:1 classid 1:10 htb
084 rate $(($UP/3))kbit ceil ${UP}kbit
085 prio 0 linklay atm $SmallQ
086
087 #HTB Klasse 1:11 "Bulk and Default"
088 $tcc 1:1 classid 1:11 htb
089 rate $(($UP/3))kbit ceil ${UP}kbit
090 prio 1 linklay atm $SmallQ
091
092 #HTB Klasse 1:12 "Second-Order Bandwidth"
093 $tcc 1:1 classid 1:12 htb
094 rate $(($UP/3))kbit ceil ${UP}kbit
095 prio 2 linklay atm $SmallQ
096
097 #Stochastic Fairness Qdisc für alle drei
098 tcq="tc qdisc add dev $DEV parent"
099 $tcq 1:10 handle 10: sfq perturb 10
100 $tcq 1:11 handle 11: sfq perturb 10
101 $tcq 1:12 handle 12: sfq perturb 10
102
103 #Pakete den HTB Klassen zuweisen.
104 #Der erste passende Filter gilt.
105 #ACK -> "Low Latency"
106 tcf="tc filter add dev $DEV parent"
107 $tcf 1: protocol ip prio 22 u32
108 match ip protocol 6 0xff
109 match u8 0x05 0x0f at 0
110 match u16 0x0000 0xffc0 at 2
111 flowid 1:10
112
113 #TOS Minimum Delay -> "Low Latency"
114 $tcf 1: protocol ip prio 10 u32
115 match ip tos 0x10 0xff flowid 1:10
116
117 #IPTables fwmark 10 -> "Low Latency"
118 $tcf 1: protocol ip prio 16
119 handle 10 fw flowid 1:10
120
121 #IPTables fwmark 11 -> "Bulk and Default"
122 $tcf 1: protocol ip prio 18
123 handle 11 fw flowid 1:11
124
125 #IPTables fwmark 12 -> "Second-Order B.w."
126 $tcf 1: protocol ip prio 20
127 handle 12 fw flowid 1:12
128
129 #ICMP -> "Low Latency"
130 $tcf 1: protocol ip prio 24 u32
131 match ip protocol 1 0xff flowid 1:10
132
133 #DEFAULT -> "Bulk and Default"
134 $tcf 1: protocol ip prio 50 u32
135 match ip dst 0.0.0.0/0 flowid 1:11
136
137 ###########################################
138 #Downlink konfigurieren (ingress)
139
140 #Ingress Root Qdisc anlegen.
141 tc qdisc add dev $DEV handle ffff: ingress
142
143 #Wenn über 98% Auslastung ...
144 $tcf ffff: protocol ip prio 110 u32
145 match ip src 0.0.0.0/0 flowid :1
146 police rate $((${DOWN}*98/100))kbit
147 burst 6k linklay atm
148 conform-exceed continue/ok
149
150 #... dann überzählige Pakete markieren.
151 $tcf ffff: protocol ip prio 111 u32
152 match u32 0 0 flowid :1
153 action ipt -j MARK --set-mark 20
154 action continue > /dev/null
155
156 #Wenn mehr als 1 weiteres % Auslastung ...
157 $tcf ffff: protocol ip prio 120 u32
158 match ip src 0.0.0.0/0 flowid :1
159 police rate $((${DOWN}*1/100))kbit
160 burst 8k linklay atm
161 conform-exceed continue/ok
162
163 #... dann große Pakete verwerfen.
164 $tcf ffff: protocol ip prio 121 u32
165 match u16 0x0400 0x0400 at 2 flowid :1
166 police rate 0kbit burst 2k linklay atm
167 conform-exceed drop/drop
168 $tcf ffff: protocol ip prio 122 u32
169 match u16 0x0800 0x0800 at 2 flowid :1
170 police rate 0kbit burst 2k linklay atm
171 conform-exceed drop/drop
172
173 #Wenn noch 1% Auslastung => verwerfen.
174 $tcf ffff: protocol ip prio 130 u32
175 match ip src 0.0.0.0/0 flowid :1
176 police rate $((${DOWN}*1/100))kbit
177 burst 10k linklay atm
178 conform-exceed drop/drop
|