MANUAL: Mikrotik, dual WAN failover, con rutas recursivas

Buenas!

Hoy os vengo a traer una solución para aquellos que tengáis dos proveedores de internet, y queráis hacer un failover automático entre rutas de salida, cuando uno de los proveedores esté inaccesible. Empezando por lo más sencillo, partimos de dos proveedores a internet (en mi caso unos DHCP y otro PPPoE), uno con distancia 1 (ruta principal) y otro con distancia 2 (ruta alternativa o de failover). Tendríamos el siguiente esquema:

1661678304895.png

El problema​

Hasta aquí todo perfecto. Todo nuestro tráfico saldría por la ruta principal de la izquierda, a menos que esa ruta se caiga, y empezaría a funcionar la pata de la derecha. Ahora viene el problema: normalmente, cuando la conexión "se cae", puede (el 99% de las veces) que no sea un fallo físico que haga que la conexiones DHCP / PPPoE se desactiven. Si lo hacen, no hay problema, porque el balanceo es inmediato. Pero, si esas interfaces siguen levantadas, y el fallo está por encima (en la red del operador), tendremos un perfecto failover... que no funciona. Me refiero a este problema:

1661678848516.png



¿Cómo solucionamos este problema? Con una solución muy elegante, llamada ruta recursiva. Pero, ¿qué es una ruta recursiva? Pues viene a ser una ruta que tiene un nexthop (siguiente salto, "algo") que no está directamente conectado a nosotros. La idea es la siguiente: tengo una ruta que monitorea un host público, algo que está siempre levantado, como un servidor DNS. Confiando en que esa IP está siempre levantada, creo una ruta dependiente de otra, de tal manera que, si ese host público cae, la ruta por defecto asociada a ese monitor, cae también. Y, si la ruta vuelve a estar disponible y tiene menor distancia que la previa, volverá a estar activa. De esta manera comprobamos que, no sólo nuestro gateway está vivo, sino que además ese gateway es capaz de llegar a internet.

La magia de este tema la conseguimos con un par de parámetros adicionales de las rutas, el scope y target-scope. Normalmente, cuando tenemos una ruta por defecto (0.0.0.0/0) tenemos uno de estos dos casos:
Código:
/ip route
dst-address=0.0.0.0/0 gateway=212.231.184.1 comment="Ejemplo ruta DHCP"
dst-address=0.0.0.0/0 gateway=pppoe-out1 comment="Ejemplo ruta PPPoE"
scope_and_target_scope.png


Esas rutas, siguiendo la documentación de Mikrotik (dibujo anterior) y al tratarse de rutas estáticas, tendrán un scope con valor 30 y un target scope con valor 10. Y hay una regla que dice que si una ruta tiene un scope igual o superior al target scope asociado, no se usará para el cálculo del nexthop o "siguiente salto", cálculo que hay que hacer en el proceso de selección de rutas, cuando el gateway no está directamente conectado al router, por ejemplo, en nuestro caso de rutas recursivas (recordemos que el gateway será una dirección de internet). Cuando creemos las rutas recursivas, hemos de tener en cuenta que, para que la ruta se use y sea capaz de calcular su nexthop (ya que el gateway no se encuentra directamente conectado a ella), su scope ha de ser menor que el target scope de la ruta por defecto asociada. Esto que parece un galimatías, se ve mucho mejor con un ejemplo:

Rutas tradicionales​

Imaginemos que tenemos direccionamiento estático (IP estática, para que nos entendamos) y nos han dado los siguientes valores a configurar en nuestro equipo.
Dirección IP: 212.231.185.100
Máscara: /21 (255.255.248.0)
Gateway: 212.231.184.1

Si tuviéramos que configurar nuestro equipo con una ruta normal, la configuración sería la siguiente:
Código:
# Creamos la IP en IP > Address
/ip address
add interface=ether1 address=212.231.185.100/21

# Creamos la ruta por defecto, apuntando a su gateway
/ip route
dst-address=0.0.0.0/0 gateway=212.231.184.1

Al dar de alta la primera instrucción, con la creación de la dirección sobre ether1, se creará una ruta en nuestra tabla de rutas, automáticamente, de tipo directamente conectada, puesto que es una IP que estamos dando sobre una interfaz local, un puerto del router. Esa ruta tendrá un scope de 10, y no tendrá target scope (como indica el dibujo de arriba). Al meter la segunda instrucción, daremos de alta la ruta por defecto, con un par de valores para scope y target scope de 30/10, como dijimos antes, y la ruta quedará como activa al tener un gateway conocido, perteneciente a la subred /21 de la ruta conectada.

Rutas recursivas​

Ahora vamos a tratar de hacer el mismo ejemplo, con una ruta recursiva. Lo primero que tenemos que elegir es una dirección de internet que sepamos esté casi siempre (por no decir siempre) levantada, y que no se use para nada más en nuestro router. No pongáis la IP de un DNS en uso!!! Por ejemplo, yo voy a elegir la IP 1.1.1.1, del DNS de cloudflare, que no suelo usar. Esa IP será el gateway de nuestra ruta por defecto, resuelto recursivamente. Siguiendo el ejemplo anterior, tendríamos:

Dirección IP: 212.231.185.100
Máscara: /21 (255.255.248.0)
Gateway: 1.1.1.1
Gateway intermedio: 212.231.184.1

Código:
# Creamos la IP en IP > Address
/ip address
add interface=ether1 address=212.231.185.100/21

# Creamos una ruta, diciendo que a la 1.1.1.1 (nuestro gateway ficticio), se llega por la IP 212.231.184.1 (nuestro gateway real)
/ip route
add comment=recursive dst-address=1.1.1.1 gateway=212.231.184.1

# Creamos la ruta por defecto, y le damos como gateway la dirección externa, la cual sabemos que se resuelve recursivamente por la ruta anterior
/ip route
add check-gateway=ping comment=default dst-address=0.0.0.0/0 gateway=1.1.1.1

Si hemos seguido los pasos, veremos que... no funciona. ¿por qué? La ruta recursiva no ha tenido problemas en instalarse, pero cuando hemos metido la ruta por defecto, el router canta y nos dice que es inválida, porque no es alcanzable (no sabe cómo llegar a la 1.1.1.1, al no ser una IP directamente conectada al router). Es aquí donde tenemos que tocar los scope y target scope de las rutas. En este caso, a ambas rutas le daremos un scope = 10, y a la ruta por defecto un target-scope=11, tal que se cumpla la regla: al tener ambas rutas un scope inferior al target scope de la ruta por defecto, la ruta recursiva se usará para calcular el nexthop de la ruta por defecto, resolviendo que, para llegar a la 1.1.1.1, primero hay que hablar con el gateway real, el 212.231.184.1. La implementación buena quedaría así:

Código:
# Creamos la IP en IP > Address
/ip address
add interface=ether1 address=212.231.185.100/21

# Creamos una ruta, diciendo que a la 1.1.1.1 (nuestro gateway ficticio), se llega por la IP 212.231.184.1 (nuestro gateway real), con scope=10
/ip route
add comment=recursive dst-address=1.1.1.1 gateway=212.231.184.1 scope=10

# Creamos la ruta por defecto, y le damos como gateway la dirección externa, la cual sabemos que se resuelve recursivamente por la ruta anterior, scope=10, target-scope=11
/ip route
add check-gateway=ping comment=default dst-address=0.0.0.0/0 gateway=1.1.1.1 scope=10 target-scope=11

Ahora sí, veremos la ruta por defecto funcionando, y además, si la editamos, veremos que como Immediate Gateway, ha metido la IP del gateway real, la cual ha calculado de manera recursiva usando el cálculo del nexthop, al tratarse de un gateway que no está en el scope del router.


La gracia del asunto: lo mismo, pero con dos conexiones​

Hasta aquí hemos visto como se crea una ruta recursiva y para qué sirve. Todo esto no tiene ninguna gracia si sólo tenemos una conexión, puesto que, si se cae, nos va a dar igual dónde esté el fallo, no vamos a navegar y punto. Pero, cuando tenemos dos, sí que nos interesa diferenciar si la ruta se está cayendo simplemente porque se nos ha ido al traste la conexión de una manera física (le hemos dado un tirón al cable) o si el problema lo tenemos fuera de casa. Si esto mismo ahora lo aplicamos a una segunda ruta, con distancia=2 (ruta de failover), le veremos la gracia al asunto:

