GNU/Linux >> Tutoriels Linux >  >> Linux

Problèmes de file d'attente TIME_WAIT

Récemment, nous avons décrit comment configurer votre serveur pour une charge élevée et la prévention des attaques DDoS. Aujourd'hui, nous parlerons du problème de la file d'attente time_wait. Ceux qui développent des services travaillant activement avec le réseau peuvent utiliser les fonctionnalités du protocole TCP :la transition de nombreux ports (ou tous libres) vers l'état TIME_WAIT. Il y a beaucoup d'informations superficielles sur Internet et beaucoup d'informations pas tout à fait correctes. Nous examinerons quelles sont ces situations et déterminerons les moyens d'en sortir.

Protocole TCP :fermeture de la connexion

Voici un schéma typique du cycle de vie d'une connexion TCP :

Schéma de durée de vie TCP

Nous ne le considérerons pas dans son ensemble, mais nous nous concentrerons sur la partie la plus importante pour nous :fermer la connexion. La partie qui a initié la fermeture de la connexion est appelée "active", et la seconde - est "passive". Et peu importe lequel d'entre eux a été l'initiateur de la connexion.

Du côté "passif", tout est simple. Après avoir reçu le paquet FIN, le système doit y répondre avec le paquet ACK approprié mais a le droit de continuer à envoyer des données. Depuis la réception du paquet FIN, la connexion du côté passif est dans l'état CLOSE_WAIT. Lorsqu'il est prêt, un paquet FIN de réponse est envoyé, après quoi la partie attend un paquet ACK. Dès réception de l'ACK à la réponse FIN, la connexion pour le côté passif est fermée.

Du point de vue du côté "actif", tout est un peu plus compliqué. Après avoir envoyé le paquet FIN, le côté actif entre FIN_WAIT_1. De plus, trois situations sont possibles :

  1. Recevoir ACK sur le paquet FIN. Ce statut est indiqué par FIN_WAIT_2, les données peuvent être livrées au côté, après quoi un paquet de réponse FIN est attendu, auquel le côté actif répond avec un ACK et met la connexion dans l'état TIME_WAIT.
  2. Si le côté passif est prêt à fermer la session, la réponse FIN peut être reçue avec un ACK simultané au paquet FIN d'origine. Dans ce cas, le côté actif répond par un ACK et transfère la connexion à TIME_WAIT, en contournant FIN_WAIT_2.
  3. Une situation est possible lorsque les parties ont initié simultanément une clôture. Dans ce cas, les deux côtés sont "actifs", des deux côtés la connexion passe à l'état TIME_WAIT.

Comme on peut le voir sur le diagramme et la description, le côté actif envoie le dernier paquet de la session (ACK au FIN passif). Puisqu'elle ne peut pas savoir si ce paquet est reçu, le statut est TIME_WAIT. Dans cet état, la connexion doit être de 2 * MSL (durée de vie maximale des paquets) :temps de livraison du paquet vers le côté passif + temps de livraison d'un éventuel paquet de réponse en retour. En pratique, à l'heure actuelle, la minuterie TIME_WAIT est réglée sur 1 à 2 minutes. Une fois ce délai expiré, la connexion est considérée comme fermée.

Problèmes de TIME_WAIT pour la connexion sortante

Une connexion dans le système d'exploitation est identifiée par quatre paramètres :IP locale, port local, IP distante, port distant. Supposons que nous ayons un client qui se connecte/se déconnecte activement à un service distant. Étant donné que l'adresse IP et le port distant restent inchangés, un nouveau port local est attribué pour chaque nouvelle connexion. Si le client était le côté actif de la fin de la session TCP, alors cette connexion sera bloquée pendant un certain temps dans l'état TIME_WAIT. Si les connexions sont établies plus rapidement que la mise en quarantaine des ports, lors de la prochaine tentative de connexion, le client recevra une erreur EADDRNOTAVAIL (errno =99).

Même si les applications accèdent à différents services et qu'aucune erreur ne se produit, la file d'attente TIME_WAIT s'allongera, consommant des ressources système. Les connexions dans l'état TIME_WAIT peuvent être vues via netstat, il est pratique de consulter des informations généralisées avec l'utilitaire ss (avec la touche -s).

