Afinar u optimizar la red de un servidor Linux
Llegamos a la séptima parte de este tutorial en el que profundizamos la afinación de nuestra máquina. Como este servidor es una unificación de seguridad informática que incluye Proxy, Firewall, Router y VPN, los paquetes de red que lo atraviesan siguen un trayecto que podemos pulir. Como este servidor goza de un buen hardware para funcionar sobradamente, nos permitimos optimizar con precisión sus recursos (a esto se le llama Tuning). Veamos entonces cómo afinar u optimizar la red de un servidor Linux.
Cuando los paquetes de red llegan a este servidor, circulan a través de sus tarjetas de red y de ahí se entregan al sistema operativo, para elevarlos a las siguientes capas (siguiendo el modelo OSI). En este blog puedes aprender más sobre el MODELO OSI.
Es importante verificar si en este flujo interviene NAPI, pero bueno, de todo este tema de flujos de paquetes y su manipulación técnica a nivel de kernel, se encarga el Subsistema de Red de Linux.
Afinar u optimizar la red de un servidor Linux – Tarjetas de red (affinity)
Una forma práctica de ver si nuestro servidor está trabajando con NAPI, es verificar la existencia de SOFTIRQs, en lugar de las IRQs. Como se supone que esto es un router y firewall que le da servicio de pasarela a más de 300 clientes para que tengan Internet, obviamente tiene un uso exhaustivo de las tarjetas de red. Así que que por este lado debe tener una estadística apreciable de solicitudes de interrupción.
Podemos verificarlo con el siguiente comando:
watch -n1 grep RX /proc/softirq
Otra forma de verlo gráficamente es generando una estadística con Monitorix:
El resultado con Monitorix evidencia una nula actividad de IRQ, pero sí un porcentaje de SOFTIRQs en el núcelo 1. Lo anterior quiere decir que nuestro servidor sí está trabajando con NAPI.
NAPI ¿malo o bueno?
Si usar NAPI es malo o bueno, es relativo. Si esta máquina fuera una estación de trabajo, o sí corriera alguna plataforma exclusivamente para capa de aplicación (tal como una Base de Datos), que recibiera mucho tráfico, es bueno. Pero para la función de nuestro servidor, usar NAPI puede ser un poco inconveniente, porque NAPI le adiciona jitter a los paquetes que circulan. Sin embargo, el espacio en memoria que usa NAPI en este caso es pequeño, por lo que no afecta, es más, nos puede ayudar a reducir pérdidas de paquetes.
Ajustar tamaño de la cola de transmisión (TX)
La cola de salida, o de transmisión, es por donde salen los paquetes de nuestro servidor. El valor de este parámetro depende del flujo de paquetes salientes, la velocidad del sistema y de sus tarjetas de red. Con el comando ifconfig podemos visualizar su valor en el sistema.
Si la tarjeta de red es más lenta que el resto del sistema (unos 100 Mbps), ajustemole su cola de transmisión a 5000 (es un buen punto de partida). Si la tarjeta de red opera, o puede operar a 1 Gbps, podemos mermarla a 2000, así ocupamos menos RAM. El comando relacionado de esto es:
sudo ifconfig ethX txqueuelen 5000
Cola de recepción de paquetes del kernel
La cola de recepción de paquetes del kernel no es otra cosa que el DMA Buffer (Subsistema de Red de Linux). Como en este contexto estamos hablando de paquetes, por eso no lo llamamos buffer.
Este componente del kernel puede ser ajustado por estas variables:
net.core.netdev_max_backlog net.core.dev_weight net.core.netdev_budget_usecs
El parámetro net.core.netdev_max_backlog determina el tamaño máximo de la cola de recepción de paquetes. Esta cola existe porque a veces la NIC entrega los paquetes a una velocidad mayor a la que el kernel puede procesarlos. Su valor por defecto es 1000, el cual podemos verificar en nuestros sistemas con el siguiente comando:
sysctl -a | grep net.core.netdev_max_backlog
Con net.core.netdev_budget_usecs se define el presupuesto de tiempo (en microsegundos), que Linux tiene para tomar los paquetes de la cola de recepción. Esta variable se encuentra a partir de la versión 4.12 del kernel (en kernels más viejos su valor está fijado a 2 jiffies).
Y el parámetro net.core.dev_weight representa el número de paquetes que Linux debe tomar en una interrupción NAPI, dentro del presupuesto net.core.netdev_budget_usecs.
Podemos fijar la variable net.core.netdev_max_backlog a 4000, es un buen valor para comenzar afinando nuestro servidor. Adicionamos este parámetro en el archivo de configuración /etc/sysctl.conf, así:
net.core.netdev_max_backlog = 4000
Luego, aplicamos el cambio con el comando sysctl -p.
También podemos activarlo de un solo golpe con el siguiente comando:
sudo sysctl -w net.core.netdev_max_backlog=4000
Este último ajuste va perfecto en un servidor que transmita grandes volúmenes de datos, tal como backups, FTP de archivos, un servidor web de alto tráfico, etc.
Resumen gráfico:
Bueno, hasta aquí los asociado a la Cola de Recepción de Paquetes. Ahora, vamos a revisar su homólogo, el Buffer de Recepción, ó Buffer de la Ventana TCP.
Parámetros adicionales para definir tamaño de buffer de la ventana TCP
Linux tiene los siguiente parámetros relacionados con la asignación de buffer para los segmentos TCP:
net.ipv4.tcp_rmem – Espacio de memoria para la recepción de segmentos TCP
net.ipv4.tcp_wmem – Espacio de memoria para la transmisión de segmentos TCP
Estos los ajusta el kernel al valor más recomendado dependiendo de la memoria RAM instalada. Sin embargo, viene con valores neutrales.
Todos los sistemas que transmiten datos, reciben de sus receptores un mensaje que les índica la cantidad de bytes que deben enviar en determinado momento. Eso se hace para que el host que transmite no desborde al receptor. A eso se le llama Ventana Deslizante, y la determina la memoria disponible y la velocidad del enlace con su retardo.
Con los siguientes comandos podemos visualizar sus valores activos:
sysctl -a | grep net.ipv4.tcp_rmem
sysctl -a | grep net.ipv4.tcp_wmem
Para mejorar el flujo de paquetes de un host a otro, por ejemplo, aprovechando que están en una red Gigabit, podemos aumentar el tamaño del buffer asignado a la ventana (no al socket TCP).
La mayoría de la literatura que aborda este tema hablan de la siguiente fórmula para calcularlo:
Buffer = BDP / 8
Buffer = (velocidad de enlace * latencia de la red) / 8
La variable BDP en resumidas cuentas es el tamaño de la ventana (por ende el buffer que la aloja). La latencia de la red (la de los paquetes) es su valor de ida y vuelta (RTT).
Laboratorio personal
He realizado este ejercicio con mis servidores, específicamente uno que almacena los backups en ambiente productivo, y esta es la conclusión:
Entre más bajo el RTT con la misma velocidad, menos buffer de ventana resulta. A más velocidad (bps) más buffer de ventana se necesita.
Atención a lo siguiente:
En el siguiente artículo de Google Cloud, sobre optimización de TCP, puedes observar el mismo ejercicio, teniendo como datos un enlace de 10 Gbps con latencia de 0,030segundos, lo que les da una ventana de 37,5 Mbytes. Pero en mi ejercicio real tengo un enlace de 1 Gbps ethernet con latencia de 0,00017 segundos en promedio!
Es decir que según la fórmula Google, o de la literatura general, mi ventana quedaría en 20 KBytes. Absurdo (lo hice, y la tasa de transferencia se me fue cuesta abajo). Más absurdo es tener un enlace de 10 Gbps con una latencia de 0,030 segundos en un ambiente controlado y académico.
Lo que sucede con el cálculo del buffer que reza la literatura general, es que está basado en una ventana deslizante de 65535 bytes (que era lo máximo que manejaba TCP hasta principios de los años 90 del siglo XX). Por eso se iba en picada mi tasa de transferencia, porque 20 KBytes es el 30% de una ventana de 65535.
Hoy todos los sistemas vienen con un parámetro llamado Opción de Escala de Ventana activado, por lo que podemos aumentar el buffer hasta un factor de 14 (214). El factor de mí ventana es 9:
Buffer = ((velocidad de enlace * latencia de la red) / 8) * xn
Buffer = ((1000000000 * 0,00017) / 8) x 29 = 10880000 bytes
Entonces, para un enlace de 1000 Mbps (1 Gbps), con latencia de 0,00017 segundos, el buffer de una ventana TCP se puede fijar a ≈ 10,8 MB.
Quiere decir esto que si la velocidad del enlace aumenta, debemos re-calcular el tamaño del buffer para que el host transmisor no desborde al receptor. Pero si pasa lo contrario, podemos mantener el buffer a 10,8 MB.
El comando:
sudo sysctl -w net.ipv4.tcp_rmem='4096 16384 10880000'
sudo sysctl -w net.ipv4.tcp_wmem='4096 16384 10880000'
Sin embargo, existen otros parámetros que pueden afectar el tamaño de nuestra ventana TCP:
net.ipv4.tcp_adv_win_scale
net.ipv4.tcp_window_scaling
net.ipv4.tcp_workaround_signed_windows
Esos 10,8 MB cubren muy bien la Cola de Recepción de Paquetes, porque ese espacio que definimos de 4000 paquetes, es aproximadamente equivalente a 5,9 MBytes ¿Por qué? Por la siguiente ecuación:
KBytes = ((1480 bytes / 1000 ) x paquetes)
La longitud de un paquete en este punto es la MSS + la cabecera TCP = 1480 bytes.
De manera que el espacio del Buffer de la Ventana de Recepción, debe ser mayor al valor de la Cola de Recepción de Paquetes, para evitar pérdida de paquetes (dropped).
Los comandos que nos ayudan a saber si tenemos pérdidas de paquetes en nuestro servidor, son los siguiente:
Ver paquetes perdidos en la tarjeta de red:
cat /sys/class/net/eth0/statistics/rx_dropped
Ver paquetes perdidos en la cola de recepción de paquetes
watch -n 1 -t -d cat /proc/net/softnet_stat
La primera columna nos muestra el total de paquetes recibidos por cada núcleo de la CPU (cada fila es un núcleo). La segunda columna nos muestra si netdev_max_backlog ha sido sobrecargado (y por ende pérdida de paquetes). Y la tercera muestra si Softirq no ha alcanzado a tomar todos los paquetes de la NIC. En fin, esta es la identificación de cada una de esas columnas:
PROCESSED | DROPPED | TIME SQUEEZED | CPU COLLISION | RECEIVED RPS | FLOW LIMIT COUNT
Otra forma de ver paquetes perdidos:
watch -n 1 -t -d "netstat -s | grep err"
Si netstat muestra paquetes perdidos, pero no así /proc/net/softnet_stat, es porque la pérdida está ocurriendo fuera de nuestro servidor.
Aunque este proceso de optimización ayuda bastante, siempre vamos a tener algunas pérdidas de paquetes en su recepción, debido a los Algoritmos de Control de Congestión. Éstos algoritmos no son malos, pero la pérdida de paquetes que suponen es un pequeño precio que se paga.
Tenemos otra variable llamada net.core.rmem_max, que define el tamaño máximo del buffer que una aplicación del servidor puede solicitarle al kernel. Podemos ajustarlo al siguiente valor:
sudo sysctl -w net.core.rmem_max=638736
Gestión de colas – Controlar el tráfico con implementaciones de QoS
Todos los dispositivos activos de red tiene un algoritmo de gestión de colas. Estos procesan el orden de llegada y salida de los paquetes de red. El buffer más básico es un FIFO, pero tiene sus limitaciones para ingentes cantidades de tráfico, pues producen un efecto llamado BufferBloat. Hoy contamos con otros buffers que nos ayudan a mitigar ese efecto.
Para tal propósito, podemos activar en Linux cualquiera de estas disciplinas de colas:
pfifo_fast: para routers
fq_codel: para routers y propósito general (mejor que pfifo_fast)
fq: para servidores de carga pesada
sch_cake: La más reciente y con mejor rendimiento (para activarla debe compilarse el kernel)
Un poco de historia. Hace un tiempo, un ingeniero de computadores llamado Jim Gettys, vio como iba en declive el desempeño de la velocidad de Internet, algo que nadie entendía. Entonces él con la ayuda de otros fundadores de Internet, como Van Jacobson, Vint Cerf, Dave Reed, Dave Clark, Stuart Cheshire, más un equipo de jóvenes geeks, estuvieron a la altura de las circunstancias y se preocuparon por las comunidades de Linux y BSD. De ahí salió el algoritmo FQ_CODEL, con el se mejoró bastante la gestión de tráfico en los equipos de red. Puedes ampliar más sobre en su correspondiente blog sobre buffletbloat y FQ_CODEL.
Podemos visualizar el algoritmo de gestión de colas activado en nuestro Linux, así:
sysctl net.core.default_qdisc
Suponiendo que el resultado muestre que el algoritmo activado el pfifo_fast, y queremos cambiarlo a fq_codel, ejecutamos entonces:
sudo sysctl -w net.core.default_qdisc=fq_codel
Ahora debemos verificar si la tarjeta de red tiene habilitada la cola FQ_CODEL. Eso lo hacemos con el siguiente comando:
sudo tc qdisc show dev la_interfaz_red
Si no coincide con el activado en el kernel, lo podemos cambiar de forma temporal con el siguiente comando.
sudo tc qdisc replace dev la_interfaz_red root fq_codel
Para hacer el cambio de forma permanente debemos identificar el ID de la tarjeta de red, y con ese id activamos FQ_CODEL permanentemente para esa interfaz:
sudo nmcli conecction show (mirar la columna UUID) sudo nmcli connection modify UUID_de_interfaz_red tc.qdiscs 'root fq_codel'
Control de congestión para optimizar la red de un servidor Linux
Las colas son la forma es que Linux drena los paquetes hacía su destino, con sus respectivas prioridades, y el control de congestión es el manejo de alto tráfico desde TCP.
El siguiente comando visualiza el algoritmo de congestión activado en nuestro kernel:
sysctl net.ipv4.tcp_available_congestion_control
En el moderno kernel de Linux contamos con el Algoritmo de Control de Congestión BBR. Este fue desarrollado por Google y es lo último en «güarachas» (lo mejor). Por ejemplo, con BBR, Google pudo mejorar el flujo de paquetes de Youtube hasta en un 14%, con relación a otros algoritmos. Para activarlo:
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
Con el siguiente comando podemos visualizar los Algoritmos de Control de Congestión disponibles en nuestro kernel:
ll /lib/modules/5.4.0-58-generic/kernel/net/ipv4/ | grep tcp_
Ajustar direccionamiento del Buffer Ring (RX) en la NIC
No es mucho el espacio que puede direccionar una NIC en la RAM, para un buffer ring. Con el comando ethtool -g interfaz, podemos verificar el tamaño del buffer ring gestionado por esa NIC, y su límite máximo:
El parámetro Pre-set maximus es el valor máximo al que se puede ajustar el buffer ring. Con el comando ethtool -G interfaz rx 16384 tx 16384, podemos aumentarlo (su valor es en bytes). Si al ejecutar el anterior comando nos aparece el error ‘Cannot set device ring parameters: Invalid argument‘, es porque la NIC no maneja un valores de 16384 para el buffer ring, en ese caso podríamos «tantear» con divisores, como por ejemplo 8192, 4096, 2048…
Afinamiento de los Buffer Rings desde la CPU
La identificación de una interrupción SOFTIRQ (NAPI), siempre está asociada a un núcleo de la CPU. El núcleo de la CPU que reciba la solicitud de interrupción desde NAPI, será el que recupere los datos almacenados en el buffer ring.
Podemos instalar irqbalance en Linux para que este demonio haga un balanceo más equitativo de las interrupciones del sistema, entre los núcleos de la CPU. Sin embargo, en versiones viejas de Linux, las solicitudes de interrupción de los dispositivos de red quedaban concentradas en un solo núcleo.
Para balancear este asunto en un kernel de los viejos, debemos tener identificadas las interfaces de red y sus interrupciones asociadas. Con la comando cat /proc/interrups podemos identificarlas:
En este caso tenemos que todas las interrupciones de las interfaces de red (enp0s8, enp0s9, enp6s4f1 y enp6s4f0) están concentradas en el núcelo 1 de la CPU. Para nuestro caso, nos interesa fijar las interrupciones de la interfaz enp0s8 (asociada la interrupción 25) al núcelo 0.
Entonces, confirmamos que efectivamente la interrupción 25 está asociada por defecto al núcleo 1 de la CPU. Con el comando cat /proc/irq/25/smp_affinity confirmamos:
El anterior comando nos da como resultado 02, porque los núcleos de la CPU se identifican en formato hexadecimal. Entonces 0x1 para núcelo 0 y 0x2 para núcelo 1.
CPU Binario Hexadecimal
0 0001 0x1
1 0010 0x2
2 0100 0x4
3 1000 0x8
Debemos indicarle al demonio irqbalance que no haga afinidad a la interrupción 25:
irqbalance --banirq=25
Para que el anterior ajuste sea permanente, debemos indicarlo en su archivo de configuración:
sudo nano /etc/default/irqbalance
Al final del archivo de configuración ingresamos este texto:
OPTIONS="--banirq=25"
Luego, procedemos a trasladar la interrupción 25 al núcelo 0, con el siguiente comando:
_# echo 1 > /proc/irq/25/smp_affinity
Si queremos que este ajuste persista, debemos acomodarlo en el archvio /etc/rc.local.
Después volvemos a verificar con el comando cat /proc/irq/25/smp_affinity
Esquema básico resultante de lo que acabamos de hacer:
Finalmente evidenciamos que los SOFTIRQ del dispositivo de red enp0s8, se han trasladado al núcelo 0:
Primer día. En este día podemos ver que antes de las 12 horas, el núcelo 0 estaba de holgazán, bien frescote atendiendo pocas interrupciones. Después de las 12 horas (momento en que hicimos el ajuste) lo pusimos a trabajar más (franja anaranjada). Le hemos mermado carga al núcleo 1.
Segundo día del ajuste.
Identificar paquetes perdidos en la NIC
Una forma de identificar una causa de paquetes perdidos en nuestro sistema, es monitoreando la cola en la tarjeta de red con los siguientes comandos:
watch -n 1 -t -d "ethtool -S eth0 | grep -v \"x_queue_\|x_pb_\""
watch -n 1 -t -d "ethtool --statistics eth0 | grep -i \"drop\|discard\|buffer\|fifo\""
watch -n 1 -t -d cat /proc/net/softnet_stat
cat /sys/class/net/eth0/statistics
Si definitivamente sospechamos que la perdida de paquetes está en la tarjeta de red, podemos validarlo con el siguiente comando:
sudo netstat -ni
La quinta y novena columna nos muestra los paquetes perdidos en la tarjeta.
Balanceo de ciclos de procesamiento
Si recordamos al Modelo OSI, damos por sabido que la integridad de los paquetes de red debe ser verificada por medio de una suma de verificación. También sabemos que cuando la capa de Aplicación produce datos de gran tamaño, estos son segmentados por la capa de Transporte (los divide en bloques pequeños) para que quepan en el formato de un paquete de red.
Ambas actividades, la de verificar y segmentar, las realiza el kernel, pero también las puede hacer la tarjeta de red (aunque no todas soportan esta función). A lo que me refiero es que podemos ahorrarle a la CPU esos ciclos de procesamiento que representa ambas actividades, trasladándolas al procesador de la tarjeta de red.
Miremos en cuáles recursos de hardware nuestros paquetes están siendo verificados y segmentados, con el siguiente comando:
_# ethtool -k enp7s0
Como vemos, ni la verificación ni la segmentación las está ejecutando la tarjeta de red. Con esto inferimos que ambas actividades las está haciendo el kernel. Pongamos a la tarjeta de red a hacer ambas actividades, con los siguientes comandos:
Para que la NIC ejecute la verificación de paquetes:
ethtool -K ethX rx on
ó
ethtool --offload ethX rx on
Para que la NIC ejecute la segmentación:
ethtool --offload ethX tso on
ó
ethtool -K ethX tso on
Si en alguno de los dos comandos te resulta algo como en la siguiente imagen, es por que esa NIC no soporta dicha función:
Bueno ¿y para qué queremos que nuestro router segmente datos si se supone que estos artefactos ya direccionan paquetes segmentados?… Pues porque nuestro router también produce datos. Recordemos que aquí también funciona un Proxy Caché, además tenemos aplicaciones que nos generan informes de uso, etc.
Cómo monitorear la red en tu Linux, aquí
Para que los ajustes con ethtool sean persistentes, debemos acomodar el mismo comando al final del archivo de configuración de red /etc/network/interfaces, así, por ejemplo:
post-up ethtool -K enp7s0 tso on
(Para Red Hat el archivo es /etc/sysconfig/network-scripts/ifcfg-ethX)
Vaya! esto de afinar u optimizar TCP y la red tu Linux es un asunto bastante entretenido.
Recibo Escalado Lateral (RSS)
Afinar u optimizar la red de un servidor Linux para atender un alto tráfico de peticiones, es pensar en un buen hardware de red. Una NIC que tenga la característica RSS, representa un buen apoyo de optimización de colas, diferente al Afinamiento de los Buffer Rings desde la CPU que vimos más arriba.
Con una tarjerta de red
Con dos tarjetas de red
Por ejemplo, la tarjeta de red Intel 82575EB soporta RSS y no es necesario ajustar el kernel para activar esta función. Para visualizar la distribución de sus colas de recepción y transmisión de paquetes, entre los núcleos del procesador, nos valemos del comando cat /proc/interrupts | grep «interfaz»:
La siguiente foto es de un router Linux con firewall iptables, donde se ve un flujo descendete de tráfico UDP (videollamadas), en el que RSS hace su “magia” distribuyendo esos paquetes de las tarjetas de red Intel 82575EB, entre los hilos de la CPU.
Receive Packet Steering (RPS)
Si no tenemos una tarjeta de red con RSS, podemos activar RPS en Linux para «emular» a RSS usando los núcleos de la CPU como colas.
Verificamos si RPS está activado (mi interfaz de red se llama enp4s0f0):
cat /sys/class/net/enp4s0f0/queues/rx-0/rps_cpus
Si el resultado es 0, es porque no está activado.
Para que RPS distribuya la llegada de los paquetes que viene por la interfaz enp4s0f0, entre los cuatro núcleos de nuestro procesador, lo activamos así:
_# echo f > /sys/class/net/enp4s0f0/queues/rx-0/rps_cpus
- f = para cuatro procesadores
- ff = para ocho procesadores
- fff = para doce procesadores
- ffff = para dieciséis procesadores
Si no funciona, es porque el kernel no está compilado con CONFIG_RPS (https://github.com/torvalds/linux/blob/v3.13/Documentation/networking/scaling.txt#L160-L164)
Para este caso (RPS), la variable net.core.dev_weight no nos servirá de a mucho para optimizar. En su lugar, podemos optimizar con la variable net.core.netdev_budget, que define el número máximo de paquetes que se pueden tomar de todas las interfaces en un ciclo de sondeo (NAPI). Igualmente dicho ciclo está limitado por netdev_budget_usecs, incluso si netdev_budget no alcanza a tomar todos los paquetes en su correspondiente ciclo.
Podemos implementar RPS si la tarjeta de red nuestro servidor carece de Recibo Escalado lateral (RSS), y si nuestro procesador cuenta con varios núcleos.
La diferencia entre RPS y RSS es que con RSS no «amarramos» una cola (o varias) a una CPU, sino que una cola puede asignarse dinámicamente a multiples CPUs.
TCP Fast Open para optimizar la red de un servidor Linux
No está demás acelerar el inicio de las conexiones TCP con la extensión de este protocolo, llamado TCP Fast Open. Lo activamos:
sudo sysctl -w net.ipv4.tcp_fastopen=3
Más colas y más tiempos de espera
TCP Window Scale es una opción del protocolo TCP que aumenta la cantidad de bytes manejados por la ventana de recepción. Si esta opción no se encuentra activa, la ventana máxima es de 65535 bytes. Si está activa se puede alcanzar una ventana máxima de 1GB (65535 * 2^14). Por defecto es 1:
net.ipv4.tcp_window_scaling=1
net.ipv4.tcp_fin_timeout
Es el tiempo que permanece un socket activo después de cerrarse una conexión (TIME_WAIT). Algunos le dicen «conexión huérfana», y Linux puede «reutilizarla» (junto con sus recursos de socket, cada huérfana representa 64 KB de RAM) mientras dure tcp_fin_timeout (cuesta menos que abrir un nuevo socket). Claro, el parámetro net.ipv4.tcp_tw_reuse debe estar activo (1) para que Linux pueda reasignar una conexión huérfana.
Al reducir el valor de esta entrada, TCP/IP puede liberar conexiones cerradas más rápidamente, haciendo que haya más recursos disponibles para nuevas conexiones.
Para un servidor se recomienda un valor de 30 segundos.
net.ipv4.tcp_max_orphans
Número máximo de conexiones huérfanas (TIME_WAIT).
Puertos efímeros y su tiempo de estado
Linux mantiene un rango de puertos efímeros para entablar una o varias conexiones con los servicios de red. Su rango por defecto es 32768:60999, definido en la variable net.ipv4.ip_local_port_range.
El tiempo en que un puerto efímero se mantiene «vivo» después de usarse, depende la variable net.ipv4.tcp_fin_timeout, y su valor por defecto es de 60 segundos.
puertos efímeros activos = 60999 – 32768 = 28231 puertos efímeros
Por otro lado, tener en espera un socket sin uso durante 60 segundos, es un derroche de recursos para otras conexiones que sí lo necesitan, por eso rebajamos tcp_fin_timeout a 30 segundos.
Cola de espera para solicitudes de conexión
Cuando un cliente necesita conectarse con algún servicio de red, por ejemplo, Squid Proxy, Apache Web Server ó un web socket construido con PHP, debe ejecutar un proceso de conexión de tres pasos. Veamolo en su forma básica:
Ahora veamolo en una forma más detallada:

Fuente: https://www.alibabacloud.com/blog/tcp-syn-queue-and-accept-queue-overflow-explained_599203
La solicitud de conexión comienza cuando el cliente envía al servidor un paquete llamado SYN. El servidor le responde con un paquete SYN-ACK. Finalmente el cliente completa el proceso con un ACK en el tercer paso.
net.ipv4.tcp_max_syn_backlog=8192
Una solicitud de conexión respondida con un SYN-ACK desde el servidor, pasa a ser un estado de espera denominado SYN-RECV, así Linux queda a la espera del tercer paso del cliente. El parámetro net.ipv4.tcp_max_syn_backlog (SYN_BACKLOG) define el tamaño de esa cola de espera.
Cada elemento SYN en la cola de espera usa 256 bytes de memoria en el kernel 4.14. Este parámetro es muy importante a la hora de mitigar ataques DoS y DDoS.
Por otra parte, el parámetro net.core.somaxconn (SOMAXCONN) representa el límite de solicitudes de conexión que un socket (el puerto TCP) debe colocar en SYN_BACKLOG. Es una secuencia. Su valor por defecto es de 128, pero se puede aumentar a 1024 ó más, claro, sí nuestro servidor es lo suficientemente potente.
La secuencia de SOMAXCONN -> SYN_BACKLOG produce el sigueinte efecto:
El límite de SYN_BACKLOG trunca a SOMAXCONN.
SOMAXCONN límita al proceso dueño del socket la cantidad de solicitudes de conexión, SYN-RECV, que debe poner en SYN_BACKLOG.
Por ejemplo, si SOMAXCONN está fijado a 20, y SYN_BACKLOG a 10, solo 10 solicitudes de conexión (SYN-RECV) se pondrán en la cola de espera.
De acuerdo al ejemplo anterior, si tenemos a funcionando a Apache Server y a Squid Proxy, se le truncarán a cada servicio 10 solicitudes de conexión en espera por un ACK. A como les lleguen los ACK pendientes del tercer paso, las conexiones se establecerán normalmente.
Recuerda, SYN_BACKLOG trunca a SOMAXCONN. Con el siguiente comando podemos ver los paquetes SYN descartados cuando se llega al tope de net.ipv4.tcp_max_syn_backlog:
netstat -s | grep -i "listen"
Entonces net.ipv4.tcp_max_syn_backlog y net.ipv4.tcp_max_syn_backlog son claves para optimizar la red de un servidor Linux en la capa de transporte, visto desde el modelo OSI.
Hagamos esto más agradable con una analogía gastronómica
Haré una analogía más o menos parecida con esto de los paquetes, el buffer de recepción y lo que acabamos de ver.
Pensemos en tcp_max_syn_backlog junto con net.core.somaxconn, como si fueran un restaurante que acaba de crear un delicioso platillo de comida mediterránea en auge. Este resturante tiene su debido administrador. El comedor es relativamente grande, pero límitado a un tope de mesas, y un pasillo que lo conecta desde la entrada, por donde circulan los clientes.
Tcp_max_syn_backlog viene siendo el pasillo y net.core.somaxconn la sala con las mesas. Entonces, llegan los clientes a las mesas y se alistan para recibir el menú (esperan el ACK). Finalmente llegan sus platillos y bebidas. Claro, cada comensal lo pide grande o pequeño, según su capacidad (net.ipv4.tcp_rmem). Como todos en la calle quiere entrar para probar tal maravilla, cada vez más habrán más clientes esperando en pasillo.
Conforme se desocupan las mesas, estas se «reutilizan» tal como están para desalojar el pasillo. Al reutilizar las mesas en ese estado, se ahorra tiempo y esfuerzo en acomodar sillas y cambiar de mantel (suponiendo que quedaron en condiciones aceptables). Si nadie en un tiempo determinado viene a ocupar las mesas desocupadas, Linux (nuestro andministrador) las ordenará nuevamente para un próximo comensal.
Laboratorio de SOMAXCONN
Podemos verificar el funcionamiento de SOMAXCONN y SYN_BACKLOG, por ejemplo, con un servidor web junto con netcat. A estos dos servicios les lanzarémos una serie de solicitudes de conexión por medio de un script desde otro computador.
Antes que nada, debemos desactivar temporalmente la prevención de Syn Flood, así:
sudo sysctl -w net.ipv4.tcp_syncookies=0
Ahora sí podemos continuar. Al script lo he llamado spoofer.sh (un humilde «spoofeador» de direcciones IP):
#!/bin/bash
IP_OBJETIVO=192.168.0.148
PUERTO_OBJETIVO=80
for in_ip in $( seq 10 100 )
do
ifconfig enp3s0:$in_ip 192.168.140.$in_ip
netcat -s 192.168.140.$in_ip $IP_OBJETIVO 80 &
netcat -s 192.168.140.$in_ip $IP_OBJETIVO 81 &
netcat -s 192.168.140.$in_ip $IP_OBJETIVO 82 &
done
exit 0
Ya tenemos a Apache Web Server funcionando por el puerto 80. Ahora ejecutamos a netcat una vez para que escuche por el puerto 81, y otra vez para que escuche por el puerto 82:
ncat --broker -k --listen -p 81 &
ncat --broker -k --listen -p 82 &
Básicamente el script se encarga de generar un rango de 90 direcciones IP (192.168.140.10 – 100). Luego, las pasamos netcat como «IP origen» para que las lance al servidor web con 90 solicitudes de conexión TCP. Así mismo para el puerto 81 y 82. También debemos garantizar que el servidor web NO tenga una ruta, conectividad o una entrada en su tabla ARP hacía nuestro spoofer. Esto con el fin de poder ver el efecto en la cola de espera.
Luego, vamos al servidor web y le bajamos el tope de SOMAXCONN a 20 y de SYN_BACKLOG a 10, para hacerlo más entendible:
sudo sysctl -w net.core.somaxconn=20
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=10
Desde el otro computador, ejecutamos el spoofer.sh.
Volvemos a nuestro servidor web y verificamos las solicitudes de conexión con el siguiente comando:
watch -n 1 "ss -at '( dport = :http or dport = :81 or dport = :82 or sport = :http or sport = :81 or sport = :82 )'"
Entonces en la imagen vemos 9 solicitudes de conexión encoladas por cada socket, a la espera del ACK, pues el servidor web no tiene una ruta hacía la direcciones IP «spoofeadas».
Adicionándole al servidor una ruta hacía las direcciones IP spoofeadas, podremos ver pues, como se van entablando las conexiones (ESTABLISHED).
La siguiente tabla muestra el efecto producido sobre los SYN-RECV, debido a la alternancia de entre SYN_BACKLOG y SOMAXCONN:
Recordemos activar nuevamente la protección contra inundaciones SYN:
sudo sysctl -w net.ipv4.tcp_syncookies=1
Un ataque por inundación SYN (SYN flood) es un tipo de ataque por Denegación de Servicio. Consiste en disparar una ráfaga de solicitudes de conexión sin intención de devolver sus correspondientes ACK. Entonces la cola de espera se llena y Linux queda sin poder atender más solicitudes.
La protección contra SYN flood es un frente que tiene Linux para mitigar ataques DoS. En este caso Linux responde a los paquetes SYN con un SYN-ACK más una cookie, que identifica la solicitud de conexión. Con este método se elimina el encolamiento de SYN-RECV. Si la solicitud de conexión es genuina, el cliente responderá con un ACK más la cookie que lo identifica. Y listo, conexión establecida.
Fragmentación del datagrama IP
Bueno, puede que de vez en cuando nos encontremos con algunos paquetes IP (datagramas, capa 3) fragmentados. Eso ocurre cuando los datos son producidos en una máquina con tecnología FDDI, Tocke Ring ó ATM, pues manejan una MTU superior a la de ethernet. En esas tecnologías un paquete IP puede superar hasta 8 veces el tamaño de una trama ethernet. De ahí es que sale el tema de la fragmentación del datagrama.
Linux reserva un límite de paquetes y un espacio en memoria para que su pila TCP/IP re-ensamble paquetes IP fragmentados. Sus variables con valores predeterminados, son estos:
net.ipv4.ipfrag_high_thresh = 4194304
net.ipv4.ipfrag_low_thresh = 3145728
Pero en la actualidad es inusual encontrar paquetes IP fragmentados, debido a un algoritmo llamado Path MTU Discovery. Pero como somos bien curiosos, vamos a echarle mano a TCPDUMP para saber si a nuestro servidor le llegan paquetes IP fragmentados.
A la caza de paquetes IP (v4) fragmentados
Para salir de caza con nuestro GNU/Linux, debemos hacer dos cosas: armarnos con TCPDUM y recordar la «morfología» de nuestra «presa»:
Para esta cacería nos interesa conocer dos «órganos» del datagrama IP. Se trata de los siguientes campos: Flags y Fragment Offset.

Bit 0: reserved, must be zero
Bit 1: (DF) 0 = May Fragment, 1 = Don’t Fragment.
Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.
Los dos campos:
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 <- 2 bytes +-----------------+---------------+ Flags | Fragment Offset +-------+-------------------------+
Tengamos en cuenta los 16 bits distribuidos entre los campos Flags y Fragments Offset. Los dos juntos componen 2 Bytes. El campo Flags trabaja con 3 bits del primer Byte (en azul), que comparte con Fragment Offset.
De los tres bits del campo Flags, el dos (2) índica si el paquete IP ha sido fragmentado o no. En caso de que sí, el campo Fragment Offset le informa a la pila TCP/IP sobre los bytes que le hacen falta para completar el re-ensamblado del datagrama IP.
Si hacemos un recuento de bytes del datagrama, desde el campo Version hasta el byte compartido por Flags y Fragment, tenemos que hay 7 bytes (de 0 a 6, byte 6 en azul).
Después de este detallado reconocimiento de nuestro objetivo, desplegamos el operativo:
En una consola, ejecutamos a TCPDUMP indicándole la interfaz por donde creemos que vienen los paquetes IP fragmentados.
tcpdump '((ip[6:2] > 0) and (not ip[6] = 64))' -i enp0s8 -w volcado1.pcap
Le hemos dicho a TCPDUMP lo siguiente:
Que capture los paquetes IP que tengan información (>0) a partir del bit 2 del byte 6, o sea, el campo Fragment Offset, pero que no capture paquetes con el flag Don’t Fragment activado (64, o sea, el byte 6 – 01000000 – en décimal), pues no nos interesa capturar paquetes que no deben ser fragmentados.
Además, los paquete capturados los guardamos en el archivo volcado1.pcap para analizarlos posteriormente con Wireshark.
Seguro pasará mucho tiempo para ver un paquete fragmentado circulando por nuestra red. Pero si estamos ansiosos por ver uno, podemos crearlos con el comando ping, así:
ping -M want -s 6000 192.168.0.72 -c 1
Lo que hacemos con ping es enviar a nuestro Linux un (1) paquete echo con 6000 bytes de datos. Como el protocolo ICMP no fragmenta o segmenta datos grandes como lo hace TCP, le toca al mismo protocolo IP hacerlo.
Abrimos el archivo volcado1.pcap con Wireshark:
Entonces vemos que la secuencia funciona así:
- El primer fragmento le informa al protocolo IP que el TOTAL de bytes fragmentados es 5920, eso incluye los 1480 bytes que él lleva.
- Luego el segundo paquete informa que restan 4440 bytes (5920 bytes del TOTAL – 1480 bytes del primero que ya fueron entregados).
- Después el tercero avisa que restan 2960 bytes por ensamblar, o sea, los 1480 bytes que él lleva y más 1480 del cuarto paquete.
- Finalmente el cuarto paquete lleva los últimos 1480 bytes y su Fragment Offset informa que no hay más bytes por ensamblar (0).
Comandos adicionales
Los siguientes comandos son útiles para verificar y consultar información acerca de los drivers de las tarjetas de red, y algunos de sus parámetros habilitados:
lshw -C network (con éste podemos averiguar el driver asociado a una NIC) modinfo 'nombre del driver' systtool -vm 'nombre del driver' (instalar con apt install sysfsutils)
Visualización de estado de sockets:
ss -inm ‘src *:8080’ | more
En el siguiente hipervínculo puedes conocer más sobre el comando ss.
Estos otros comandos son útiles para consultar más información acerca de los dispositivos de red y sus informes sobre paquetes de red procesados, informes que también pueden ser valiosos para identificar problemas de red con este equipo:
Informes sobre velocidades soportadas, autonegociación, etc.
ethtool ethX
Información como nombre del driver de la interfaz, acceso a la EEPROM, etc.
ethtool -i ethX
Cuantificación de paquetes enviados, recibidos, pérdidos, colisiones, así como la cuatificación de datos enviados y recibidos desde su memoria EEPROM por medio SMBus, etc.
ethtool -S ethX
Verifica si tiene activada la auto-negociación.
ethtool -a ethX
Hace titilar los LEDs de la NIC para que la podamos ubicar físicamente.
ethtool -p ethX
Deshabilita o habilita la autonegociación.
ethtool -s eth0 autoneg off ó ethtool -s eth0 autoneg on
Mirar la estadística de paquetes enviados, recibidos, perdidos, retransmitidos, etc.
netstat -s | grep segments
netstat -s | grep retransmitted
Optimización de File Descriptors
A veces es necesario aumentar el límite de los File Descriptor si el usuario en el sistema trabaja con muchos sockets (generalmente no es necesario modificar este punto).
A. Verificar como usuario normal la configuración del número de Files Descriptors
ulimit -a | grep 'open files'
Generalmente da como salida 1024
B. Agregar las siguientes líneas en /etc/security/limits.conf
* soft nofile 8192
* hard nofile 8192
C. Luego, adicionamos la siguiente linea en el archivo /etc/pam.d/common-session
session required pam_limits.so
D. Aplicar el cambio:
sudo sysctl -p
En los últimas versiones de Ubuntu este procedimiento no tiene efecto, y toca hacerlo directamente en los archivos ~/.bashrc y ~/.profile. Entonces le escribimos el comando ulimit -n 8192 al final de cada archivo. El número de file descriptors ya depende de tus necesidades.
Configuración permanente de ajustes
Algunos comandos acá mostrados aplican ajustes de forma permanente, pero otros no. Estos son algunos ajustes que podemos aplicar al iniciar el sistema, colocandolos en el archivo /etc/rc.local
tc qdisc replace dev la_interfaz_de_red root fq_codel
ifconfig ethX txqueuelen 5000
echo 1 > /proc/irq/25/smp_affinity
Puedes ver cómo habilitar el archivo /etc/rc.local en Ubuntu, aquí
El hardware
Hemos hablado mucho de la parte lógica ¿pero qué hay del hardware? Bueno, sin importar si nuestro Linux funciona sobre una máquina básica o potente, debemos aplicar estas recomendaciones, aunque en el siguiente artículo podemos ver la configuración de hardware recomendada para un servidor proxy en Linux al que se le exige bastante.
¿Qué diferencia tenemos con equipos propietarios?
En caso de que usemos este Linux como router y seguridad perimétral, podemos tener tres diferencias básicas frente a soluciones propietarias:
- Hardware: A Linux podemos implementarlo en casi cualquier hardware, así que tendríamos dos, tres, cuatro veces, o más de capacidad de hardware y rendimiento, frente a una solución gama entrada o media tipo Fortinet. En este enlace podemos ver el hardware de los Fortigate.
- Costos de licenciamiento: Ninguno con GNU/Linux. Algunas decenas de dólares, hasta algunos miles con marcas propietarias.
- Flexibilidad: Código abierto con GNU/Linux, más opciones de monitoreo sin costos financieros adicionales y más control de los recursos. Con marcas propietarias no tenemos acceso a parámetros del kernel.
Más comandos
En la página web de Ubunlog podemos conocer a Netutils-linux, una batería de comandos Python para monitorear el rendimiento de los paquetes, en los recursos de hardware asociados.
En el próximo artículo explicamos cómo configurar una VLAN en una tarjeta de red, de nuestro Linux.
Seguro no esperabas que afinar u optimizar TCP y la red de Linux, fuera un algo dispendioso.
Puedes ver Comandos Linux para la Gestión de Redes
< Sexta Parte – Octava Parte – VLAN en Linux >
Valora este artículo:
Está genial
























Pingback: 🥇 El Modelo OSI de 7 capas ó modelo de red TCP IP
Pingback: Optimizar el servidor Proxy para una red de datos de alto tráfico
Pingback: 🥇 Herramientas de Diagnostico de Hardware en Linux - Ubuntu
Pingback: 🥇 Optimizar o Acelerar tu máquina con Linux - Disco Duro y SWAP
Pingback: Funcionamiento del subsistema de red de Linux - Espacio Tecnológico
Pingback: 🥇 Alternativa al Directorio Activo de Windows Server, con Linux
Pingback: 🥇 Optimizar o Acelerar a Apache Web Server en Linux
Pingback: 🥇 Comandos Linux para la Gestión de Redes LAN - Networking
Pingback: 🥇 Configurar una VLAN en Linux como Router y Firewall
Pingback: 🥇 Matemáticas de redes - Subnetting - Conversión de Velocidades