GNU/Linux >> LINUX-Kenntnisse >  >> Linux

TIME_WAIT Warteschlangenprobleme

Kürzlich haben wir beschrieben, wie Sie Ihren Server für hohe Last und Verhinderung von DDoS konfigurieren. Heute werden wir über die Probleme mit der time_wait-Warteschlange sprechen. Diejenigen, die Dienste entwickeln, die aktiv mit dem Netzwerk arbeiten, können auf die Funktionen des TCP-Protokolls zugreifen:den Übergang vieler (oder aller freien) Ports in den Zustand TIME_WAIT. Es gibt viele oberflächliche Informationen im Internet und viele nicht ganz korrekte Informationen. Wir werden prüfen, was diese Situationen sind, und die möglichen Auswege aus ihnen ermitteln.

TCP-Protokoll:Verbindung geschlossen

Das Folgende ist ein typisches Lebenszyklusdiagramm einer TCP-Verbindung:

TCP-Lebensdauerschema

Wir werden es nicht als Ganzes betrachten, sondern uns auf den für uns wichtigsten Teil konzentrieren – das Schließen der Verbindung. Die Partei, die die Schließung der Verbindung initiiert hat, wird als „aktiv“ bezeichnet, und die zweite – als „passiv“. Dabei spielt es keine Rolle, wer von ihnen der Initiator der Verbindung war.

Von der „passiven“ Seite ist alles einfach. Nach Erhalt des FIN-Pakets sollte das System darauf mit dem entsprechenden ACK-Paket antworten, hat aber das Recht, mit dem Senden von Daten fortzufahren. Seit Empfang des FIN-Pakets befindet sich die Verbindung auf der passiven Seite im Zustand CLOSE_WAIT. Wenn bereit, wird ein Antwort-FIN-Paket gesendet, wonach die Partei auf ein ACK-Paket an sie wartet. Nach Erhalt des ACK an das Antwort-FIN wird die Verbindung für die passive Seite geschlossen.

Aus Sicht der „aktiven“ Seite ist alles etwas komplizierter. Nach dem Senden des FIN-Pakets tritt die aktive Seite in FIN_WAIT_1 ein. Weiterhin sind drei Situationen möglich:

  1. Empfange ACK auf dem FIN-Paket. Dieser Status wird durch FIN_WAIT_2 angezeigt, Daten können an die Seite geliefert werden, woraufhin ein FIN-Antwortpaket erwartet wird, worauf die aktive Seite mit einem ACK antwortet und die Verbindung in den TIME_WAIT-Zustand versetzt.
  2. Wenn die passive Seite bereit ist, die Sitzung zu schließen, dann kann die Antwort FIN mit einem gleichzeitigen ACK auf das ursprüngliche FIN-Paket empfangen werden. In diesem Fall antwortet die aktive Seite mit einem ACK und leitet die Verbindung an TIME_WAIT weiter, wobei FIN_WAIT_2 umgangen wird.
  3. Eine Situation ist möglich, wenn die Parteien gleichzeitig eine Schließung eingeleitet haben. In diesem Fall sind beide Seiten „aktiv“, auf beiden Seiten geht die Verbindung in den Zustand TIME_WAIT.

Wie aus dem Diagramm und der Beschreibung ersichtlich ist, sendet die aktive Seite das letzte Paket in der Sitzung (ACK an das passive FIN). Da sie nicht herausfinden kann, ob dieses Paket empfangen wurde, ist der Status TIME_WAIT. In diesem Zustand sollte die Verbindung 2 * MSL (maximale Paketlebensdauer) betragen:Paketzustellungszeit zur passiven Seite + Zustellungszeit eines möglichen Antwortpakets zurück. In der Praxis wird derzeit der Timer TIME_WAIT auf 1 – 2 Minuten eingestellt. Nach Ablauf dieses Timers gilt die Verbindung als geschlossen.

TIME_WAIT Probleme bei ausgehender Verbindung

Eine Verbindung im Betriebssystem wird durch vier Parameter identifiziert:lokale IP, lokaler Port, entfernte IP, entfernter Port. Angenommen, wir haben einen Client, der sich aktiv mit einem Remotedienst verbindet/trennt. Da sowohl die IP als auch der entfernte Port unverändert bleiben, wird für jede neue Verbindung ein neuer lokaler Port zugewiesen. Wenn der Client am Ende der TCP-Sitzung die aktive Seite war, wird diese Verbindung für eine Weile im Zustand TIME_WAIT blockiert. Wenn die Verbindungen schneller hergestellt werden als die Quarantäne der Ports, erhält der Client beim nächsten Verbindungsversuch einen EADDRNOTAVAIL-Fehler (errno =99).

Selbst wenn Anwendungen auf verschiedene Dienste zugreifen und kein Fehler auftritt, wächst die TIME_WAIT-Warteschlange und nimmt Systemressourcen in Anspruch. Verbindungen im TIME_WAIT-Zustand können über netstat eingesehen werden, es ist praktisch, allgemeine Informationen mit dem Dienstprogramm ss (mit der Taste -s) anzuzeigen.

Was getan werden kann:

  • Das Linux-TIME_WAIT-Intervall kann nicht geändert werden, ohne den Kernel neu zu kompilieren. Im Internet findet man Hinweise auf den Parameter net.ipv4.tcp_fin_timeout mit der Formulierung „in einigen Systemen betrifft es TIME_WAIT“. Was diese Systeme sind, ist jedoch unklar. Laut Dokumentation bestimmt der Parameter die maximale Wartezeit für das Antwort-FIN-Paket, d.h. begrenzt die Zeit, die die Verbindung in FIN_WAIT_2 verbringt, aber nicht TIME_WAIT.
  • Öffne weniger Verbindungen. Der Fehler wird am häufigsten während der Netzwerkinteraktion innerhalb des Clusters beobachtet. In diesem Fall wäre die Verwendung von Keep-Alive eine kluge Entscheidung.
  • Beim Entwurf eines Dienstes kann es sinnvoll sein, TIME_WAIT auf die andere Seite zu verschieben, wofür wir möglichst darauf verzichten sollten, die Schließung von TCP-Verbindungen zu initiieren.
  • Wenn es schwierig ist, die Anzahl der Verbindungen zu reduzieren, dann ist es sinnvoll, den Remote-Dienst auf mehreren Ports zu starten und nacheinander darauf zuzugreifen.
  • Der Kernel-Parameter „net.ipv4.ip_local_port_range“ legt den Portbereich fest, der für ausgehende Verbindungen verwendet wird. Größere Reichweite – mehr Verbindungen für einen Remote-Dienst verfügbar.
  • Ein harter und äußerst gefährlicher Weg:Reduzieren Sie den Wert des Parameters net.ipv4.tcp_max_tw_buckets auf einen Wert, der kleiner ist als die Anzahl der IPs im Bereich von ip_local_port_range. Dieser Parameter legt die maximale Größe der TIME_WAIT-Warteschlange fest und dient zum Schutz vor DOS-Angriffen. Dieser „Trick“ kann vorübergehend angewendet werden, bis eine korrekte Lösung entwickelt ist.
  • Aktivieren Sie den Parameter net.ipv4.tcp_tw_reuse. Dieser Parameter ermöglicht die Verwendung von Verbindungen im Zustand TIME_WAIT für ausgehende Verbindungen.
  • Parameter net.ipv4.tcp_tw_recycle aktivieren.
  • Benutze den SO_LINGER-Modus (eingestellt über setsockopt). In diesem Fall wird die TCP-Session nicht geschlossen (Austausch von FIN-Paketen), sondern verworfen. Die Partei, die einen Reset durchführen möchte, sendet ein RST-Paket. Nach Erhalt dieses Pakets gilt die Verbindung als beendet. Laut Protokoll sollte das Senden eines RST-Pakets jedoch nur im Fehlerfall erfolgen (Empfang von Daten, die eindeutig nichts mit dieser Verbindung zu tun haben).