Ce qui peut être fait :

  • L'intervalle Linux TIME_WAIT ne peut pas être modifié sans recompiler le noyau. Sur Internet, vous pouvez trouver des références au paramètre net.ipv4.tcp_fin_timeout avec le libellé "dans certains systèmes, cela affecte TIME_WAIT". Cependant, ce que sont ces systèmes n'est pas clair. Selon la documentation, le paramètre détermine le temps d'attente maximal du paquet FIN de réponse, c'est-à-dire qu'il limite le temps passé par la connexion en FIN_WAIT_2, mais pas en TIME_WAIT.
  • Ouvrez moins de connexions. L'erreur est le plus souvent observée lors de l'interaction réseau au sein du cluster. Dans ce cas, utiliser Keep-Alive serait une sage décision.
  • Lors de la conception d'un service, il peut être judicieux de décaler TIME_WAIT de l'autre côté, pour lequel nous devrions nous abstenir d'initier la fermeture des connexions TCP si possible.
  • S'il est difficile de réduire le nombre de connexions, il est logique de démarrer le service distant sur plusieurs ports et d'y accéder tour à tour.
  • Le paramètre du noyau "net.ipv4.ip_local_port_range" définit la plage de ports utilisés pour les connexions sortantes. Plus grande portée :plus de connexions disponibles pour un service distant.
  • Une méthode difficile et extrêmement dangereuse :réduisez la valeur du paramètre net.ipv4.tcp_max_tw_buckets à une valeur inférieure au nombre d'adresses IP dans la plage de ip_local_port_range. Ce paramètre définit la taille maximale de la file d'attente TIME_WAIT et est utilisé pour se protéger contre les attaques DOS. Cette "astuce" peut être utilisée temporairement jusqu'à ce qu'une solution correcte soit développée.
  • Activez le paramètre net.ipv4.tcp_tw_reuse. Ce paramètre permet l'utilisation de connexions dans l'état TIME_WAIT pour les connexions sortantes.
  • Activer le paramètre net.ipv4.tcp_tw_recycle.
  • Utiliser le mode SO_LINGER (défini via setsockopt). Dans ce cas, la session TCP ne sera pas fermée (échange de paquets FIN) mais rejetée. La partie souhaitant effectuer une réinitialisation envoie un paquet RST. A réception de ce paquet, la connexion est considérée comme terminée. Cependant, selon le protocole, l'envoi d'un paquet RST ne doit se faire qu'en cas d'erreur (réception de données qui ne sont manifestement pas liées à cette connexion).

TIME_WAIT sur les serveurs

Le principal danger d'extension de la file d'attente TIME_WAIT sur le serveur est le manque de ressources.

Néanmoins, il peut y avoir des incidents désagréables lors de l'utilisation de clients NAT (lorsqu'un grand nombre de clients de serveur sont situés derrière une adresse IP). Dans le cas d'un petit temps de quarantaine de port sur le Firewall, il est probable que le serveur reçoive une demande de connexion du même port dont la connexion n'est pas encore fermée (situé à TIME_WAIT). Dans ce cas, deux-trois scénarios sont possibles :

  • Le client (peu probable) devinera le numéro SEQ, ce qui est hautement improbable. Dans ce cas, le comportement n'est pas défini.
  • Le client enverra le paquet avec le mauvais (du point de vue du serveur, le numéro SEQ), auquel le serveur répondra avec le dernier paquet ACK, que le client ne comprend plus. Le client envoie généralement RST à cet ACK et attend quelques secondes avant une nouvelle tentative de connexion. Si le paramètre « net.ipv4.tcp_rfc1337 » est désactivé sur le serveur (off par défaut), une nouvelle tentative réussira. Cependant, principalement en raison du délai d'expiration, une baisse des performances sera observée.
  • Si, dans la situation décrite en p.2, le paramètre net.ipv4.tcp_rfc1337 est activé, le serveur ignorera le paquet RST du client. Les tentatives répétées de connexion au serveur à partir du même port échoueront. Pour le client, le service deviendra indisponible.

