⏱️ Lectura: 15 min

Cuando subís un archivo grande y la velocidad cae a la mitad apenas otra persona abre un video en la misma red, no es magia: es el control de congestión TCP decidiendo, paquete a paquete, cuánto tráfico puede soportar el enlace antes de perder datos. Linux permite cambiar el algoritmo que toma esa decisión con un solo comando, y la diferencia entre CUBIC (el que usás hoy sin saberlo) y BBR (el que diseñó Google en 2016) se mide en milisegundos de latencia y megabits por segundo reales.

📑 En este artículo
  1. TL;DR
  2. ¿Qué es el control de congestión TCP?
  3. Por qué importa la gestión de tráfico en redes compartidas
  4. Cómo funciona: de slow start a fast recovery
  5. Ejemplos prácticos y cómo empezar
  6. Casos de uso reales
  7. Errores comunes y buenas prácticas
  8. Comparativa con alternativas
  9. Profundizando: el modelo de BBR y qué viene después
  10. Preguntas frecuentes
    1. ¿Qué es el control de congestión TCP y quién lo decide, el cliente o el servidor?
    2. ¿Por qué CDNs y plataformas de video prefieren BBR en vez de CUBIC?
    3. ¿Cómo sé qué algoritmo de congestión usa mi conexión ahora mismo?
    4. ¿BBR reemplaza a CUBIC en todos los casos?
    5. ¿Qué relación tiene el bufferbloat con estos algoritmos de congestión?
  11. Referencias

TL;DR

  • El control de congestión TCP ajusta la ventana de envío (cwnd) para aprovechar el ancho de banda disponible sin saturar la red.
  • CUBIC es el algoritmo por defecto en Linux desde el kernel 2.6.19 (2006) y crece la ventana con una función cúbica del tiempo.
  • BBR, publicado por Google en 2016, mide ancho de banda y RTT en vez de reaccionar recién cuando se pierde un paquete.
  • sysctl net.ipv4.tcp_congestion_control muestra y permite cambiar el algoritmo activo en cualquier Linux moderno.
  • Elegir mal el algoritmo en una red compartida puede llenar el buffer del router: el fenómeno se llama bufferbloat.

¿Qué es el control de congestión TCP?

El control de congestión TCP es el mecanismo del protocolo que regula cuántos segmentos puede tener un emisor pendientes de confirmación, ajustando ese número según el estado real de la red. Forma parte del estándar desde 1988 y evita que varias conexiones saturen un enlace compartido hasta perder paquetes en cascada.

Antes de 1988 no existía: TCP enviaba datos tan rápido como el receptor lo permitía, sin mirar la red intermedia. El resultado, documentado por Van Jacobson en su paper de 1988, fue una serie de colapsos de congestión en la primera NSFNET, con el rendimiento entre dos nodos cercanos desplomado durante horas. Ese incidente motivó el algoritmo que hoy corre, modificado, en cada servidor web del planeta.

Por qué importa la gestión de tráfico en redes compartidas

Internet no reserva ancho de banda por conexión: miles de flujos TCP comparten el mismo cable, el mismo router y la misma cola de paquetes. Sin un mecanismo que frene el envío cuando el camino se satura, cualquier descarga grande desplazaría a las demás conexiones hasta dejarlas sin servicio.

El costo de ignorar la congestión no es solo velocidad: es pérdida de paquetes, retransmisiones, y en el peor caso un colapso donde el rendimiento útil cae mucho más rápido de lo que crece la demanda. Por eso cada sistema operativo moderno, de un router doméstico a un servidor de Google, implementa alguna variante de este control.

Cómo funciona: de slow start a fast recovery

El algoritmo clásico (Reno, y con matices CUBIC) pasa por varias fases. En slow start, la ventana de congestión (cwnd) arranca en un segmento y se duplica cada RTT confirmado, hasta tocar un umbral llamado ssthresh o hasta perder un paquete. Es un crecimiento exponencial deliberado: mejor explorar rápido cuánto aguanta la red que arrancar a ciegas con un valor fijo.

Al superar ssthresh entra en congestion avoidance: ahí el crecimiento se vuelve lineal, un segmento adicional por RTT completo (additive increase). Si llega una pérdida, Reno corta la ventana a la mitad (multiplicative decrease); de ahí el nombre AIMD que describe a toda la familia de algoritmos basados en pérdida.

En términos concretos, additive increase equivale a sumar cwnd = cwnd + MSS*MSS/cwnd por cada ACK recibido durante congestion avoidance, una aproximación que logra sumar cerca de un MSS por RTT completo sin necesitar contar RTTs explícitamente. La multiplicative decrease es simplemente cwnd = cwnd / 2 ante la primera señal de pérdida. Esa asimetría, subir despacio y bajar rápido, es intencional: prioriza drenar la congestión ya detectada antes de volver a probar el límite de la red.

