Debian 13 enrutamiento por políticas: depurar ip rule e ip route sin dolor

¿Te fue útil?

No cambiaste nada “importante”, solo añadiste una segunda salida, una VPN o un puente de contenedores. De repente la mitad de tu tráfico saliente desaparece,
o las respuestas vuelven por la interfaz equivocada y mueren en silencio. Los logs no dicen nada. Tu monitor indica “pérdida de paquetes”.
Tus compañeros dicen “el enrutamiento en Linux es magia negra”.

No es magia. Es enrutamiento por políticas. Es determinista. Y además es implacable en la forma en que solo los subsistemas maduros pueden serlo: hará
exactamente lo que pediste, no lo que quisiste. Esta es una guía práctica para producción en Debian 13 sobre depuración de ip rule y ip route,
con el tipo de comandos que puedes ejecutar bajo presión y las conclusiones que puedes explicar en un postmortem.

El modelo mental: qué le ocurre realmente a un paquete

Cuando Debian 13 enruta un paquete, no elige “la ruta por defecto” en una única tabla global y da por cerrado el asunto.
Ejecuta un motor de políticas. Ese motor evalúa una lista de reglas (ip rule) de arriba a abajo.
Cada regla puede coincidir con metadatos del paquete (dirección origen, destino, fwmark, interfaz de entrada, UID y más) y apuntar la consulta a una tabla de enrutamiento.
La tabla elegida se busca entonces para la mejor ruta (ip route), y solo entonces el kernel decide la interfaz de salida, el gateway y la dirección fuente.

La clave es que las tablas de enrutamiento no son solo “las rutas”. Son “un universo posible de rutas”,
y las reglas deciden qué universo aplica a este paquete.
Si estás depurando, no preguntes “¿cuál es mi ruta?” Pregunta: “¿qué regla gana, qué tabla se consulta y qué ruta se selecciona en esa tabla?”

Cómo se siente la “magia negra” en la práctica

Los fallos de enrutamiento por políticas tienen una firma: las cosas funcionan desde una IP pero no desde otra; ping funciona pero TCP no; el tráfico saliente va pero las respuestas no;
una subred está bien mientras otra está muerta. Los fallos de enrutamiento estándar suelen ser más uniformes. Los fallos por políticas son selectivos.

Otra firma: ejecutas ip route, ves una ruta por defecto perfectamente razonable y aún así el tráfico sale por la interfaz equivocada.
Eso ocurre porque el paquete relevante nunca consultó la tabla principal. Consultó otra tabla debido a una regla que olvidaste que existía.
O una regla insertada por un gestor de red. O un cliente VPN. O tu propio “workaround” temporal de hace tres cuartos de año.

El enrutamiento por políticas no es opcional cuando hay complejidad

Si tienes cualquiera de estos, estás usando enrutamiento por políticas aunque no lo admitas:

  • Dos salidas (dual-WAN, respaldo LTE, migración de ISP)
  • Split tunneling de VPN
  • Múltiples IPs de origen en un mismo host
  • Kubernetes/CNI con peculiaridades de enrutamiento del host
  • Dirigir tráfico por aplicación (marcas UID/cgroup)
  • Cualquier política de seguridad que utilice fwmarks para dirigir tráfico

El truco es hacerlo observable y aburrido. Aburrido es cómo conservas tus fines de semana.

Una cita que vale la pena tener en el escritorio

La esperanza no es una estrategia. — General Gordon R. Sullivan

La “esperanza” en enrutamiento suele parecer “funcionó en staging” o “Linux lo arreglará”. Lo hará. Solo que no de la forma que querías.

Hechos e historia que hacen que las rarezas de hoy tengan sentido

  • El enrutamiento por políticas en Linux no es nuevo. Está en mainline desde la era del kernel 2.2, y iproute2 creció alrededor de ello.
  • La tabla “main” no es especial por diseño, solo por convención. Las reglas deciden cuándo se usa; muchos sistemas enrutan la mayor parte del tráfico vía main solo porque las reglas son por defecto.
  • ip rule se evalúa por prioridad, no por orden de inserción. Las prioridades pueden solaparse, y “el número menor gana” es el orden real.
  • Incluso en un host “simple” hay varias reglas integradas. Típicamente: consulta de la tabla local, luego main, luego default. Esa consulta local es la razón por la que tu host puede alcanzar sus propias IPs aunque hayas roto todo lo demás.
  • El filtrado de ruta inversa (rp_filter) se construyó por cordura, no para multi-homing. El modo estricto descarta paquetes que entran por una interfaz que el kernel no usaría para alcanzar la fuente.
  • El enrutamiento basado en fwmark es más antiguo que la mayoría de las carreras profesionales. El marcado de Netfilter y el enrutamiento por políticas han estado emparejados mucho tiempo porque es una separación limpia: clasificar con el firewall, enrutar con reglas.
  • “Ruta por defecto” no es singular en el kernel. Puedes tener múltiples rutas por defecto, las métricas deciden la preferencia, y las reglas deciden si una ruta por defecto se considera siquiera.
  • A los gestores de red les encanta añadir reglas. systemd-networkd, ifupdown2, clientes VPN y stacks de contenedores a menudo instalan sus propias reglas y tablas para aislamiento.
  • Conntrack puede conservar decisiones que ya no quieres. Un flujo enrutado de una manera puede seguir yendo por ese camino hasta que expire, incluso después de que “arreglaste” el enrutamiento.

Esto no es trivia. Son las razones por las que el enrutamiento por políticas parece tener estados de ánimo.
No los tiene. Simplemente cambiaste las reglas, y las reglas cambiaron el mundo.

Guía de diagnóstico rápido (primero/segundo/tercero)

Cuando el tráfico “misteriosamente” elige la interfaz equivocada, no empieces editando archivos de configuración. Empieza preguntándole al kernel qué haría.
Estas comprobaciones están ordenadas para colapsar el espacio de problemas rápidamente.

Primero: confirma la decisión de enrutamiento para un paquete específico

  • Usa ip route get para el destino, con una IP de origen si es relevante.
  • Si intervienen fwmarks, prueba con marca usando ip route get ... mark (cuando sea compatible) o emula mediante inspección de reglas y consultas a tablas.
  • Confirma la tabla elegida con ip rule y la ruta seleccionada con ip route show table X.

Segundo: identifica qué regla gana y por qué

  • Vuelca reglas con prioridades. Busca coincidencias: from, to, iif, fwmark, uidrange.
  • Revisa reglas que no creaste: clientes VPN, cloud init, networkd, stacks de contenedores.
  • Confirma que la ruta exista en la tabla consultada; las rutas “blackhole” o “unreachable” pueden ser deliberadas.

Tercero: valida el camino de retorno y el filtrado

  • Comprueba la selección de dirección fuente y la configuración de rp_filter.
  • Busca enrutamiento asimétrico: salida por una interfaz, respuestas entrantes por otra.
  • Valida el comportamiento de NAT/conntrack; vacía con cuidado si es necesario.

Si sigues este orden, normalmente evitas el ritual de las 2 a. m. de “reiniciar la red hasta que parezca arreglado.”
Ese ritual es la forma en que conviertes un problema de enrutamiento en una caída.

Tareas prácticas: 12+ comandos, salida esperada y qué hacer después

Cada tarea abajo tiene tres partes: un comando que puedes ejecutar, qué significa la salida y la decisión que tomas a partir de ella.
Esta es la diferencia entre depurar y hacer danza interpretativa en el terminal.

Tarea 1: Instantánea de las reglas (con prioridades) y detectar sorpresas

cr0x@server:~$ ip -details -statistics rule show
0:	from all lookup local
32764:	from all fwmark 0x1 lookup vpn
32765:	from 192.0.2.10 lookup isp2
32766:	from all lookup main
32767:	from all lookup default

Qué significa: El kernel intentará primero la tabla local. Luego cualquier paquete marcado 0x1 usa la tabla vpn.
Cualquier paquete con origen 192.0.2.10 usa isp2. Todo lo demás va a main.

Decisión: Si el tráfico “aleatoriamente” va a la VPN o al ISP equivocado, ahora conoces las condiciones. Si no reconoces una regla, averigua qué herramienta la instaló antes de borrarla.

Tarea 2: Listar nombres de tablas de enrutamiento y asegurar que no adivinas IDs

cr0x@server:~$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255	local
254	main
253	default
0	unspec
#
# local
#
100	isp2
200	vpn

Qué significa: Los nombres de tablas se mapean a IDs numéricos. Si los scripts se refieren a “tabla 200” y alguien la renombra, has creado un rompecabezas para el Tú del Futuro.

Decisión: Usa nombres en la automatización cuando sea posible (lookup vpn), mantiene este archivo bajo gestión de configuración y trata los IDs de tablas como parte de la estabilidad de la API.

Tarea 3: Pregunta al kernel “¿cómo enrutarías esto?”

cr0x@server:~$ ip route get 1.1.1.1
1.1.1.1 via 203.0.113.1 dev eth0 src 203.0.113.10 uid 0
    cache

Qué significa: Para ese destino, el kernel eligió el gateway 203.0.113.1 en eth0 y usaría como fuente 203.0.113.10.

Decisión: Si esperabas “usar eth1” o “usar 192.0.2.10 como fuente”, tienes un desajuste de reglas/tablas, no un problema de la aplicación.

Tarea 4: Probar explícitamente la política basada en origen (el fallo común)

cr0x@server:~$ ip route get 1.1.1.1 from 192.0.2.10
1.1.1.1 via 192.0.2.1 dev eth1 src 192.0.2.10 uid 0
    cache

Qué significa: Con origen 192.0.2.10, el kernel ahora usa eth1. Eso implica una regla que coincide con from 192.0.2.10.

Decisión: Si las respuestas fallan, comprueba que el tráfico de retorno también use la misma fuente y que rp_filter no esté descartando caminos asimétricos.

Tarea 5: Volcar la tabla main y comprobar la ruta por defecto y las métricas

cr0x@server:~$ ip -statistics route show table main
default via 203.0.113.1 dev eth0 metric 100
default via 192.0.2.1 dev eth1 metric 200
203.0.113.0/24 dev eth0 proto kernel scope link src 203.0.113.10
192.0.2.0/24 dev eth1 proto kernel scope link src 192.0.2.10

Qué significa: Existen dos rutas por defecto; la métrica menor (100) gana cuando se usa la tabla main.

Decisión: Si tu intención era failover, asegúrate de tener chequeos de salud (no solo métricas). Si querías dividir tráfico, las métricas por sí solas no bastan; necesitas reglas y tablas separadas.

Tarea 6: Inspeccionar una tabla no-main (donde suele esconderse la “magia”)

cr0x@server:~$ ip route show table isp2
default via 192.0.2.1 dev eth1
192.0.2.0/24 dev eth1 scope link src 192.0.2.10

Qué significa: La tabla isp2 tiene su propio default y ruta conectada. Esta es la tabla mínima y sensata para policy-routing: puede alcanzar el gateway y la internet.

Decisión: Si una tabla solo tiene un default pero carece de la ruta de subred conectada, la resolución ARP/neighbor puede volverse rara. Añade la ruta conectada o usa proto kernel vía asignación de dirección.

Tarea 7: Buscar rutas blackhole/unreachable que silenciosamente “funcionan como diseñado”

cr0x@server:~$ ip route show table vpn
blackhole 10.0.0.0/8
default via 10.8.0.1 dev tun0
10.8.0.0/24 dev tun0 scope link src 10.8.0.2

Qué significa: Alguien intencionalmente hizo blackhole del espacio RFC1918 en la tabla VPN. Eso puede prevenir el hairpinning accidental hacia redes corporativas.

Decisión: Si tu aplicación necesita alcanzar servicios 10.x y falla, no tienes un “bug de enrutamiento”, tienes una decisión de política. Arregla la política: elimina o estrecha el blackhole, o ajusta reglas para que solo cierto tráfico llegue a esa tabla.

Tarea 8: Confirmar si rp_filter está descartando tus paquetes

cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.eth0.rp_filter net.ipv4.conf.eth1.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1

Qué significa: El filtrado inverso estricto está activado. En entornos multi-homed/policy-routing, el modo estricto a menudo descarta tráfico asimétrico legítimo.

Decisión: Si tienes asimetría intencional o múltiples salidas, cambia a modo loose (2) en las interfaces relevantes o globalmente—después de entender las implicaciones de seguridad.

Tarea 9: Verificar que las reglas realmente coincidan (origen, iif, fwmark)

cr0x@server:~$ ip rule show
0:	from all lookup local
32764:	from all fwmark 0x1 lookup vpn
32765:	from 192.0.2.10 lookup isp2
32766:	from all lookup main
32767:	from all lookup default

Qué significa: Las reglas son amplias. La regla fwmark coincide con cualquier paquete marcado desde cualquier origen. La regla de origen coincide exactamente con una IP.

Decisión: Si demasiado tráfico va a la VPN, deja de marcarlo todo. Si muy poco tráfico va a isp2, amplia from a una subred o añade reglas from adicionales para otras direcciones.

Tarea 10: Inspeccionar marcado en nftables/iptables (el volante oculto)

cr0x@server:~$ nft list ruleset | sed -n '1,120p'
table inet filter {
  chain output {
    type filter hook output priority 0; policy accept;
    meta skuid 1001 meta mark set 0x1
  }
}

Qué significa: El tráfico generado por UID 1001 obtiene la marca 0x1 en el hook OUTPUT, lo que desencadena la regla de enrutamiento por fwmark.

Decisión: Si la aplicación equivocada está siendo dirigida, corrige la selección de UID, usa marcas de cgroup o etiqueta solo destinos/puertos específicos. Evita marcas amplias que capturen actualizaciones de paquetes, monitoreo o DNS por accidente.

Tarea 11: Usar tcpdump con enfoque de interfaz para probar asimetría

cr0x@server:~$ sudo tcpdump -ni eth0 'host 1.1.1.1 and (tcp or icmp)'
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:10:55.120001 IP 203.0.113.10 > 1.1.1.1: ICMP echo request, id 511, seq 1, length 64

Qué significa: ICMP saliente está saliendo por eth0. Si las respuestas vuelven por eth1, has encontrado la asimetría.

Decisión: Si ves salida por una interfaz y entrada por otra, revisa rp_filter y asegúrate de que el camino de retorno esté fijado con reglas basadas en origen y rutas por tabla correctas.

Tarea 12: Validar neighbor/ARP en la interfaz de salida prevista

cr0x@server:~$ ip neigh show dev eth1
192.0.2.1 lladdr 52:54:00:12:34:56 REACHABLE

Qué significa: La MAC del gateway es conocida y alcanzable. Si apareciera FAILED o faltara, el enrutamiento podría estar correcto pero la conectividad L2 no.

Decisión: Si la resolución de neighbor falla, deja de culpar a ip rule. Revisa etiquetado VLAN, puertos de switch, grupos de seguridad o configuraciones de filtrado ARP.

Tarea 13: Comprobar la tabla local y la regla 0 (porque localhost engaña)

cr0x@server:~$ ip route show table local | head
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 203.0.113.10 dev eth0 proto kernel scope host src 203.0.113.10
local 192.0.2.10 dev eth1 proto kernel scope host src 192.0.2.10
broadcast 203.0.113.0 dev eth0 proto kernel scope link src 203.0.113.10

Qué significa: La tabla local es la razón por la que siempre puedes alcanzar tus propias direcciones incluso si tu enrutamiento principal está roto.
Puede enmascarar problemas durante las pruebas (“¡curl a mi IP funciona!”).

Decisión: Al depurar, prueba desde otro host o usa ip route get para ver decisiones reales de egress. El éxito en la tabla local no prueba la conectividad externa.

Tarea 14: Inspeccionar conntrack para flujos que siguen tomando la ruta antigua

cr0x@server:~$ sudo conntrack -L -p tcp 2>/dev/null | head
tcp      6 431999 ESTABLISHED src=203.0.113.10 dst=93.184.216.34 sport=42412 dport=443 src=93.184.216.34 dst=203.0.113.10 sport=443 dport=42412 [ASSURED] mark=1 use=1

Qué significa: Este flujo está establecido y lleva mark=1. Incluso si cambias reglas de marcado, las conexiones existentes pueden persistir en su ruta original.

Decisión: Si pruebas cambios de enrutamiento, usa conexiones nuevas (puerto de origen distinto) o elimina quirúrgicamente entradas conntrack del flujo de prueba. Evita vaciar conntrack en producción a menos que disfrutes explicarlo.

Tarea 15: Validar que se elige la dirección fuente correcta (y corregir con ip route si hace falta)

cr0x@server:~$ ip route show table isp2 default
default via 192.0.2.1 dev eth1

Qué significa: La ruta por defecto no especifica src. El kernel puede elegir una dirección fuente que no pretendías si existen múltiples direcciones.

Decisión: En entornos con varias direcciones, establece src explícitamente en rutas clave dentro de tablas de política para que el comportamiento sea estable y fácil de depurar.

Chiste #1: El enrutamiento por políticas es como la política de oficina: existe un organigrama oficial, pero las decisiones reales pasan por las reglas laterales.

Tres mini-historias corporativas (cómo los adultos rompen el enrutamiento)

Incidente #1: La suposición equivocada (“la tabla main significa comportamiento por defecto”)

Una empresa mediana de SaaS migró de un solo ISP a dos salidas: fibra primaria y un router LTE de respaldo.
La flota de hosts tenía múltiples IPs por máquina debido a allowlists heredadas. Todo parecía bien en una prueba rápida:
ip route mostraba la default de fibra con métrica menor. Genial. A desplegar.

A la mañana siguiente, un subconjunto de llamadas API salientes empezó a fallar por timeout. No todas. Ni siquiera la mayoría.
Solo las llamadas de un servicio específico, ejecutándose bajo un usuario de sistema dedicado, y solo a unas pocas redes de partners.
El equipo persiguió DNS. Persiguió MTU. Persiguió el firewall del partner. El partner los persiguió a ellos.

El problema real fue una única ip rule insertada meses antes por un cliente VPN “temporal”:
from all fwmark 0x1 lookup vpn. El tráfico del usuario del servicio estaba siendo marcado por una regla antigua de nftables que nadie recordaba.
Ese tráfico nunca consultó la tabla main. Fue a la tabla VPN, que tenía una ruta blackhole para uno de los rangos RFC1918 del partner usados detrás de NAT.

La suposición equivocada fue sutil: “si ip route se ve bien, el enrutamiento está bien.” No lo está.
ip route sin una tabla o una consulta get muestra solo el universo por defecto.
La solución fue aburrida: eliminar la regla de marcado obsoleta, re-encauzar la VPN solo a los destinos previstos y añadir una prueba de regresión de enrutamiento en CI que ejecute ip route get con orígenes de servicio.

Acción en el postmortem: “Ninguna regla de política va a producción sin dueño y un comentario en la gestión de configuración.” Eso no es burocracia; es memoria.

Incidente #2: La optimización que se volvió en contra (“marquemos todo por rendimiento”)

Una gran empresa tenía una capa de proxies basada en Debian con dos salidas: un enlace económico para tráfico masivo y un enlace premium para llamadas sensibles a latencia.
Alguien tuvo la idea de marcar tráfico por UID de proceso y dejar que el enrutamiento por políticas hiciera el resto. Sin necesidad de ACLs complejas en el proxy.
Funcionó. También incentivó más “ingenio”.

La optimización fue marcar todo el tráfico saliente de un grupo de servicios para usar el enlace premium, bajo el argumento de reducir la latencia de cola.
La regla nftables fue amplia: “si UID en este rango, set mark 0x1.”
Eso incluyó no solo el servicio, sino el agente de actualizaciones, un shipper de métricas y un renovador de certificados TLS que corrían bajo la misma cuenta.

Durante un tiempo, nada se rompió. Luego hubo un flap de enrutamiento en el enlace premium durante una ventana de mantenimiento.
El diseño de failover dependía de métricas en la tabla main, pero el tráfico marcado evitaba main por completo y fue a una tabla de política con una única ruta por defecto.
Cuando el gateway premium quedó inalcanzable, el tráfico marcado no falló por conmutación; simplemente se estancó.
El monitoreo se veía raro porque el tráfico sin marcar estaba bien y el marcado moría en silencio.

La solución no fue abandonar el enrutamiento por políticas. La solución fue respetar los modos de fallo:
proporcionar un default de respaldo en la tabla de política con métrica mayor, o usar una regla que caiga a main cuando la ruta premium no esté disponible.
Y dejar de usar el marcado por UID como instrumento contundente; empata por prefijos de destino o puertos cuando sea posible.

Chiste #2: Si “optimizar” el enrutamiento sin un plan de fallo, has inventado una nueva forma para que los paquetes se tomen un largo descanso.

Incidente #3: La práctica aburrida que salvó el día (“route get como test estándar”)

Una fintech ejecutaba hosts Debian con requisitos estrictos de cumplimiento: todo el tráfico de base de datos debe ir por una red privada, el resto por internet.
Tenían múltiples NICs, múltiples VLANs y una VPN para acceso admin. Mucho que coordinar.
El equipo de red no dependía del conocimiento tribal; codificaron el comportamiento de enrutamiento como pruebas.

Durante un ciclo de actualización del SO, un cambio en la gestión de configuración de red inadvertidamente intercambió un ID de tabla: vpn pasó de 200 a 201 en algunos hosts.
Las reglas seguían referenciando lookup 200 en un script legado, así que esas máquinas tenían una regla que apuntaba a una tabla vacía.
El kernel cayó hacia main para el tráfico admin, lo cual “estaba bien” hasta que el equipo de seguridad notó conexiones admin viniendo desde la IP de salida equivocada y lo marcó.