Conexión A (ether1)
Dirección IP: 212.231.185.100
Máscara: /21 (255.255.248.0)
Gateway: 1.1.1.1
Gateway intermedio (real): 212.231.184.1

Conexión B (ether2)
Dirección IP: 80.9.63.155
Máscara: /21 (255.255.248.0)
Gateway: 9.9.9.9
Gateway intermedio (real): 80.9.56.1

Tendríamos la siguiente configuración:

Código:
# Direccionamiento
/ip address
add interface=ether1 address=212.231.185.100/21
add interface=ether2 address=80.9.63.155/21

# Rutas
/ip route
add comment=recursive-wanA dst-address=1.1.1.1 gateway=212.231.184.1 scope=10
add check-gateway=ping comment=default-wanA dst-address=0.0.0.0/0 gateway=1.1.1.1 scope=10 target-scope=11 distance=1

add comment=recursive-wanB dst-address=9.9.9.9 gateway=80.9.56.1 scope=10
add check-gateway=ping comment=default-wanB dst-address=0.0.0.0/0 gateway=9.9.9.9 scope=10 target-scope=11 distance=2

Ahora sí, tenemos dos rutas con un failover de verdad. Al tener una sonda de tipo "ping" sobre la IP pública en la ruta por defecto, si esa IP se vuelve irresoluble vía el gateway real, significará que la conexión se ha caído, y se desactivará esa ruta, pasando a la ruta con siguiente distancia menor. Si el primer enlace vuelve en sí, automáticamente el ping detectará el cambio y volverá a poner la primera ruta como ruta principal, puesto que tiene distancia menor.

Y... si has llegado hasta aquí, probablemente estés pensando: claro, muy bonito, (so cabronaz...) pero yo no tengo direccionamiento estático! Esto es para los que le sangran pagan religiosamente los 30 euritos mensuales!

Pues, aquí viene lo bueno, esto se puede hacer también con direccionamiento dinámico, simplemente tirando de inventiva. ¿Cómo? Con scripting. Si recordamos, en el manual de balanceo PCC, vimos que podíamos crear rutas con marcas desde el script del DHCP. Lo mismo vamos a hacer en este caso, siguiendo estos pasos:

La re-gracia del asunto: lo mismo, pero con dos conexiones dinámicas

Ejemplo para un dual wan con failvoer DHCP (WAN1 - principal) / PPPoE (WAN2 - failover)

DHCP: simplemente editamos el cliente DHCP y desmarcamos la creacción de la ruta automática (Add default route = no). Y colocamos el siguiente script en el apartado "Advanced"
Código:
# Script DHCP rutas recursivas
{
  :local scope 10
  :local targetscope 11
  :local distance 1
  :local rmonitor 1.1.1.1
  :local rreccomment "recursive-wan1"
  :local rdefcomment "default-wan1"
  :local rcount [/ip route print count-only where comment=$rreccomment]
  :if ($bound=1) do={
    :if ($rcount = 0) do={
      /ip route add dst-address=$rmonitor gateway=$"gateway-address" comment=$rreccomment scope=$scope
      /ip route add check-gateway=ping comment=$rdefcomment distance=$distance dst-address=0.0.0.0/0 \
        gateway=$rmonitor scope=$scope target-scope=$targetscope
    } else={
        :if ($rcount = 1) do={
          :local test [/ip route find where comment=$rreccomment]
          :if ([/ip route get $test gateway] != $"gateway-address") do={
            /ip route set $test gateway=$"gateway-address"
          }
        } else={
            :error "Multiple routes found with the same comment!"
        }
    }
  } else={
      /ip route remove [find comment=$rreccomment]
      /ip route remove [find comment=$rdefcomment]
  }
}

