📜 ⬆️ ⬇️

Asterisk: Prioritizing VoIP traffic and reserving Internet access from two providers on MikroTik

It would seem that the things in the title are quite trivial and described in many places on the global network, but this is only at first glance. Having tried the most common advice, I found several “pitfalls”, boulders and even rock formations.

But these are all words, more to the point.
A fairly common situation is Asterisk inside the LKS, behind the MikroTik router.
In order to allocate traffic to the server where the PBX is installed, the administrator cuts off part of the provider's channel, allocating it exclusively for a specific IP.
Or another implementation, when the necessary traffic is determined not only by the IP address of the PBX, but also by the packet size and protocol.
Tried - it works. Can I forget? And no.

What if the administrator wants to merge something from the Internet while in the server console, or vice versa, send a large amount of traffic to the Internet somewhere? That's right - it is prioritized on MikroTik as well as the useful traffic from the PBX, which ultimately leads to problems with IP-telephony.
')
The solution here is as old as IPv4 itself - mark traffic on the server with Asterisk generated only by it, and so that MikroTik can “see”, kick off (sorry for such rude anglicism) and prioritize only it.

The next item we have is reservation of channels from two Internet providers.
I think that every system administrator who uses MikroTik routers on his farm is familiar with the script from the wiki - wiki.mikrotik.com/wiki/Failover_Scripting
He is all good, but as in the previous situation there is a number of “buts”.
The most significant of them is the name “Connection tracking” and it is this:
when our main ISP deigns to rest from the works of the righteous, traffic switches to backup.

Everyone seems to be happy, YouTube works, yap, too, but no matter how much we shout to the expo
sip reload 

and in desperation they did not try to use higher order magic
 core restart now 

SIP registrations do not rise.

But the fact is that the records from the “old” (main) Internet channel remained in the “Connection tracking” mechanism and need to be removed, after which the registrations will successfully rise and the calls will start to pass.

If you are interested in how to prove to MikroTik who is still a camel, and also how to automate the reset of "old" connections in the script, then you are right under the cat.


Part 1. Proper prioritization of VoIP traffic.


From the very talkative introduction let's move on to the practical part.
How to mark traffic so that MikroTik will recognize this tag?
The answer is simple - DSCP!
OK, but what about the traffic of a particular daemon, in this case Asterisk?
And here without problems, sir (madam) - iptables --uid-owner!

On the server with PBX, we will find out what user id our Asterisk has (asterisk is it)
 grep -i asterisk /etc/passwd|cut -d: -f3 1001 


And we will add a rule to specify the DSCP value we need (for VoIP traffic, it is customary to specify CS5, or, in the numeric format, 40)
comment from ksg222
I would like to note that usually voice traffic is “painted” with two DSCP values: CS5 (CS3 in the case of Cisco) for signaling and EF for RTP traffic. Of course, in your example, this is not critical, since your main point in the configuration is to distinguish voice traffic from the rest. Anyway, there will be no further prioritization of the DSCP field on the Internet.

tools.ietf.org/html/rfc4594#section-2.3

www.cisco.com/c/en/us/td/docs/solutions/Enterprise/WAN_and_MAN/QoS_SRND/QoS-SRND-Book/QoSIntro.html#pgfId-46256


 iptables -I OUTPUT 1 -t mangle -m owner --uid-owner 1001 -j DSCP --set-dscp 40 

* for the curious - at the end of the article there will be links where you can find out where I got this from


as suggested by varnav in the comments, you can set the desired values ​​immediately in sip.conf
And why put a DSCP tag through Iptables, if it can be done through sip.conf:

tos_sip = cs3; Sets TOS for SIP packets.
tos_audio = ef; Sets TOS for RTP audio packets.
tos_video = af41; Sets TOS for RTP video packets.

We will finish the sim on the server with IP PBX and proceed to the configuration of the router.
I took this material as a basis - voxlink.ru/kb/voip-devices-configuration/mikrotik-voip-traffic-configuration
I will not repost, everything is available there, I'll show only the listings.
Table MANGLE firewall:
 /ip fm exp # nov/26/2015 17:09:20 by RouterOS 6.21.1 # software id = 5QIF-MH9A # /ip firewall mangle add action=mark-packet chain=forward new-packet-mark=def_out src-address=192.168.5.0/24 add action=mark-packet chain=forward new-packet-mark=def_out src-address=192.168.6.0/24 add action=mark-packet chain=forward dst-address=192.168.5.0/24 new-packet-mark=def_in add action=mark-packet chain=forward dst-address=192.168.6.0/24 new-packet-mark=def_in add action=mark-packet chain=forward dscp=40 new-packet-mark=voip_out src-address=192.168.7.10 add action=mark-packet chain=forward dscp=40 dst-address=192.168.7.10 new-packet-mark=voip_in 