Lo que salvó el día fue aburrido: su pipeline de despliegue ejecutaba un conjunto de aserciones de rutas en cada host tras cambios de red.
Ejecutaba comprobaciones ip route get para destinos representativos con orígenes representativos, incluyendo subredes admin, APIs de partners y rangos privados de bases de datos.
La aserción para tráfico admin falló inmediatamente en los hosts afectados. Sin caída, sin misterio, solo una luz roja.

Hicieron rollback, arreglaron los scripts para usar nombres de tabla en lugar de IDs numéricos y mantuvieron las pruebas.
El sistema siguió siendo complejo. El comportamiento siguió siendo predecible. Esa es la ganancia real.

Errores comunes: síntoma → causa raíz → solución

1) “El tráfico sale por la interfaz equivocada, pero ip route parece correcto”

Síntoma: ip route muestra el default esperado, pero los paquetes salen por otra NIC.

Causa raíz: Una regla de política envía ese tráfico a otra tabla (fwmark, basada en origen, basada en iif).

Solución: Usa ip rule show y ip route get DEST from SRC. Arregla la regla ganadora o la tabla consultada. Deja de confiar en la vista de la tabla main.

2) “Salida funciona, respuestas entrantes se descartan (o viceversa)”

Síntoma: SYNs salen, SYN-ACKs no completan, o respuestas ICMP desaparecen.

Causa raíz: Enrutamiento asimétrico más rp_filter=1 (estricto) o anti-spoofing en el upstream.

Solución: Fija el camino de retorno con reglas basadas en origen y rutas conectadas por tabla; pon rp_filter=2 donde aplique; asegúrate de src correcto en rutas de política.

3) “Split tunnel de VPN filtra tráfico o secuestra todo”

Síntoma: Parte del tráfico va inesperadamente por la VPN, o tráfico destinado a la VPN va directo.

Causa raíz: Regla de marcado demasiado amplia, prioridad de regla equivocada o rutas específicas faltantes en la tabla VPN.

Solución: Estrecha el marcado a destinos/puertos; asegura que la prioridad de la regla VPN esté por encima de main; verifica que la tabla VPN tenga rutas explícitas para los prefijos tunelizados y una estrategia deliberada de default.

4) “Existe un default en la tabla de política pero nada funciona a través de ella”

Síntoma: Los paquetes seleccionan la tabla correcta pero nunca alcanzan el gateway.

Causa raíz: Falta la ruta conectada en esa tabla, o el gateway es inalcanzable en esa interfaz, o problemas ARP/neighbor.

Solución: Añade la ruta de enlace en esa tabla; verifica ip neigh; confirma la IP de la interfaz y la conectividad L2.

5) “Tras arreglar reglas, el comportamiento no cambia para conexiones activas”

Síntoma: Las conexiones nuevas se comportan correctamente, las antiguas siguen fallando o usan la ruta vieja.

Causa raíz: Entradas conntrack preservan estado y marcas para flujos establecidos.

Solución: Prueba con conexiones nuevas; elimina entradas conntrack específicas; considera reducir timeouts para flujos impactados durante respuesta a incidentes en lugar de vaciar globalmente.

6) “Los paquetes se enrutan correctamente, pero la IP de origen es incorrecta”

Síntoma: El tráfico sale por la interfaz prevista pero usa una dirección fuente inesperada, provocando drops upstream.

Causa raíz: La selección de dirección fuente escoge otra dirección en esa interfaz o de otra interfaz debido al scope de la ruta y etiquetas de dirección.

Solución: Especifica src en rutas críticas dentro de las tablas de política; asegura que cada interfaz tenga la dirección primaria correcta; verifica con ip route get ... from ....

7) “Todo funciona hasta que falla un enlace, luego solo parte del tráfico hace failover”

Síntoma: El tráfico sin marcar hace failover bien; el marcado o el basado en origen se queda estancado.

Causa raíz: Las tablas de política carecen de un default secundario o no tienen failover consciente de salud; las métricas de main no aplican a búsquedas en tablas de política.

Solución: Construye failover dentro de las tablas de política (default secundario con métrica mayor) o implementa cambio de reglas basado en estado del enlace.

8) “El enrutamiento funciona interactuando manualmente, falla en el servicio en producción”

Síntoma: Tu curl manual funciona; el daemon falla.

Causa raíz: Marcado por UID, diferencias de namespace de red o el servicio se liga a una IP de origen específica.

Solución: Revisa reglas nftables de marcado en OUTPUT; comprueba el usuario de la unidad del servicio y su namespace de red; valida la configuración de bind del servicio.

Listas de comprobación / planes paso a paso que puedes entregar al on-call

Lista A: “El tráfico toma la salida equivocada” (15 minutos, sin cambios aún)

  1. Ejecuta instantánea de reglas.

    cr0x@server:~$ ip rule show
    0:	from all lookup local
    32764:	from all fwmark 0x1 lookup vpn
    32765:	from 192.0.2.10 lookup isp2
    32766:	from all lookup main
    32767:	from all lookup default
    

    Decisión: Identifica la regla que probablemente coincide con la clase de tráfico (¿IP origen? ¿fwmark?).

  2. Ejecuta consulta de decisión de ruta para el destino exacto, luego con el origen sospechado.

    cr0x@server:~$ ip route get 93.184.216.34
    93.184.216.34 via 203.0.113.1 dev eth0 src 203.0.113.10 uid 0
        cache
    
    cr0x@server:~$ ip route get 93.184.216.34 from 192.0.2.10
    93.184.216.34 via 192.0.2.1 dev eth1 src 192.0.2.10 uid 0
        cache
    

    Decisión: Confirma si el comportamiento equivocado depende del origen. Si es así, enfócate en reglas de origen y rutas por tabla.

  3. Vuelca la tabla consultada.

    cr0x@server:~$ ip route show table isp2
    default via 192.0.2.1 dev eth1
    192.0.2.0/24 dev eth1 scope link src 192.0.2.10
    

    Decisión: Si la tabla carece de elementos esenciales (ruta conectada, gateway correcto), arregla la tabla; no “arregles” cambiando main.

  4. Prueba la ruta de paquetes con tcpdump en ambas interfaces (captura corta).

    cr0x@server:~$ sudo tcpdump -ni eth0 'host 93.184.216.34 and tcp'
    tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    
    cr0x@server:~$ sudo tcpdump -ni eth1 'host 93.184.216.34 and tcp'
    tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
    listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    

    Decisión: Si ves asimetría, salta a rp_filter y reglas de camino de retorno.

Lista B: “El split tunnel de VPN se comporta mal”

  1. Identifica la tabla VPN y la regla.

    cr0x@server:~$ ip rule show | grep -i vpn
    32764:	from all fwmark 0x1 lookup vpn
    

    Decisión: Si la selección de VPN es por marca, debes auditar el marcado. Si es por origen, audita la selección de bind/origen de la aplicación.

  2. Inspecciona reglas de marcado.

    cr0x@server:~$ nft list ruleset | grep -n 'mark set' | head
    12:    meta skuid 1001 meta mark set 0x1
    

    Decisión: Si la coincidencia es demasiado amplia, estrecha la regla. Marca solo lo que puedas justificar por escrito.

  3. Confirma que las rutas de la tabla VPN incluyan lo que pretendes tunelizar.

    cr0x@server:~$ ip route show table vpn
    blackhole 10.0.0.0/8
    default via 10.8.0.1 dev tun0
    10.8.0.0/24 dev tun0 scope link src 10.8.0.2
    

    Decisión: Si necesitas alcanzar prefijos privados, elimina o estrecha blackholes y añade rutas explícitas.

Lista C: “El failover no funciona para cierto tráfico”

  1. Determina qué tabla usa ese tráfico (regla que coincide).

    cr0x@server:~$ ip -details rule show
    0:	from all lookup local
    32764:	from all fwmark 0x1 lookup vpn
    32765:	from 192.0.2.10 lookup isp2
    32766:	from all lookup main
    32767:	from all lookup default
    

    Decisión: Si no usa main, las métricas de main no te salvarán.

  2. Asegura que la tabla de política tenga una ruta por defecto secundaria (si tu diseño lo requiere).

    cr0x@server:~$ ip route show table isp2 | grep '^default'
    default via 192.0.2.1 dev eth1
    

    Decisión: Si solo hay un default y ese gateway falla, ese tráfico se quedará estancado. Añade una ruta de respaldo en esa tabla o implementa cambios de reglas al fallar el enlace.

Preguntas frecuentes

1) ¿Por qué me “miente” ip route?

No miente; muestra la tabla main por defecto. El enrutamiento por políticas puede estar enviando tu paquete a otra tabla.
Usa ip rule show y ip route get DEST from SRC para ver la decisión real.

