Open Source im professionellen Einsatz

© Nanduu, Photocase.com

Traffic Shaping: Niedrige Latenz bei voller Bandbreite mit Linux

Schnell sein, Held sein

Die Reaktionszeit ausgelasteter Internetzugänge steht und fällt mit dem Vorhandensein und der Art des Traffic Shaping im eigenen Gateway. Beim Zusammentreffen des stadtbekannten Wondershaper und einer selbst entwickelten Lösung des Magazin-Autors Martin Stern bleibt nur einer in der Senkrechten.

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.

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.

Glossar

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.

Listing 1:
»trafficcontrol-inetgw«

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

Diesen Artikel als PDF kaufen

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook