GNU/Linux >> Tutoriales Linux >  >> Linux

TIME_WAIT problemas de cola

Recientemente, describimos cómo configurar su servidor para alta carga y prevención de DDoS. Hoy, hablaremos sobre el problema de la cola time_wait. Aquellos que desarrollan servicios que trabajan activamente con la red pueden aprovechar las características del protocolo TCP:la transición de muchos (o todos libres) puertos al estado TIME_WAIT. Hay mucha información superficial en Internet y mucha información no del todo correcta. Consideraremos cuáles son estas situaciones y determinaremos las posibles formas de salir de ellas.

Protocolo TCP:cierre de conexión

El siguiente es un diagrama típico del ciclo de vida de una conexión TCP:

Esquema de por vida de TCP

No lo consideraremos como un todo, sino que nos centraremos en la parte más importante para nosotros:cerrar la conexión. La parte que inició el cierre de la conexión se llama "activa", y la segunda es "pasiva". Y no importa cuál de ellos fue el iniciador de la conexión.

Desde el lado "pasivo", todo es simple. Habiendo recibido el paquete FIN, el sistema debe responder con el paquete ACK apropiado pero tiene derecho a continuar enviando datos. Desde que recibió el paquete FIN, la conexión en el lado pasivo está en el estado CLOSE_WAIT. Cuando está listo, se envía un paquete FIN de respuesta, después de lo cual la parte espera un paquete ACK. Al recibir el ACK a la respuesta FIN, la conexión para el lado pasivo se cierra.

Desde el punto de vista del lado “activo”, todo es algo más complicado. Después de enviar el paquete FIN, el lado activo ingresa a FIN_WAIT_1. Además, son posibles tres situaciones:

  1. Recibir ACK en el paquete FIN. Este estado se indica mediante FIN_WAIT_2, los datos se pueden entregar al lado, después de lo cual se espera un paquete de respuesta FIN, al que el lado activo responde con un ACK y pone la conexión en estado TIME_WAIT.
  2. Si el lado pasivo está listo para cerrar la sesión, la respuesta FIN se puede recibir con un ACK simultáneo al paquete FIN original. En este caso, el lado activo responde con un ACK y transfiere la conexión a TIME_WAIT, omitiendo FIN_WAIT_2.
  3. Una situación es posible cuando las partes iniciaron simultáneamente un cierre. En este caso, ambos lados están "activos", en ambos lados la conexión pasa al estado TIME_WAIT.

Como puede verse en el diagrama y la descripción, el lado activo envía el último paquete de la sesión (ACK al FIN pasivo). Como no puede averiguar si se recibió este paquete, el estado es TIME_WAIT. En este estado, la conexión debe ser 2 * MSL (vida útil máxima del paquete):tiempo de entrega del paquete al lado pasivo + tiempo de entrega de un posible paquete de respuesta. En la práctica, en la actualidad, el temporizador TIME_WAIT se establece en 1 – 2 minutos. Una vez que expira este temporizador, la conexión se considera cerrada.

Problemas de TIME_WAIT para la conexión saliente

Una conexión en el sistema operativo se identifica mediante cuatro parámetros:IP local, puerto local, IP remota, puerto remoto. Supongamos que tenemos un cliente que se está conectando/desconectando activamente a un servicio remoto. Dado que tanto la IP como el puerto remoto permanecen sin cambios, se asigna un nuevo puerto local para cada nueva conexión. Si el cliente era el lado activo del final de la sesión TCP, esta conexión se bloqueará durante un tiempo en el estado TIME_WAIT. Si las conexiones se establecen más rápido que los puertos en cuarentena, la próxima vez que se intente la conexión, el cliente recibirá un error EADDRNOTAVAIL (errno =99).

Incluso si las aplicaciones acceden a diferentes servicios y no se produce un error, la cola TIME_WAIT crecerá y consumirá recursos del sistema. Las conexiones en estado TIME_WAIT se pueden ver a través de netstat, conviene mirar información generalizada con la utilidad ss (con la tecla -s).

Qué se puede hacer:

  • El intervalo TIME_WAIT de Linux no se puede cambiar sin volver a compilar el kernel. En Internet, puede encontrar referencias al parámetro net.ipv4.tcp_fin_timeout con la frase "en algunos sistemas, afecta a TIME_WAIT". Sin embargo, no está claro cuáles son estos sistemas. Según la documentación, el parámetro determina el tiempo máximo de espera para el paquete FIN de respuesta, es decir, limita el tiempo que pasa la conexión en FIN_WAIT_2, pero no en TIME_WAIT.
  • Abrir menos conexiones. El error se observa con mayor frecuencia durante la interacción de la red dentro del clúster. En este caso, usar Keep-Alive sería una sabia decisión.
  • Al diseñar un servicio, podría tener sentido cambiar TIME_WAIT al otro lado, para lo cual debemos abstenernos de iniciar el cierre de las conexiones TCP si es posible.
  • Si es difícil reducir la cantidad de conexiones, tiene sentido iniciar el servicio remoto en varios puertos y acceder a ellos por turnos.
  • El parámetro del kernel "net.ipv4.ip_local_port_range" establece el rango de puertos utilizados para las conexiones salientes. Mayor alcance:más conexiones disponibles para un servicio remoto.
  • Una forma difícil y extremadamente peligrosa:reduzca el valor del parámetro net.ipv4.tcp_max_tw_buckets a un valor menor que el número de direcciones IP en el rango de ip_local_port_range. Este parámetro establece el tamaño máximo de la cola TIME_WAIT y se usa para proteger contra ataques de DOS. Este "truco" se puede usar temporalmente hasta que se desarrolle una solución correcta.
  • Habilite el parámetro net.ipv4.tcp_tw_reuse. Este parámetro permite el uso de conexiones en el estado TIME_WAIT para conexiones salientes.
  • Habilite el parámetro net.ipv4.tcp_tw_recycle.
  • Utilice el modo SO_LINGER (configurado a través de setsockopt). En este caso, la sesión TCP no se cerrará (intercambio de paquetes FIN) sino que se descartará. La parte que desea realizar un restablecimiento envía un paquete RST. Al recibir este paquete, la conexión se considera terminada. Sin embargo, de acuerdo con el protocolo, el envío de un paquete RST debe realizarse solo en caso de error (recepción de datos que claramente no están relacionados con esta conexión).

TIME_WAIT en servidores

El principal peligro de que la cola TIME_WAIT se expanda en el servidor se está quedando sin recursos.

Sin embargo, puede haber incidentes desagradables cuando se trabaja con clientes NAT (cuando una gran cantidad de clientes del servidor se encuentran detrás de una IP). En el caso de un tiempo de cuarentena de puerto pequeño en el Firewall, es probable que el servidor reciba una solicitud de conexión del mismo puerto, cuya conexión aún no está cerrada (ubicado en TIME_WAIT). En este caso, son posibles dos o tres escenarios:

  • El cliente (improbable) adivinará el número SEQ, lo cual es muy poco probable. En este caso, el comportamiento no está definido.
  • El cliente enviará el paquete con el incorrecto (desde el punto de vista del servidor, el número SEQ), a lo que el servidor responderá con el último paquete ACK, que el cliente ya no entiende. El cliente suele enviar RST a este ACK y espera unos segundos antes de un nuevo intento de conexión. Si el parámetro "net.ipv4.tcp_rfc1337" está desactivado en el servidor (desactivado de forma predeterminada), se realizará un nuevo intento. Sin embargo, principalmente debido al tiempo de espera, se observará una caída en el rendimiento.
  • Si, en la situación descrita en la página 2, el parámetro net.ipv4.tcp_rfc1337 está habilitado, el servidor ignorará el paquete RST del cliente. Los intentos repetidos de conectarse al servidor desde el mismo puerto fallarán. Para el cliente, el servicio dejará de estar disponible.

Qué se puede hacer en el lado del servidor.

  1. Intente cambiar la iniciación del cierre de la conexión al cliente. Al hacerlo, se deben establecer tiempos de espera razonables.
  2. Tenga cuidado con el parámetro net.ipv4.tcp_max_tw_buckets. Configurarlo demasiado grande hará que el servidor sea vulnerable a un ataque de DOS.
  3. Use SO_LINGER para consultas obviamente incorrectas. Si el cliente se conecta y envía "tonterías", entonces es probable que haya un ataque en el que es mejor gastar la cantidad mínima de recursos.
  4. Habilite net.ipv4.tcp_tw_recycle si está seguro de que los clientes no pasan por NAT. Es importante tener en cuenta que net.ipv4.tcp_tw_reuse no afecta el procesamiento de las conexiones entrantes.
  5. En algunos casos, tiene sentido no "luchar" contra la cola, sino distribuirla correctamente. En particular, las siguientes recetas pueden ayudar:
    • Al usar el balanceador L7, todos los paquetes provienen de la misma IP, lo que provoca "golpes" en la conexión TIME_WAIT, pero en este caso, puede habilitar tcp_tw_recycle de manera segura.
    • Cuando se usa el equilibrador L3, el servidor ve las direcciones IP de origen. El equilibrio de IP-HASH, al mismo tiempo, reenviará todas las conexiones de un NAT a un servidor, lo que también aumenta la probabilidad de una colisión. Round-Robin es más confiable en este sentido.
    • Evite usar NAT dentro de la red cuando sea posible. Si es necesario, es mejor preferir la traducción 1 en 1.
    • Puede aumentar la cantidad de conexiones disponibles alojando el servicio en varios puertos. Por ejemplo, para un servidor WEB, la carga se puede equilibrar no en un puerto 80, sino en un grupo de puertos.
  6. Si el problema es causado por NAT dentro de la red, puede resolver la situación reconfigurando la traducción en el dispositivo de red:es necesario asegurarse de que el tiempo de "cuarentena" del puerto en NAT sea más largo que TIME_WAIT. Pero en este caso, aumenta el riesgo de quedarse sin puertos en el traductor NAT (como opción, la traducción no es a una IP, sino al grupo).

Parámetros del núcleo net.ipv4.tcp_tw_reuse y net.ipv4.tcp_tw_recycle

Hay dos parámetros en el kernel de Linux que le permiten violar los requisitos del protocolo TCP, liberando conexiones de TIME_WAIT antes de lo previsto. Ambas opciones se basan en la extensión TCP-timestamps (marcando paquetes con marcas de tiempo relativas).

net.ipv4.tcp_tw_reuse le permite usar la conexión en TIME_WAIT para una nueva conexión saliente. En este caso, la nueva marca de tiempo de la conexión TCP debe ser un orden de magnitud mayor que el último valor de la sesión anterior. En este caso, el servidor podrá distinguir el paquete "tardío" de la conexión anterior de la actual. Usar un parámetro es seguro en la mayoría de los casos. Pueden surgir problemas si hay un firewall de "seguimiento" a lo largo de una ruta que decide no perder el paquete en la conexión, que debe estar en TIME_WAIT.

net.ipv4.tcp_tw_recycle reduce el tiempo de conexión en la cola TIME_WAIT al valor RTO (tiempo de espera de retransmisión), que se calcula en función del tiempo de ida y vuelta (RTT) y la dispersión de este valor. Al mismo tiempo, el último valor de marca de tiempo de TCP se guarda en el kernel y los paquetes con un valor más bajo simplemente se descartan. Esta opción hará que el servicio no esté disponible para los clientes detrás de NAT si las marcas de tiempo TCP de los clientes se "omiten" durante la traducción (si NAT las elimina o las reemplaza con las suyas, no habrá problemas). Dado que no es posible predecir la configuración de los dispositivos externos, no se recomienda enfáticamente incluir esta opción en servidores accesibles desde Internet. Además, en servidores “internos” donde no hay NAT (o se usa la opción 1 en 1), la opción es segura.


Linux
  1. Linux - ¿Detectando la conexión/desconexión de los auriculares en Linux?

  2. Errores de conexión RDP:certificado autofirmado caducado

  3. Ejemplos de comandos de conexión iSCSI (hoja de referencia)

  4. Cómo destruir completamente una conexión de socket en C

  5. 2 impresoras 1 cola

Configurar una conexión de red estática en Linux

Cómo crear una cola SQS en AWS

Solución de problemas:errores de conexión del servidor

Analizador de paquetes:15 ejemplos de comandos TCPDUMP

¿Cómo salgo de una conexión SSH?

Verifique continuamente el estado de la conexión OpenVPN