PPPoE: creamos un nuevo perfil, copia del perfil por defecto y metemos lo siguiente en los eventos on-up/on-down de dicho perfil. Al crear el PPPoE, en lugar del perfil por defecto elegimos este nuevo perfil, y desmarcamos igualmente la opción de añadir la ruta por defecto (ya lo hará el script)
Código:
# Script PPPoE rutas recursivas - On UP
{
  :local scope 10
  :local targetscope 11
  :local distance 2
  :local rmonitor 9.9.9.9
  :local rreccomment "recursive-wan2"
  :local rdefcomment "default-wan2"
  :local pppinterface [/interface get $interface name]

  /ip route add dst-address=$rmonitor gateway=$pppinterface comment=$rreccomment scope=$scope
  /ip route add check-gateway=ping comment=$rdefcomment distance=$distance dst-address=0.0.0.0/0 \
    gateway=$pppinterface scope=$scope target-scope=$targetscope

}

# Script PPPoE rutas recursivas - On Down
{
  :local rreccomment "recursive-wan2"
  :local rdefcomment "default-wan2"
  /ip route remove [find comment=$rreccomment]
  /ip route remove [find comment=$rdefcomment]
}

Si habéis seguido estos pasos, tendremos lo mismo que el en el caso de las rutas recursivas con IPs estáticas, pero para direccionamiento dinámico.

Y, con esto y un bizcocho, otro manual a la saca. Espero que os guste y sea de utilidad.

Saludos!
 
Sólo le puedo dar una vez al like pero le daría 100!

Llevo mucho tiempo dándole vueltas a cómo se podría hacer esto, y más aún con IP dinámica.

Me quito el sombrero otra vez ante ti, @pokoyo!
 
Sólo le puedo dar una vez al like pero le daría 100!

Llevo mucho tiempo dándole vueltas a cómo se podría hacer esto, y más aún con IP dinámica.

Me quito el sombrero otra vez ante ti, @pokoyo!
Nada, ya ves, que lo disfrutes!

Saludos!
 
@pokoyo tengo alguna forma de lanzar un script cuando se hace el cmabio, como para poder mandarme un aviso al telegram?
No te hace falta otro script para eso, solo añade esta línea con tus datos al de @pokoyo :

Código:
/tool fetch url="https://api.telegram.org/bot123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/sendMessage?chat_id=-123456789&text=Failover activado" as-value output=user
 
No te hace falta otro script para eso, solo añade esta línea con tus datos al de @pokoyo :

Código:
/tool fetch url="https://api.telegram.org/bot123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/sendMessage?chat_id=-123456789&text=Failover activado" as-value output=user
Pero en una IP estática?
@hugomon ahi estuve probando con IP dinámica, solo manda alerta si la interfaz se cae, si bloqueo la IP desde el firewall no me manda la alerta
 
Última edición:
Pon dos monitores con netwatch a la IP DNS (o cualquier otra) que estés usando como recursiva, y si no llegas ya sabes que esa salida está KO. Otra que se me ocurre es un script que, con un scheduler que corra cada N minutos, compruebe el estado de la tabla de rutas, y te alerte de un cambio. Pero como tal, en la ruta no le puedes meter el script.

Saludos!
 
Pon dos monitores con netwatch a la IP DNS (o cualquier otra) que estés usando como recursiva, y si no llegas ya sabes que esa salida está KO. Otra que se me ocurre es un script que, con un scheduler que corra cada N minutos, compruebe el estado de la tabla de rutas, y te alerte de un cambio. Pero como tal, en la ruta no le puedes meter el script.

Saludos!
Lo del script no lo veo muy viable, se va a tomar su tiempo
Creo que ya lo solucione, en el firewal bloquee la ip 9.9.9.9 (que uso para chequear) cuando sale por el puerto 2 y después lo agregue al netwatch tanto para el up como para el down.

PD: que lido seria si al netwatch se le pudiera indicar que interfaz chequar...
 
No entiendo por qué tienes que bloquear nada. La gracia de la ruta recursiva es que sólo salga por esa ruta cuando está viva.

Saludos!
 
No entiendo por qué tienes que bloquear nada. La gracia de la ruta recursiva es que sólo salga por esa ruta cuando está viva.