Cuando la pérdida se detecta por tres ACKs duplicados (no por timeout), el emisor no vuelve a slow start: entra en fast recovery, retransmite solo el segmento perdido y retoma el crecimiento lineal desde una ventana reducida. Un timeout completo, en cambio, sí fuerza volver a slow start desde cero, porque indica que la red perdió por completo el rastro de la conexión.

stateDiagram-v2
 [*] --> SlowStart
 SlowStart --> CongestionAvoidance: cwnd alcanza ssthresh
 CongestionAvoidance --> FastRecovery: perdida por ACKs duplicados
 FastRecovery --> CongestionAvoidance: segmento retransmitido confirmado
 SlowStart --> FastRecovery: perdida por ACKs duplicados
 CongestionAvoidance --> SlowStart: timeout de retransmision

CUBIC, el algoritmo por defecto en Linux, reemplaza el crecimiento lineal de la congestion avoidance por una función cúbica del tiempo transcurrido desde la última pérdida: crece rápido al principio, se aplana cerca del punto donde perdió la vez anterior y vuelve a acelerar si la red lo permite. Esto le da mejor uso del ancho de banda en enlaces de alta capacidad y alta latencia (los llamados long fat networks) que el AIMD puro de Reno.

CUBIC es el algoritmo por defecto en el kernel de Linux desde la versión 2.6.19, lanzada en 2006. Foto de Florian Olivo en Unsplash

Ejemplos prácticos y cómo empezar

Todo esto se puede inspeccionar y cambiar en cualquier distribución Linux moderna sin instalar nada adicional. El primer paso es ver qué algoritmos están disponibles en el kernel:

$ sysctl net.ipv4.tcp_available_congestion_control
net.ipv4.tcp_available_congestion_control = cubic reno

Esa salida lista solo los módulos ya cargados. Para ver cuál usan las conexiones nuevas por defecto:

$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic

BBR viene como módulo del kernel (tcp_bbr) en casi todas las distros desde Linux 4.9, pero hay que cargarlo antes de poder seleccionarlo:

$ sudo modprobe tcp_bbr
$ lsmod | grep bbr
tcp_bbr 20480 0

Con el módulo cargado, cambiar el algoritmo activo es una sola línea (el cambio no sobrevive un reinicio salvo que lo agregues a /etc/sysctl.conf):

$ sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
net.ipv4.tcp_congestion_control = bbr

Para confirmar qué algoritmo está usando una conexión activa en particular, ss -i lo muestra como parte del resumen de cada socket:

$ ss -i
...
 cubic wscale:7,7 rto:204 rtt:1.2/0.6 mss:1448 cwnd:10 ssthresh:7
 bbr wscale:7,7 rto:216 rtt:14.8/7.4 mss:1448 cwnd:28 pacing_rate 1.2Mbps

Si la prueba corre dentro de la misma LAN y no hay nada que simular, la diferencia entre algoritmos casi no se nota: el control de congestión TCP importa cuando hay latencia y pérdida real. Para reproducir esas condiciones en una máquina de prueba, tc con netem agrega latencia y pérdida artificial a una interfaz:

$ sudo tc qdisc add dev eth0 root netem delay 100ms loss 1%

Con esa regla activa, correr iperf3 entre dos máquinas (una como servidor con iperf3 -s, la otra como cliente con iperf3 -c 192.168.1.10 -t 30) deja ver cómo CUBIC pierde rendimiento por cada pérdida que interpreta como congestión, mientras BBR sostiene mejor el rendimiento porque no depende de esa señal. Para quitar la regla de simulación: sudo tc qdisc del dev eth0 root.

sequenceDiagram
 participant E as Emisor
 participant R as Receptor
 E->>R: segmento 1 con cwnd en 1
 R-->>E: ACK del segmento 1
 Note over E,R: cwnd crece a 2
 E->>R: segmento 2
 E->>R: segmento 3
 R-->>E: ACK del segmento 2
 R-->>E: ACK del segmento 3
 Note over E,R: cwnd crece a 4

Casos de uso reales

Google activó BBR para el tráfico de YouTube y de su red interna B4 porque CUBIC, al tratar cualquier pérdida como señal de congestión, rinde mal en enlaces de larga distancia donde parte de la pérdida es ruido de radio o conmutación, no saturación real. En una videollamada o un stream, esa diferencia se traduce en menos buffering y menos saltos de calidad.

