Mikrotik automatic switching to a backup channel for a dynamic ip address (issued by DHCP)
Greetings, Habr! Due to the poor quality of the line, I was asked to set up automatic switching to the backup channel. For this purpose, they provided the MikroTik RB 951Ui router.
I thought that there would be no problems ... Just configure the channel check and routes. But, unfortunately, both providers issue IP dynamically. At first I tried to substitute the name of the interface in the route, but the ping did not pass. After reading several articles, including foreign sites, but did not find a solution to the problem that would suit me. I had to get to know RouterOS closer, and in particular with creating scripts ...
In this OS, you can create a route in two ways:
- manually (via a graphical interface or via a terminal);
- automatically by the DHCP client.
When you create a route manually, you cannot update the gateway dynamically. To do this, you will have to write several fairly large scripts and add a lot of checks. When writing them, I noticed that in RouterOS it is problematic to make complex scripts work. It is very difficult to track the logic of work, although logs and variables were used to check. Scripts were written, but they were unstable, despite the optimization of the code and the addition of checks. When the number of checks began to grow exponentially and repeated optimization of the logic circuit did not improve much - I decided to abandon this option and try to use the option of automatic route creation by the DHCP client in the script.
/ip dhcp-client set [/ip dhcp-client find interface=$Iface] add-default-route=yes [no]
For the desired interface, the option to automatically create a default route is set in the DHCP client settings.
So, the script will work according to this algorithm:
Algorithm Explanation
A copy of the script will be launched for each interface. Each copy will autonomously create a default route. The priority of the route will depend on distance. That is, when the connection “falls” on the route with Distance 10, a switch to 11. This switches over seamlessly.
To start, the script pings the selected host on the Internet along the default route. If ping is less than ($ PingCount- $ Margin) (Margin - the error is set for accuracy control), then we ping along the test route to check if the connection is “live”. In case of a negative result, we check the route and the presence of problems with the settings:
- overload the interface every $ TimeToWait times (reduce the load on the processor);
- waiting for the interface to load;
- we check whether there are DHCP client settings for this interface, otherwise we create it;
- we check the status of the DHCP client (sometimes RouterOS can “slip a pig”);
- waiting for the DHCP lease;
- add the desired interface to the value of $ CurrentGateway;
- check if there is a test route;
- check if the gateway is correct in the test route.
The reaction rate to the state of the compound can be individually adjusted using the following variables:
- PingCount - the number of icmp requests sent (you can also add another variable to determine the number of requests sent along the test route and to the provider's gateway, that is, the number of requests will decrease and the script will run faster);
- Margin - the coefficient is needed to set the error. For example, with $ Margin = 1, the route checking cycle starts only when more than one packet disappears, which is important in my situation;
- TimeToWait when waiting for a connection, the interface is reloaded every $ TimeToWait times (this is necessary in order to remove the load on the processor)
Preparatory setup
I will not describe the standard settings of the router for two reasons: firstly, this topic has been raised on the Internet more than once, including on Habré, and secondly, networks differ in their configuration. Since the script only affects the default routes and the DHCP client settings, I think you will have no difficulty adapting the script to your network.
For the script to work, you do not need to create default routes - it will create them automatically. The only thing is to choose the appropriate distance for test routes (both with $ Distance = 1) and $ DistanceDefault 10 and 11 for default routes (one for each provider). Also no need to create dhcp clients.
When setting up the router, I used SSH and Winbox (a specialized program for configuring devices managed by RouterOS, even works in * nix using Wine).
Let's get started.
In Interfaces, we change the names of two interfaces to coincide with the value of the $ Iface variable in the script (I have isp1, isp2):
Change the DNS addresses to google:
Create a script: System → Scripts → Add and paste the code shown below:
Script code
:delay 10s
:local Iface "isp1"
:local StatusIface
:local CurrentGateway
:local pingInet
:local pingLink
:local pingGateway
:local IPToPingInet "213.180.193.3"
:local IPToPing "8.8.4.4"
:local PingCount 5
:local Margin 1
:local Distance 1
:local DistanceDefault 10
:local RunTime 0
:local TimeToWait 20
#Первый цикл
while (true) do={
# пингуем общий интернет
:set pingInet [/ping $IPToPingInet count=$PingCount interface=$Iface]
:log debug "$pingInet $Iface $IPToPingInet"
:if ($pingInet < ($PingCount-$Margin)) do={
:log error "No internet connection on $Iface."
/ip dhcp-client set [/ip dhcp-client find interface=$Iface] add-default-route=no
# Второй цикл
:while ($pingInet < ($PingCount-$Margin)) do={
# пингуем интернет через тест
:set pingLink [/ping $IPToPing count=$PingCount interface=$Iface]
:log debug "$pingLink $Iface $IPToPing"
:if ($pingLink < ($PingCount-$Margin)) do={
# Первая перезагрузка
/interface ethernet disable $Iface; /interface ethernet enable $Iface
:while ($pingLink < ($PingCount-$Margin)) do={
:log debug "$pingLink $Iface $IPToPing"
:set RunTime ($RunTime + 1)
:log debug $RunTime
# Time to wait
:if ( $RunTime = $TimeToWait ) do={
# Reboot interface
:log info "reboot and release $Iface"
/interface ethernet disable $Iface; /interface ethernet enable $Iface
:set RunTime 0
}
# Ждем загрузки интерфейса
:if ([/interface ethernet get $Iface disabled] = false) do={
:log debug "Interface $Iface enabled"
# Проверяем линк
/interface ethernet monitor $Iface once do={
:set StatusIface $status
}
:if ($StatusIface = "link-ok") do={
:log debug "$Iface link-ok."
# Проверяем dhcp
:if ([/ip dhcp-client find interface=$Iface] != "") do={
:log debug "test1"
# Проверяем или нет ошибки DHCP
:if ( [/ip dhcp-client get [/ip dhcp-client find interface=$Iface] invalid ] != true) do={
:log debug "test2"
# Ждем получения DHCP lease
:set CurrentGateway [/ip dhcp-client get [/ip dhcp-client find interface=$Iface] gateway ]
:log debug "Waiting DHCP lease"
:if ($CurrentGateway != nil) do={
:set CurrentGateway [:put ("$CurrentGateway" . "%$Iface")]
:log debug "CurrentGateway $CurrentGateway"
# Looking for route test
:log debug "Cheking test route for $Iface..."
:local a [ /ip route find comment="$Iface" ]
:if (($a) = "") do={
:log info ("Adding test route for $Iface...")
/ip route add dst-address=$IPToPing gateway=$CurrentGateway comment="$Iface" distance=$Distance
} else={
:local EstablishedGateway [/ip route get [/ip route find comment="$Iface"] gateway ]
:log debug "EstablishedGateway $EstablishedGateway"
:if ( $CurrentGateway = $EstablishedGateway ) do={
:log debug "No route changes needed for $Iface."
} else={
:log info "Updating test route for $Iface..."
/ip route set [/ip route find comment="$Iface" ] dst-address=$IPToPing gateway=$CurrentGateway comment="$Iface" distance=$Distance
}
}
:set pingGateway [/ping [/ip dhcp-client get [/ip dhcp-client find interface=$Iface] gateway ] count=$PingCount interface=$Iface]
:log debug "$pingGateway $Iface $IPToPing"
:if ($pingGateway < ($PingCount-$Margin)) do={
:log error "route error on $Iface"
:log debug [/ip dhcp-client get [/ip dhcp-client find interface=$Iface] gateway ]
/ip dhcp-client release [/ip dhcp-client find interface=$Iface]
}
} else={
:log error "DHCP no lease."
:delay 1s
}
} else={
:log error "DHCP failure on $Iface."
:log info "reboot and release $Iface"
/interface ethernet disable $Iface; /interface ethernet enable $Iface
:delay 1s
}
} else={ :log info "adding DHCP client for $Iface"
/ip dhcp-client add interface=$Iface disabled=no add-default-route=yes default-route-distance=$DistanceDefault use-peer-dns=no use-peer-ntp=no
}
} else={
:log debug "No-link on $Iface."
:delay 1s
}
} else={
:log error "Interface $Iface disabled."
}
:set pingLink [/ping $IPToPing count=$PingCount interface=$Iface]
}
} else={
:log info "add default route= yes for $Iface"
/ip dhcp-client set [/ip dhcp-client find interface=$Iface] add-default-route=yes
}
:set pingInet [/ping $IPToPingInet count=$PingCount interface=$Iface]
}
} else={
:log debug "Internet on $Iface connected."
}
}
We repeat the previous step for the second interface, just replace the value of the $ Iface variable with “isp2”, also change $ DistanceDefault with the above values (I have 10 for isp1 and 11 for isp2).
Now you need to configure the scheduler to automatically run scripts when the router boots.
System → Scheduler->
It can also be done using ssh or from the console if there are problems with the Cyrillic alphabet in the date:
/system scheduler add name=CheckTestRoute1 start-time=startup on-event=CheckTestRoute1
We overload ...
I thought that there would be problems with NAT when switching routes, but so far the
flight is normal. If you get in touch ...
That's all. I hope this article is useful to someone.
PS: Finally, RouterOS threw up another task ...
As you can see, despite the fact that the route is correct, ping does not work.
To fix this, I added one more check (it has already been added above in the script code).