As described in the introduction - match the necessary traffic over IP and across the DSCP field.

And queues:
 /queue tree export # nov/26/2015 17:09:27 by RouterOS 6.21.1 # software id = 5QIF-MH9A # /queue tree add max-limit=30M name=in parent=global add max-limit=28M name=def-in packet-mark=def_in parent=in add limit-at=1M max-limit=2M name=voip-in packet-mark=voip_in parent=in priority=1 add max-limit=30M name=out parent=global add max-limit=28M name=def-out packet-mark=def_out parent=out add limit-at=1M max-limit=2M name=voip-out packet-mark=voip_out parent=out priority=1 

By the way, the router should prioritize traffic with a large DSCP value, but I still separated a 2 Mb / s band for it.

Part 2. The failover script


Let's start from the very beginning, let me tell you how the provider links and routing are configured.
There are two ISPs, one connects via ethernet, the second via PPPoE.
For clarity, here are the listings. Ethernet connection:
 [f@777777] > ip addr pr where interface=ether7 Flags: X - disabled, I - invalid, D - dynamic # ADDRESS NETWORK INTERFACE 0 46.11.6.78/30 46.11.6.76 ether7 

PPPoE:
 [f@777777] > int pppoe-cl pr Flags: X - disabled, R - running 0 R name="rtk" max-mtu=auto max-mru=auto mrru=disabled interface=ether8 user="f" password="w" profile=default keepalive-timeout=60 service-name="" ac-name="" add-default-route=no dial-on-demand=no use-peer-dns=yes allow=pap,chap,mschap1,mschap2 

note - I do not add a default route when raising the interface, it will be added manually, statically

the next step is to configure the routing
 [f@777777] > ip rpdt 0 AS comment=RTK_TABLE dst-address=0.0.0.0/0 gateway=rtk gateway-status=rtk reachable distance=1 scope=30 target-scope=10 routing-mark=RTK_mark 1 AS comment=GoodLine_TABLE dst-address=0.0.0.0/0 gateway=46.18.6.77 gateway-status=46.18.6.77 reachable via ether7 distance=1 scope=30 target-scope=10 routing-mark=GoodLine_mark 2 AS comment=MAIN_TABLE dst-address=0.0.0.0/0 gateway=46.18.6.77 gateway-status=46.18.6.77 reachable via ether7 distance=1 scope=30 target-scope=10 3 S comment=MAIN_TABLE dst-address=0.0.0.0/0 gateway=rtk gateway-status=rtk reachable distance=2 scope=30 target-scope=10 4 ADC dst-address=10.155.177.1/32 pref-src=10.155.177.13 gateway=pptp-out1 gateway-status=pptp-out1 reachable distance=0 scope=10 5 S dst-address=10.155.177.1/32 gateway=pptp-out1 gateway-status=pptp-out1 reachable distance=1 scope=30 target-scope=10 6 ADC dst-address=46.18.6.76/30 pref-src=46.18.6.78 gateway=ether7 gateway-status=ether7 reachable distance=0 scope=10 7 ADC dst-address=172.16.79.1/32 pref-src=172.16.79.251 gateway=l2tp-out1 gateway-status=l2tp-out1 reachable distance=0 scope=10 8 ADC dst-address=192.168.77.0/24 pref-src=192.168.77.1 gateway=bridge-local gateway-status=bridge-local reachable distance=0 scope=10 9 AS comment=thecall dst-address=192.168.254.0/24 gateway=172.16.79.1 gateway-status=172.16.79.1 reachable via l2tp-out1 distance=1 scope=30 target-scope=10 10 ADC dst-address=213.22.11.99/32 pref-src=217.11.15.125 gateway=rtk gateway-status=rtk reachable distance=0 scope=10 

there is a lot of superfluous, we are only interested in 0,1,2,3
2 and 3 are default routes, 2 with metric (distance) "1" and is currently active and 3 with metric "2", currently not used

0 and 1 are needed so that the marshtutizer can respond from the same interface to which the request came, i.e. no matter which route is active by default
the same logic is served by the rules 0,1 from the following listing of the routing rules:
 [f@777777] > ip r ru pr Flags: X - disabled, I - inactive 0 src-address=217.11.15.125/32 action=lookup table=RTK_mark 1 src-address=46.18.6.78/30 action=lookup table=GoodLine_mark 2 dst-address=192.168.77.0/24 action=lookup table=main 

Rule 2 is needed for LAN devices behind MikroTik.

All this is called the capacious words “Policy based routing” and at the end of the opus I will give some useful links for understanding the work of these mechanisms-rules.
* Yes, I realized the minimum, but this is what I need

