Praktische Nutzung des Keepalive-Timers bei TCP


Das nachfolgend beschriebene, unter Solaris 2.5.1 durchgeführte Experiment soll die praktische Nutzung des Keepalive-Timers bei TCP demonstrieren.

Versuchsaufbau:

Versuch

Ein TCP-Klient (tcpcli.c) auf Rechner kirke (IP-Adresse 134.109.192.39, Betriebssystem Linux 2.0.33) kommuniziert mit einem TCP-Server (tcpserv.c) am Port 5000 auf Rechner yang (IP-Adresse 134.109.184.150, Betriebssystem Solaris 2.5.1). Nach dem Verbindungsaufbau werden zwischen Server und Klient je eine Nachricht und eine zugehörige Bestätigung ausgetauscht. Danach unterbrechen wir die Verbindung softwaremäßig durch Entfernen der Default-Route aus der Routing-Tabelle der kirke.

Um die Manipulation der Routing-Tabelle zu vereinfachen, definieren wir uns zunächst drei Kommandos:

Die Definition der Kommandos erfolgt mittels des Alias-Mechanismus der tcsh:
root@kirke 1 # alias show 'route -n'
root@kirke 2 # alias del 'route delete default'
root@kirke 3 # alias add 'route add default gw 134.109.192.254'
Die Routing-Tabelle der kirke sieht zunächst so aus:
root@kirke 4 # show
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
134.109.192.0   0.0.0.0         255.255.255.0   U     0      0       11 eth0
127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        2 lo
0.0.0.0         134.109.192.254 0.0.0.0         UG    0      0        1 eth0
Die Steuerung des Experiments kann auch nach dem Löschen der Default-Route problemlos von der kirke aus erfolgen, da diese z.B. über die im gleichen Subnetz befindliche ultra (IP-Adresse 134.109.192.89) den yang erreicht.

Der Standardwert des Keepalive-Timers beträgt bei Solaris 2.5.1 zwei Stunden (7200 Sekunden bzw. 7.200.000 Millisekunden). Vor dem Experiment wird dieser Wert auf 10 Sekunden reduziert:

ROOT@yang 1 # ndd -set /dev/tcp tcp_keepalive_interval 10000
Das Kommando ndd ist Solaris-spezifisch und gestattet zur Laufzeit die Einstellung bestimmter Parameter von im Systemkern enthaltenen Treibern. Der Wert 10000 ist der Minimalwert für das Intervall des Keepalive-Timers. Kleinere Zahlen werden mit der Meldung operation failed, Invalid argument zurückgewiesen.

Die gesamte Kommunikation zwischen kirke und yang wird mittels snoop auf dem yang aufgezeichnet:

ROOT@yang 2 # snoop -o dump host yang and port 5000

Nun wollen wir also mal sehen, wie Solaris auf eine unterbrochene Verbindung reagiert. Zuerst wird unser Server gestartet:

hot@yang 1 > ./tcpserv -k 5000
Bei noch existierender Default-Route lassen wir den Klienten eine Nachricht senden:
hot@kirke 10 > ./tcpcli yang 5000
Nachricht: eins
Nachricht: 
Der Server bekommt diese Nachricht ordnungsgemäß:
hot@yang 1 > ./tcpserv -k 5000
Option SO_KEEPALIVE hat Wert 8 und Laenge 4
1 Byte an Klienten gesendet; zum Test auf SIGPIPE (sollte hier nicht passieren)
Nachricht der Laenge 4: eins
Der Server sendet vor dem Empfang der ersten Nachricht des Klienten an diesen ein Byte, um zu zeigen, daß die Ausgabe-Operation kein Signal SIGPIPE generiert, wie das später nach den Keepalive-Probes der Fall sein wird.

Nun entfernen wir die Default-Route der kirke:

root@kirke 5 # del
root@kirke 6 # show
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
134.109.192.0   0.0.0.0         255.255.255.0   U     0      0       11 eth0
127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        2 lo
Mittels ping können wir uns sehr leicht davon überzeugen, daß jetzt keine IP-Pakete mehr zum yang durchkommen:
root@kirke 7 # ping yang
PING yang.informatik.tu-chemnitz.de (134.109.184.150): 56 data bytes
ping: sendto: Network is unreachable
ping: wrote yang.informatik.tu-chemnitz.de 64 chars, ret=-1
ping: sendto: Network is unreachable
ping: wrote yang.informatik.tu-chemnitz.de 64 chars, ret=-1
...

--- yang.informatik.tu-chemnitz.de ping statistics ---
18 packets transmitted, 0 packets received, 100% packet loss
Jetzt schlägt auch das Senden einer weiteren Nachricht fehl. Der Server bekommt davon natürlich überhaupt nichts mit:
hot@kirke 16 > ./tcpcli yang 5000
Nachricht: eins
Nachricht: zwei
write socket: Host is unreachable
Nachricht: 
Nun gut. Warten wir geduldig ab, was passiert. Der Paket-Zähler von snoop läßt erkennen, daß trotz der logisch unterbrochenen Verbindung Pakete gesendet werden. Der Keepalive-Timer muß also bereits aktiv geworden sein.

snoop zeigt an, daß yang nach dem Austausch von insgesamt 15 Paketen aufgibt:

ROOT@yang 2 # snoop -o dump proto tcp host yang and port 5000
Using device /dev/le (promiscuous mode)
15 
Der Server kehrte aus seinem blockierenden read() mit dem Fehlercode Connection timed out zurück. Die Verbindung wurde durch den Solaris-Kern automatisch abgebrochen. Das erkennt man an dem Signal SIGPIPE, das beim Versuch, auf den Socket zu schreiben, generiert wird:
hot@yang 1 > ./tcpserv -k 5000
Option SO_KEEPALIVE hat Wert 8 und Laenge 4
1 Byte an Klienten gesendet; zum Test auf SIGPIPE (sollte hier nicht passieren)
Nachricht der Laenge 4: eins
read socket: Connection timed out
SIGPIPE abgefangen
msgsock geschlossen
Ende
Als die Verbindung noch existierte, verlief die gleiche Schreiboperation erfolgreich.

Der folgende Snoop-Mitschnitt zeigt den gesamten Verlauf bis zum Verbindungsabbruch sehr anschaulich:

  1   0.00000 kirke -> yang TCP D=5000 S=1105 Syn Seq=3147275981 Len=0 Win=512
  2   0.00076 yang -> kirke TCP D=1105 S=5000 Syn Ack=3147275982 Seq=528934210 Len=0 Win=64240
  3   0.00293 kirke -> yang TCP D=5000 S=1105     Ack=528934211 Seq=3147275982 Len=0 Win=32120
  4   0.00570 yang -> kirke TCP D=1105 S=5000     Ack=3147275982 Seq=528934211 Len=1 Win=64240
  5   0.01798 kirke -> yang TCP D=5000 S=1105     Ack=528934212 Seq=3147275982 Len=0 Win=32120
  6   6.14229 kirke -> yang TCP D=5000 S=1105     Ack=528934212 Seq=3147275982 Len=5 Win=32120
  7   0.04484 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934212 Len=0 Win=64240
  8  12.54995 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
  9   1.46990 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
 10   2.93003 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
 11   5.85005 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
 12  11.69996 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
 13  23.40000 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
 14  46.80002 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
 15  56.25079 yang -> kirke TCP D=1105 S=5000     Ack=3147275987 Seq=528934211 Len=1 Win=64240
Für mehr Details stehen die Ausgaben des Verbose Mode sowie des Verbose Summary Mode zur Verfügung.

Die erste Keepalive-Probe (Paket 8) folgt ca. 12,5 Sekunden nach der letzten regulären Bestätigung (Paket 7). Die Abstände zwischen den Probes wachsen zunehmend bis zu einem Maximum von ca. 56 Sekunden:

Wiederholung der Keepalive-Probe Abstand in Sekunden
1 1.46990
2 2.93003
3 5.85005
4 11.69996
5 23.40000
6 46.80002
7 56.25079

Wie wir sehen, spielen hier die bei Stevens in TCP/IP Illustrated Vol. I angegebenen 75 Sekunden keine Rolle. Nach insgesamt 8 Keepalive-Probes gab Solaris in unserem Beispiel auf, wobei statt einer zahlenmäßigen auch eine zeitliche Obergrenze überschritten worden sein kann.

Nach dem Abbruch der Verbindung sendet der Server kein FIN- oder RST-Segment, was ja auch keinen Sinn machen würde, da der Klient nicht erreichbar ist.


Holger Trapp, 29. Mai 1998