2) ¿Cuál es la diferencia entre ip route show e ip route get?

show lista rutas en una tabla. get pide al kernel resolver un destino específico (y origen opcional),
devolviendo el gateway elegido, la interfaz y la IP fuente. En incidentes, get es tu suero de la verdad.

3) ¿Puedo tener dos rutas por defecto en Debian 13?

Sí. El kernel elige en función de métricas dentro de una tabla. Pero si las reglas de política dirigen el tráfico a otra tabla, aplican los defaults de esa tabla.
Dos defaults son normales; defaults duales no gestionados son caos.

4) ¿Por qué el enrutamiento por políticas falla solo para una IP de origen?

Porque muchas políticas son basadas en origen: ip rule add from 192.0.2.10 lookup isp2.
Aplicaciones que se enlazan a una fuente específica, o el kernel eligiendo otra fuente, pueden cambiar qué regla coincide.

5) ¿Necesito desactivar rp_filter para enrutamiento por políticas?

No siempre, pero el modo estricto (1) frecuentemente entra en conflicto con asimetría intencional en entornos multi-homed.
El modo loose (2) es un compromiso común. Ten en cuenta la seguridad: rp_filter ayuda contra suplantación, así que no lo cambies a la ligera.

6) ¿Por qué mi arreglo funcionó para conexiones nuevas pero no para las antiguas?

Conntrack mantiene estado para flujos establecidos, incluyendo marcas en muchas configuraciones. Los flujos antiguos pueden mantenerse con decisiones de enrutamiento antiguas.
Prueba con conexiones nuevas o elimina quirúrgicamente entradas conntrack para flujos específicos.

7) ¿Cuál es la forma más limpia de implementar split tunneling?

Pon solo los prefijos tunelizados en la tabla VPN y mantén una política por defecto deliberada (o ninguna default, o un default solo para tráfico marcado).
Luego dirige solo el tráfico previsto usando reglas específicas (basadas en destino cuando sea posible; marcas cuando sea necesario).

8) ¿Cómo sé qué servicio provoca el enrutamiento por fwmark?

Revisa las reglas nftables que establecen marcas (a menudo coincidiendo con UID/cgroup). Luego mapea UID a un servicio.
También inspecciona las entradas conntrack con mark= para ver qué flujos están marcados.

9) ¿Debo usar IDs numéricos de tabla directamente en scripts?

Evítalo cuando puedas. Usa tablas con nombre y mantén /etc/iproute2/rt_tables estable.
Los IDs numéricos están bien, pero son fáciles de desalinear en flotas y difíciles de auditar en postmortems.

10) ¿Está bien “vaciar” reglas y rutas durante un incidente?

En una máquina de producción que maneje tráfico real: no, no como primer movimiento.
Puedes desconectar SSH, romper bindings de servicio y borrar la evidencia que necesitabas para diagnosticar. Haz una instantánea primero, luego cambia de forma quirúrgica.

Conclusión: próximos pasos que reducen el dolor futuro

El enrutamiento por políticas en Debian 13 solo es “magia negra” cuando lo tratas como folklore.
Trátalo como un programa: las reglas son control de flujo, las tablas son datos, y ip route get es tu depurador.
El kernel es consistente. Tu configuración puede no serlo.

Pasos prácticos que valen la pena:

  • Estandariza en tablas de enrutamiento con nombre y mantén /etc/iproute2/rt_tables bajo gestión de configuración.
  • Añade una prueba mínima de regresión de enrutamiento: unas pocas comprobaciones ip route get para destinos y orígenes críticos.
  • Audita ip rule y reglas de marcado nftables trimestralmente; elimina reglas “temporales” antes de que se conviertan en política.
  • Decide explícitamente cómo funciona el failover en cada tabla de política. Si no diseñaste el fallo, diseñaste la sorpresa.
  • Documenta el propietario y el propósito de cada regla no por defecto. Si no tiene propietario, no tiene derecho a existir.

Haz eso, y la “magia negra” del enrutamiento vuelve a ser lo que siempre fue: un conjunto de búsquedas deterministas sobre las que puedes razonar,
incluso cuando el pager te está gritando.

← Anterior
Ubuntu 24.04 “Stale file handle” en NFS: por qué ocurre y cómo evitarlo
Siguiente →
Copias de seguridad inmutables con ZFS: readonly y políticas de snapshots que realmente resisten

Deja un comentario