En redes móviles y Wi-Fi pasa algo similar: una ráfaga de interferencia tira paquetes sin que exista congestión real en la red troncal. CUBIC frena la ventana igual que si hubiera un router saturado; BBR, que no basa su decisión en la pérdida sino en mediciones de ancho de banda y RTT mínimo, tiende a sostener mejor el rendimiento en ese escenario.

Los operadores móviles enfrentan el mismo dilema en la red de acceso radio, donde el RTT varía segundo a segundo según la cantidad de usuarios conectados a la misma celda. Un algoritmo que confunde esa variación con congestión real termina frenando conexiones que en realidad tienen ancho de banda disponible, y es una de las razones por las que operadores y CDNs evalúan BBR o variantes propias para el último tramo de la conexión.

En el otro extremo, dentro de un data center con enlaces cortos y de alta capacidad, muchos operadores prefieren variantes como DCTCP (Data Center TCP), que usa ECN en vez de pérdida para señalar congestión y reacciona con ajustes mucho más finos, pensados para colas de microsegundos en vez de rutas con decenas de milisegundos de RTT.

Errores comunes y buenas prácticas

El error más repetido es asumir que BBR es superior en todos los casos. Varios estudios de la comunidad de redes documentaron que, cuando un flujo BBR comparte un buffer saturado con flujos CUBIC, BBR puede quedarse con una porción desproporcionada del ancho de banda porque no reduce su envío ante la señal que sí frena a CUBIC. En un enlace mixto, eso no es una mejora: es inequidad entre conexiones.

Otro error común es confundir el algoritmo de congestión del sistema operativo con un ajuste por aplicación. El valor de sysctl solo define el default para sockets nuevos; cualquier aplicación puede pedir otro algoritmo con setsockopt(TCP_CONGESTION), y muchos frameworks de red ya lo hacen sin que el administrador se entere.

Cambiar el valor de sysctl tampoco afecta a las conexiones TCP que ya estaban abiertas en ese momento: el algoritmo se fija al establecer la conexión y no se puede reasignar a mitad de una sesión activa. Si necesitás que un servicio ya corriendo use el nuevo algoritmo, hay que reiniciar esa conexión o el proceso que la abrió.

⚠️ Ojo: si un router doméstico o un ISP tiene buffers sobredimensionados, ningún algoritmo de congestión soluciona el bufferbloat por sí solo: la cola llena igual acumula milisegundos de latencia antes de descartar un paquete. Ahí la solución complementaria es gestión activa de colas (AQM) como CoDel o fq_codel, no cambiar de CUBIC a BBR.

Por último, cargar el módulo tcp_bbr sin verificar que quedó activo es un descuido típico: si modprobe falla silenciosamente (kernel viejo, módulo no compilado), sysctl -w devuelve un error de invalid argument en vez de aplicar el cambio, y conviene revisar dmesg antes de asumir que el algoritmo cambió.

Comparativa con alternativas

Elegir entre Reno, CUBIC, BBR y DCTCP depende del enlace, no de cuál es mejor en abstracto:

AlgoritmoCuándo se usaVentajaLimitación
RenoReferencia académica, kernels muy antiguosSimple de implementar y de razonarTarda varios RTT en recuperar velocidad tras una pérdida
CUBICDefault en Linux, Android y la mayoría de servidores web desde 2006Buen uso del ancho de banda en enlaces de alta capacidad y alta latenciaInterpreta cualquier pérdida como congestión, aunque sea ruido de radio
BBRCDNs, videollamadas, enlaces móviles o satelitales con pérdida no relacionada a congestiónMide ancho de banda y RTT en vez de depender de la pérdida de paquetesPuede tomar más ancho de banda que CUBIC cuando comparten el mismo buffer
DCTCPRedes internas de data center con RTT de microsegundosReacciona a señales ECN mucho más finas que la pérdidaNecesita switches con soporte ECN y no está pensado para internet público

Profundizando: el modelo de BBR y qué viene después

Los algoritmos basados en pérdida (Reno, CUBIC) asumen una premisa de los años 80: si se descarta un paquete, hay congestión. BBR invierte esa lógica y construye un modelo explícito de la red con dos variables, el ancho de banda máximo observado (BtlBw) y el RTT mínimo observado (RTprop), y calcula la tasa de envío ideal como el producto de ambos, la regla conocida como BDP (bandwidth-delay product).

Para mantener ese modelo actualizado, BBR cicla por cuatro fases: Startup (crecimiento exponencial parecido a slow start), Drain (vacía el exceso de datos que Startup metió en las colas intermedias), ProbeBW (alterna breves subas y bajas de envío para seguir midiendo el ancho de banda disponible) y ProbeRTT (reduce drásticamente el envío por un instante para volver a medir el RTT mínimo sin la interferencia de colas propias).