TIME_WAIT auf Servern

Die Hauptgefahr, dass sich die TIME_WAIT-Warteschlange auf dem Server ausdehnt, besteht darin, dass die Ressourcen knapp werden.

Dennoch kann es beim Arbeiten mit NAT-Clients zu unangenehmen Zwischenfällen kommen (wenn sich viele Server-Clients hinter einer IP befinden). Bei einer kleinen Port-Quarantänezeit auf der Firewall ist es wahrscheinlich, dass der Server eine Verbindungsanfrage von demselben Port erhält, mit dem die Verbindung noch nicht geschlossen ist (befindet sich bei TIME_WAIT). In diesem Fall sind zwei bis drei Szenarien möglich:

  • Der (unwahrscheinliche) Kunde wird die SEQ-Nummer erraten, was höchst unwahrscheinlich ist. In diesem Fall ist das Verhalten undefiniert.
  • Der Client sendet das Paket mit dem falschen (aus Sicht des Servers die SEQ-Nummer), worauf der Server mit dem letzten ACK-Paket antwortet, das der Client nicht mehr versteht. Der Client sendet normalerweise RST an dieses ACK und wartet einige Sekunden, bevor er einen neuen Verbindungsversuch unternimmt. Wenn der Parameter „net.ipv4.tcp_rfc1337“ auf dem Server deaktiviert ist (standardmäßig deaktiviert), ist ein neuer Versuch erfolgreich. Allerdings wird vor allem aufgrund des Timeouts ein Leistungsabfall beobachtet.
  • Wenn in der in S.2 beschriebenen Situation der Parameter net.ipv4.tcp_rfc1337 aktiviert ist, ignoriert der Server das RST-Paket des Clients. Wiederholte Versuche, sich über denselben Port mit dem Server zu verbinden, schlagen fehl. Für den Client wird der Dienst nicht mehr verfügbar sein.

Was auf der Serverseite getan werden kann.

  1. Versuchen Sie, die Initiierung des Verbindungsabbaus auf den Client zu verlagern. Dabei müssen angemessene Timeouts gesetzt werden.
  2. Seien Sie vorsichtig mit dem Parameter net.ipv4.tcp_max_tw_buckets. Eine zu große Einstellung macht den Server anfällig für einen DOS-Angriff.
  3. Verwenden Sie SO_LINGER für offensichtlich falsche Abfragen. Wenn der Client eine Verbindung herstellt und „Unsinn“ sendet, ist es wahrscheinlich, dass es sich um einen Angriff handelt, für den es besser ist, die minimale Menge an Ressourcen aufzuwenden.
  4. Aktivieren Sie net.ipv4.tcp_tw_recycle, wenn Sie sicher sind, dass Clients NAT nicht durchlaufen. Es ist wichtig zu beachten, dass net.ipv4.tcp_tw_reuse die Verarbeitung eingehender Verbindungen nicht beeinflusst.
  5. In manchen Fällen ist es sinnvoll, die Warteschlange nicht zu „bekämpfen“, sondern richtig zu verteilen. Insbesondere die folgenden Rezepte können dabei helfen:
    • Wenn Sie den L7-Balancer verwenden, kommen alle Pakete von der gleichen IP, was „Treffer“ in der TIME_WAIT-Verbindung provoziert, aber in diesem Fall können Sie tcp_tw_recycle sicher aktivieren.
    • Bei Verwendung des L3-Balancers sieht der Server die Quell-IP-Adressen. IP-HASH-Balancing leitet gleichzeitig alle Verbindungen für ein NAT an einen Server weiter, was auch die Wahrscheinlichkeit einer Kollision erhöht. Round-Robin ist in dieser Hinsicht zuverlässiger.
    • Vermeiden Sie nach Möglichkeit die Verwendung von NAT innerhalb des Netzwerks. Bei Bedarf ist es besser, eine 1-in-1-Übersetzung zu bevorzugen.
    • Sie können die Anzahl der verfügbaren Verbindungen erhöhen, indem Sie den Dienst auf mehreren Ports hosten. Bei einem WEB-Server kann die Last beispielsweise nicht auf einen 80. Port verteilt werden, sondern auf einen Pool von Ports.
  6. Wenn das Problem durch NAT innerhalb des Netzwerks verursacht wird, können Sie die Situation lösen, indem Sie die Übersetzung auf dem Netzwerkgerät neu konfigurieren:Es muss sichergestellt werden, dass die Quarantänezeit des Ports auf NAT länger als TIME_WAIT ist. Aber in diesem Fall erhöht sich das Risiko, dass dem NAT-Übersetzer die Ports ausgehen (optional erfolgt die Übersetzung nicht auf eine IP, sondern auf den Pool).

