Rheinisch-Westfälische Technische Hochschule Aachen Lehrstuhl für Informatik IV Prof. Dr. rer. nat. Otto Spaniol TCP/UDP und Varianten Seminar: Kommunikationsprotokolle SS02 Dirk Sabath Matrikelnummer: 228752 Betreuung: Markus Fidler Lehrstuhl für Informatik IV, RWTH Aachen Inhaltsverzeichnis 1 Einleitung 3 2 Grundlagen 3 3 User Datagram Protocol 4 3.1 Der UDP-Header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 3.2 Fragmentierung durch IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 4 5 6 Transmission Control Protocol 7 4.1 Der TCP-Header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4.2 Verbindungsaufbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 4.3 Verbindungsabbau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 4.3.1 Verbindungsabbau per Half Close . . . . . . . . . . . . . . . . . . . . . . . 10 4.3.2 Simultaner Verbindungsabbau . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.3.3 Reset einer Verbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.4 Datentransfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 4.5 Verbindungssicherheit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 4.5.1 Erkennung von Segmentverlust auf Senderseite . . . . . . . . . . . . . . . . 13 4.5.2 Erkennung von Segmentverlust auf Empfängerseite . . . . . . . . . . . . . . 14 4.5.3 Roll-Back-N . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Flow- und Congestioncontrol in TCP 15 5.1 Sliding Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 5.2 Nagle Algorithmus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 5.3 Delayed Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 5.4 Slowstart/Congestion Avoidance . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Varianten von TCP 18 6.1 TCP-Tahoe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 6.2 TCP-Reno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 6.3 SACK-TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2 1 Einleitung Seit der Entstehung des ARPANET, dem Vorläufer des Internet, hat die Bedeutung heterogener Netzwerke stark zugenommen. Dabei auftretende Schwierigkeiten sind nicht nur das Vernetzen von sehr unterschiedlichen Rechnersystemen, sondern auch die verschiedenen Netzwerktopologien und die dadurch entstehende unterschiedliche Güte von Verbindungen im Netzwerk. Auch ist eine einheitliche Programmierschnittstelle unabdingbare Voraussetzung für ein heterogenes Netzwerk, denn es wird sich kaum ein Mensch die Arbeit machen wollen, sich für jedes erdenkbare System und jeden vorstellbaren Anwendungszweck mit anderen Schnittstellen vertraut zu machen. Daher wird ein flexibles Protokoll wie das Transmission Control Protocol (TCP) benötigt, ein verbindungsorientiertes Protokoll, dass die Dienste des Internet Protokoll (IP) nutzt, und eine Programmierschnittstelle zur Verfügung stellt, die es möglich macht, unabhängig von der Netzwerktopologie und der Verbindungsqualität zu programmieren. TCP ist mittlerweile für alle gängigen Systeme verfügbar, meistens ist es schon mehr oder weniger fest im Betriebssystem verankert. Da es in der Vergangenheit wegen zu hohem Datenverkehr zu Verbindungszusammenbrüchen kam, wurde es notwendig Varianten von TCP zu entwickeln, die vor allem zusätzliche Fluss- und Staukontrolle beherrschen, um Überlastungen des Netzwerks vorzubeugen. Neben dem verbindungsorientiertem TCP gibt es noch ein verbindungsloses Gegenstück, das User Datagram Protocol (UDP). Auf dieses Protokoll möchte ich zuerst eingehehen, da es nicht nur zeitlich vor TCP (wenn auch nur kurz) liegt, sondern auch auf der technischen Seite, allein schon aufgrund der Verbindungslosigkeit einfacher ist. 2 Grundlagen Um einen Überblick zu erhalten, was ein in der Transportschicht angesiedeltes Protokoll leisten soll, ist ein kurzer Blick auf die Transportschicht unabdingbar. Die Transportschicht ist im ISO/OSI-Referenzmodell als vierte Schicht zwischen der Vermittlungsschicht und der Sitzungsschicht angesiedelt. Die TCP/IP Protkokollsuite macht eine grobere Aufteilung in nur vier Schichten, welche im Folgendem erläutert werden: Die unterste Schicht ist die Link-Schicht. Sie beinhaltet sehr hardwarenahe Dienste, wie zum Beispiel Netzwerkkartentreiber. Hier ist auch Address Resolution Protokoll (ARP) [5] enthalten, das für die Zuordnung von IP-Adressen zu Hardwareadressen (MAC) zuständig ist. Es schickt dazu ein EthernetFrame an alle Hosts in einem Netzwerk, in dem die MAC-Adresse zu einer IP-Adresse angefordert wird (ARP-Request), woraufhin sich der entsprechende Host meldet, indem er mit einem Ethernetframe mit der entsprechenden MAC- sowie IP-Adresse antwortet (ARP-Reply). ARP verwendet ein Cache, um Adressen für einen gewissen Zeitraum zwischenzuspeichern. In der Netzwerkschicht ist das Internet Protocol (IP) und das Internet Control Message Protocol (ICMP) interessant, denn vor allem auf die Dienste von IP baut die Transportschicht auf. Sie beinhaltet TCP und UDP und stellt den in der Application-Schicht zu findenden Anwendungen, wie z.B. FTP, Email, usw. einen Dienst bereit. Einer der Hauptgründe, Netzwerk- und Transportschicht zu 3 trennen, besteht darin, dass die Netzwerkschicht nicht klar von der verwendeten Netzwerkarchitektur und Hardware getrennt ist, mit der Transsportschicht steht ein von all dem unabhängiger Dienst bereit. Wichtig ist hierbei die Unterscheidung zwischen Protokollen, die die Datagramme von Host zu Host versenden, wie IP eines ist und Protokollen, die Datagramme zwischen zwei Prozessen versenden, wie TCP und UDP. Bei ersterem werden IP-Datagramme immer zum nächsten Router weitergeleitet, der das Datagram anhand der angegebenen Adresse weiterleitet, bis der entsprechende Host erreicht ist, und das Paket vom Netzwerk entfernt. IP definiert für jedes Datagram eine maximale Lebensdauer eines Datagrams, die Time to live (TTL). Diese beim Versand auf einen Startwert gesetzt, und bei jedem Hop, den das Datagram passieren muss, wird sie dekrementiert. Sollte sie 0 werden, so wird das Datagram verworfen. In der Transportschicht gilt es, zwei Protokollarten zu unterscheiden. Die verbindungslosen Protokolle, wie UDP eines ist, bauen keine explizite Verbindung zum Ziel auf. Es wird angenommen, das der Zielrechner immer erreichbar und bereit ist, Daten zu empfangen. UDP ist ausserdem ein unsicherer Dienst (unreliable), d.h. es wird nicht sichergestellt, dass Daten, die versendet wurden, auch ihr Ziel erreichen. Es ist leicht einzusehen, dass diese Protokolle einen recht einfachen Aufbau haben. Dem gegenüber stehen die verbindsorientierten Protokolle wie TCP. Sie bauen immer eine Verbindung zum Zielrechner auf und stellen sicher, dass der Zielhost zu einer Kommunikation bereit ist. TCP ist ein sicherer Dienst (reliable), d.h. hier wird dem Prozess, der TCP benutzt, garantiert, dass alle Daten ihr Ziel erreichen. Beiden Protokollen ist gemein, dass sie von mehreren Prozessen gleichzeitig benutzt werden können. Durch Multiplexing werden eingehende Daten den Prozessen zugeordnet. Dazu werden den Prozessen Ports zugeteilt, anhand derer die Prozesse identifiziert werden. 3 User Datagram Protocol Das User Datagram Protocol (UDP) [6] ist ein sehr einfaches Protokoll. Es arbeitet verbindungslos, das heisst es wird keine Verbindung zu einem Host aufgebaut. Anfallende Daten werden von UDP einfach abgeschickt, ohne das sichergestellt wird, ob das Ziel überhaupt erreichbar ist, oder ob das Ziel bereit, ist Daten zu empfangen. Daten werden von beliebiger Größe in Form von Datagrammen verschickt. Wenn man sich nun vor Augen führt, dass ein Netzwerk keine unendlichen Kapazitäten besitzt, wird schnell klar, dass sich der Programmierer durchaus Gedanken über die Grösse der zu versendenden Daten machen muss. Übersteigt die entstehende Datagrammgrösse einen bestimmten, netzwerkabhängigen Grenzwert, so erfolgt eine Fragmentierung durch das in der Vermittlungsschicht agierende Internet Protokoll (IP). Doch dazu später mehr. UDP stellt einem Benutzerprozess einen Dienst bereit. Da möglicherweise mehrere Prozesse diesen Dienst in Anspruch nehmen moc̈hten, benutzt UDP Portnummern, um Prozesse zu identifizieren, für welche die Daten bestimmt sind (Multiplexing). Die Portnummern sind aus dem Bereich 0-65535 frei wählbar. Einige sind jedoch bestimmten Anwendungen standardmässig zugewiesen, allerdings nicht verbindlich. 4 3.1 Der UDP-Header Zunächst werfen wir einen Blick auf den Header eines UDP-Datagramms. Ein Datagramm besteht aus einem Kopf mit vier Feldern dem ein Datenblock variabler Grösse folgt. Die Felder des Kopfs sind wie folgt besetzt: Abb. 3.1 Der UDP-Header Source Port (Bit 0 bis 15, insgesamt 16 Bit) In dieses Feld schreibt der Absender seine Portadresse, an welche der Zielhost dem sendenden Prozess Daten zurückschicken kann. Es ist nicht zwingend notwendig, dass der Host auf diesem Port bereit ist, Daten zu empfangen. Destination Port (Bit 16 bis 31, insgesamt 16 Bit) Dieses Feld beinhaltet den Port des Empfängers für den die Daten bestimmt sind. Anhand dieser Nummer wird das Datagramm einem Benutzerprozess zugeordnet. Länge des UDP-Datagramms (Bit 32 bis 47, insgesamt 16 Bit) Hier ist die Länge des Datagramms in Byte angegeben. Die Länge beinhaltet das gesamte Datagramm, also Kopf und Daten zusammen. Die Angabe der Länge ist redundant, da sie sich sehr leicht aus der angegebenen Länge im IP-Header errechnen lässt. Da bei IP für die Länge ebenfalls ein 16-Bit Feld reserviert ist, sieht man, dass die Datenmenge in einem Datagramm beschränkt ist auf 65535 Byte - 20 Byte (IP-Header) - 8 Byte (UDP-Header) = 65507 Byte. Checksum des UDP-Datagramms Die Angabe ist optional, es wird aber empfohlen sie immer zu verwenden. Wird sie nicht verwendet, so sind die 16 Bit mit Nullen zu füllen. Wird sie benutzt, berechnet sie sich wie folgt: Dem eigentlichen Header wird ein Pseudo-Header vorangestellt, der die IP-Adresse des Absenders und des Empfängers (jeweils 32 Bit), 8 Bit Nullen, die Angabe des Protokolls (8 Bit) und die Länge des Datagramms (16 Bit). Header, Pseudo-Header und Datenteil werden dann als 16-Bit Wörter modulo 2 aufaddiert. Hat der Datenteil ungerade Länge, so wird er mit 8 Bit Nullen aufgefüllt. Das Ergebnis wird im Einerkomplement in das Feld geschrieben, daher ist 5 es unterscheidbar, ob die Checksum 0 (es sind dann alle Bits gesetzt) ist oder gar keine Berechnung stattgefunden hat (kein Bit gesetzt). Man sieht, dass die Länge zweimal in die Berechnung der Checksum eingeht. Ist die Checksum fehlerhaft, so wird das Paket verworfen und es findet keinerlei Rückmeldung statt. Obwohl die Checksum optional ist, sollte sie jedoch immer berechnet werden, da durch sie Modifikationen (durch fehlerhafte Verbindung o.ä.) erkannt werden können. Allerdings kann eine Vertauschung von 16-Bit Wörtern nicht erkannt werden, ein Nachteil der recht einfachen Berechnung. 3.2 Fragmentierung durch IP Die Datenmenge, die in einem Paket im Netzwerk verschickt werden kann, ist durch die in der LinkSchicht festgelegte Maximum Transfer Unit (MTU) [2] beschränkt. Daher wird ein sehr grosses Datagramm unweigerlich zu einer Fragmentierung durch IP in der Vermittlungsschicht führen. Da diese Fragmentierung, d.h. eine Aufteilung des Datagramms in mehrere IP-Datagramme, im Normalfall zu einem Overhead, bedingt durch zusätzlichen Platz für IP-Header, führt, sollte sie vermieden werden1 . Da eine Fragmentierung an jedem Punkt, den das Datagramm passieren muss, geschehen kann, ist die kleinste MTU in der Verbindung zwischen den Hosts zu bestimmen, um die Fragmentierung unterbinden zu können. Dies kann durch setzen des DF-Bit (Do not fragment) im IP-Header und senden eines Datagramms der Grösse der ausgehenden MTU geschehen. Kommt eine Fehlerrückmeldung durch ICMP, so kann man es mit einem Datagramm der im ICMP Paket angegebenen MTU des nächsten Hop erneut versuchen. Sollte die Angabe im ICMP Paket nicht erfolgt sein, so setzt man die Grösse des Datagramms beliebig herab. Durch Fragmentierung kann ein weiterer Effekt zwischen UDP und ARP auftreten. Wenn der ARP Cache leer ist, dann impliziert ein verschicktes Datagramm einen ARP-Request, und es wird ein Reply erwartet, um den anderen Host adressieren zu können. Da IP die Fragmente sehr schnell generiert, wird zu jedem Fragment ein ARP-Request abgeschickt (ARP-flooding). Nach der ersten Antwort auf eine Anfrage wird das letzte Fragment gesendet, da ein Fragment im Puffer überschrieben wird, wenn ein neues ankommt. Es treten keine Fehlermeldungen durch ICMP auf, da ICMP den Benutzerprozess nicht darüber informieren kann. IP muss nach dem Ablauf eines Timeouts das empfangene Segment verwerfen, damit es nicht zu einem Überlauf des Empfangspuffers kommt. Das fragmentierte Datagramm muss danach erneut vom Benutzerprozess gesendet werden. Aus dem Obigem erkennt man, das UDP keine Verbindungssicherheit garantieren kann, und deshalb für Dienste, die dies erfordern, nicht sehr geeignet 2 ist. So eignet sich UDP wegen des geringen Verwaltungsaufwand eher für Anwendungen, die eine schnelle Verbindung erfordern, aber bei denen Verluste von Daten nicht sehr bedeutend ist. 1 Im Prinzip ist es hier egal, wer die Fragmentierung durchführt, klar ist, dass sie durchgef ührt werden muss, da ein zu grosses Datagram nicht von der Link-Schicht nicht versendet wird 2 nicht sehr geeignet heisst, es ist möglich, jedoch nötigt es dem Programmierer nur zusätzliche Arbeit auf 6 4 Transmission Control Protocol Das Transmission Control Protocol (TCP) arbeitet im Gegensatz zu UDP verbindungsorientiert, d.h. es wird zwischen zwei Hosts, die miteinander kommunizieren wollen, eine Verbindung aufgebaut. Dadurch kann Verbindungssicherheit realisiert werden. So wird ein Dienst zur Verfügung gestellt, der die zugrundeliegende Netzwerkarchitektur den Benutzerprozessen transparent macht, d.h. es ist für eine Anwendung irrelevant, wie das Netzwerk über das kommuniziert wird aufgebaut ist. TCP teilt zu versendende Daten selbstständig in Segmente auf, die einzeln verschickt werden. Es ergibt sich ein Byte-Strom orientierter Dienst. Eine Anwendung kann also kontinuierlich Daten versenden, was eine gewisse Ähnlichkeit mit dem Schreiben und Lesen von Dateien hat. Zuverlässigkeit erhält eine TCP-Verbindung durch das Bestätigen jedes Segments. Werden Segmente nicht bestätigt, so müssen sie wiederholt werden. Dadurch können auftretende Übertragungsfehler im Netzwerk behoben werden. Es gibt aber auch Mechanismen, die die versuchen, durch Überlastung eines Hosts oder des Netzwerks hervorgerufene Fehler zu vermeiden. Diese Mechanismen zählt man zur Fluss- und Staukontrolle. Effiziente Mechanismen zur Fluss- und Staukontrolle sind in den Varianten von TCP, wie TCP Tahoe, TCP Reno und TCP-SACK, implementiert. TCP arbeitet im Fullduplex Modus, d.h. es können gleichzeitig in beide Richtungen über eine Verbindung Daten gesendet werden. So wird meistens der Empfang von Segmenten in Segmenten bestätigt, die selbst wiederum Daten enthalten (Piggyback). 4.1 Der TCP-Header Betrachten wir zunächst die Form der Segmente, die von TCP verschickt werden. Ein Segment besteht aus einem Header, dessen Grösse zwischen 20 Byte und 60 Byte variiert. Dem angehängt ist der Datenteil von ebenfalls variabler Grösse. Im Header enthalten sind (in dieser Reihenfolge): 16 Bit Source Port 16 Bit Destination Port Wie UDP verwendet auch TCP Ports um eingehende Segmente den Anwendungsprozessen zuzuordnen. 32 Bit Sequence Number Die hier angegebene Sequenznummer zeigt an, an welche Stelle das Segment im Datenstrom einzusortieren ist. Durch diese Nummer wird es möglich, Pakete in falscher Reihenfolge zuzulassen, da sie so in die ursprüngliche Reihenfolge gebracht werden können. Bei einem Verbindungsaufbau ist hier die Startnummer des Stroms, die Initial Sequence Number (ISN), angegeben. Sie wird nach dem Booten mit einer Zufallszahl initialisiert. 7 32 Bit Acknowledgement Number In diesem Feld wird die Nummer des Segments angegeben, das als nächstes erwartet wird. Die Angabe ist immer vorhanden, auch wenn sie manchmal nur für eine Richtung gebraucht wird. Dies ist der Fall, wenn die Verbindung nur in eine Richtung Daten verschickt. 4 Bit Header Length Da die Länge des Headers variabel ist, ist diese Angabe zwingend, um den Datenteil des Segments erreichen zu können. 6 Bit reserviert Diese Bits werden bisher nicht verwendet. 6 Bit Options Hier sind Flags in der folgenden Reihenfolge anzutreffen: – URG : Ist dieses Bit gesetzt, so wird signalisiert, dass der Urgent Pointer gesetzt wurde. – ACK : Dieses Bit zeigt an, das die Acknowledgement Number gesetzt ist. Ein Segment mit gesetztem ACK-Bit wird im folgendem als Quittung bezeichnet. – PSH : Ist das Bit gesetzt, so müssen die enthaltenen Daten so schnell wie möglich an den zuständigen Benutzerprozess weitergeleitet werden. – RST : Dieses Bit veranlasst das Zurücksetzen der Verbindung. – SYN : Dieses Bit zeigt dem Gegenüber an, das eine Verbindung aufgebaut werden soll. Es werden die Sequenznummern synchronisiert. – FIN : Mit diesem Bit signalisiert der Sender, dass er keine weiteren Daten zu versenden hat und die Verbindung schliessen möchte. 16 Bit Window Size Hier wird der freie Platz im Empfangspuffer angegeben. Diese Angabe wird für die Verfahren zur Fluss- und Staukontrolle benötigt. 16 Bit Checksum Die Checksum berechnet sich analog zu der Checksum eines UDP-Datagramms. 16 Bit Urgent Pointer Der Urgent Pointer zeigt auf das letzte Byte dringender Daten im Datenteil. Diese Daten werden dann umgehend, d.h. unabängig von der Reihenfolge im Datenstrom, an den Benutzerprozess weitergeleitet. Am Ende des Headers können weitere Optionen angehängt sein. Diese Optionen haben einen zugeordneten Typ, mit dem man die Option identifiziert wird. Ist eine Option nur ein Flag, dann reicht die Angabe des Typs, sonst muss nach dem Typ die gesamte Länge der Option angegeben werden, worauf dann die eigentliche Option folgen kann. Die gesamte Länge ist ein Vielfaches von 4 Byte. Sämtliche möglichen Typen können [8] entnommen werden. In der ursprünglichen Spezifikation[7] sind folgende Optionen enthalten: 8 – End Of Option List (Typ 0) Diese Option markiert das Ende der Optionenliste im Header – No Operation (Typ 1) Diese Option dient zum Auffüllen der Gesamtlänge aller Optionen auf ein Vielfaches von 4 Bytes – Maximum Segment Size (MSS) (Typ 2) Die Länge dieser Option ist 4 Bytes, denn es wird hier die maximal erlaubte Segmentgrösse in 2 Bytes angegeben. Der Datenteil in einem TCP Segment ist optional. 4.2 Verbindungsaufbau Es gibt zwei Möglichkeiten eine Verbindung aufzubauen. Die erste Möglichkeit ist, dass die Verbindung von einem Host initiiert wird. Die zweite Möglichkeit ist der simultane Verbindungsaufbau, bei dem zwei Hosts quasi gleichzeitig, d.h. der zweite Host initiiert ebenfalls eine Verbindung zum ersten Host noch bevor die erstere Verbindungsanforderung eingetroffen ist. Im ersteren Fall schickt der Initiator ein Segment mit gesetztem SYN-Bit. Dieser Host vollzieht ein active open. Dieser Schritt wird im Normalfall von einem Client ausgeführt, daher soll er ab jetzt auch hier so genannt werden. In dem ersten Paket mit dem SYN-Flag gibt er als Sequenznummer seine Initial Sequence Number (ISN) an. Die ISN ist dabei die erste Sequenznummer die verwendet wird. Der gegenüberliegende Host führt ein passive open aus. Er wird hier ab jetzt Server genannt. Er quittiert 3 die ISN des Clients, und ausserdem ist das SYN-Flag gesetzt, um anzuzeigen, dass er den Verbindungsaufbau erwidert. Der Client quittiert wiederum die ISN des Servers. Diese Art des Verbindungsaufbau nennt sich Three-Way Handshake. Beim simultanen Verbindungsaufbau wird dagegen der Austausch von vier Segmenten benötigt, um die Verbindung herzustellen. Hier führen beide Hosts ein active open durch, in dem sie fast gleichzeitig Segmente mit gesetztem SYN-Bit und ihrer ISN verschicken. Nach dem Empfang dieser Segmente senden die Hosts Quittungen, mit denen sie die ISN des Gegenübers bestätigen Nach Empfang dieser Quittungen ist die Verbindung hergestellt. Der simultane Verbindungsaufbau geschieht eher zufällig, im Normalfall erfolgt eine einseitige Verbindungsanforderung. Eine zentrale Rolle bei der Synchronisation der Sequenznummern übernimmt dabei die ISN. Es ist anzustreben, dass jede Verbindung eine eigene ISN zugeteilt bekommt, damit sie sich nicht gegenseitig beeinflussen. Schliesslich kann man sich vorstellen, dass ein Host abstürzt und eine neue Verbindung mit den gleichen Verbindungsdaten aufgebaut wird. Kommen alte Segmente verspätet an, so kann es passieren, dass ein altes Segment fälschlicherweise akzeptiert wird. Daher wird die ISN regelmässig per Timer und bei jedem Verbindungsaufbau iteriert. So soll sie nach [7] alle 4 Microsekunden inkrementiert werden. Da die ISN eine 32 Bit Zahl ist, wird sie nach ca. 4,5 Stunden überlaufen. Unter der 3 Quittung bezeichnet ein Segment mit gesetztem ACK-Bit und entsprechender ACK-Nr. 9 Voraussetzung, dass die maximale Lebensdauer eines Segments kürzer ist, kann so eine Zweideutigkeit ausgeschlossen werden. Durch das Setzen der MSS-Option im Header können sich die Hosts beim Verbindungsaufbau mitteilen, welche die grösste von ihnen akzeptierte Segmentgröße ist. Wie oben bereits erwähnt, werden die Prozesse über einen Port adressiert. Sollte wider Erwarten auf dem Zielhost der Verbindunsanfrage kein Prozess auf dem adressierten Port lauschen, wird ein Reset gesendet, siehe dazu auch den nächsten Abschnitt. Ein Reset wird ebenfalls gesendet, wenn unerwartet Acknowledgements für Verbindungsanforderungen ankommen. Dies kann durch unterschiedliche Routen im Netzwerk entstehen. Sollte eine Verbindungsanforderung einen langen Weg im Netzwerk eingeschlagen haben4 , so trifft die Quittung beim Sender nicht rechtzeitig ein, was zur Wiederholung des SYN führt. Dieses SYN kann nun einen kürzeren Weg einschlagen, und eine Verbindung aufgebaut und wieder abgebaut werden, bevor das erste SYN sein Ziel erreicht hat. Wenn das erste SYN nun sein Ziel erreicht, wird es als erneuter Wunsch eines Verbindungsaufbaus betrachtet. Es wird dann quittiert, diese Quittung jedoch beim Initiator nicht mehr erwartet. Dazu muss der Weg des ersten SYN nur hinreichend lang sein, und das SYN darf nicht von IP aufgrund einer TTL von 0 verworfen werden. 4.3 Verbindungsabbau 4.3.1 Verbindungsabbau per Half Close Der Verbindungsabbau wird durch ein Half Close realisiert. Das bedeutet, dass eine Seite den Verbindungsabbau anfordert, wenn sie keine Daten mehr zu senden hat. Sie bleibt aber in einem Zustand, in dem sie noch Daten vom anderen Host empfangen kann. Der den Verbindungsabbau anfordernde Host führt einen active close durch. Man sieht, dass beide Richtungen der Fullduplex-Verbindung unabhängig voneinander geschlossen werden. Um die Senderichtung für einen Host zu schliessen, schickt dieser ein Segment mit gesetztem FINFlag an den anderen Host. Dieser quittiert den Empfang. Damit gilt eine Richtung der Verbindung als geschlossen. Der erstere Host befindet sich nun in einem Zustand, in dem er auf Daten oder ein Segment mit gesetztem FIN-Flag, das anzeigt, dass nun die Verbindung ganz abgebaut werden soll, wartet. Der Empfang dieses Segmentes wird wiederum bestätigt. Nun muss der Host zweimal die Länge der Maximum Segment Lifetime (MSL) warten. Danach gilt die Verbindung als vollständig abgebaut5 . Das Warten hat den Grund, dass die Quittung für den Verbindungsabbau verloren gegangen sein kann. Da ein Paket nach Ablaufen der MSL verworfen wird, muss ein wiederholtes Segment !#"%$&'(() *+-,/.0 !#" eingetroffen mit FIN-Flag innerhalb von sein. Dies hat zur Folge, dass innerhalb der MSL nach dem Booten eines Systems keine Verbindung aufgebaut werden sollte, da es sein kann, dass das System in diesem wartenden Zustand abgestürzt sein kann. Bei kurzer Bootzeit könnten sonst die Verbindungsparameter einer alten, nicht vollständig 4 5 worauf TCP keinen Einfluss hat Das entspricht dem Zustand TIME WAIT in Abb. 4.1 10 abgebauten Verbindung wiederverwendet werden. Nach dem Verbindungsaufbau muss den Prozessen ein End-Of-File (EOF) signalisiert werden, da diese von nun an nicht mehr auf den Datenstrom zugreifen können. 4.3.2 Simultaner Verbindungsabbau Wie beim Verbindungsaufbau gibt es auch beim Abbau die Möglichkeit des simultanen Abbaus. Hierbei wird wiederum vorausgesetzt, dass beide Hosts gleichzeitig ein Segment mit gesetztem FIN-Bit abschicken. Diese Segmente werden dann von beiden Seiten wie beim einseitigen Verbindungsabbau quittiert. Auch warten beide Hosts 2*MSL bis sie die Verbindung als komplett aufgelöst betrachten. Insbesondere liegt hier kein Half Close vor, sondern beide Richtungen gelten als geschlossen. 4.3.3 Reset einer Verbindung Neben einem Verbindungsabbau kann eine Verbindung auch zurückgesetzt werden. Dies geschieht, wenn ein Host ein Segment mit gesetztem RST-Bit sendet. Das wird notwendig, wenn Segmente empfangen werden, die nicht erwartet werden, weil sie z.B. aus einer alten Verbindung stammen oder eine zu lange Laufzeit hatten und schon wiederholt wurden, oder dass sie einer falschen Adresse zugeordnet wurden. 11 Anschaulich werden Verbindungsauf- und abbau im State Transitions Diagram in Abb. 4.1 dargestellt[9]. Abb.4.1 Das State Transition Diagram [9] 12 4.4 Datentransfer Befindet sich die Verbindung im ESTABLISHED-Zustand im State Transition Diagram, oder nach entsprechendem Half Close im Zustand CLOSE WAIT oder FIN WAIT, so können über die Verbindung Daten verschickt werden 6 . Die Benutzerprozesse, die per TCP miteinander kommunizieren sehen die Verbindung als kontinuierlichen Datenstrom. Es ist für sie daher irrelevant, in welcher Form die Daten verschickt werden. Daher ist Aufgabe von TCP, die Daten so in Segmente aufzuteilen, dass das Netzwerk optimal genutzt wird7 . Man kann zwei Arten des Datenaustauschs unterscheiden: 1. Interaktiver Datenaustausch (Interactive Data Flow [9]) Hierbei werden nur wenig Daten ausgetauscht, auf die aber in kurzer Zeit reagiert wird. Das impliziert das Segmente abgeschickt werden müssen, auch wenn nur sehr wenig Daten anstehen. Die Folge ist, dass die Datenmenge pro Segment im Vergleich zum Header verschwindend gering sein kann. Es entsteht ein grosser Overhead durch die Header. 2. Nicht interaktiver Datenaustausch (Bulk Data Flow [9]) Es wird hauptsächlich in eine Richtung gesendet (z.B. Dateitransfer). Es ist daher sinnvoll, die gesamte Kapazität des Netzwerks auszuschöpfen, ohne dabei die Verbindung zu überlasten, denn das würde zu unnötigem wiederholtem Senden von Segmenten führen. 4.5 Verbindungssicherheit Abschliessend bleibt noch die Frage offen, was bei Verlust von Segmenten in einem Netzwerk geschieht. Da TCP eine zuverlässige Verbindung bereitstellt, muss dafür Sorge getragen werden, dass solche Verluste erkannt und Segmente gegebenenfalls wiederholt gesendet werden. 4.5.1 Erkennung von Segmentverlust auf Senderseite Dafür wird in [7] auf der Senderseite ein Timer gefordert, vor dessen Ablauf eine Quittung eingetroffen sein muss. Geschieht dies nicht, so muss das Segment erneut übertragen werden. Es wird dazu ein Retransmission Timeout (RTO) berechnet. Diese Berechnung findet nicht für jedes Segment statt, vielmehr ist nur ein Timer pro Verbindung gefordert. Zunächst wird die Sample Round Trip Time berechnet, die einen Erwartungswert für die Umlaufzeit eines Segmentes angibt. )2113 46578)2119$;:<94="=5>)211?A@CB34BD: RTT bezeichnet hier die Round Trip Time. Sie ist die tatsächlich gemessene Umlaufzeit eines Seg4 ments. sollte um 0,8 gewählt werden [9], damit gehen dann 20% der neu gemessenen und 80% der alten berechneten Umlaufzeit in die neue SRTT ein. Den RTO erhält man dann aus [7] )212E GFIHKJMLNEPOQ?RSUTVLXW2OQ?RY5-8)211Z[Z?\:]?A^CB_Y`Ba 6 7 In den letzteren Zuständen nur in eine Richtung Was in diesem Sinn optimal heisst und wie es erreicht wird, wird im nächsten Kapitel aufgezeigt 13 Y OG und UG bezeichnen eine definierte Ober- bzw. Untergrenze für den Timeout. bezeichnet einen Verzögerungsfaktor, der zwischen 1,3 und 2 liegen sollte [7], damit eine geringe Überschreitung der berechneten SRTT nicht sofort zu einer Wiederholung des Segments führt 8 . Der RTO enthält einen ganzzahligen Wert, da die Umlaufzeit der Segmente in Ticks gemessen wird. Die Ticks erfolgen üblicherweise in Abständen von 500ms. Es sei angemerkt, dass diese Ticks unabhängig vom Start einer Messung der RTT laufen, wodurch ein gemessener RTT-Wert davon abhängt, wieviel Zeit seit dem letztem Tick vergangen ist. Daher ist dieser Wert nicht eindeutig. Die Messung der Umlaufzeit geschieht mit einem Zeitstempel, dessen Wert der Empfänger in das Optionsfeld des Quittungssegments einträgt. TCP reagiert auf den Verlust eines Segments mit einer Verdoppelung der RTO bei jeder wiederholten Sendung des Segmentes, da das Netzwerk stark ausgelastet zu sein scheint. So würde dann eine Wiederholung des Segments bei gleichem RTO wahrscheinlich erneut zum Verlust führen. Dieses Vorgehen nennt man Exponential Backoff [9]. Sollten nun wieder Quittungen eintreffen, so muss der RTO wieder angepasst werden, da sich die Netzwerkauslastung wieder normalisiert haben kann, und ein zu hoher Timeout dazu führt, das weitere Verluste erst sehr spät erkannt werden. Der Zeitpunkt der neuen Berechnung wird nach Karn’s Algorithmus [9] bestimmt. Dieser Algorithmus besagt, dass die RTT neu gemessen (und dann der RTO neu berechnet) wird, wenn nach erfolgreicher Wiederholung von Segmenten das erste neue Segment quittiert worden ist. 4.5.2 Erkennung von Segmentverlust auf Empfängerseite Auf der Empfängerseite wird gefordert, bei Empfang eines Segmentes mit falscher Sequenznummer das letzte in der richtigen Reihenfolge empfangene Segment wiederholt zu quittieren. Das hat zur Folge, dass beim Sender die Quittung eines Segments mehrfach eintrifft. Der Sender kann so den Verlust eines Segments erkennen. Da es möglich ist, dass ein fehlendes Segment nur verspätet beim Empfänger eintrifft und eine Wiederholung daher unnötig ist, reagiert der Sender erst bei Empfang der dritten duplizierten Quittung. Dann hat der Sender dieses Segment zu wiederholen. 4.5.3 Roll-Back-N Roll-Back-N [1] ist der einfachste Mechanismus zur Segmentwiederholung. Sollte der RetransmissionTimer ablaufen, bzw. der Sender die dritte duplizierte Quittung erhalten haben, dann werden alle nicht quittierten Segmente wiederholt. Dabei ist es nicht von Bedeutung, ob nachfolgende Segmente bereits erfolgreich empfangen wurden. Roll-Back-N arbeitet daher nicht sehr ressourcenschonend. Andere, effizientere Methoden zur Segmentwiederholung sind in Varianten von TCP implementiert. Sie werden in Kapitel 6 vorgestellt. 8 Würde gerade eine unterdurchschnittliche RTT gemessen, so würde das Fehlen dieses Faktors sehr schnell zu einem tatsächlich nicht vorhandenem Übertragungsfehler führen 14 5 Flow- und Congestioncontrol in TCP TCP versucht das Netzwerk optimal auszulasten. Optimal auslasten heisst, nach einem erfolgreichem Verbindungsaufbau möglichst schnell den Datenfluss auf ein Maximum zu erhöhen, dabei aber nicht das Netzwerk zu überlasten. Dies geschieht in TCP mittels Flow- und Congestioncontrol. Zu Flowcontrol zählt man die Verfahren, die auf der Empfängerseite, zu Congestioncontrol die Verfahren die auf der Senderseite den Datenfluss beeinflussen. Dazu wird ein Sende- und Empfangsfenster benutzt. Das Empfangsfenster kann als abstrakte Darstellung des tatsächlich unbenutzten Platz im Puffer des Empfänger betrachtet werden. Daraus kann der Sender ableiten wieviel Daten er maximal versenden darf. Es ist nicht gefordert, dass jedes Segment einzeln quittiert wird. Quittungen können auch kumulativ versandt werden, d.h. das Eintreffen einer Quittung mit Sequenznummer n+1 betstätigt den Empfang aller Segmente bis zur Sequenznummer n. Es kann kein Verlust von Segmenten mit Sequenznummer , bc stattgefunden haben, denn dann müssen duplizierte Quittungen, die den Empfang der Segmente bis zur Sequenznummer c-1 bestätigen, beim Sender eingehen. 5.1 Sliding Window Im Sende- und Empfangsfenster können Segmente, die noch nicht quittiert wurden, nicht berücksichtigt werden. Daher hat der Sender sich zu merken, wieviel nicht quitterte Segmente welcher Grösse er verschickt hat. Daraus ergibt sich die von ihm maximal zu verschickende Datenmenge: hf(hi\jh(z{h|7h h(i/z{h ~ d e SU;Sgfh(ij;k;lnmoh d pqsr+St!#u>\vu\;\w2<yx x;} !g\ 5>] Das Sliding Window [9] gibt die Datenmenge an, die versendet werden kann, ohne dass der Empfängspuffer überlaufen wird. Es beginnt nach der letzten bestätigten Sequenznummer und hat die im Empfangsfenster angegebene Grösse. Werden Segmente quittiert so verschiebt sich das gesamte Fenster nach rechts, bis es wieder an der entsprechenden Sequenznummer beginnt. Dieses Fenster ist das Maximum an Daten, die der Sender verschicken darf. Der Sender muss aus oben genannten Gründen das Fenster anpassen, in dem er die linke Grenze (letzte Quittung) bis zur letzten verschickten Sequenznummer nach rechts verschiebt. Das übriggebliebene Fenster gibt das tatsächliche Maximum an Daten, die versendet werden können, an. Wenn der Sender das Maximum ausgeschöpft hat und der Empfänger seinen Empfangspuffer nicht rechtzeitig leeren konnte, so hat sich der Empfangspuffer vollständig gefüllt. Dann schickt der Empfänger eine Quittung für das letzte empfangene Segment, welches seinen Puffer gefüllt hat. Er teilt dem Sender mit, das sein Empfangsfenster die Grösse 0 erreicht hat. Der Sender hört auf Daten zu verschicken, da er weiss, dass der Empfangspuffer gefüllt ist. Damit der Sender den Datenversand wieder aufnehmen kann, muss der Empfänger den Sender über einen geänderten Füllgrad des Empfangspuffers informieren. Dies geschieht durch das Window Update. Das Window Update ist eine Quittung, die 15 nicht den Empfang eines neuen Segmentes bestätigt, sondern dem Sender mitteilt, dass sich das Empfangsfenster geändert hat. Damit der Empfänger erkennt, wann er wieder Daten empfangen und das Window Update verschicken kann, wird der Persist Timer [9] verwendet. Mit dem Sliding Window besteht die Gefahr, dass das Silly Window Phänomen [9] auftritt. Der Empfänger bietet in jeder Quittung den freien Platz in seinem Empfangspuffer an, welcher gegebenenfalls sehr klein werden kann. Der Sender richtet sich nach diesem Wert und sendet entsprechend kleine Segmente, auch wenn eine wesentlich grössere Menge an Daten zum Versand ansteht. Dadurch entsteht ein Overhead durch die Header der Segmente. Diesem Problem wird vorgebeugt, in dem gefordert wird, dass mindestens ein Segment mit maximaler Segmentgrösse (angegeben in der MSS-Option) verschickt werden kann. 5.2 Nagle Algorithmus Während das Sliding Window versucht, eine Überlastung der kommunizierenden Hosts zu vermeiden, versucht der Nagle Algorithmus [4] eine unnötige Last auf dem Netzwerk zu vermeiden. Dieser Algorithmus wird verwendet, wenn nur wenig Daten zum Versenden anstehen, so dass versendete Segmente sehr klein sind. Klein heisst hier, dass der Overhead durch Header Relevanz besitzt. Solche kleinen Segmente werden als Tinygrams [9] bezeichnet. Der Nagle Algorithmus arbeitet dann wie folgt: Voraussetzungen: Es steht ein Tinygram zum Versand an Es ist bisher keine Quittung für das zuletzt versendete Segment eingegangen Dann Sammle weitere Daten die evtl. anfallen und füge sie dem Tinygram an Wenn die noch austehende Quittung eintrifft, dann sende das aus mehreren Daten zusammengesetzte Segment Der grosse Vorteil dieses Algorithmus ist, dass er selbstskalierend ist, d.h. wenn die Netzwerkauslastung gering ist (die Quittungen treffen schnell ein), werden Daten ohne grösse Verzögerung versendet. Ist das Netzwerk jedoch stark ausgelastet, so werden resourcenschonende grosse Segmente in grossen Abständen versandt, die dazu beitragen, dass sich die Netzwerklast wieder verringert. Aber dieser Algorithmus hat auch einen Nachteil, denn er ist für Anwendungen, die ihre Daten schnell benötigen, nicht einsetzbar (z.B. Echtzeitanwendungen). 5.3 Delayed Acknowledgements Im Gegensatz zum Nagle Algorithmus setzt man mit Delayed Acknowledgements [9] auf der Empfängerseite an. Hat der Empfänger ein Segment erhalten, so wartet er eine gewisse Zeit, bis er die Quittung sendet. Fallen in dieser Zeit Daten an, die zurück an den Sender geschickt werden müssen, so können die Daten und die Quittung zusammen übertragen werden. 16 5.4 Slowstart/Congestion Avoidance Ein Nachteil des Sliding Window ist, dass es zu Überlastungen an Knoten kommen kann, die auf dem Weg zum Empfänger passiert werden müssen, da diese keinen Einfluss auf die Grösse des Fensters ausüben können. Diesem Nachteil tritt Slowstart [9] entgegen. Auf der Senderseite wird die Eingangsrate von Quittungen beobachtet. Mittels Slowstart versucht man, die Ausgangsrate von Segmenten der Eingangsrate von Quittungen anzugleichen. Ein optimaler Zustand wird dann erreicht wenn der Sendkanal vollständig gefüllt ist. Abb. 5.1 Füllung des SendeKanals: 1)gering 2)optimal Slowstart versucht, die Kapazität des Kanals möglichst schnell vollständig auszunutzen, solange kein Segment verloren geht. Dazu benötigt Slowstart für jede Verbindung ein Congestionwindow, das zu Anfang der Verbindung mit 1 initialisiert wird. Für jede korrekt empfangene Quittung wird das Congestionwindow inkrementiert. q +!0>u\ 2(v a !0>u $a: Dies führt zu einem exponentiellem Wachstum der Anzahl der Übertragenen Segmente, denn während des Sendens von Segmente treffen Quittungen für die zuletzt gesendeten Segmente ein. Sind z.B. 2 Segmente verschickt worden, so kommen 2 Quittungen zurück, so dass sich die Grösse des Congestionwindow verdoppelt. Der Sender sendet dann Datenmenge = min[Empfangsfenster, Congestionwindow]. Dies führt recht schnell zu einem gefülltem Sendekanal, solange der Empfänger seinen Puffer schnell genug leeren kann. Sollte jedoch ein Segment verloren gehen, so ist das Congestionwindow wieder auf den Anfangswert zu setzen, und der Füllgrad des Sendekanals nimmt dementsprechend ab. Um eine solche Situation zu verhindern, arbeitet Slowstart in Kombination mit Congestion Avoidance. So wird ein Schwellwert SSThreshold eingeführt, der anfangs zu 65535 Bytes initialisiert wird [10]. Sollte das Congestionwindow kleiner sein als der Schwellwert, so wird das Fenster gemäss dem Slowstart Algorithmus erhöht. Ist der Schwellwert überschritten, so geht das Verfahren in Congestion Avoidance über. Dann gilt für des q die Erhöhung Congestionwindow q !0>u 2(v a : +!#>u 2 $ !0>u v 17 Wenn man diese Formel mit dem gleichen Argument wie bei der Erhöhung beim Slowstart Algorithmus betrachtet, dann ergibt sich effektiv eine Erhöhung um 1. Sind z.B. zwei Pakete gesendet worden und die zugehörigen Quittungen eingetroffen, so ergibt sich für die Erhöhung des Congestionwindow !0>u v i\h s$DI5 !0>u v moj$: . Sollte es nun doch zu einem Verlust eines Segmentes kommen, so wird der Schwellwert SSThreshold angepasst ?Rpsr+St+!u>\vu;\w7Z u>u\w>u> FHJL !0>u P Das Congestionwindow muss ebenfalls anpasst werden, allerdings gilt es hier zu unterscheiden, ob ein Timeout vorliegt oder duplizierte Quittungen eingetroffen sind. Im letzteren Fall wird es auf den gleichen Wert wie SSThreshold gesetzt. Da weiterhin Segmente ihr Ziel erreichen, ist es in diesem Fall sinnvoll den Datenfluss auf geringerem Niveau aufrecht zu erhalten. Im ersten Fall kann jedoch die Verbindung vollständig abgebrochen sein, und daher ist ein Übertragen von mehreren Segmenten hintereinander nicht sinnvoll. Das Staukontrollfenster wird auf 1 gesetzt, und die Verbindung fällt zurück in die Slowstart-Phase. 6 6.1 Varianten von TCP TCP-Tahoe Ein grosser Nachteil des einfachen Go-Back-N Verfahren ist, dass Segmente unter Umständen unnötig wiederholt werden. Das geschieht dann, wenn wirklich nur ein Segment bei der Übertragung verloren gegangen ist, später gesendete jedoch erfolgreich übertragen wurden. Durch die ausschliesslich positiven Quittungen ist es dem Empfänger nicht möglich, dem Sender mitzuteilen, welches Segment nicht empfangen wurde. Um diese Netzlast zu vermeiden muss man erkennen können, wann ein einzelnes Segment verloren gegangen ist, um dann nur dieses einzeln zu wiederholen. Der Verlust eines einzelnen Segmentes kann einerseits durch Überfüllung des Empfängerpuffers geschehen, andererseits werden im Netzwerk Router eingesetzt, die ebenfalls einer Überlastung unterliegen können. Dann werden von ihnen eintreffende Pakete verworfen. Da dadurch keine Daten mehr über das Netzwerk verschickt werden können, verwendet man in den Routern vermehrt das Verfahren Random Early Discard (RED). Dieses Verfahren verwirft mit zunehmender Wahrscheinlichkeit bei zunehmender Auslastung einzelne Segmente, um so einer Überlastung vorzubeugen. In der Variante TCP-Tahoe das Fast Retransmit Verfahren [9] implementiert. Es beruht auf der Annahme, dass der Empfang duplizierter Quittungen auf den Verlust eines einzelnen Segments deutet. Liegt jedoch ein Timeout vor, dann ist das Netzwerk überlastet, denn es kommen schliesslich auch keine Quittungen zurück. Es sind dann alle nicht quittierten Segmente erneut zu senden. Nach Empfang der dritten duplizierten Quittung wird von einem einfachen Übertragungsfehler ausgegangen. Mit dem Fast Retransmit Verfahren wird nur das Segment wiederholt, das auf das mehrfach quittierte Segment folgt. Hat der Empfänger nun dieses Segment empfangen, so quittiert er kumulativ 18 alle Segmente die er nach dem Übertragungsfehler korrekt erhalten hat. Nach Empfang dieser Quittung beginnt der Sender wieder, Segmente ab dem quittierten Segment zu übertragen. Jedoch fällt die Verbindung zurück in den Slowstart-Modus, womit der Datenfluss sich enorm verringert. 6.2 TCP-Reno Ein Nachteil des in TCP-Tahoe implentierten Fast-Retransmit Verfahren ist, dass die Verbindung nach einem durch duplizierte Quittungen gekennzeichneten Fehler zurück in den Slowstart-Modus geht. Die Tasache, dass einzelne Segmente verloren gehen, ist ein Hinweis darauf, dass der evtl. hohe Datenfluss nicht vollständig abgebrochen ist, sondern nur an seine obere mögliche Grenze stösst [9]. Daher ist es nicht notwendig, den Datenfluss zu minimieren. In TCP-Reno tritt dieses Verhalten nicht auf, da nicht nur das Fast Retransmit Verfahren, sondern auch das Fast-Recovery Verfahren implementiert wurde. Dieses Verfahren soll den Datenfluss auf einem hohem Niveau halten. Wie oben vorgestellt, wird bei Slowstart/Congestion Avoidance der Schwellwert SSThresh verwendet, bis zu diesem das Congestionwindow bei empfangenen Quittungen verdoppelt wird. Dieser Wert wird bei einem Übertragungsfehler auf die Hälfte des Minimums aus dem Congestionwindow und dem Empfangsfenster gesetzt. Fast-Recovery arbeitet nun genauso wie FastRetransmit, nur wird hierbei der nach dem Übertragungsfehler neu gesetzte Schwellwert SSThresh als angemessener Wert für das Congestionwindow angesehen und deshalb auf diesen Wert gesetzt. So geht erstens die Verbindung nicht in den Slowstart-Modus, sondern das Congestionwindow wird gemäss des Congestion Avoidance Algorithmus inkrementiert, und zweitens wird der Datenfluss auf einem angemessenen Niveau erhalten. Sollten jedoch mehrere Segmente im übertragenen Fenster verloren gegangen oder die Wiederholung eines Segmentes ebenfalls fehlgeschlagen sein, dann wird auch hier in den Slowstart-Modus übergegangen. TCP-Reno ist heutzutage die am weitesten verbreitete Ausführung von TCP. 6.3 SACK-TCP In TCP mit Selctive Acknowledgements [3], kurz SACK-TCP, wird zusätzlich zu den positiven und kumulativen Acknowledgements eine Option eingeführt, mit der Blöcke von Segmenten bestätigt werden können. Dazu werden zwei Headeroptionen benutzt. Zum einen ist das die SACK-permitted Option(Typ 4, Länge 2), mit der angezeigt wird, das SACK verwendet wird. Diese Option wird üblicherweise zusammen mit dem SYN-Flag beim Verbindungsaufbau gesetzt. Die zweite Option ist TCP-SACK(Typ 5, Länge variabel). In dieser Option werden Blöcke von Segmenten angegeben, die der Empfänger quittieren möchte. Diese Blöcke werden in der folgenden Form angegeben: 1.Block: Sequenznummer des ersten Segments des 1.Blocks 1.Block: Sequenznummer die auf das letzte Segment des 1.Blocks folgt 19 ... n.Block: Sequenznummer des ersten Segments des n.Blocks n.Block: Sequenznummer die auf das letzte Segment des n.Blocks folgt Da jeder Header maximal 40 Bytes an Optionen aufnehmen darf und jeder Block 64 Bit ( = 8Bytes, 4Byte Anfangssequenznummer, 4 Byte folgende Sequenznummer) beansprucht, können höchstens 4 Blöcke angegeben werden. Der Empfänger beobachtet ständig seinen Empfangspuffer. Soll nun ein Segment quittiert werden, geschieht das nach den folgenden Regeln: Ist das zu quittierende Segment in korrekter Reihenfolge eingetroffen ( d.h. vor allem kein fehlendes Segment vor diesem), so wird eine normale Quittung ohne SACK-Option verschickt. Sollte (genau) ein Segment vor diesem fehlen, so wird ein Segment mit SACK-Option versendet. In diesem Segment wird der Empfang des Segments vor dem fehlenden quittiert (eine übliche duplizierte Quittung). In der SACK-Option wird ein Block angegeben, in dem das empfangene Segment quittiert wird. Als erste Sequenznummer wird die auf das fehlende Segment folgende Sequenznummer angegeben. Sollten bereits mehrere Segmente fehlen, so wird wie im vorangegangenem Punkt vorgegangen, ausser das nun für jedes fehlende Segment ein Block eingetragen wird. Der i-te Block enthält dabei zuerst die auf das (Blöcke - i)-te fehlende Segment folgende Sequenznummer, danach die Sequenznummer des nächsten fehlenden Segments, bzw. die des zuletzt empfangenen Segments (der erste Block enthält die Nummer des zuletzt empfangenen Segments). Anhand dieser SACK-Optionen kann der Sender erkennen, welche Segmente nicht korrekt ausgeliefert wurden und diese selektiv wiederholen. TCP-SACK geht nach einem mehrfachen Übertragungsfehler nicht in den Slowstart-Modus zurück. Literatur [1] Fall, K., Floyd, S., Comparisons of Tahoe, Reno, and Sack TCP, Tech. Report, 1995 [2] Ross Keith, Kames F. Kurose Computer Networking: A topdown approach featuring the Internet, Addison Wesley, 2001 [3] Mathis et al., Selctive Acknowledgement Options, RFC 2018, 1996 [4] Nagle,Congestion Control in IP/TCP Internetworks, RFC 896, 1984 [5] Plummer, David C., An Ethernet Address Resolution Protocol, RFC 826, 1982 20 [6] Postel, J., User Datagram Protocol, RFC 768, 1980 [7] Postel, J., Transmission Control Protocol, RFC 793, 1981 [8] Postel, J., The Magic Numbers in TCP/IP, RFC 1340, 1992 [9] Stevens, W. Richard, TCP/IP Illustrated Volume I, Addison Wesley, 1994 [10] Stevens et al., TCP Congestion Control, RFC 2581, 1999 21