¿Cuál es el costo de muchos TIME_WAIT en el lado del servidor?

Supongamos que hay un cliente que hace muchas conexiones de corta vida a un servidor.

Si el cliente cierra la conexión, habrá muchos puertos en el estado TIME_WAIT en el lado del cliente. Como el cliente se queda sin puertos locales, es imposible hacer un nuevo bash de conexión rápidamente.

Si el servidor cierra la conexión, veré muchos TIME_WAIT s en el lado del servidor. Sin embargo, ¿esto hace algún daño? El cliente (u otros clientes) puede seguir realizando bashs de conexión, ya que nunca se queda sin puertos locales, y el número de estados de TIME_WAIT boostá en el lado del servidor. ¿Qué pasa eventualmente? ¿Pasa algo malo? (ralentización, locking, conexiones caídas, etc.)

Tenga en cuenta que mi pregunta no es “¿Cuál es el propósito de TIME_WAIT ?” pero “¿Qué sucede si hay tantos estados TIME_WAIT en el servidor?” Ya sé lo que sucede cuando se cierra una conexión en TCP / IP y por qué se requiere el estado TIME_WAIT . No estoy tratando de solucionar el problema, solo quiero saber cuál es el problema potencial con él.

Para decirlo simplemente, digamos netstat -nat | grep :8080 | grep TIME_WAIT | wc -l netstat -nat | grep :8080 | grep TIME_WAIT | wc -l netstat -nat | grep :8080 | grep TIME_WAIT | wc -l imprime 100000 . ¿Qué pasaría? ¿La red de O / S se ralentiza? Error “Demasiados archivos abiertos”? ¿O simplemente no hay nada de qué preocuparse?

Cada socket en TIME_WAIT consume algo de memoria en el kernel, usualmente algo menos que un socket ESTABLISHED pero aún significativo. Un número suficientemente grande podría agotar la memoria del kernel, o al menos degradar el rendimiento porque esa memoria podría usarse para otros fines. TIME_WAIT sockets TIME_WAIT no contienen descriptores de archivos abiertos (suponiendo que se hayan cerrado correctamente), por lo que no debería preocuparse por un error de “demasiados archivos abiertos”.

El zócalo también ata a esa dirección IP y puerto src / dst particular, por lo que no puede reutilizarse durante el intervalo TIME_WAIT . (Este es el propósito previsto del estado TIME_WAIT .) Normalmente no es un problema atar el puerto a menos que necesite volver a conectar uno con el mismo par de puertos. Muy a menudo, un lado usará un puerto efímero, con un solo lado anclado a un puerto bien conocido. Sin embargo, una gran cantidad de sockets TIME_WAIT pueden agotar el espacio de puerto efímero si se conecta de manera frecuente y frecuente entre las mismas dos direcciones IP. Tenga en cuenta que esto solo afecta a este par de direcciones IP particulares, y no afectará el establecimiento de conexiones con otros hosts.

Hallazgos hasta el momento:

Incluso si el servidor cerró el socket usando la llamada al sistema, su descriptor de archivo no se liberará si entra en el estado TIME_WAIT. El descriptor de archivo se lanzará más tarde cuando el estado TIME_WAIT haya desaparecido (es decir, después de 2 * MSL segundos). Por lo tanto, demasiados TIME_WAIT posiblemente generarán un error de “demasiados archivos abiertos” en el proceso del servidor.

Creo que la stack O / S TCP / IP se ha implementado con la estructura de datos adecuada (por ejemplo, tabla hash), por lo que el número total de TIME_WAIT no debería afectar el rendimiento de la stack TCP / IP de O / S. Solo el proceso (servidor) que posee los sockets en el estado TIME_WAIT sufrirá.

Cada conexión se identifica mediante una tupla (IP del servidor, puerto del servidor, IP del cliente, puerto del cliente). Fundamentalmente, las conexiones TIME_WAIT (ya sean del lado del servidor o del lado del cliente) ocupan cada una de estas tuplas.

Con TIME_WAIT s en el lado del cliente, es fácil ver por qué no puede hacer más conexiones: no tiene más puertos locales. Sin embargo, el mismo problema se aplica en el lado del servidor: una vez que tiene 64k conexiones en estado TIME_WAIT para un solo cliente , no puede aceptar más conexiones de ese cliente , porque no tiene forma de diferenciar entre la conexión anterior y la nueva conexión: ambas conexiones están identificadas por la misma tupla. En este caso, el servidor solo debería devolver los RST a los nuevos bashs de conexión de ese cliente.

Si tiene muchas conexiones desde muchas IP de cliente diferentes a las direcciones IP del servidor, es posible que tenga limitaciones con la tabla de seguimiento de la conexión.

Comprobar:

 sysctl net.ipv4.netfilter.ip_conntrack_count sysctl net.ipv4.netfilter.ip_conntrack_max 

En todas las tuplas src ip / port y dest ip / port solo puede tener net.ipv4.netfilter.ip_conntrack_max en la tabla de seguimiento. Si se alcanza este límite, verá un mensaje en sus registros “nf_conntrack: table full, droping packet”. y el servidor no aceptará nuevas conexiones entrantes hasta que haya espacio en la tabla de seguimiento nuevamente.

Esta limitación puede golpearlo mucho antes de que se agoten los puertos efímeros.

En mi caso, ejecuté un script que progtwig archivos repetidamente, mi producto realiza algunos cálculos y envía una respuesta al cliente, es decir, el cliente realiza una llamada http repetitiva para obtener la respuesta de cada archivo. Cuando se progtwign unos 150 archivos, los puertos del socket van en mi servidor en el estado time_wait y se lanza una excepción en el cliente que abre una conexión http, es decir,

  Error : [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted 

El resultado fue que mi aplicación se colgó. No sé si los subprocesos pueden estar en estado de espera o qué ha sucedido, pero necesito matar a todos los procesos o reiniciar mi aplicación para que funcione nuevamente.

Traté de reducir el tiempo de espera a 30 segundos, ya que es 240 segundos por defecto, pero no funcionó.

Así que, básicamente, el impacto global fue crítico ya que hizo que mi aplicación no respondiera

parece que el servidor puede quedarse sin puertos para asignar conexiones entrantes (durante la vigencia de TIMED_WAITs existentes): un caso para un ataque DOS.