Ce qui peut être fait côté serveur.

  1. Essayez de déplacer l'initiation de la fermeture de la connexion vers le client. Ce faisant, des délais d'attente raisonnables doivent être définis.
  2. Soyez prudent avec le paramètre net.ipv4.tcp_max_tw_buckets. Une valeur trop grande rendra le serveur vulnérable à une attaque DOS.
  3. Utilisez SO_LINGER pour les requêtes manifestement incorrectes. Si le client se connecte et envoie des "non-sens", il est probable qu'il y ait une attaque sur laquelle il vaut mieux dépenser le minimum de ressources.
  4. Activez net.ipv4.tcp_tw_recycle si vous êtes sûr que les clients ne passent pas par NAT. Il est important de noter que net.ipv4.tcp_tw_reuse n'affecte pas le traitement des connexions entrantes.
  5. Dans certains cas, il est logique de ne pas "combattre" la file d'attente, mais de la répartir correctement. En particulier, les recettes suivantes peuvent vous aider :
    • Lorsque vous utilisez l'équilibreur L7, tous les paquets proviennent de la même adresse IP, ce qui provoque des "hits" dans la connexion TIME_WAIT, mais dans ce cas, vous pouvez activer en toute sécurité tcp_tw_recycle.
    • Lorsque vous utilisez l'équilibreur L3, le serveur voit les adresses IP source. L'équilibrage IP-HASH, en même temps, transmettra toutes les connexions d'un NAT à un serveur, ce qui augmente également la probabilité d'une collision. Round-Robin est plus fiable à cet égard.
    • Évitez d'utiliser NAT à l'intérieur du réseau dans la mesure du possible. Si nécessaire, mieux vaut privilégier la traduction 1 en 1.
    • Vous pouvez augmenter le nombre de connexions disponibles en hébergeant le service sur plusieurs ports. Par exemple, pour un serveur WEB, la charge peut être équilibrée non pas sur un 80e port, mais sur un pool de ports.
  6. Si le problème est causé par NAT à l'intérieur du réseau, vous pouvez résoudre la situation en reconfigurant la traduction sur le périphérique réseau :il est nécessaire de s'assurer que le temps de "quarantaine" du port sur NAT est supérieur à TIME_WAIT. Mais dans ce cas, le risque de manquer de ports sur le traducteur NAT est accru (en option, la traduction ne se fait pas vers une IP, mais vers le pool).

Paramètres du noyau net.ipv4.tcp_tw_reuse et net.ipv4.tcp_tw_recycle

Il existe deux paramètres dans le noyau Linux qui vous permettent de violer les exigences du protocole TCP, libérant les connexions de TIME_WAIT plus tôt que prévu. Ces deux options sont basées sur l'extension TCP-timestamps (marquant les paquets avec des horodatages relatifs).

net.ipv4.tcp_tw_reuse permet d'utiliser la connexion à TIME_WAIT pour une nouvelle connexion sortante. Dans ce cas, le nouvel horodatage de connexion TCP doit être supérieur d'un ordre de grandeur à la dernière valeur de la session précédente. Dans ce cas, le serveur pourra distinguer le paquet "en retard" de la connexion précédente de celle en cours. L'utilisation d'un paramètre est sûre dans la plupart des cas. Des problèmes peuvent survenir s'il existe un pare-feu de "suivi" le long d'un chemin qui décide de ne pas manquer le paquet dans la connexion, qui devrait être en TIME_WAIT.

net.ipv4.tcp_tw_recycle réduit le temps de connexion dans la file d'attente TIME_WAIT à la valeur RTO (Re-Transmission Time-Out), qui est calculée en fonction du Round-Trip-Time (RTT) et de la propagation de cette valeur. Dans le même temps, la dernière valeur d'horodatage TCP est enregistrée dans le noyau et les paquets de valeur inférieure sont simplement ignorés. Cette option rendra le service indisponible pour les clients derrière NAT si les horodatages TCP des clients sont "ignorés" pendant la traduction (si NAT les supprime ou les remplace par les leurs, il n'y aura aucun problème). Puisqu'il n'est pas possible de prédire les paramètres des périphériques externes, cette option est fortement déconseillée pour une inclusion sur des serveurs accessibles depuis Internet. De plus, sur les serveurs "internes" où il n'y a pas de NAT (ou l'option 1-en-1 est utilisée), l'option est sûre.


Linux
  1. Ssh – Vous utilisez un canal Ssh déjà établi ?

  2. Comment surveiller une connexion série à 250 000 bauds ?

  3. Échecs de connexion RDP :certificat auto-signé expiré

  4. Exemples de commandes de connexion iSCSI (aide-mémoire)

  5. Traquer les fuites de connexion MySQL

Configurer une connexion réseau statique sous Linux

Comment créer une file d'attente SQS sur AWS

Dépannage :erreurs de connexion au serveur

Analyseur de paquets :15 exemples de commandes TCPDUMP

Comment détruire complètement une connexion socket en C

moustique-client obtient une connexion refusée