Kernel-Parameter net.ipv4.tcp_tw_reuse und net.ipv4.tcp_tw_recycle

Es gibt zwei Parameter im Linux-Kernel, mit denen Sie die Anforderungen des TCP-Protokolls verletzen und Verbindungen vorzeitig von TIME_WAIT befreien können. Beide Optionen basieren auf der Erweiterung TCP-timestamps (Kennzeichnung von Paketen mit relativen Zeitstempeln).

net.ipv4.tcp_tw_reuse ermöglicht es Ihnen, die Verbindung um TIME_WAIT für eine neue ausgehende Verbindung zu verwenden. In diesem Fall sollte der Zeitstempel der neuen TCP-Verbindung um eine Größenordnung größer sein als der letzte Wert in der vorherigen Sitzung. In diesem Fall kann der Server das „verspätete“ Paket von der vorherigen Verbindung von der aktuellen unterscheiden. Die Verwendung eines Parameters ist in den meisten Fällen sicher. Es können Probleme auftreten, wenn es entlang eines Pfades eine „Tracking“-Firewall gibt, die entscheidet, das Paket in der Verbindung nicht zu verpassen, das in TIME_WAIT sein sollte.

net.ipv4.tcp_tw_recycle reduziert die Verbindungszeit in der TIME_WAIT-Warteschlange auf den RTO-Wert (Re-Transmission Time-Out), der anhand der Round-Trip-Time (RTT) und der Streuung dieses Werts berechnet wird. Gleichzeitig wird der letzte TCP-Timestamp-Wert im Kernel gespeichert und Pakete mit niedrigerem Wert werden einfach verworfen. Diese Option macht den Dienst für Clients hinter NAT nicht verfügbar, wenn TCP-Zeitstempel von Clients während der Übersetzung „übersprungen“ werden (wenn NAT sie entfernt oder durch ihre eigenen ersetzt, gibt es keine Probleme). Da es nicht möglich ist, die Einstellungen externer Geräte vorherzusagen, wird diese Option dringend für die Einbindung auf Servern empfohlen, auf die über das Internet zugegriffen werden kann. Darüber hinaus ist die Option auf „internen“ Servern, auf denen kein NAT vorhanden ist (oder die 1-in-1-Option verwendet wird), sicher.


Linux
  1. Beispiele für iSCSI-Verbindungsbefehle (Spickzettel)

  2. Aufspüren von MySQL-Verbindungslecks

  3. So zerstören Sie eine Socket-Verbindung in C vollständig

  4. mosquitto-client erhält verweigerte verbindung

  5. Wie beende ich eine SSH-Verbindung?

Richten Sie unter Linux eine statische Netzwerkverbindung ein

So erstellen Sie eine SQS-Warteschlange auf AWS

Fehlerbehebung:Serververbindungsfehler

Linux – Kopfhörerverbindung/-trennung in Linux erkennen?

RDP-Verbindungsfehler:Abgelaufenes selbstsigniertes Zertifikat

Packet Analyzer:15 Beispiele für TCPDUMP-Befehle