WireGuard lento: cómo encontrar la causa real
WireGuard lento suele indicar un problema de MTU. Busca la MTU por bisección, limita TCP MSS, revisa steal time y mide la ruta antes de culpar al túnel.
De dónde proceden realmente las velocidades bajas de WireGuard
Las velocidades bajas de WireGuard se deben a una de cuatro causas, y no todas tienen la misma probabilidad. La primera es la MTU (unidad máxima de transmisión): el túnel crea paquetes demasiado grandes para uno de los enlaces de la ruta, por lo que las transferencias grandes se bloquean mientras las pequeñas funcionan correctamente. La segunda es la propia ruta, que ya era el límite antes de crear el túnel. La tercera es la CPU de un VPS pequeño y compartido, donde el cifrado compite con todos los demás huéspedes del mismo host. La cuarta es la conexión del propio peer.
Compruébelas en ese orden. La MTU va primero porque es la única causa de la lista que introduce el propio WireGuard y porque sus síntomas no parecen lentitud. Una MTU incorrecta suele manifestarse como un túnel que se conecta al instante, responde a ping, acepta un inicio de sesión SSH y después se bloquea la primera vez que copia un archivo.
Conviene descartar un síntoma antes de todo lo anterior. Si cada sitio nuevo tarda varios segundos en empezar a cargar y después transfiere a toda velocidad, el problema está en la resolución de nombres, no en el rendimiento de la conexión. DNS mediante WireGuard tiene sus propios modos de fallo, y ningún cambio de MTU los soluciona.
¿Por qué la MTU de WireGuard es 1420?
Cada paquete que envía al túnel se cifra y se encapsula dentro de un paquete nuevo. La encapsulación consume bytes, que se restan de la carga útil.
La cabecera de datos de WireGuard ocupa 32 bytes: un campo de tipo de 4 bytes, un índice de receptor de 4 bytes, un contador de 8 bytes y una etiqueta de autenticación Poly1305 de 16 bytes. Alrededor se añade una cabecera UDP de 8 bytes. Después se añade la cabecera IP externa, que ocupa 20 bytes en IPv4 y 40 bytes en IPv6. Por tanto, la encapsulación total es de 60 bytes cuando su Endpoint es una dirección IPv4 y de 80 bytes cuando es IPv6. La página del protocolo WireGuard documenta el formato de los mensajes del que proceden estas cifras.
wg-quick no hace una suposición. Lee la MTU de la interfaz que enruta hacia su Endpoint y después resta 80. En una ruta Ethernet normal de 1500 bytes, el resultado es 1420, que es el valor que muestra ip link show wg0. Resta 80 en lugar de 60 para que la misma cifra siga siendo segura si ese endpoint alguna vez se alcanza mediante IPv6, donde la cabecera externa es 20 bytes mayor.
The data behind this chart
[
{
"label": "Ethernet, IPv4 endpoint",
"path_mtu": 1500,
"encap_overhead": 60,
"usable_wg0_mtu": 1440
},
{
"label": "Ethernet, IPv6 endpoint",
"path_mtu": 1500,
"encap_overhead": 80,
"usable_wg0_mtu": 1420
},
{
"label": "PPPoE DSL, IPv4 endpoint",
"path_mtu": 1492,
"encap_overhead": 60,
"usable_wg0_mtu": 1432
},
{
"label": "Extra tunnel in the path",
"path_mtu": 1400,
"encap_overhead": 80,
"usable_wg0_mtu": 1320
}
]Esas 4 filas son cálculos aritméticos, no mediciones. En una ruta limpia de 1500 bytes con un endpoint IPv4, 1440 cabría, por lo que el valor predeterminado de 1420 deja 20 bytes sin utilizar. Ese margen es intencionado y no constituye un problema.
La última fila es la importante. Cuando algún enlace de la ruta sólo admite 1400 bytes, un túnel configurado todavía con 1420 genera un paquete externo de 1500 bytes en cada segmento de tamaño completo, es decir, 100 bytes más de lo que ese enlace acepta. El valor que cabe es 1320.
Tampoco copie 1320. La MTU de su ruta depende de la ruta concreta, y la única forma de conocerla es medirla.
Cómo se manifiesta un MTU incorrecto
El fallo no es gradual. Es una separación clara entre los paquetes pequeños y los grandes.
pinga través del túnel funciona con cualquier tamaño normal.- El inicio de sesión SSH termina y la escritura responde con normalidad.
curl -I https://example.comdevuelve las cabeceras de inmediato.curl https://example.comen una página grande se queda bloqueado después de los primeros kilobytes.scpde un archivo grande comienza y después se detiene en un determinado porcentaje.- Una sesión SSH se bloquea en cuanto se ejecuta un comando que muestra mucho contenido.
Todo esto ocurre porque una conexión TCP sólo crea segmentos de tamaño completo cuando tiene muchos datos que transferir. El handshake y la primera petición caben con cualquier MTU de la ruta. El bloqueo comienza con el primer segmento de tamaño completo. Por eso la conexión parece funcionar hasta el momento en que deja de ser útil.
Un paquete exterior que supera el MTU del siguiente enlace puede tener uno de dos resultados.
Se fragmenta. Un router lo divide y el extremo remoto vuelve a ensamblar las partes. La transferencia funciona, pero es más lenta, porque se envían dos paquetes en lugar de uno y el receptor mantiene el estado hasta que llegan ambos. Si se pierde un fragmento, se pierde todo el paquete original. Por eso una ruta con una pérdida del 1% se comporta como una ruta mucho peor. Muchos firewalls también descartan los fragmentos IP por política, lo que convierte este resultado en el siguiente.
Se descarta y es posible que no reciba ninguna notificación. Un router que no puede fragmentar envía al remitente un mensaje ICMP (protocolo de mensajes de control de Internet) de «fragmentación necesaria» con el MTU que puede aceptar. Si el mensaje llega, el descubrimiento de MTU de la ruta funciona y el remitente reduce por sí solo el tamaño de sus segmentos. Muchas redes filtran ICMP, por lo que el mensaje suele no llegar. Ningún otro mecanismo informa de la pérdida. Ese es el agujero negro: el paquete sale, no vuelve nada, no aparece ningún error en los registros de ninguno de los extremos y la transferencia se bloquea hasta que se agota algún tiempo de espera.
¿Cómo encuentro el MTU correcto?
Mida la ruta y haga la resta. Pruebe la red subyacente, no el túnel. Para ello, haga ping a la dirección pública del servidor desde el cliente y prohíba la fragmentación.
ping -M do -s 1472 -c 3 203.0.113.10-M do establece el bit DF (don't fragment), por lo que ningún router intermedio puede dividir el paquete. -s es el tamaño de la carga útil ICMP. Un paquete IPv4 completo contiene esa carga útil, más 8 bytes de cabecera ICMP y 20 bytes de cabecera IP. Por tanto, -s 1472 coloca exactamente 1500 bytes en la red.
Hay tres resultados importantes. Las respuestas correctas indican que caben 1500 bytes y que el MTU no es el problema. Un error local indica que la propia interfaz ya tiene un tamaño menor que el solicitado:
ping: local error: message too long, mtu=1500Una respuesta de un router intermedio proporciona directamente la respuesta, y puede detenerse ahí:
From 203.0.113.1 icmp_seq=1 Frag needed and DF set (mtu = 1492)Una pérdida del 100% de paquetes con 1472, junto con respuestas correctas con un tamaño menor, indica un caso de agujero negro. Ningún router le informa del problema, así que debe encontrar el límite mediante bisección. Mantenga un tamaño que sepa que funciona y otro que sepa que falla. Pruebe el punto medio y mueva el límite al que corresponda según el resultado. Cada ronda divide por dos el intervalo restante, por lo que cinco o seis rondas son suficientes.
Una bisección completa, una ronda cada vez
Cada línea es un comando que ejecuta en el cliente contra la dirección pública del servidor. El comentario registra la respuesta obtenida.
ping -M do -s 1472 -c 3 203.0.113.10 # 100% loss, so 1500 is too big
ping -M do -s 1272 -c 3 203.0.113.10 # replies, so 1300 fits
ping -M do -s 1372 -c 3 203.0.113.10 # replies, so 1400 fits
ping -M do -s 1422 -c 3 203.0.113.10 # 100% loss, so 1450 is too big
ping -M do -s 1397 -c 3 203.0.113.10 # 100% loss, so 1425 is too big
ping -M do -s 1384 -c 3 203.0.113.10 # 100% loss, so 1412 is too bigLa carga útil máxima que funcionó es 1372. Por tanto, esta ruta admite al menos 1400 bytes y menos de 1412. Use el límite seguro. Un MTU de ruta de 1400, menos 80 bytes de encapsulación, produce un MTU de 1320 para wg0.
tracepath ejecuta la misma búsqueda por sí solo. Conviene ejecutarlo una vez antes de iniciar la bisección:
tracepath -n 203.0.113.10Su última línea informa del resultado:
Resume: pmtu 1492 hops 12 back 12Considere ambas herramientas un punto de partida, no una prueba definitiva. Algunos hosts limitan la tasa o descartan completamente ICMP. Por ello, una bisección puede informar de un MTU menor que el que realmente admite la ruta. La transferencia que fallaba es la prueba real.
Aplique primero el valor de forma temporal. Si la estimación es incorrecta, podrá deshacerla con un solo comando:
sudo ip link set mtu 1320 dev wg0Vuelva a intentar la transferencia que se quedaba bloqueada. Si termina correctamente, haga permanente el valor en el bloque [Interface] del cliente:
[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
MTU = 1320sudo wg-quick down wg0 && sudo wg-quick up wg0
ip link show wg0ip link show wg0 ahora debería mostrar mtu 1320. Si sigue mostrando el valor anterior, wg-quick no leyó el archivo que editó. Compruebe que editó /etc/wireguard/wg0.conf y que MTU está dentro de [Interface], no dentro de [Peer], donde se ignora.
El MTU es una propiedad de una interfaz y nunca se negocia entre pares. Establecerlo sólo en el cliente reduce el tamaño de los paquetes que envía el cliente. El servidor sigue generando paquetes con su propio MTU de wg0, por lo que las descargas pueden seguir sufriendo un agujero negro después de que las subidas empiecen a funcionar. Establezca el valor en ambos extremos o limite el MSS en el servidor.
Por qué el ajuste de MSS corrige TCP y nada más
Si el servidor reenvía tráfico para sus pares, como ocurre en cualquier configuración estándar de WireGuard en un VPS que usa NAT (traducción de direcciones de red), una regla corrige TCP para todos los pares y evita tener que buscar un valor en cada cliente que no controla.
MSS (tamaño máximo de segmento) es una opción de TCP que cada extremo incluye en su paquete SYN para indicar el tamaño máximo del segmento que está dispuesto a recibir. El ajuste de MSS modifica esa opción durante el tránsito para que coincida con la MTU real de la ruta. De este modo, ambos extremos acuerdan un segmento más pequeño antes de transmitir datos. Funciona porque se aplica durante el handshake y porque no depende de un mensaje ICMP que probablemente filtre la ruta.
Con nftables, añada esta tabla a /etc/nftables.conf debajo de las tablas existentes:
table inet mangle {
chain forward {
type filter hook forward priority mangle; policy accept;
tcp flags syn tcp option maxseg size set rt mtu
}
}Recargue con sudo systemctl reload nftables. En un sistema con iptables, el equivalente es una sola línea:
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuConfirme que la regla está en la ruta que siguen los paquetes. Ejecute sudo nft list table inet mangle o sudo iptables -t mangle -L FORWARD -n -v mientras un cliente abre conexiones nuevas y observe cómo aumenta el contador. Si el contador permanece en cero, significa que los paquetes no pasan por ese hook. Por tanto, la regla no hace nada.
Estas son las limitaciones reales. El ajuste de MSS cubre TCP y nada más. Además, sólo cubre el tráfico reenviado. Un servicio que se ejecuta en el propio servidor WireGuard nunca atraviesa el hook forward y nunca se le aplica el ajuste. La regla también afecta únicamente a las conexiones abiertas después de cargarla. Las sesiones existentes conservan el MSS que ya habían acordado.
UDP no se modifica porque UDP no tiene un handshake que se pueda modificar. La mayoría del tráfico UDP funciona de todos modos. QUIC, el transporte en el que se basa HTTP/3, prueba su propio tamaño de paquete utilizable y empieza con un tamaño pequeño de forma intencionada. Lo que no funciona es el tráfico UDP que envía un datagrama grande y espera que llegue completo, como una respuesta DNSSEC (extensiones de seguridad de DNS) de más de 1400 bytes. Esas consultas agotan el tiempo de espera y se reintentan mediante TCP. Para el usuario, el resultado es un sitio lento, no un sitio que parezca averiado.
¿El límite es la CPU de mi VPS?
WireGuard cifra con ChaCha20-Poly1305 e intercambia claves con Curve25519. No hay AES en ninguna parte de la ruta de datos, y esto tiene una consecuencia que se suele interpretar mal: las instrucciones AES-NI de la CPU no hacen nada por WireGuard. Que un host anuncie AES-NI no significa que le ofrezca una función de rendimiento para WireGuard. ChaCha20 se eligió porque es rápido mediante software convencional, incluso en CPU sin aceleración criptográfica.
Esto no significa que WireGuard no consuma recursos. En un VPS con 1 vCPU, un núcleo gestiona tanto el cifrado como las interrupciones de red, además de lo que esté haciendo la aplicación.
Mídalo mientras se ejecuta una transferencia:
sudo apt install -y sysstat
mpstat -P ALL 1Revise tres columnas. %soft es el tiempo de softirq, donde se procesa el tráfico de paquetes del kernel. %steal es el tiempo que el hipervisor entregó a otro proceso. %idle es el tiempo restante.
Un valor de %soft cercano a 100 en su único núcleo significa que el equipo ha alcanzado su límite de procesamiento de paquetes. Es un límite real que se puede ampliar con más núcleos. top muestra ksoftirqd/0 en la parte superior de la lista de procesos en ese mismo momento. Es el mismo resultado visto desde otro ángulo.
Un valor de %steal superior a unos pocos puntos porcentuales significa que no puede corregir el límite, porque el host está sobresuscrito y su vCPU está esperando un núcleo físico. Esto es habitual en los planes compartidos más baratos y varía a lo largo del día. El tiempo steal de un vecino ruidoso requiere una investigación específica. Ningún valor de MTU lo solucionará.
Otro factor es la implementación que ejecuta el cliente. El módulo del kernel de Linux es la ruta rápida y distribuye entre varios núcleos el cifrado de los peers. wireguard-go, la implementación en espacio de usuario, es más lenta. Es la que usan los clientes de macOS e iOS porque esas plataformas no permiten que una aplicación cargue un módulo del kernel.
¿Es la ruta o el propio enlace del extremo remoto?
Antes de ajustar nada, obtenga dos valores del mismo cliente con pocos minutos de diferencia: el rendimiento sin el túnel y el rendimiento con él. Sin ese par de valores, sólo estará haciendo suposiciones.
Ejecute iperf3 -s en el servidor. La prueba directa necesita que TCP 5201 sea accesible en la dirección pública, así que abra el puerto durante la prueba y elimine la regla después. Confirme que el puerto vuelve a estar cerrado en lugar de darlo por hecho.
# on the client, outside the tunnel
iperf3 -c 203.0.113.10
# then through the tunnel
iperf3 -c 10.8.0.1Si los dos valores son similares, WireGuard apenas reduce el rendimiento y la limitación está en la ruta. Si el valor del túnel es muy inferior al valor directo mientras %soft se mantuvo bajo, vuelva a revisar la MTU. La fragmentación reduce el rendimiento sin interrumpir nada, por lo que aquí aparece como una pérdida porcentual y no como una conexión bloqueada.
Pruebe ambas direcciones, porque las conexiones domésticas suelen ser asimétricas. iperf3 -c 10.8.0.1 -R invierte el flujo para que el servidor envíe. Un cliente con una conexión de 500/20 nunca podrá enviar más de 20 Mbit de subida por el túnel, y ningún cambio en el servidor puede modificar ese límite.
Después, pruebe con flujos paralelos:
iperf3 -c 10.8.0.1 -P 4Si cuatro flujos transfieren juntos mucho más que uno, una sola conexión TCP no consigue llenar la capacidad de la ruta. El rendimiento de un flujo está limitado por la ventana de recepción dividida por el tiempo de ida y vuelta, por lo que una ruta de 150 ms necesita una ventana grande para transportar mucho volumen de datos. Consulte sus propios límites:
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmemEl tercer valor de cada comando es el máximo al que Linux ajustará automáticamente el valor. La pérdida de paquetes también limita mucho un flujo individual, porque TCP reacciona a la pérdida mediante el control de congestión y una ruta larga hace que la recuperación sea costosa. Ejecute mtr -rwc 100 203.0.113.10 desde el cliente durante cien ciclos para comprobar dónde aparece la pérdida en la ruta. La pérdida que comienza en un salto y continúa hasta el salto final es real. La pérdida en un salto intermedio que desaparece después indica que ese router está dando menor prioridad a ICMP y no significa nada.
Para obtener una referencia reproducible del propio servidor, separada de la red, compare el rendimiento del VPS con un método documentado para poder repetir la misma prueba después de un cambio y comparar resultados equivalentes.
Lo que WireGuard no puede solucionar
WireGuard es un túnel. No puede ser más rápido que el enlace más lento de la ruta que utiliza, y añadirlo siempre hace que la ruta sea ligeramente más lenta.
No comprime. No existe un equivalente de comp-lzo de OpenVPN ni hay planes para incorporarlo, porque comprimir antes de cifrar filtra información sobre el texto sin formato. La mayoría de los datos voluminosos ya están comprimidos, por lo que en la práctica esto no supone ningún coste. Es una de las diferencias reales que debe valorar al comparar WireGuard con OpenVPN, y se trata de una decisión de diseño deliberada.
Un túnel completo cambia la ruta que sigue cada paquete. El tráfico que antes iba desde usted hasta una CDN (red de distribución de contenido) cercana ahora va desde usted hasta su VPS y después hasta la CDN. Si el VPS está en otro continente, cada solicitud recorre ese desvío y el tiempo de ida y vuelta aumenta en consecuencia. Ningún valor de configuración puede acortarlo. Mueva el VPS a una ubicación más cercana o use un túnel dividido para que sólo el tráfico que necesita la VPN tome la ruta larga. AllowedIPs decide por completo qué tráfico va por cada ruta, y el enrutamiento mediante claves criptográficas explica cómo se toma esa decisión.
Los límites del proveedor quedan fuera del túnel y es fácil olvidarlos. Un plan con una cuota mensual de ancho de banda suele limitar el puerto a una velocidad mucho menor cuando se agota la cuota, y entonces el túnel parece estar averiado. Consulte el panel antes de dedicar una tarde al MTU.
PersistentKeepalive no afecta al rendimiento. Existe para mantener abierta una asignación NAT y permitir que el servidor siga llegando a un cliente situado detrás de un router doméstico. Reducirlo por debajo de 25 segundos añade paquetes y no soluciona nada.
Mida en este orden
- Reproduzca el problema e indique si se trata de un bloqueo o de una ralentización uniforme. Un bloqueo apunta a la MTU. Una ralentización uniforme no.
- Haga una búsqueda binaria con
ping -M dodesde el cliente hasta la dirección pública del servidor y anote la MTU de la ruta. - Reste 80, establezca esa MTU en wg0 en ambos extremos y vuelva a probar la transferencia que fallaba.
- Añada el ajuste de MSS en el servidor si reenvía tráfico de los peers.
- Ejecute
mpstat -P ALL 1durante una transferencia y lea%softy%steal. - Ejecute
iperf3fuera del túnel y dentro de él, en ambas direcciones, con un único flujo y con-P 4. - Ejecute
mtr -rwc 100hacia el servidor y busque pérdidas que persistan hasta el último salto.
Cambie el plan del servidor sólo después de que el paso 5 indique que la CPU es el factor limitante. Los pasos 1 a 4 no cuestan nada y resuelven la mayoría de los informes sobre túneles lentos.
FAQ
¿Por qué mi túnel WireGuard responde rápido al hacer ping, pero las descargas son lentas?
Esa diferencia indica un problema de MTU. Los paquetes pequeños caben en todos los enlaces de la ruta, por lo que ping y una conexión SSH funcionan. Una transferencia masiva envía segmentos del tamaño máximo. La versión encapsulada de esos segmentos es más grande de lo que aceptan algunos enlaces. Si ese router los descarta sin devolver un mensaje ICMP, nadie informa de la pérdida y la transferencia se bloquea. Determine la MTU de la ruta mediante una búsqueda binaria con ping -M do hasta la dirección pública del servidor. Reste 80 bytes por la encapsulación y establezca el resultado como MTU de wg0 en ambos extremos.
¿Qué MTU debo establecer para WireGuard?
No existe un valor universal. Por eso el valor predeterminado de 1420 falla para algunas personas. 1420 es 1500 menos 80 bytes correspondientes a la cabecera de WireGuard, la cabecera UDP y la cabecera IPv6 exterior. Si la ruta admite menos de 1500 bytes, algo habitual en DSL con PPPoE y cuando el tráfico atraviesa otro túnel, necesita un valor menor. Mida primero la MTU de la ruta y réstele 80.
¿El ajuste de MSS sustituye la configuración de la MTU?
No. El ajuste modifica la opción MSS durante el establecimiento de la conexión TCP para que ambos extremos envíen segmentos más pequeños. Esto corrige TCP sin modificar la interfaz. UDP no tiene un establecimiento de conexión que se pueda modificar, por lo que no se ve afectado. Además, el ajuste sólo se aplica al tráfico que reenvía el servidor. Un servicio que se ejecuta en el propio servidor WireGuard no obtiene ese beneficio. Use ambos mecanismos: una MTU correcta en la interfaz y el ajuste de MSS para los peers cuya configuración no controla.
¿Un plan VPS más rápido hará que WireGuard sea más rápido?
Sólo si la CPU es el límite. Un comando permite comprobarlo. Ejecute mpstat -P ALL 1 mientras se realiza una transferencia. %soft cerca de 100 en el único core disponible indica que el procesamiento de paquetes es el límite. En ese caso, más cores aumentarán el rendimiento. Un valor alto de %steal indica que el host está sobrecargado. La solución es otro plan o un host diferente. Si ambos valores son bajos y el túnel sigue siendo lento, la CPU está inactiva y un plan más grande no cambiará nada.
¿Por qué mi Mac es más lento que mi cliente Linux en la misma red?
El cliente Linux usa el módulo WireGuard integrado en el kernel. Este procesa los paquetes en el espacio del kernel y distribuye el cifrado de un peer entre varios cores de CPU. Las aplicaciones para macOS e iOS usan wireguard-go, una implementación en el espacio de usuario, porque esas plataformas no permiten que una aplicación cargue un módulo del kernel. El espacio de usuario copia cada paquete entre el kernel y la aplicación, y esas copias reducen el rendimiento. Esta diferencia es esperable y ningún ajuste del cliente la elimina.