flowchart TD
 A["Startup: duplica el envio cada RTT"] --> B["Drain: vacia el buffer acumulado"]
 B --> C["ProbeBW: alterna entre subir y bajar el envio"]
 C --> D["ProbeRTT: reduce el envio para medir el RTT minimo"]
 D --> C

BBR además distribuye el envío de cada ventana a lo largo del RTT estimado (pacing) en vez de mandar toda la ventana de golpe, como hacían Reno y CUBIC. Enviar en ráfaga satura momentáneamente los buffers intermedios aunque el promedio esté dentro de lo que la red soporta; espaciar los paquetes según el ancho de banda medido evita ese pico y es una de las razones por las que BBR suele generar menos pérdida real en los routers que atraviesa.

Un complemento ortogonal al algoritmo de congestión es ECN (Explicit Congestion Notification): en vez de esperar a que un router descarte un paquete, marca un bit en la cabecera IP cuando la cola empieza a llenarse, y el receptor devuelve esa marca en el ACK. Es una señal más temprana y más barata que la pérdida, y es la base de variantes como DCTCP. Linux la soporta, pero sigue deshabilitada por default en muchas distribuciones porque algunos middleboxes viejos la descartan mal.

BBR mide el producto ancho de banda por retardo (BDP) en vez de esperar a perder un paquete. Foto de Ilya Pavlov en Unsplash

📖 Resumen en Telegram: Ver resumen

Tu próximo paso: corré sysctl net.ipv4.tcp_available_congestion_control en tu propia máquina Linux y compará con ss -i qué algoritmo está usando ahora mismo cada conexión activa.

📬 Recibí lo nuevo en tu email

Te avisamos de artículos grandes (1-2 por mes).

Preguntas frecuentes

¿Qué es el control de congestión TCP y quién lo decide, el cliente o el servidor?

Cada extremo de la conexión corre su propia copia del algoritmo sobre su propia ventana de envío. No hay negociación previa: cada lado decide de forma independiente, a partir de los ACKs que recibe, cuánto puede enviar a continuación.

¿Por qué CDNs y plataformas de video prefieren BBR en vez de CUBIC?

Porque en enlaces móviles o de larga distancia, buena parte de la pérdida de paquetes no viene de congestión real sino de interferencia o conmutación. CUBIC frena igual ante cualquier pérdida; BBR, al medir ancho de banda y RTT en vez de depender de la pérdida, sostiene mejor el rendimiento en esas condiciones.

¿Cómo sé qué algoritmo de congestión usa mi conexión ahora mismo?

ss -i muestra el algoritmo activo como parte del resumen de cada socket TCP abierto, junto con la ventana de congestión (cwnd) y el RTT medido en ese momento.

¿BBR reemplaza a CUBIC en todos los casos?

No. En un buffer compartido con flujos CUBIC, BBR puede tomar una porción desproporcionada del ancho de banda, y en data centers con RTT de microsegundos suele preferirse DCTCP, que usa ECN en vez de un modelo de ancho de banda y RTT.

¿Qué relación tiene el bufferbloat con estos algoritmos de congestión?

El bufferbloat ocurre cuando un router acumula paquetes en una cola sobredimensionada antes de descartar alguno, lo que agrega latencia sin que ningún algoritmo de congestión lo note a tiempo. La solución no es cambiar de algoritmo sino aplicar gestión activa de colas, como CoDel, en el router.

Referencias

  • ACM Digital Library: el paper original de Van Jacobson, Congestion Avoidance and Control (1988).
  • IETF RFC 5681: la especificación formal del control de congestión TCP.
  • ACM Queue: BBR Congestion-Based Congestion Control, el paper de Google que introdujo BBR en 2016.
  • Linux Kernel Documentation: referencia de los parámetros sysctl de red, incluyendo tcp_congestion_control.
  • Wikipedia: resumen histórico de las variantes de control de congestión TCP.

📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.

Imagen destacada: Foto de Dimitri Karastelev en Unsplash

¿Te sirvió? ¿Te dio otro error? Contalo abajo: las preguntas se responden y le sirven al siguiente que llegue.

Dejar un comentario

Andrés Morales

Desarrollador e investigador en inteligencia artificial. Escribe sobre modelos de lenguaje, frameworks, herramientas para devs y lanzamientos open source. Cubre papers de ML, ecosistema de startups tech y tendencias de programación.

0 Comentarios

Deja un comentario

Marcador de posición del avatar

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Podés incluir código entre <code>…</code> o, para varias líneas, <pre><code>…</code></pre>.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.