Saludos!
Para la notificación cuando de cuando cae una conexión y entra la otra, que era lo que no me estaba funcionando
 
Pero no necesitas tocar el firewall para eso, no? Haces el netwatch a la IP DNS y listo.

Saludos!
 
Gran tutorial y explicación. Gracias.

Una pregunta sobre este tema: Para hacer el failover con un pincho USB 3G (LTE) se haría de forma similar? (Lo tenía configurado en un mikrotik pero ya ni me acuerdo de como lo configuré, y últimamente creo que se hace un lío y no funciona el failover)
 
Gran tutorial y explicación. Gracias.

Una pregunta sobre este tema: Para hacer el failover con un pincho USB 3G (LTE) se haría de forma similar? (Lo tenía configurado en un mikrotik pero ya ni me acuerdo de como lo configuré, y últimamente creo que se hace un lío y no funciona el failover)
en conexiones LTE no suele haber problema, porque si pierdes conectividad, la LTE se te suele ir al garete. Pero, además, tienes el añadido de que no hay script de DHCP ni nada que puedas poner sobre esa interfaz, así que te va a resultar difícil implementar algo así en una de ellas. Con las LTE, yo me conformaría con ponerles una distancia superior, si quieres que haga de failover, e implementar en la conexión principal uno de estos dos métodos, dependiendo de si es PPPoE o DHCP

Saludos!
 
en conexiones LTE no suele haber problema, porque si pierdes conectividad, la LTE se te suele ir al garete. Pero, además, tienes el añadido de que no hay script de DHCP ni nada que puedas poner sobre esa interfaz, así que te va a resultar difícil implementar algo así en una de ellas. Con las LTE, yo me conformaría con ponerles una distancia superior, si quieres que haga de failover, e implementar en la conexión principal uno de estos dos métodos, dependiendo de si es PPPoE o DHCP

Saludos!
Probaré a dejarlo como comentas, gracias
 
Buenas otra vez, @pokoyo.

Vengo del hilo del PPC + failover que hablábamos antes:

Buf, me estoy atascando bastante para integrar ahora el failover con rutas recursivas manteniendo el balanceo PPC. A ver si puedes arrojarme algo de luz.

Parto de la config del otro manual. A mis interfaces wan les he llamado wan1 y wan2 (soy poco original) a diferencia de las tuyas internet-pepephone e internet-o2. Todo lo demás es lo mismo.

He deshabilitado las rutas creadas en ese manual y he ajustado los scripts de mis dhcp client (en mi escenario de pruebas no tengo pppoe, sino 2 dhcps):

1664127534589.png


Por la wan1 obtengo una IP del rango 192.168.10.0/24 y por la wan2 una 192.168.0.0/24. Estoy detrás de sendos routers de operadora en mis pruebas.

Todo levanta en orden pero no hace el balanceo sino que sale todo por la wan1 (la que tiene distancia 1).

Veo que todo está marcado con la routing table "main":
1664127882228.png

Lo mismo para ambas rutas referentes a la wan2.

Modifico entonces el script del DHCP y le añado una variable local :rtable="to_wan1".

Código:
{
  :local scope 10
  :local targetscope 11
  :local distance 1
  :local rtable "to_wan1"
  :local rmonitor 1.1.1.1
  :local rreccomment "recursive-wan1"
  :local rdefcomment "default-wan1"
  :local rcount [/ip route print count-only where comment=$rreccomment]
  :if ($bound=1) do={
    :if ($rcount = 0) do={
      /ip route add dst-address=$rmonitor gateway=$"gateway-address" comment=$rreccomment scope=$scope \
    routing-table=$rtable
      /ip route add check-gateway=ping comment=$rdefcomment distance=$distance dst-address=0.0.0.0/0 \
        gateway=$rmonitor scope=$scope target-scope=$targetscope routing-table=$rtable
    } else={
        :if ($rcount = 1) do={
          :local test [/ip route find where comment=$rreccomment]
          :if ([/ip route get $test gateway] != $"gateway-address") do={
            /ip route set $test gateway=$"gateway-address"
          }
        } else={
            :error "Multiple routes found with the same comment!"
        }
    }
  } else={
      /ip route remove [find comment=$rreccomment]
      /ip route remove [find comment=$rdefcomment]
  }
}

