Seit Kernel 2.2 beherrscht Linux Bandbreitenkontrolle. Der dafür zuständige Befehl »tc« ist nur für Eingeweihte verständlich. TCng implementiert eine einfache C-ähnliche Sprache, um Traffic Control übersichtlicher und leichter zu konfigurieren und bei Bedarf Verkehrsstaus für Testzwecke zu simulieren.
Die Quality-of-Service-Funktionen der großen Router von Cisco & Co. kennen die meisten Admins. Dass Linux Vergleichbares zu bieten hat, ist vielen jedoch nicht bewusst. Eine Ursache ist, dass der Traffic-Control-Befehl »tc« lange Zeit mangelhaft dokumentiert war. Die Lücke füllt inzwischen das LARTC- Howto[1] (Linux Advanced Routing and Traffic Control), seither steigt der Bekanntheitsgrad des Tools. Zudem erklärt ein Artikel zu QDisc in diesem Heft die Grundlagen und beschreibt den Einsatz von »tc« anhand einiger Bespiele.
Trotz aller Dokumentation ist »tc« unnötig kompliziert und folglich fehleranfällig. Um auch diese Hürde zu überwinden, hat Werner Almesberger mit dem Befehl »tcng« ein neues Framework für die Konfiguration der Bandbreitenkontrolle geschaffen[2]. Das Traffic Control Next Generation besteht im Wesentlichen aus den Befehlen »tcng« (ehemals »tcc«) und »tcsim«.
Das »tcng«-Programm arbeitet als Compiler, der eine eigens entwickelte Hochsprache zur Bandbreitenkonfiguration in die komplizierten Einzel-Konfigurationsschritte übersetzt. Das Kommando erzeugt auf Wunsch ein Skript mit »tc«-Befehlen oder direkt ein neues, in C implementiertes Linux-Kernelmodul.
Wer auf seinem Host die Konfiguration für andere Traffic-Shaping-Geräte entwickeln will, bekommt von TCng Unterstützung. Der Compiler vereinfacht die Konfiguration und übergibt sie als Zwischensprache an ein plattformspezifisches Programm, das die korrekte Syntax für die Zielhardware schreibt.
Nur simuliert
Der Simulator »tcsim« verarbeitet Skripte in den Sprachen von »tc« und »tcng«. Dabei setzt »tcsim« Originalcode aus dem Linux-Kernel und aus IProute2[3] ein, um näher am Original zu sein als dies mit allgemeinen Simulatoren gelingt. Mit TCsim lassen sich neue Konfigurationen virtuell testen, bevor sie im realen Einsatz Probleme verursachen. Traffic Shaping muss sich besonders in Grenzsituationen bewähren, die hoffentlich nicht täglich eintreten. Ein simulierter Test gibt dem Admin die nötige Gewissheit, dass sein TCng-Skript wie gewünscht arbeitet.
Das Übersetzen von TCng[2] ist wegen der komplizierten Architektur von TCsim recht aufwändig. Da der Simulator fremden Code einbindet, müssen die Kernel- und IProute2-Pakete im Quelltext zur Verfügung stehen. Dabei sollte es sich um einen Linux-Kernel 2.4.20 bis 2.4.27 handeln und das IProute2-Paket sollte mindestens die Versionsnummer 2.6.8-ss040730 tragen[3]. In älteren Paketen fehlt noch der HTB-Filter (Hierarchical Token Bucket).
Eine kleine Falle steckt in den Quellen von IProute2: Hier sind nach dem Herunterladen und Auspacken noch Rechte anzupassen. Das ist mit »chmod 644 iproute2-2.6.8/lib/* iproute2-2.6.8/tc/*« aber schnell erledigt. Danach folgt der Configure-Aufruf im Quellbaum von TCng: »./configure -k Kernelquellen -i IProute2-Quellen« gefolgt von »make && make test && make install«. Achtung: Die Kernelquellen müssen vor dem Übersetzen von TCng bereits konfiguriert sein (»make config«). Wer den Simulator »tcsim« nicht braucht, kommt auch ohne die zusätzlichen Quelltextbäume aus. Dann genügt der Configure-Aufruf »./configure –no-tcsim«.
|
Grundlagen des |
|---|
|
Klassisches Traffic Shaping ordnet die zu sendenden Pakete mit einer Queueing Discipline. QDiscs sind Puffer, die Pakete annehmen und weitersenden. Um einzelne Pakete oder Verbindungen unterschiedlich zu priorisieren, richtet ein Algorithmus mehrere Warteschlangen unterschiedlicher Priorität ein. Es gibt viele QDiscs mit verschiedenen Charakteristika. Am einfachsten ist das Fifo. Es nimmt die Pakete auf, speichert sie und schickt sie in derselben Reihenfolge weiter. Wird der Verkehr auf zwei Fifos mit unterschiedlicher Priorität aufgeteilt, behandelt der Kernel eine Hälfte bevorzugt. Der Nachteil: Innerhalb der Fifos erfolgt keine gleichmäßige Bearbeitung des Verkehrs. Es handelt sich trotzdem um die Default-QDisc unter Linux. Sinnvoller ist der Einsatz einer SFQ-QDisc (Stochastic Fair Queueing). Sie verteilt den Verkehr auf 127 interne Fifo-Warteschlangen, die sie gleichmäßig abarbeitet. Das erzeugt eine stochastisch faire Verteilung. Token Bucket Ein Token Bucket dient dazu, die Bandbreite zu begrenzen. Es arbeitet ähnlich wie ein Eimer (Bucket), der sich mit konstanter Geschwindigkeit mit neuen Tokens füllt. Ist der Eimer voll, gehen ab diesem Zeitpunkt alle neuen Token verloren. Will der Kernel ein Paket senden, prüft er, ob im Eimer noch ausreichend Tokens übrig sind. Wenn ja, sendet er das Paket und entnimmt die entsprechende Anzahl Tokens. Andernfalls verzögert er das Paket. Treffen Pakete und Tokens mit gleicher Geschwindigkeit ein, können alle Pakete sofort gesendet werden. Erreichen die Pakete den Bucket schneller als die Tokens, verzögert sich der Versand. Eine besondere Variante ist der Hierarchical Token Bucket. Damit lässt sich die Netzwerkbandbreite hierarchisch immer weiter unterteilen. Engpass Traffic Shaping ist nur am Nadelöhr einer Verbindung sinnvoll. Es setzt voraus, dass sich die Pakete auf keinem anderen System nennenswert lange in Warteschlangen aufhalten und dass kein Paket die Warteschlange vor dem Nadelöhr umgeht. Wenn es sich bei dem Gerät nicht um den Flaschenhals handelt, muss das Traffic Shaping den Engpass künstlich erzeugen. Ist das TC-Gerät mit 100 MBit/s an ein Netzwerk angebunden, das nur über eine DSL-Leitung am Internet hängt, kann Traffic Shaping auch lediglich die Bandbreite der DSL-Leitung aufteilen und nicht die vollen 100 MBit/s. Sobald sich weiter in Richtung Upstream ein engerer Flaschenhals befindet, muss es auch diesen berücksichtigen. |
Hochsprache
Die TCng-Hochsprache erlaubt es, in einer C-ähnlichen Syntax die Bandbreiten und Filter übersichtlich und hierarchisch strukturiert zu definieren. Das folgende einfache TCng-Skript befördert alle Pakete, die über die erste Netzwerkkarte »dev eth0« das System verlassen (im Beispiel ein »egress«-Filter) in einen Fifo-Buffer (First in, first out):
dev eth0 {
egress {
fifo;
}
}
Trägt dieses Skript den Namen »example .tcng«, dann übersetzt »tcng -r example .tcng« es in ein TC-Skript. Die Ausgabe ist in Listing 1 dargestellt. Die Option »-r« sorgt dafür, dass der Compiler einen Befehl hinzufügt, der zu Beginn alle eventuell vorhandenen QDiscs (Queueing Disciplines) entfernt. Die Syntax des TC-Befehls beschreibt der QDisc-Artikel in diesem Heft.
|
Listing 1: TCng |
|---|
01 tc qdisc del dev eth0 root 02 03 # ================= Device eth0 ================== 04 05 tc qdisc add dev eth0 handle 1:0 root dsmark indices 1 default_index 0 06 tc qdisc add dev eth0 handle 2:0 parent 1:0 pfifo |
Ein aufwändigeres Beispiel priorisiert den Netzwerkverkehr. Das TCng-Skript in Listing 2 bevorzugt gezielt den (meist interaktiven) SSH-Verkehr. In einer C-ähnlichen Syntax lädt das Skript in den Zeilen 1 und 2 die beiden Dateien »ports .tc« und »fields.tc«. Erstere enthält Platzhalter für viele bekannte und übliche TCP- und UDP-Ports (etwa »PORT_ SMTP« statt Nummer 25), während »fields.tc« den verschiedenen Feldern in den TCP/IP-Headern einfachere Namen gibt. Statt »tcp_hdr[0].ns« darf der Admin nun wesentlich lesbarer »tcp_sport« schreiben. Das Skript muss die Dateien nicht explizit laden, TCng erledigt das per Default automatisch.
|
Listing 2: |
|---|
01 #include <ports.tc>
02 #include <fields.tc>
03
04 dev eth0 {
05 egress {
06 class (<$smtp>) if tcp_hdr[2].ns == 25;
07 class (<$ssh>) if tcp_dport == PORT_SSH;
08 class (<$other>) if 1;
09 prio {
10 $smtp = class {fifo (limit 10kB);};
11 $ssh = class {fifo (limit 10kB);};
12 $other = class {fifo;};
13 }
14 }
15 }
|
In Zeile 4 folgt das Interface »eth0«, auf das sich die folgenden Filter und QDiscs in den geschweiften Klammern beziehen. Für jedes Interface dürfen eine Egress (das Netz verlassend) und eine Ingress Queueing Discipline definiert sein (für hereinkommende Daten, siehe Kasten “Ingress Shaping”). Das Listing begnügt sich mit einer Egress-QDisc.
|
Ingress |
|---|
|
Ingress Shaping war lange Zeit mit Linux nicht möglich. Inzwischen gibt es mit dem Linux Intermediate Queueing Device (IMQ,[4]) eine Möglichkeit, HTB, SFQ und andere QDiscs im Ingress Queueing mit Egress-QDiscs einzusetzen. Derzeit benötigt IMQ eigene Patches für den Linux-Kernel und für das IPtables-Kommando. Für die Bandbreitenkontrolle des eingehenden Verkehrs sind die virtuellen Netzwerkkarten »imq0« und »imq1« zuständig, an die der Administrator Egress-QDiscs bindet. Mit IPtables kann er in den »PREROUTING«- und »POSTROUTING«-Mangle-Ketten definieren, welche Pakete davon betroffen sein sollen. So ist es möglich, den Verkehr, der auf einem System ankommt, zu shapen. Ein Gateway kann den Datenstrom auch ohne IMQ in beiden Richtungen shapen. Auf der externen Karte regelt es den Verkehr mit Ziel Internet, auf der internen Karte den Verkehr, der für das lokale LAN gedacht ist. |
Klassenarbeit
Die Konfiguration weist in den Zeilen 6 bis 8 drei verschiedenen Pakettypen unterschiedliche Klassen zu. Das bezeichnet TCng als Class Selection Path. Die Klassennamen sind dabei als Variable » in spitzen Klammern anzugeben »<>«. Das Skript klassifiziert alle ausgehenden Pakete anhand ihres Zielports. Zunächst verwendet es zur Illustration die Syntax ohne Hilfe von »fields .tc« und »ports.tc«: Geht das Paket an den TCP-Port 25, gehört es in die Klasse » (Zeile 6). Dem SSH-Zielport 22 weist Zeile 7 die Klasse » zu. In allen anderen Fällen gehört das Paket zur Klasse ».
Die Spezifikation einer Klasse beginnt immer mit dem Schlüsselwort »class« gefolgt vom Namen der Klasse in Klammern. Danach folgen Selektoren. Das sind meist ein oder mehrere »if«-Konstrukte, abgeschlossen mit einem Semikolon. Alle Pakete, auf die das If-Konstrukt passt, gehören zur davor stehenden Klasse. Die If-Bedingung darf auf jede Information im Paket zugreifen:
class (<$dns>)
if ip_dst:8 == 10.0.0.0 &&
(udp_dport == 53 || udp_sport == 53);
Hier gehört ein Paket der Klasse » an, wenn seine Zieladresse sich in dem Netzwerk »10.0.0.0/8« befindet und es den Port UDP/53 als Quelle oder Ziel verwendet. Interessant ist die Angabe der Netzmaske: »:8« wird auf den zu prüfenden Wert »ip_dst« angewendet, das Ergebnis mit »10.0.0.0« verglichen. E
Die booleschen Operatoren »&&« und »||« erlauben beliebig komplizierte Operationen. Im Zweifelsfall helfen Klammern, um den Vorrang der Operationen zu definieren. Es ist auch möglich, die Teilbedingungen einzeln in Variablen abzuspeichern, zum Beispiel », und diese dann einzusetzen:
$dnsport = udp_dport==53 || udp_sport==53; class (<$dns>) if ip_dst:8 == 10.0.0.0 && $dnsport;
Die weiteren Zeilen in Listing 2 füllen die Klassen mit Leben. In einer Prio-QDisc (Zeile 9) erhält jede Klasse eine eigene Fifo-Warteschlange. Die Priorität sinkt bei jedem neuen Fifo.
Da es als Erstes angegeben ist, kommt das »-Fifo zuerst an die Reihe, dann folgt das »-Fifo, bevor » zum Zuge kommt. Die »limit«-Angabe definiert die Fifo-Größe. Werden mehr Pakete angeliefert, als das Fifo weiterleiten kann, speichert es diese Pakete. Hat es das Limit erreicht, verwirft es alle neu eintreffenden Pakete.
Viele Admins wollen eine Verbindung mit fester maximaler Bandbreite gerecht auf mehrere User aufteilen. Bei ADSL stehen typischerweise 128 KBit/s oder 192 KBit/s Upload-Geschwindigkeit zur Verfügung.
Sollen sich zwei User diese Bandbreite teilen, ist der Einsatz einer HTB-QDisc sinnvoll (Hierarchical Token Bucket). Dabei handelt es sich um einen hierarchischen QDisc-Algorithmus von Martin Devera. Seit Version 2.4.20 gehört HTB zum Lieferumfang des Standardkernels. Diese QDisc erlaubt eine hierarchische Aufteilung.
Hierarchische Eimer
Das Skript in Listing 3a verwendet eine HTB-QDisc, um innerhalb einer Root-Klasse weitere Klassen zu definieren. Zunächst sind in diesem Skript jedoch einige Variablen definiert, um spätere Änderungen zu vereinfachen (Zeilen 4 bis 9). Zeile 13 erzeugt die HTB-QDisc. Sie erhält zunächst eine Root-Klasse (Zeile 14) mit einer garantierten (»rate«) und maximalen (»ceil«) Bandbreite von 192 KBit/s (Variable »).
|
Listing 3a: |
|---|
01 #include "fields.tc"
02 #include "ports.tc"
03
04 $INTERFACE="eth0";
05 $MAX=192kbps;
06 $RATE_USER1=96kbps;
07 $RATE_USER2=96kbps;
08
09 dev $INTERFACE {
10 egress {
11 class (<$user1>) if ip_src == 192.168.0.1;
12 class (<$user2>) if ip_src == 192.168.0.2;
13 htb () {
14 class (rate $MAX, ceil $MAX) {
15 $user1 = class (rate $RATE_USER1, ceil $MAX) {sfq;};
16 $user2 = class (rate $RATE_USER2, ceil $MAX) {sfq;};
17 }
18 }
19 }
20 }
|
Anschließend erzeugen die Zeilen 15 und 16 unterhalb dieser Klasse zwei weitere Klassen mit reduzierter, garantierter Bandbreite von jeweils 96 KBit/s. Jede dieser Klassen darf bei der anderen zusätzliche Bandbreite leihen, wenn sie gerade nicht belegt ist (bis zur »ceil«-Grenze). Die Unterklassen erhalten als QDisc eine Stochastic Fair Queueing Discipline (SFQ). Durch den Class-Selection-Pfad verwaltet jede Klasse die Pakete eines Benutzers, den sie anhand seiner Source-IP-Adresse erkennt (Zeilen 11 und 12).
Der Admin hätte die Bandbreite eines Benutzers weiter aufteilen können. Listing 3b zeigt, wie die Zeile 17 aus Listing 3a umzuformulieren wäre, um beispielsweise SSH bevorzugt zu behandeln und dafür 20 KBit/s zu reservieren. Die restlichen Daten des Users erhalten nur noch 76 KBit/s garantierte Bandbreite.
|
Listing 3b: User |
|---|
01 $user1 = class (rate $RATE_USER1, ceil $MAX) {
02 $ssh = class (rate 20kbps, ceil $MAX) {sfq;};
03 $other = class (rate 76kbps, ceil $MAX) {sfq;};
04 }
|
Einheitenfrage
Achtung Falle: Beim Übersetzen in die TC-Kommandosprache wechseln manche Einheiten-Abkürzungen ihre Bedeutung. Beispielsweise steht »bps« bei TCng für Bits pro Sekunde, bei TC sind es Bytes pro Sekunde. Tabelle 1 gibt einen Überblick über die Zuordnung.
|
Tabelle |
||
|---|---|---|
|
Einheit |
TCng |
TC |
|
Byte/s |
Bps |
bps |
|
Bit/s |
bps |
bit |
|
KByte/s |
kBps |
kbps |
|
KBits/s |
kbps |
kbit |
TCng enthält auch virtuelle Messgeräte (so genannte Meter) zur weiteren Verwaltung des Netzwerkverkehrs: SLB (Single Leaky Bucket Meter), DLB (Double Leaky Bucket Meter), SRTCM (Single Rate Three Color Meter, RFC 2697) und TRTCM (Two Rate Three Color Meter, RFC 2698). Sie messen den Verkehr und erlauben es, Pakete ab einem bestimmten Schwellenwert zu verwerfen. Die beiden Letzteren messen den Verkehr und markieren die Pakete grün, gelb oder rot. Je nach Netzwerkzustand lassen sich damit Pakete unterschiedlichen Klassen zuweisen.
Verkehrsampeln
In Listing 4 definiert ein TRTCM (siehe Abbildung 1). Es verwendet einen Peak Bucket (oben links) zusammen mit einem Committed Bucket (oben rechts). Die Parameter »pbs« und »cbs« bestimmen die Größe der Buckets (Peak/Committed Burst Size), »cir« und »pir« definieren die Geschwindigkeit, mit der sich die Buckets füllen (Peak/Committed Information Rate).
|
Listing 4: Messung |
|---|
01 #define EXCEPTION 192.168.0.100
02 #define INTERFACE eth0
03
04 $meter = trTCM (cir 64kbps, cbs 10kB, pir 128kbps, pbs 20kB);
05
06 dev eth0 {
07 egress {
08 class (<$full>) if ip_src == EXCEPTION ;
09 class (<$fast>) if trTCM_green ($meter);
10 class (<$slow>) if trTCM_yellow($meter);
11 drop if trTCM_red ($meter);
12 htb {
13 class (rate 192kbps, ceil 192kbps) {
14 $fast = class (rate 128kbps, ceil 128kbps) {sfq;};
15 $slow = class (rate 64kbps, ceil 64kbps) {sfq;};
16 $full = class (rate 192kbps, ceil 192kbps) {sfq;};
17 }
18 }
19 }
20 }
|

Abbildung 1: Das Two Rate Three Color Meter klassifiziert die Pakete als rot, gelb oder grün, abhängig vom Füllstand seiner Buckets. Damit misst und bewertet der virtuelle Sensor den aktuellen Netzverkehr.
Bei jedem Paket prüft die Traffic Control, ob im Peak Bucket genügend Tokens für den Versand des Pakets übrig sind. Ist dies nicht der Fall, erhält das Paket den Rot-Anstrich. Sind genügend Tokens vorhanden, entfernt der Algorithmus die entsprechende Anzahl und prüft den Committed Bucket. Sind in diesem genug Tokens übrig, färbt er das Paket grün und entfernt die entsprechenden Tokens. Sonst ist das Paket gelb.
In Listing 4 definiert der Administrator zu Beginn einige Werte. Statt der TCng-Variablen verwendet er Definitionen im C-Stil (Zeilen 1 und 2). Anschließend konfiguriert er ein TRTCM. Es besitzt zwei Buckets mit einer Größe von 20 und 10 KByte, sie füllen sich mit einer Geschwindigkeit von 128 KBit/s und 64 KBit/s (Zeile 4).
Die Egress-QDisc benutzt wieder einen Class Selection Path (Zeilen 8 bis 10). Pakete vom Absender 192.168.0.100 (definiert in der Konstanten »EXCEPTION«) erhalten die Klasse » (Zeile 8). Die Klasse aller weiteren Pakete hängt vom aktuellen Netzzustand ab. Steht das TRTCM-Meter für das Paket auf Grün, dann landet es in der Klasse » (Zeile 9). Für Gelb ist die Klasse » zuständig (Zeile 10), bei Rot werden sie mit »drop« verworfen (11). Zu welchen Datenraten die Klassifizierung führt, ist ab Zeile 12 festgelegt.
Bequem und übersichtlich
Die TCng-Sprache ist ausdrucksstark und flexibel, dennoch bleibt sie übersichtlich. Besonders die hierarchischen Filtereinstellungen und die Variablen helfen beim Schreiben von TCng-Skripten. Damit verliert die Konfiguration eines leistungsfähigen Linux Traffic Control ihren Schrecken. (fjl)
|
Infos |
|---|
|
[1] LARTC-Howto (Linux Advanced Routing and Traffic Control): [http://lartc.org] [2] TCng-Homepage: [http://tcng.sf.net] [3] IProute2: [http://developer.osdl.org/dev/iproute2/] [4] Linux-IMQ: [http://www.linuximq.net] |
|
Der |
|---|
|
Ralf Spenneberg arbeitet als freier Unix/ Linux-Trainer, Berater und Autor und sucht immer neue Herausforderungen. 2002 veröffentlichte er sein erstes Buch “Intrusion Detection für Linux Server”. Ende 2003 erschien “VPN mit Linux”, vor einigen Wochen folgte “Intrusion Detection und Prevention mit Snort und Co.” |