We dealt with the aperitif, we will deal with the main course.
The script listing is packaged under the spoiler, because too large (all UPDs are already included).
UPD1 - the script is fixed 11/28/2015
changed the condition (true for ISP2):
 - :if ($PingFailCountISP1 < ($FailTreshold)) + :if ($PingFailCountISP1 < ($FailTreshold+1)) 

Also added route adding
 :if ([/interface get value-name=running $InterfaceISP1]) do={ /ip route add dst-address=$PingTarget gateway=$GatewayISP1 :set PingResult [ping $PingTarget count=3] 

because if you simply specify the desired interface, but at the moment the route is not active, then the ping will not pass

at the end of each ISP check, the route is deleted
 /ip route rem [find dst-address="$PingTarget" . "/32"] 



UPD2 - the script successfully works on RouterOS version 6.33.1

UPD3
PBXIP variable must be indicated in quotes
 :local PBXIP "192.168.77.10" 

and each line of connection tracking deletion should be without quotes
 /ip firewall connection { remove [find src-address~$PBXIP] } 



Hidden text
 # ------------------- header ------------------- # Script by Tomas Kirnak, version 1.0.7 # If you use this script, or edit and # re-use it, please keep the header intact. # # For more information and details about # this script please visit the wiki page at # http://wiki.mikrotik.com/wiki/Failover_Scripting # ------------------- header ------------------- #------------------- header_1 ------------------ # FessAectan has made some changes: # - add ISPs link state checking - # - add clearing connection tracking states for selected IP - # ------------------ header_1 ------------------ # ------------- start editing here ------------- # Edit the variables below to suit your needs # Please fill the WAN interface names :local InterfaceISP1 ether7 :local InterfaceISP2 rtk # Please fill the gateway IPs (or interface names in case of PPP) :local GatewayISP1 46.18.6.77 :local GatewayISP2 rtk # Please fill the ping check host - currently: resolver1.opendns.com :local PingTarget 21.7.2.77 # Please fill how many ping failures are allowed before fail-over happends :local FailTreshold 1 # Define the distance increase of a route when it fails :local DistanceIncrease 2 # Editing the script after this point may break it # -------------- stop editing here -------------- # Declare the global variables :global PingFailCountISP1 :global PingFailCountISP2 :global InterfaceFailISP1 :global InterfaceFailISP2 # This inicializes the PingFailCount variables, in case this is the 1st time the script has ran :if ([:typeof $PingFailCountISP1] = "nothing") do={:set PingFailCountISP1 0} :if ([:typeof $PingFailCountISP2] = "nothing") do={:set PingFailCountISP2 0} # IntercaceFail variables. First time initialization. :if ([:typeof $InterfaceFailISP1] = "nothing") do={:set InterfaceFailISP1 0} :if ([:typeof $InterfaceFailISP2] = "nothing") do={:set InterfaceFailISP2 0} # This variable will be used to keep results of individual ping attempts :local PingResult # Your PBX IP :local PBXIP "192.168.77.10" # Check ISP1 :if ([/interface get value-name=running $InterfaceISP1]) do={ /ip route add dst-address=$PingTarget gateway=$GatewayISP1 :set PingResult [ping $PingTarget count=3 interface=$InterfaceISP1] :if ($PingResult = 0) do={ :if ($PingFailCountISP1 < ($FailTreshold+1)) do={ :set PingFailCountISP1 ($PingFailCountISP1 + 1)} :if ($PingFailCountISP1 = $FailTreshold) do={ :log warning "ISP1 has a problem en route to $PingTarget - increasing distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP1 && static && comment=MAIN_TABLE] do=\ {/ip route set $i distance=([/ip route get $i distance] + $DistanceIncrease)} :log warning "Route distance increase finished." /ip firewall connection { remove [find src-address~$PBXIP] } } } } else={ :if ($InterfaceFailISP1 = 0) do={ :set InterfaceFailISP1 1 :log warning "ISP1 intarface link is down - clear all connections from $PBXIP" /ip firewall connection { remove [find src-address~$PBXIP] } } } :if ([/interface get value-name=running $InterfaceISP1]) do={ :if ($InterfaceFailISP1 = 1) do={ :set InterfaceFailISP1 0 :log warning "ISP1 intarface link is up - clear all connections from $PBXIP" /ip firewall connection { remove [find src-address~$PBXIP] } } } :if ($PingResult > 0) do={ :if ($PingFailCountISP1 > 0) do={ :set PingFailCountISP1 ($PingFailCountISP1 - 1) :if ($PingFailCountISP1 = ($FailTreshold -1)) do={ :log warning "ISP1 can reach $PingTarget again - bringing back original distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP1 && static && comment=MAIN_TABLE] do=\ {/ip route set $i distance=([/ip route get $i distance] - $DistanceIncrease)} :log warning "Route distance decrease finished." /ip firewall connection { remove [find src-address~$PBXIP] } } } } /ip route rem [find dst-address="$PingTarget" . "/32"] # Check ISP2 :if ([/interface get value-name=running $InterfaceISP2]) do={ /ip route add dst-address=$PingTarget gateway=$GatewayISP2 :set PingResult [ping $PingTarget count=3 interface=$InterfaceISP2] :if ($PingResult = 0) do={ :if ($PingFailCountISP2 < ($FailTreshold+1)) do={ :set PingFailCountISP2 ($PingFailCountISP2 + 1)} :if ($PingFailCountISP2 = $FailTreshold) do={ :log warning "ISP2 has a problem en route to $PingTarget - increasing distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP2 && static && comment=MAIN_TABLE] do=\ {/ip route set $i distance=([/ip route get $i distance] + $DistanceIncrease)} :log warning "Route distance increase finished." /ip firewall connection { remove [find src-address~$PBXIP] } } } } else={ :if ($InterfaceFailISP2 = 0) do={ :set InterfaceFailISP2 1 :log warning "ISP2 interface link is down - clear all connections from $PBXIP" /ip firewall connection { remove [find src-address~$PBXIP] } } } :if ([/interface get value-name=running $InterfaceISP2]) do={ :if ($InterfaceFailISP2 = 1) do={ :set InterfaceFailISP2 0 :log warning "ISP2 intarface link is up - clear all connections from $PBXIP" /ip firewall connection { remove [find src-address~$PBXIP] } } } :if ($PingResult > 0) do={ :if ($PingFailCountISP2 > 0) do={ :set PingFailCountISP2 ($PingFailCountISP2 - 1) :if ($PingFailCountISP2 = ($FailTreshold -1)) do={ :log warning "ISP2 can reach $PingTarget again - bringing back original distance of routes." :foreach i in=[/ip route find gateway=$GatewayISP2 && static && comment=MAIN_TABLE] do=\ {/ip route set $i distance=([/ip route get $i distance] - $DistanceIncrease)} :log warning "Route distance decrease finished." /ip firewall connection { remove [find src-address~$PBXIP] } } } } /ip route rem [find dst-address="$PingTarget" . "/32"] 


With the description of variables, everything is clear, further briefly the logic itself:
1. Check if there is a link on the interface (does a PPPoE connection exist)
2. Yes, we check availability of $ PingTarget
2a No - delete all connections from the src-address of our PBX. The route with the metric “2” becomes active, so that old connections need to be removed by SIP registrations.
3. $ PingTarget is available - we are going to check ISP2
3a. $ PingTarget is not available - reduce the route metric through ISP1 by 2 and delete the old connection from the “Connection tracking”

Why did I add an interface status check?
If there is no link on the port, then there is no point in sending ping.
Or, in the case of PPP interfaces (like mine), ping will not work at all and the script logic will be broken.
 [f@777777] <SAFE> interface pppoe-cl di 0 [f@777777] <SAFE> [f@777777] <SAFE> [f@777777] <SAFE> [f@777777] <SAFE> [f@777777] <SAFE> :if ([/ping 8.8.8.8 interface=rtk count=3]>0) do={:put "Yes sir"} SEQ HOST SIZE TTL TIME STATUS <!--         -    --> [f@777777] <SAFE> interface pppoe-cl en 0 [f@777777] <SAFE> :if ([/ping 8.8.8.8 interface=rtk count=3]>0) do={:put "Yes sir"} SEQ HOST SIZE TTL TIME STATUS 0 8.8.8.8 timeout 1 8.8.8.8 timeout 2 8.8.8.8 56 254 52ms sent=3 received=1 packet-loss=66% min-rtt=52ms avg-rtt=52ms max-rtt=52ms Yes sir 


Things are a matter of fact, add a task to the scheduler of the router.
 [f@777777] > sys sch exp # nov/26/2015 17:37:29 by RouterOS 6.30 # software id = LY7Z-747B # /system scheduler add interval=30s name=check_ISPs on-event=check_gateways policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive start-date=nov/20/2015 start-time=05:37:37 

And here we finished, let's move on to the dessert - the resources used.

Part 3. Conclusion, resources used


If you want to read about DSCP, I advise you to use these links here:
1. cisco
2. en.wikipedia
3. msdn.microsoft
4. microsin

How packs go to MikroTik, how Policy routing is configured:
1. wiki Manual: Packet_Flow
2. wiki Policy_Base_Routing
3. nixman Policy_Base_Routing
4. blog.butchevans Policy_Base_Routing

A couple of useful, although I think well-known, manuals on iptables:
1. ru.wikibooks.org/wiki/Iptables
2. opennet iptables guid

Behind this otklanivayus, thanks to those who read it.
I will be immensely happy if I find myself useful, because this is the essence.
Good luck everyone, everyone!

Source: https://habr.com/ru/post/271747/


All Articles