He probado a establecer "to_wan1" tanto en la regla default como en la recursive e incluso en ambas pero siempre se me queda alguna ruta en rojo tras forzar la renovación del cliente dhcp.
1664128439329.png


En el pantallazo aparece "to_wan1" en ambas rutas, pero he probado el resto de combinaciones.

Si además se lo meto en las rutas de la wan2 ("to_wan2"), me quedo sin conexión (obviamente).

El caso es que ya no sé dónde tengo que indicarle que envíe el tráfico por una wan u otra o si lo estoy haciendo todo mal.

Cualquier pista para intentar resolverlo será bienvenida!

Gracias!!
 

Adjuntos

  • 1664128376846.png
    1664128376846.png
    52.7 KB · Visitas: 23
Buenas otra vez, @pokoyo.

Vengo del hilo del PPC + failover que hablábamos antes:

Buf, me estoy atascando bastante para integrar ahora el failover con rutas recursivas manteniendo el balanceo PPC. A ver si puedes arrojarme algo de luz.

Parto de la config del otro manual. A mis interfaces wan les he llamado wan1 y wan2 (soy poco original) a diferencia de las tuyas internet-pepephone e internet-o2. Todo lo demás es lo mismo.

He deshabilitado las rutas creadas en ese manual y he ajustado los scripts de mis dhcp client (en mi escenario de pruebas no tengo pppoe, sino 2 dhcps):

Ver el adjunto 99636

Por la wan1 obtengo una IP del rango 192.168.10.0/24 y por la wan2 una 192.168.0.0/24. Estoy detrás de sendos routers de operadora en mis pruebas.

Todo levanta en orden pero no hace el balanceo sino que sale todo por la wan1 (la que tiene distancia 1).

Veo que todo está marcado con la routing table "main":
Ver el adjunto 99639
Lo mismo para ambas rutas referentes a la wan2.

Modifico entonces el script del DHCP y le añado una variable local :rtable="to_wan1".

Código:
{
  :local scope 10
  :local targetscope 11
  :local distance 1
  :local rtable "to_wan1"
  :local rmonitor 1.1.1.1
  :local rreccomment "recursive-wan1"
  :local rdefcomment "default-wan1"
  :local rcount [/ip route print count-only where comment=$rreccomment]
  :if ($bound=1) do={
    :if ($rcount = 0) do={
      /ip route add dst-address=$rmonitor gateway=$"gateway-address" comment=$rreccomment scope=$scope \
    routing-table=$rtable
      /ip route add check-gateway=ping comment=$rdefcomment distance=$distance dst-address=0.0.0.0/0 \
        gateway=$rmonitor scope=$scope target-scope=$targetscope routing-table=$rtable
    } else={
        :if ($rcount = 1) do={
          :local test [/ip route find where comment=$rreccomment]
          :if ([/ip route get $test gateway] != $"gateway-address") do={
            /ip route set $test gateway=$"gateway-address"
          }
        } else={
            :error "Multiple routes found with the same comment!"
        }
    }
  } else={
      /ip route remove [find comment=$rreccomment]
      /ip route remove [find comment=$rdefcomment]
  }
}

He probado a establecer "to_wan1" tanto en la regla default como en la recursive e incluso en ambas pero siempre se me queda alguna ruta en rojo tras forzar la renovación del cliente dhcp.
Ver el adjunto 99645

En el pantallazo aparece "to_wan1" en ambas rutas, pero he probado el resto de combinaciones.

Si además se lo meto en las rutas de la wan2 ("to_wan2"), me quedo sin conexión (obviamente).

El caso es que ya no sé dónde tengo que indicarle que envíe el tráfico por una wan u otra o si lo estoy haciendo todo mal.

Cualquier pista para intentar resolverlo será bienvenida!

Gracias!!
Me pillas de viaje, pero mañana le echo un vistazo tranquilamente y te digo.

Saludos!
 
Arriba