Community discussions

MikroTik App
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Jan 02, 2019 4:53 pm

Greetings and happy new year!

My old scripts and schedulers used to have a lot of hard coded values, were limited to only two gateways, did not utilize functions and were fairly inconsistent.
After nearly two years of putting it off and the acquisition of my new toys, I finally found the time to sit and rewrite the entirety of the scripts that I use on a daily basis.

You can check them out here:
https://github.com/FrostbyteGR/RouterSCRIPTS

Tip: If you plan on using them yourself: make sure you peruse the README section, as it will save you a lot of time and pain, in the long run.
Last edited by Frostbyte on Wed Jan 02, 2019 9:34 pm, edited 1 time in total.
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Jan 02, 2019 4:54 pm

Frequently Asked Questions:
< To be filled when there are questions >
Last edited by Frostbyte on Mon Aug 17, 2020 7:23 pm, edited 5 times in total.
 
paulavalencia
just joined
Posts: 1
Joined: Tue Jun 25, 2019 5:49 pm

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Tue Jun 25, 2019 6:38 pm

Hey, frostbyte

Don't know if I should be posting here since there are no other replies, but it says to do so in the Github, so here I am.

I keep getting errors saying my variables are empty or contain invalid values in every module. I even tested it with the same values you put in the example, only changing the IP address on WANGateways, but can't make it work. I mainly wanna test your failover script in place of another I have, but also liked the DNS one and would like to set it up as well.

Please, if you can help me on this issue. I'm running the 6.44.3 version, if that's relevant. Thank you in advance.
 
tedd77
newbie
Posts: 39
Joined: Sun Dec 18, 2011 5:05 pm

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Oct 02, 2019 3:01 am

I am facing the same challenge of @paulavalencia
Could anyone assist ?
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Oct 02, 2019 6:00 pm

Hello,

Apologies for the late reply, due to my fairly busy daily schedule this one managed to slip under my radar.

This behavior occurs when the configuration file is saved with the UNIX (\n) instead of Windows (\r\n) EOL scheme.
I have rolled out an update (to the parser function) where it properly detects and adjusts the EOL sequence, which fixes the issue.

Thank you for reporting this to me and for your patience.
 
Tecleo
just joined
Posts: 1
Joined: Thu Oct 31, 2019 11:55 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Thu Oct 31, 2019 12:05 pm

Hi, please assist.
Irrespective of the values used I am getting the message:
"Provision][Error]: Gateways configuration for XXXXXXXXX does not have a corresponding default route.
[Provision][Error]: Gateways configuration for XXXXXXXX does not have a corresponding default route.
[Provision][Error]: The gateways configuration is invalid, please check the config file and try again.

****** Script output ********
[admin@MikroTik] > /import RouterSCRIPTS_Installer.rsc
[RouterSCRIPTS][Info]: Install finished. Assuming you have read through the supplied readme file, you may now remove any modules that you have no use for, then run:
$provision auto

Script file loaded and executed successfully
[admin@MikroTik] > $provision auto
[Provision][Info]: Provisioning all available modules.
[Provision][Info]: Provisioning gateways.
[Provision][Error]: Gateways configuration for Vodafone does not have a corresponding default route.
[Provision][Error]: Gateways configuration for Verizon does not have a corresponding default route.
[Provision][Error]: The gateways configuration is invalid, please check the config file and try again.
[Provision][Info]: Provisioning failover.
[Provision][Error]: The failover module depends on the gateway selector module, please provision it and try again.
[Provision][Info]: Provisioning dyndns.
[Provision][Info]: Provisioning resolver.
[Provision][Info]: Provisioning livestream.
[Provision][Error]: Completed with errors.
[admin@MikroTik] >
*******
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Fri Nov 15, 2019 3:32 pm

The provisioning script is complaining because you haven't designated corresponding routes to the connections you have defined in your cfg file.

From the readme file:
WANGatewayPrefix: The comment prefix to use for identifying default gateway routes in the routing table

The value which follows this configuration variable, must be present as a comment (I personally prefer to prefix it but it doesn't matter), on the 0.0.0.0 routes of each gateway defined in the cfg file.
The corresponding routes are being matched/designated via their comment and gateway fields. Without the comment being in place, the script cannot guess which ones to utilize for it's functions.


An example (using values from the readme) cfg file of:
WANGateways=192.168.1.1,192.168.2.1
WANGatewayPrefix=Default route
Would require you to have two routes like these:
/ip route add comment="Default route blah blah" distance=1 gateway=192.168.1.1
/ip route add comment="Default route blah blah" distance=2 gateway=192.168.2.1
Regarding the distance: The allowed range is from 1 to 2. Because you have declared two gateways in the cfg file.
If you had declared three gateways in the cfg file, then it would've been from 1 to 3 and so on..
 
Nic8756
just joined
Posts: 3
Joined: Wed Apr 22, 2020 7:22 pm

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Apr 22, 2020 7:50 pm

Greetings and happy new year!

My old scripts and schedulers used to have a lot of hard coded values, were limited to only two gateways, did not utilize functions and were fairly inconsistent.
After nearly two years of putting it off and the acquisition of my new toys, I finally found the time to sit and rewrite the entirety of the scripts that I use on a daily basis.

You can check them out here:
https://github.com/FrostbyteGR/RouterSCRIPTS

Tip: If you plan on using them yourself: make sure you peruse the README section, as it will save you a lot of time and pain, in the long run.
Hi Frostbyte,
thank you for the script! is very interesting. I'm doing some experiments but I have a problem, I cannot make it work. How can the Failover script check the WAN reachability of the WAN target through the various secondary gateways if the current default route of the Mikrotik router is on the active gateway? I mean, if I try to ping the WAN target (e.g. 8.8.8.8) from the 2nd gateway interface the response is ICMP timeout even if the 2nd gateway has full Internet connectivity. The ICMP request does not go out on the 2nd gatewa interface, so the entire script assumes that the 2nd gw has not WAN connectivity. I have to do something with NAT? Actually I'm only masquerating with src nat to the two gateway interfaces

I have a routerboard with two ISP Internet connections.

Could you help me? thanks a lot,
Best Regards
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Sat Apr 25, 2020 8:18 pm

How can the Failover script check the WAN reachability of the WAN target through the various secondary gateways if the current default route of the Mikrotik router is on the active gateway?

ping <WAN Target> count=<# of Attempts> interface=<WAN Interface>
The bold part in red is what forces the ping to actually go out from the proper WAN Interface, regardless of what the currently active gateway is.

I mean, if I try to ping the WAN target (e.g. 8.8.8.8) from the 2nd gateway interface the response is ICMP timeout even if the 2nd gateway has full Internet connectivity. The ICMP request does not go out on the 2nd gatewa interface, so the entire script assumes that the 2nd gw has not WAN connectivity. I have to do something with NAT? Actually I'm only masquerating with src nat to the two gateway interfaces

I have a routerboard with two ISP Internet connections.

In order to better understand where the problem lies, some additional information is required:
  • The contents of the *.cfg file you're trying to provision the device with.
  • The output of the following commands (in terminal) from the device:
    1. /ip address print detail
    2. /ip route print detail
    3. $provision auto
    4. $failover check
 
Nic8756
just joined
Posts: 3
Joined: Wed Apr 22, 2020 7:22 pm

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Apr 29, 2020 5:29 pm

How can the Failover script check the WAN reachability of the WAN target through the various secondary gateways if the current default route of the Mikrotik router is on the active gateway?

ping <WAN Target> count=<# of Attempts> interface=<WAN Interface>
The bold part in red is what forces the ping to actually go out from the proper WAN Interface, regardless of what the currently active gateway is.

I mean, if I try to ping the WAN target (e.g. 8.8.8.8) from the 2nd gateway interface the response is ICMP timeout even if the 2nd gateway has full Internet connectivity. The ICMP request does not go out on the 2nd gatewa interface, so the entire script assumes that the 2nd gw has not WAN connectivity. I have to do something with NAT? Actually I'm only masquerating with src nat to the two gateway interfaces

I have a routerboard with two ISP Internet connections.

In order to better understand where the problem lies, some additional information is required:
  • The contents of the *.cfg file you're trying to provision the device with.
  • The output of the following commands (in terminal) from the device:
    1. /ip address print detail
    2. /ip route print detail
    3. $provision auto
    4. $failover check
Dear Frostbyte, thank you for the reply. I'm not completely sure about function of the "interface=" option of the ping command. According to me this command option is used to recursively learn the IP address associated to the specified interface and than create a ping packet with source IP address the IP address of that interface. However this not means that the ping will exit from that interface, because if the destination address of the ping is not in a local network the router still looks at the actual default gateway to decide to which dest address send the packet and accordingly on which interface. But I'm not an expert about Mikrotik world, so I can mistake.

All of this to say that in my small lab your script seems not able to ping the target host from the 2nd path to verify the connectivity, so it can't switch to the 2nd ISP when the first fails. Could you please help me?

Thanks a lot!

I attach the output of your commands, also the ping command.

[admin@MikroTik] > /ip address print detail
Flags: X - disabled, I - invalid, D - dynamic
0 ;;; defconf
address=192.168.80.1/24 network=192.168.80.0 interface=bridge actual-interface=bridge

1 address=192.168.10.71/24 network=192.168.10.0 interface=ether1 actual-interface=ether1

2 address=192.168.88.2/24 network=192.168.88.0 interface=ether5 actual-interface=ether5


[admin@MikroTik] > /ip route print detail
Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit
0 A S ;;; Default route
dst-address=0.0.0.0/0 gateway=192.168.10.1 gateway-status=192.168.10.1 reachable via ether1 distance=1 scope=30 target-scope=10

1 S ;;; Default route
dst-address=0.0.0.0/0 gateway=192.168.88.1 gateway-status=192.168.88.1 reachable via ether5 check-gateway=ping distance=4 scope=30 target-scope=10

2 ADC dst-address=192.168.10.0/24 pref-src=192.168.10.71 gateway=ether1 gateway-status=ether1 reachable distance=0 scope=10

3 ADC dst-address=192.168.80.0/24 pref-src=192.168.80.1 gateway=bridge gateway-status=bridge reachable distance=0 scope=10

4 ADC dst-address=192.168.88.0/24 pref-src=192.168.88.2 gateway=ether5 gateway-status=ether5 reachable distance=0 scope=10


[admin@MikroTik] > $provision auto
[Provision][Info]: Provisioning all available modules.
[Provision][Info]: Provisioning gateways.
[Provision][Info]: Provisioning failover.


[admin@MikroTik] > $failover check
SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 56 57 12ms
sent=1 received=1 packet-loss=0% min-rtt=12ms avg-rtt=12ms max-rtt=12ms

SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 timeout
sent=1 received=0 packet-loss=100%


[admin@MikroTik] > ping 8.8.8.8 interface=ether5
SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 timeout
2 8.8.8.8 timeout
3 8.8.8.8 timeout
4 8.8.8.8 timeout
5 8.8.8.8 timeout
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Apr 29, 2020 8:34 pm

Dear Frostbyte, thank you for the reply. I'm not completely sure about function of the "interface=" option of the ping command. According to me this command option is used to recursively learn the IP address associated to the specified interface and than create a ping packet with source IP address the IP address of that interface. However this not means that the ping will exit from that interface, because if the destination address of the ping is not in a local network the router still looks at the actual default gateway to decide to which dest address send the packet and accordingly on which interface. But I'm not an expert about Mikrotik world, so I can mistake.

According to the documentation, the "interface" parameter applies a "Which interface to use" restriction to the command, so the ping is forced to exit from that interface. Since there is a route (0.0.0.0/0) to your target address (8.8.8.8 ) where it's gateway (192.168.88.1) belongs in the same subnet (192.168.88.0/24) that your interface (ether5) participates in, the ping should go through, otherwise it should fail. Once we get your setup working, you will be able to verify this yourself as well (by actually bringing down connections).

All of this to say that in my small lab your script seems not able to ping the target host from the 2nd path to verify the connectivity, so it can't switch to the 2nd ISP when the first fails. Could you please help me?

Thanks a lot!

I attach the output of your commands, also the ping command.

[admin@MikroTik] > /ip address print detail
Flags: X - disabled, I - invalid, D - dynamic
0 ;;; defconf
address=192.168.80.1/24 network=192.168.80.0 interface=bridge actual-interface=bridge

1 address=192.168.10.71/24 network=192.168.10.0 interface=ether1 actual-interface=ether1

2 address=192.168.88.2/24 network=192.168.88.0 interface=ether5 actual-interface=ether5


[admin@MikroTik] > /ip route print detail
Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit
0 A S ;;; Default route
dst-address=0.0.0.0/0 gateway=192.168.10.1 gateway-status=192.168.10.1 reachable via ether1 distance=1 scope=30 target-scope=10

1 S ;;; Default route
dst-address=0.0.0.0/0 gateway=192.168.88.1 gateway-status=192.168.88.1 reachable via ether5 check-gateway=ping distance=4 scope=30 target-scope=10

2 ADC dst-address=192.168.10.0/24 pref-src=192.168.10.71 gateway=ether1 gateway-status=ether1 reachable distance=0 scope=10

3 ADC dst-address=192.168.80.0/24 pref-src=192.168.80.1 gateway=bridge gateway-status=bridge reachable distance=0 scope=10

4 ADC dst-address=192.168.88.0/24 pref-src=192.168.88.2 gateway=ether5 gateway-status=ether5 reachable distance=0 scope=10


[admin@MikroTik] > $provision auto
[Provision][Info]: Provisioning all available modules.
[Provision][Info]: Provisioning gateways.
[Provision][Info]: Provisioning failover.


[admin@MikroTik] > $failover check
SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 56 57 12ms
sent=1 received=1 packet-loss=0% min-rtt=12ms avg-rtt=12ms max-rtt=12ms

SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 timeout
sent=1 received=0 packet-loss=100%


[admin@MikroTik] > ping 8.8.8.8 interface=ether5
SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 timeout
2 8.8.8.8 timeout
3 8.8.8.8 timeout
4 8.8.8.8 timeout
5 8.8.8.8 timeout

This is a bit weird. On the surface there doesn't seem to be anything wrong with what the scripts are expecting to find. (That means that, concerning the scripts at least, the configuration should be valid).

However, I find two things which are alarming:
1) The provision command did not end with a success/failure message. (If you didn't crop it on purpose, something may have went wrong, which we will need to investigate)
2) Assuming your interface, address, route and firewall (since you mentioned you're NATing) configurations are proper; you should be able to ping with that last command. Something is definitely going on with the configuration of the device in general.

As a small experiment, could you try disabling the route #0 (dst-address=0.0.0.0/0 gateway=192.168.10.1) and try a ping to 8.8.8.8 without the interface parameter? Does it succeed?
 
Nic8756
just joined
Posts: 3
Joined: Wed Apr 22, 2020 7:22 pm

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Sat May 02, 2020 11:16 pm

Dear Frostbyte, thank you for the reply. I'm not completely sure about function of the "interface=" option of the ping command. According to me this command option is used to recursively learn the IP address associated to the specified interface and than create a ping packet with source IP address the IP address of that interface. However this not means that the ping will exit from that interface, because if the destination address of the ping is not in a local network the router still looks at the actual default gateway to decide to which dest address send the packet and accordingly on which interface. But I'm not an expert about Mikrotik world, so I can mistake.

According to the documentation, the "interface" parameter applies a "Which interface to use" restriction to the command, so the ping is forced to exit from that interface. Since there is a route (0.0.0.0/0) to your target address (8.8.8.8 ) where it's gateway (192.168.88.1) belongs in the same subnet (192.168.88.0/24) that your interface (ether5) participates in, the ping should go through, otherwise it should fail. Once we get your setup working, you will be able to verify this yourself as well (by actually bringing down connections).

All of this to say that in my small lab your script seems not able to ping the target host from the 2nd path to verify the connectivity, so it can't switch to the 2nd ISP when the first fails. Could you please help me?

Thanks a lot!

I attach the output of your commands, also the ping command.

[admin@MikroTik] > /ip address print detail
Flags: X - disabled, I - invalid, D - dynamic
0 ;;; defconf
address=192.168.80.1/24 network=192.168.80.0 interface=bridge actual-interface=bridge

1 address=192.168.10.71/24 network=192.168.10.0 interface=ether1 actual-interface=ether1

2 address=192.168.88.2/24 network=192.168.88.0 interface=ether5 actual-interface=ether5


[admin@MikroTik] > /ip route print detail
Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit
0 A S ;;; Default route
dst-address=0.0.0.0/0 gateway=192.168.10.1 gateway-status=192.168.10.1 reachable via ether1 distance=1 scope=30 target-scope=10

1 S ;;; Default route
dst-address=0.0.0.0/0 gateway=192.168.88.1 gateway-status=192.168.88.1 reachable via ether5 check-gateway=ping distance=4 scope=30 target-scope=10

2 ADC dst-address=192.168.10.0/24 pref-src=192.168.10.71 gateway=ether1 gateway-status=ether1 reachable distance=0 scope=10

3 ADC dst-address=192.168.80.0/24 pref-src=192.168.80.1 gateway=bridge gateway-status=bridge reachable distance=0 scope=10

4 ADC dst-address=192.168.88.0/24 pref-src=192.168.88.2 gateway=ether5 gateway-status=ether5 reachable distance=0 scope=10


[admin@MikroTik] > $provision auto
[Provision][Info]: Provisioning all available modules.
[Provision][Info]: Provisioning gateways.
[Provision][Info]: Provisioning failover.


[admin@MikroTik] > $failover check
SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 56 57 12ms
sent=1 received=1 packet-loss=0% min-rtt=12ms avg-rtt=12ms max-rtt=12ms

SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 timeout
sent=1 received=0 packet-loss=100%


[admin@MikroTik] > ping 8.8.8.8 interface=ether5
SEQ HOST SIZE TTL TIME STATUS
0 8.8.8.8 timeout
2 8.8.8.8 timeout
3 8.8.8.8 timeout
4 8.8.8.8 timeout
5 8.8.8.8 timeout

This is a bit weird. On the surface there doesn't seem to be anything wrong with what the scripts are expecting to find. (That means that, concerning the scripts at least, the configuration should be valid).

However, I find two things which are alarming:
1) The provision command did not end with a success/failure message. (If you didn't crop it on purpose, something may have went wrong, which we will need to investigate)
2) Assuming your interface, address, route and firewall (since you mentioned you're NATing) configurations are proper; you should be able to ping with that last command. Something is definitely going on with the configuration of the device in general.

As a small experiment, could you try disabling the route #0 (dst-address=0.0.0.0/0 gateway=192.168.10.1) and try a ping to 8.8.8.8 without the interface parameter? Does it succeed?
Hi Frostbyte, thank you again for your help and explanations. As soon as possible I'll do some tests and I let you know.
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Fri May 15, 2020 10:12 am

Hi Frostbyte, thank you again for your help and explanations. As soon as possible I'll do some tests and I let you know.

It's been nearly two weeks since your last reply, are you still in need of assistance?
 
luddite
just joined
Posts: 22
Joined: Fri Apr 06, 2012 12:09 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Jun 03, 2020 11:59 am

Just want to say thanks for this amazing work, there is a lot in it, and a lot in your replies to people, good effort helping the community.

Hope to use and explore these scripts.
 
norenberg
newbie
Posts: 49
Joined: Mon Nov 23, 2009 2:26 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Mon Aug 17, 2020 1:03 pm

Thanks for putting this together!
I'm trying to make it work, however my problem is that I have just one physical interface where your script seems to rely on two interfaces to work, by using ping with source interface, am I right?

Is there a way I could make this work only with one interface?

Thanks!
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Mon Aug 17, 2020 7:17 pm

Thanks for putting this together!
I'm trying to make it work, however my problem is that I have just one physical interface where your script seems to rely on two interfaces to work, by using ping with source interface, am I right?

Is there a way I could make this work only with one interface?

Thanks!

Answer #1:

If you have only one internet gateway/connection, then no.
Unless you're planning to perform a "proof of concept" test, which can be achieved by utilizing/configuring multiple MikroTik devices/VMs.

Technically you need at least two different internet gateways/connections so you can have a proper failover. There is no point to it otherwise, I'm sure you can understand why.
Furthermore the provision algorithm explicitly disallows for duplicate values in WANNames and WANGateways, so you can't bypass it by inputting the same information twice in the configuration file.

Answer #2:

In case you have two or more different internet gateways/connections, but accessible through the same interface or on the same subnet, it will still be required of you to split them into different subnets and interfaces (bridges and vlans are allowed too). This limitation exists because there's no other clean way (without confusing the scripts, that is) to differentiate between the available gateways when checking for each one's internet availability.

As per the documentation available on github:
  • You can only have one gateway per subnet. Multiple gateways over the same bridge/master-interface are not supported

Disclaimer: If you're after the Balancing functionality of the gateway module, it is assumed (besides having two different internet gateways/connections) that you know your way around configuring PCC Load Balancing, to achieve a desirable result. Since all that option provides for, is an on/off toggle (sensitive to internet outages) for mangle rules with a matching comment prefix (as defined in the configuration file).
Last edited by Frostbyte on Mon Aug 17, 2020 8:07 pm, edited 1 time in total.
 
norenberg
newbie
Posts: 49
Joined: Mon Nov 23, 2009 2:26 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Tue Aug 18, 2020 2:37 am

Thanks for the reply.
Sorry I forgot to mention, yes I have 2 different gateways in two different subnets on the same interface.
I have tried to fiddle a litte with VLAN's but it's been a while, I couldn't make it work as I'm doing
GW1
--------------untagged,no vlan-------------RouterBoardETH1
GW2

On that scenario I have to get the RB to tag the ingress packets and untag back to a couple of bridges inside that same eth1... or something like that. I'm a bit rusty with VLANs and I'm thinking it might be overkill for my situation so I'm considering just implementing recursive without script instead, although I really like the toggle functionalities of your script.
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Tue Aug 18, 2020 3:46 am

Thanks for the reply.
Sorry I forgot to mention, yes I have 2 different gateways in two different subnets on the same interface.
I have tried to fiddle a litte with VLAN's but it's been a while, I couldn't make it work as I'm doing
GW1
--------------untagged,no vlan-------------RouterBoardETH1
GW2

On that scenario I have to get the RB to tag the ingress packets and untag back to a couple of bridges inside that same eth1... or something like that. I'm a bit rusty with VLANs and I'm thinking it might be overkill for my situation so I'm considering just implementing recursive without script instead, although I really like the toggle functionalities of your script.

In that situation and if utilizing the scripts is the desirable goal, there are two options to consider:
  1. Connect GW1 and GW2 onto two different physical ports of the RouterBoard device.
  2. Pass GW1 and GW2 as tagged VLANs through a single physical port of the RouterBoard device.
At this point I'm completely unaware of your exact physical setup and as to why you're not just connecting CPE1 to one physical port of the RouterBoard device and CPE2 to another.
That would appear to be the simplest method both physical-wise and configuration-wise (just remember: different subnets, different interfaces).

Now, in case there is some complication or particularity that I am missing (and I probably might be), let's assume the diagram you drew above.
I would not consider option 2 to be an overkill (since you would have one cable reach the RouterBoard device and one of it's physical ports occupied, instead of two). Though you will have to ensure that the combined bandwidth of GW1 and GW2 does not exceed the speed specification of the RouterBoard device port that you're plugging into (to avoid a potential bottleneck).
If you choose to bring in GW1 and GW2 as tagged VLANs over the same physical port of your RouterBoard device, things are really simple from there and onward:
You create two VLANs, each with it's respective VLAN ID (that you tagged it with on the other side) and with the physical port that you're passing them through as the Interface (RouterBoardETH1 in your case). Then you go into IP > Address and you assign a corresponding address and subnet mask to each one of the VLANs you created (remember, different subnets). Lastly, you go into IP > Route and you create your two routes (make sure to peruse the github documentation as the distances and comment contents on this step are important).
 
norenberg
newbie
Posts: 49
Joined: Mon Nov 23, 2009 2:26 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Aug 19, 2020 3:19 pm

Thanks for your reply
At this point I'm completely unaware of your exact physical setup and as to why you're not just connecting CPE1 to one physical port of the RouterBoard device and CPE2 to another.
That would appear to be the simplest method both physical-wise and configuration-wise (just remember: different subnets, different interfaces).
These are two wan gateways that are physically on the other side of the city. The link where both are accessible via Layer3 arrives through P2P on a single cable to eth1. It doesn't make sense to re-split into two cables. This is a bit of a temporary scenario for until I have the ability to have 2 physical links coming to the RB.
You create two VLANs, each with it's respective VLAN ID (that you tagged it with on the other side) and with the physical port that you're passing them through as the Interface (RouterBoardETH1 in your case). Then you go into IP > Address and you assign a corresponding address and subnet mask to each one of the VLANs you created (remember, different subnets). Lastly, you go into IP > Route and you create your two routes (make sure to peruse the github documentation as the distances and comment contents on this step are important).
I don't have means to tag on the other side at the moment but will soon. Since it's a 3011UIAS it should be ok to cope with traffic. I will try that again, for the time being the recursive failover is working.
Thanks!
 
CraigZA
just joined
Posts: 4
Joined: Wed Aug 04, 2010 11:52 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Mon Sep 14, 2020 3:18 pm

I'm having the same issue as a previous poster.

Two WAN interfaces, the $failover check command times out when attempting to ping the external IP:
[admin@xxxxx] > $failover status
[Failover][Info]: Failover status: Active.
[Failover][Info]: ether1: Online        (up for at least 00:01:15)
[Failover][Info]: ether2: Offline       (down for at least 00:01:15)
[admin@xxxxx] > $failover status
[Failover][Info]: Failover status: Active.
[Failover][Info]: ether1: Online        (up for at least 00:01:15)
[Failover][Info]: ether2: Offline       (down for at least 00:01:15)
[admin@xxxxx] > $failover check 
  SEQ HOST                                     SIZE TTL TIME  STATUS             
    0 9.9.9.9                                    56  56 22ms 
    sent=1 received=1 packet-loss=0% min-rtt=22ms avg-rtt=22ms max-rtt=22ms 

  SEQ HOST                                     SIZE TTL TIME  STATUS             
    0 9.9.9.9                                                 timeout            
    sent=1 received=0 packet-loss=100%
I have two default routes, distance of 1 & 2 respectively. NAT both out to the internet. Provision completed fine with no errors (I removed DynDNS, ResolveFQDN & Livestream modules).

Any hints on how to resolve? Thanks!
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Tue Sep 15, 2020 3:01 pm

I'm having the same issue as a previous poster.

Two WAN interfaces, the $failover check command times out when attempting to ping the external IP:
[admin@xxxxx] > $failover status
[Failover][Info]: Failover status: Active.
[Failover][Info]: ether1: Online        (up for at least 00:01:15)
[Failover][Info]: ether2: Offline       (down for at least 00:01:15)
[admin@xxxxx] > $failover status
[Failover][Info]: Failover status: Active.
[Failover][Info]: ether1: Online        (up for at least 00:01:15)
[Failover][Info]: ether2: Offline       (down for at least 00:01:15)
[admin@xxxxx] > $failover check 
  SEQ HOST                                     SIZE TTL TIME  STATUS             
    0 9.9.9.9                                    56  56 22ms 
    sent=1 received=1 packet-loss=0% min-rtt=22ms avg-rtt=22ms max-rtt=22ms 

  SEQ HOST                                     SIZE TTL TIME  STATUS             
    0 9.9.9.9                                                 timeout            
    sent=1 received=0 packet-loss=100%
I have two default routes, distance of 1 & 2 respectively. NAT both out to the internet. Provision completed fine with no errors (I removed DynDNS, ResolveFQDN & Livestream modules).

Any hints on how to resolve? Thanks!

I had noticed an issue (somewhere between RouterOS 6.46.6 and 6.47.1 I can't remember exactly) where a gateway would refuse to ping the target, after a reboot.
This however is not an issue that can be remedied by or stemming from the scripts.
I haven't been able to reproduce it, so far, on RouterOS 6.47.3

Is your second gateway able to reach 9.9.9.9 without the scripts? If yes:
  1. Try disabling the failover module with "$failover toggle"
  2. Switch to the other gateway with "$gateway switch ether2"
  3. Attempt to "ping 9.9.9.9", does it still timeout?
  4. If it still times out, try enabling the failover module again with "$failover toggle", does it solve the issue?
Additionally, which version and firmware of RouterOS you're using?
 
MikeRoTik
just joined
Posts: 21
Joined: Wed Jul 08, 2020 4:47 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Sep 23, 2020 6:09 am

Would it possible to modify these scripts for use when the WAN IP addresses and gateway addresses are assigned via DHCP?
 
MikeRoTik
just joined
Posts: 21
Joined: Wed Jul 08, 2020 4:47 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Fri Sep 25, 2020 6:39 am

I am trying to make some changes to the scripts. How are you building the RouterSCRIPTS_Installer.rsc file from the source files in the src directory?
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Fri Sep 25, 2020 7:22 pm

Would it possible to modify these scripts for use when the WAN IP addresses and gateway addresses are assigned via DHCP?

It's been quite a long time since I made them, but if memory serves me correct, you need to do modifications in the following order:
  1. The provision function in the mod-provision script
  2. The gateway function in the mod-gateway script
  3. The failover function in the mod-failover script

While the failover function/provisioning doesn't possess strict verification against the validity of the supplied gateway IP addresses (meaning that you could get away by supplying a gateway IP address within the subnet or range of addresses that the DHCP provides), the gateway function/provisioning does and the failover module is dependent on it for obvious reasons.

The scripts currently support only statically configured gateways (with fixed/static IP addresses and static routes), as it would be difficult to ensure smooth operation for a less deterministric or even completely non-deterministric dynamic counterpart. (would require extra, more complicated code and a lot more checks and validations; and scripts/schedulers have a character limit as well, mind you)
Furthermore, as per by the restrictions section of the README (just to keep it in mind as well):
You can only have one gateway per subnet. Multiple gateways over the same bridge/master-interface are not supported
However, since I'm providing this as a completely open source project, you're free to review the code and modify it to your liking/needs.
Just make sure that if you wish to redistribute it afterwards, to:
  1. Give credit to the original source
  2. Pick a name that will not cause confusion between it and the original material
  3. State any added/altered/removed functionality in it's respective README, so it can be discerned

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

I am trying to make some changes to the scripts. How are you building the RouterSCRIPTS_Installer.rsc file from the source files in the src directory?

Again it's been a long time since I needed to make a significant modification to them, but from what I can remember, that's how I worked on them:
  1. I would create the code in a text editor of choice (Notepad++ for example)
  2. Then I would create the corresponding script/scheduler (with the needed permissions) on the device, copy the code over and test them
  3. When the code was good enough across all modules, I would copy the contents of each script/scheduler to the RouterSCRIPTS_Installer.rsc file

Specifically now for the RouterSCRIPTS_Installer.rsc file, it's only just a wrapper whose purpose is to import said scripts/schedulers/code in an easy fashion.
It's composed by:
  1. A notify function on the top (so it can give you feedback when it's finished executing)
  2. Commands that simply create each script with the necessary/corresponding permissions and code contents
  3. A command that bootstraps the whole thing by executing the provision script/module and another command that removes/cleans-up the installer file from the device

If you're wondering why this file looks way different than the ones residing in the "src" folder, the code following the "source=" parameter on each of the commands has been modified slightly (so it can be human-readable from the device via console/ssh/winbox/etc as well):
  1. With \r and \n sequences instead of newline characters, because they're otherwise ignored when contained within commands
  2. With \t sequences instead of <TAB> characters because, again, they're otherwise ignored when contained within commands
These are purely cosmetic and have no effect on the installation or operation of the scripts/schedulers/etc.

Lastly, have in mind that the scripts make heavy use of local and global functions. The only thing that the scripts do is to actually initialize such functions on the device's memory, and the schedulers simply gain access to them and periodically execute them. All of the code that actually does things, exists within those functions.

I hope that you find this information useful.


----------------------------------------------------------------------------------------------------------------------------------------------------------------------


@CraigZA: It's been about 10 days since your last reply, do you still require assistance?
 
MikeRoTik
just joined
Posts: 21
Joined: Wed Jul 08, 2020 4:47 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Sat Sep 26, 2020 1:49 am

I hope that you find this information useful.
Extremely! Thanks for sharing! Quite a feat given that there is no IDE to correct syntax, debug, step, watch, breakpont, inspect, etc..... You did it old school!

One thing that I'm still trying to figure out is how the variables which are setup in memory from the config files stay persistent over a router reboot.
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Feb 24, 2021 5:12 pm

Extremely! Thanks for sharing! Quite a feat given that there is no IDE to correct syntax, debug, step, watch, breakpont, inspect, etc..... You did it old school!

My pleasure, glad that it was of assistance. I'd say it was more like a matter of time, perseverance, trial and error. :)

One thing that I'm still trying to figure out is how the variables which are setup in memory from the config files stay persistent over a router reboot.

If it requires
  • Reboot persistence only (static values): through the configuration-parsing segment of the provision module (which runs on-boot).
  • Reboot persistence and run-time manipulation: through reading/writing to the device's configuration (i.e. WAN availability which is determined by route distance).
  • Run-time manipulation only: through reading/writing to the variables stored in memory (i.e. Failover counters).

Apologies for the late response, as I've had lots of (work-related) things to deal with, during the course of these months.
 
txfz
Frequent Visitor
Frequent Visitor
Posts: 57
Joined: Tue Mar 10, 2020 9:02 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Mon Aug 30, 2021 1:13 pm

Hi,

I've just found this after unsuccessfully having tried a number of other solutions for failover functionality. I can't get it to work properly. Namely, it won't switch back to the primary connection once that comes back online. I'm not able to ping anything (timeout) using the interface of the primary connection, not even its gateway, but the interface is up. Manually taking the secondary connection down gets it working, however. The script seems to report that whichever isn't the active connection is always down, and the script logs it coming up when the active connection goes down. Both are never reported up at the same time.
 
txfz
Frequent Visitor
Frequent Visitor
Posts: 57
Joined: Tue Mar 10, 2020 9:02 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Sep 01, 2021 4:32 pm

Seems to work if you change the pings to use alternative routing tables rather than interfaces, as per this post .
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Tue Sep 14, 2021 2:48 pm

Please ensure that you have read and understood the instructions provided in the README section properly.
To me it seems that you may have misconfigured something and things are not properly detected.
I'm still using those scripts to date and I've never stumbled across such behavior, nor did any of their users thus far.

Which version of RouterOS are you using?
 
txfz
Frequent Visitor
Frequent Visitor
Posts: 57
Joined: Tue Mar 10, 2020 9:02 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Sep 15, 2021 11:09 am

I have read the instructions. I'm not ruling out my missing something. All I need to do before provisioning is filling out the configuration file, and make sure I have all gateway routes configured with the appropriate comment, right?

I'm testing this on 6.47.10.
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Sep 15, 2021 6:50 pm

Could you post the contents of your configuration file, alongside your routes (/ip route print) please?
 
txfz
Frequent Visitor
Frequent Visitor
Posts: 57
Joined: Tue Mar 10, 2020 9:02 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Mon Sep 20, 2021 5:01 pm

[Gateways]
WANNames=ISP1,ISP2
WANGateways=10.1.0.1,10.2.0.1
WANGatewayPrefix=failover-route
BalancingRulePrefix=test

[Failover]
FailoverTarget=8.8.4.4
FailoverThreshold=3
FailoverInterval=5

[DynDNS]
DDNSService=
DDNSInterval=
DDNSUsername=
DDNSPassword=
DDNSHostname=

[Livestream]
LVStreamList=
LVStreamFQDN=
LVStreamRulePrefix=

 #      DST-ADDRESS        PREF-SRC        GATEWAY            DISTANCE
 0 A S  ;;; failover-route 1
        0.0.0.0/0                          10.1.0.1                  1
 1   S  ;;; failover-route 2
        0.0.0.0/0                          10.2.0.1                  4
 2 ADC  10.1.0.0/30        10.1.0.2        ether1                    0
 3 ADC  10.2.0.0/30        10.2.0.2        ether2                    0
 4 ADC  172.30.0.0/24      172.30.0.1      bridge                    0
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Tue Sep 21, 2021 6:36 pm

Both the configuration and the routes appear to be proper (with regards to what the scripts expect).
You could also omit the DynDNS and Livestream sections from your configuration (rather than keeping them empty), if you have removed the corresponding scripts too.

The only other thing I can imagine being the culprit here would be the RouterOS version itself; as strange behaviors with the ping command (and the interface parameter) have been observed in the past. Could you try on 6.48 at some point and report back your findings? Thanks.
 
txfz
Frequent Visitor
Frequent Visitor
Posts: 57
Joined: Tue Mar 10, 2020 9:02 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Wed Sep 22, 2021 9:46 am

No improvement in 6.48.4, I'm afraid.

When I asked about this here, the answer was clear on this not being possible, so I don't understand where I'm going wrong?

Here is my entire configuration:
# sep/22/2021 08:45:43 by RouterOS 6.48.4
# software id = FWIF-LI4F
#
# model = RB3011UiAS
# serial number = E14B0E626A15
/interface bridge add name=bridge
/interface list add name=wan
/interface wireless security-profiles set [ find default=yes ] supplicant-identity=MikroTik
/ip pool add name=pool1 ranges=172.30.0.10/31
/ip dhcp-server add address-pool=pool1 disabled=no interface=bridge name=server1
/interface bridge port add bridge=bridge interface=ether3
/interface bridge port add bridge=bridge interface=ether10
/ip neighbor discovery-settings set discover-interface-list=!dynamic
/interface list member add interface=ether1 list=wan
/interface list member add interface=ether2 list=wan
/ip address add address=10.1.0.2/30 interface=ether1 network=10.1.0.0
/ip address add address=10.2.0.2/30 interface=ether2 network=10.2.0.0
/ip address add address=172.30.0.1/24 interface=bridge network=172.30.0.0
/ip dhcp-server network add address=172.30.0.0/24 gateway=172.30.0.1
/ip dns set allow-remote-requests=yes servers=9.9.9.9
/ip firewall filter add action=passthrough chain=forward out-interface=ether1
/ip firewall filter add action=passthrough chain=forward out-interface=ether2
/ip firewall nat add action=masquerade chain=srcnat out-interface-list=wan
/ip route add comment="failover-route 1" distance=1 gateway=10.1.0.1
/ip route add comment="failover-route 2" distance=4 gateway=10.2.0.1
/system clock set time-zone-name=Europe/Stockholm
/system identity set name=customer
/system routerboard settings set auto-upgrade=yes
/system scheduler add name=init-provision on-event="delay 10;/system script run mod-provision;global provision;\$provision auto" policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-time=startup
/system scheduler add interval=5s name=cron-failover on-event="global failover;\$failover check" policy=read,write,policy,test start-date=sep/22/2021 start-time=08:32:29
/system script add dont-require-permissions=no name=mod-provision owner=admin policy=read,write source="#Provision module script\r\
    \n#Script permissions: read, write\r\
    \n#Script dependencies: none\r\
    \n\r\
    \n#Declare scripts version\r\
    \nglobal scriptsVersion \"1.0.1\"\r\
    \n\r\
    \n#If the scheduler entry for the initialization of the provision module doesn't exist\r\
    \nif ([/system scheduler find name=init-provision]=\"\") do={\r\
    \n\t#Add it\r\
    \n\t/system scheduler add name=init-provision start-time=startup on-event={delay 10;/system script run mod-provision;global provision;\$provision auto}\r\
    \n}\r\
    \n\r\
    \n#Provision function\r\
    \nglobal provision do={\r\
    \n\t#Configuration parser subfunction\r\
    \n\t#Inputs: <Config attribute>\r\
    \n\t#Output: <Config value>\r\
    \n\tlocal parseConfig do={\r\
    \n\t\t#Pull the configuration file contents\r\
    \n\t\tlocal cfgFileContent [/file get value-name=contents [find name~([/system identity get value-name=name].\".cfg\")]]\r\
    \n\r\
    \n\t\t#Detect and adjust the configuration EOL sequence\r\
    \n\t\tlocal cfgEOLSequence \"\\n\"\r\
    \n\t\tif ([typeof [find \$cfgFileContent \"\\r\\n\" 0]]=\"num\") do={set cfgEOLSequence \"\\r\\n\"}\r\
    \n\r\
    \n\t\t#Declare helper pointers\r\
    \n\t\tlocal cfgLineStart 0\r\
    \n\t\tlocal cfgLineEnd 0\r\
    \n\r\
    \n\t\t#Iterate through the configuration file contents\r\
    \n\t\tdo {\r\
    \n\t\t\t#Find out where the line ends\r\
    \n\t\t\tset cfgLineEnd [find \$cfgFileContent \$cfgEOLSequence \$cfgLineStart]\r\
    \n\r\
    \n\t\t\t#If an EOL sequence cannot be found\r\
    \n\t\t\tif ([typeof \$cfgLineEnd]!=\"num\") do={\r\
    \n\t\t\t\t#Adjust the line end to the end of contents\r\
    \n\t\t\t\tset cfgLineEnd [len \$cfgFileContent]\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Fetch the line\r\
    \n\t\t\tlocal cfgLine [pick \$cfgFileContent \$cfgLineStart \$cfgLineEnd]\r\
    \n\r\
    \n\t\t\t#Pull the configuration attribute of the line\r\
    \n\t\t\tlocal cfgAttribute [pick \$cfgLine 0 [find \$cfgLine \"=\" 0]]\r\
    \n\r\
    \n\t\t\t#If the attribute matches the request\r\
    \n\t\t\tif (\$cfgAttribute=\$1) do={\r\
    \n\t\t\t\t#Initialize the configuration value\r\
    \n\t\t\t\tlocal cfgValue \"\"\r\
    \n\r\
    \n\t\t\t\t#If the value is populated\r\
    \n\t\t\t\tif ([find \$cfgLine \"=\" 0]!=[len \$cfgLine]) do={\r\
    \n\t\t\t\t\t#Set the configuration value\r\
    \n\t\t\t\t\tset cfgValue [pick \$cfgLine ([find \$cfgLine \"=\" 0]+1) [len \$cfgLine]]\r\
    \n\r\
    \n\t\t\t\t\t#If the value is a boolean, properly convert it\r\
    \n\t\t\t\t\tif (\$cfgValue=\"true\") do={set cfgValue true}\r\
    \n\t\t\t\t\tif (\$cfgValue=\"false\") do={set cfgValue false}\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#Return the requested configuration value\r\
    \n\t\t\t\treturn \$cfgValue\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Advance to the next line\r\
    \n\t\t\tset cfgLineStart (\$cfgLineEnd+[len \$cfgEOLSequence])\r\
    \n\t\t} while (\$cfgLineStart<[len \$cfgFileContent])\r\
    \n\r\
    \n\t\t#If this part has been reached, it means that the requested attribute was not found\r\
    \n\t\t#Exit\r\
    \n\t\treturn \"\"\r\
    \n\t}\r\
    \n\r\
    \n\t#Non-empty variable validator subfunction\r\
    \n\t#Inputs: <Array of variable names> <Array of variable values>\r\
    \n\t#Output: <Exit code>\r\
    \n\tlocal validateVars do={\r\
    \n\t\t#Error handling functions\r\
    \n\t\tlocal error do={local msg \"[Provision][Error]: \$1\";put \$msg;log error \$msg;return -1}\r\
    \n\t\tlocal execStatus 0\r\
    \n\r\
    \n\t\t#Iterate through the requested variables\r\
    \n\t\tforeach varIndex,cfgVar in=\$1 do={\r\
    \n\t\t\t#If any of them are not specified or invalid\r\
    \n\t\t\tif ((\$2->\$varIndex)=\"\") do={\r\
    \n\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\tset execStatus [\$error (\"The configuration variable \$cfgVar cannot be left empty or contain an invalid value.\")]\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#Error handling functions\r\
    \n\tlocal notify do={local msg \"[Provision][Info]: \$1\";put \$msg;log info \$msg}\r\
    \n\tlocal error do={local msg \"[Provision][Error]: \$1\";put \$msg;log error \$msg;return -1}\r\
    \n\tlocal execStatus 0\r\
    \n\r\
    \n\t#If the configuration file does not exist\r\
    \n\tif ([/file find name~([/system identity get value-name=name].\".cfg\")]=\"\") do={\r\
    \n\t\t#Throw error and exit\r\
    \n\t\tset execStatus [\$error (\"Unable to locate the config file, please upload it to the router and try again.\")]\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"auto\"\r\
    \n\tif (\$1=\"auto\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Notify\r\
    \n\t\t\$notify (\"Provisioning all available modules.\")\r\
    \n\r\
    \n\t\t#Gain function access\r\
    \n\t\tglobal provision\r\
    \n\r\
    \n\t\t#Iterate through the module inventory\r\
    \n\t\tlocal availCommands \"\"\r\
    \n\t\tlocal systemScripts {\"mod-gateway\";\"mod-failover\";\"mod-dyndns\";\"mod-resolvefqdn\";\"mod-livestream\"}\r\
    \n\t\tforeach cmdIndex,moduleName in={\"gateways\";\"failover\";\"dyndns\";\"resolver\";\"livestream\"} do={\r\
    \n\t\t\t#If any of the corresponding scripts are installed\r\
    \n\t\t\tif ([/system script find name=(\$systemScripts->\$cmdIndex)]!=\"\") do={\r\
    \n\t\t\t\t#Append their corresponding command to the provision commands string\r\
    \n\t\t\t\tset availCommands \"\$availCommands,\$moduleName\"\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Iterate through the available commands\r\
    \n\t\tforeach command in=[toarray \$availCommands] do={\r\
    \n\t\t\t#Execute each command and if any error was encountered\r\
    \n\t\t\tif ([\$provision \$command]<0) do={\r\
    \n\t\t\t\t#Adjust the execution status\r\
    \n\t\t\t\tset execStatus -1\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#If any errors occured\r\
    \n\t\tif (\$execStatus<0) do={\r\
    \n\t\t\t#Throw error\r\
    \n\t\t\t\$error (\"Completed with errors.\")\r\
    \n\t\t} else {\r\
    \n\t\t\t#Notify success\r\
    \n\t\t\t\$notify (\"Completed successfully.\")\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"purge\"\r\
    \n\tif (\$1=\"purge\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Notify\r\
    \n\t\t\$notify (\"Automatically purging provisioned configuration.\")\r\
    \n\r\
    \n\t\t#Construct an inventory of variable names belonging to each module\r\
    \n\t\tlocal varsLiveStream {\"livestream\";\"LVStreamList\";\"LVStreamFQDN\";\"LVStreamRulePrefix\"}\r\
    \n\t\tlocal varsResolver {\"resolvefqdn\"}\r\
    \n\t\tlocal varsDynDNS {\"dyndns\";\"WANAddress\";\"DDNSService\";\"DDNSInterval\";\"DDNSUsername\";\"DDNSPassword\";\"DDNSHostname\"}\r\
    \n\t\tlocal varsFailover {\"failover\";\"FailoverCounters\";\"FailoverTarget\";\"FailoverThreshold\";\"FailoverInterval\"}\r\
    \n\t\tlocal varsGateway {\"gateway\";\"WANNames\";\"WANGateways\";\"WANGatewayPrefix\";\"BalancingRulePrefix\"}\r\
    \n\t\tlocal moduleVars {\$varsLiveStream;\$varsResolver;\$varsDynDNS;\$varsFailover;\$varsGateway}\r\
    \n\r\
    \n\t\t#Iterate through the system scripts\r\
    \n\t\tforeach modIndex,systemScript in={\"mod-livestream\";\"mod-resolvefqdn\";\"mod-dyndns\";\"mod-failover\";\"mod-gateway\"} do={\r\
    \n\t\t\t#If any of them is not installed\r\
    \n\t\t\tif ([/system script find name=\$systemScript]=\"\") do={\r\
    \n\t\t\t\t#Iterate through its corresponding variables\r\
    \n\t\t\t\tforeach modVar in=(\$moduleVars->\$modIndex) do={\r\
    \n\t\t\t\t\t#If it's a function variable that possesses a cron job\r\
    \n\t\t\t\t\tforeach varWithCron in={\"dyndns\";\"failover\"} do={\r\
    \n\t\t\t\t\t\tif (\$modVar=\$varWithCron) do={\r\
    \n\t\t\t\t\t\t\t#If it's currently active\r\
    \n\t\t\t\t\t\t\tif ([/system scheduler find disabled=no name=\"cron-\$modVar\"]!=\"\") do={\r\
    \n\t\t\t\t\t\t\t\t#Disable it\r\
    \n\t\t\t\t\t\t\t\texecute (\"global \$modVar;\\\$\$modVar toggle\")\r\
    \n\r\
    \n\t\t\t\t\t\t\t\t#Allow enough time for any additional actions to complete\r\
    \n\t\t\t\t\t\t\t\tdelay 1\r\
    \n\t\t\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t\t\t#Remove it\r\
    \n\t\t\t\t\t\t\t/system scheduler remove [find name=\"cron-\$modVar\"]\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t\t\r\
    \n\t\t\t\t\t#Clear their corresponding variables\r\
    \n\t\t\t\t\texecute (\"global \".\$modVar.\"; set \".\$modVar)\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"gateways\"\r\
    \n\tif (\$1=\"gateways\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Array duplicate checker subfunction\r\
    \n\t\t#Inputs: <Array of arrays>\r\
    \n\t\t#Output: <Duplicates flag>\r\
    \n\t\tlocal containsDuplicates do={\r\
    \n\t\t\t#Iterate through the requested arrays\r\
    \n\t\t\tforeach array in=\$1 do={\r\
    \n\t\t\t\t#Iterate through each array element\r\
    \n\t\t\t\tforeach primaryElement in=\$array do={\r\
    \n\t\t\t\t\t#Declare helper counter\r\
    \n\t\t\t\t\tlocal elementOccurences 0\r\
    \n\r\
    \n\t\t\t\t\t#Iterate through each array element a second time\r\
    \n\t\t\t\t\tforeach secondaryElement in=\$array do={\r\
    \n\t\t\t\t\t\t#If the element is found\r\
    \n\t\t\t\t\t\tif (\$primaryElement=\$secondaryElement) do={\r\
    \n\t\t\t\t\t\t\t#Adjust the helper counter\r\
    \n\t\t\t\t\t\t\tset elementOccurences (\$elementOccurences+1)\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t#If an element has been found more than once\r\
    \n\t\t\t\t\tif (\$elementOccurences>1) do={\r\
    \n\t\t\t\t\t\t#Exit\r\
    \n\t\t\t\t\t\treturn true\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Exit\r\
    \n\t\t\treturn false\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Notify\r\
    \n\t\t\$notify (\"Provisioning \$1.\")\r\
    \n\r\
    \n\t\t#Request variables from config\r\
    \n\t\tglobal WANNames [toarray [\$parseConfig \"WANNames\"]]\r\
    \n\t\tglobal WANGateways [toarray [\$parseConfig \"WANGateways\"]]\r\
    \n\t\tglobal WANGatewayPrefix [\$parseConfig \"WANGatewayPrefix\"]\r\
    \n\t\tglobal BalancingRulePrefix [\$parseConfig \"BalancingRulePrefix\"]\r\
    \n\r\
    \n\t\t#Validate that the mandatory variables are not empty\r\
    \n\t\tlocal cfgMandatoryVars {\"WANNames\";\"WANGateways\";\"WANGatewayPrefix\";\"BalancingRulePrefix\"}\r\
    \n\t\tlocal cfgMandatoryValues {\$WANNames;\$WANGateways;\$WANGatewayPrefix;\$BalancingRulePrefix}\r\
    \n\t\tset execStatus [\$validateVars \$cfgMandatoryVars \$cfgMandatoryValues]\r\
    \n\r\
    \n\t\t#If the WAN names or gateways contain duplicate values\r\
    \n\t\tlocal arrays {\$WANNames;\$WANGateways}\r\
    \n\t\tif ([\$containsDuplicates \$arrays]) do={\r\
    \n\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\tset execStatus [\$error (\"Gateways configuration contains duplicate values.\")]\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#If the WAN name and gateway pairings are even\r\
    \n\t\tif ([len \$WANNames]=[len \$WANGateways]) do={\r\
    \n\t\t\t#Iterate through the gateways\r\
    \n\t\t\tforeach wanIndex,WANName in=\$WANNames do={\r\
    \n\t\t\t\t#If it's assigned with a proper IP\r\
    \n\t\t\t\tif ([typeof [toip (\$WANGateways->\$wanIndex)]]=\"ip\") do={\r\
    \n\t\t\t\t\t#If it does not have a corresponding default route\r\
    \n\t\t\t\t\tif ([/ip route find gateway=(\$WANGateways->\$wanIndex) dst-address=0.0.0.0/0 comment~\$WANGatewayPrefix]=\"\") do={\r\
    \n\t\t\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\t\t\tset execStatus [\$error (\"Gateways configuration for \$WANName does not have a corresponding default route.\")]\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\t\tset execStatus [\$error (\"Gateways configuration for \$WANName contains an invalid IP address: \".(\$WANGateways->\$wanIndex).\".\")]\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\t\t} else {\r\
    \n\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\tset execStatus [\$error (\"Gateways configuration is incomplete. Declared items are not evenly populated or may be enclosed in quotes.\")]\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#If any errors occured\r\
    \n\t\tif (\$execStatus<0) do={\r\
    \n\t\t\t#Gain variable access\r\
    \n\t\t\tglobal gateway\r\
    \n\r\
    \n\t\t\t#Clear all corresponding variables\r\
    \n\t\t\tset gateway\r\
    \n\t\t\tset WANNames\r\
    \n\t\t\tset WANGateways\r\
    \n\t\t\tset WANGatewayPrefix\r\
    \n\t\t\tset BalancingRulePrefix\r\
    \n\r\
    \n\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\tset execStatus [\$error (\"The \$1 configuration is invalid, please check the config file and try again.\")]\r\
    \n\t\t} else {\r\
    \n\t\t\t#If the function script is not installed\r\
    \n\t\t\tif ([/system script find name=mod-gateway]=\"\") do={\r\
    \n\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\tset execStatus [\$error (\"The gateway selector module script is missing, please install it and try again.\")]\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\t#Initialize function\r\
    \n\t\t\t\t/system script run mod-gateway\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"failover\"\r\
    \n\tif (\$1=\"failover\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Notify\r\
    \n\t\t\$notify (\"Provisioning \$1.\")\r\
    \n\r\
    \n\t\t#Request variables from config\r\
    \n\t\tglobal FailoverTarget [toip [\$parseConfig \"FailoverTarget\"]]\r\
    \n\t\tglobal FailoverThreshold [tonum [\$parseConfig \"FailoverThreshold\"]]\r\
    \n\t\tglobal FailoverInterval [tonum [\$parseConfig \"FailoverInterval\"]]\r\
    \n\r\
    \n\t\t#Validate that the mandatory variables are not empty\r\
    \n\t\tlocal cfgMandatoryVars {\"FailoverTarget\";\"FailoverThreshold\";\"FailoverInterval\"}\r\
    \n\t\tlocal cfgMandatoryValues {\$FailoverTarget;\$FailoverThreshold;\$FailoverInterval}\r\
    \n\t\tset execStatus [\$validateVars \$cfgMandatoryVars \$cfgMandatoryValues]\r\
    \n\r\
    \n\t\t#If any errors occured\r\
    \n\t\tif (\$execStatus<0) do={\r\
    \n\t\t\t#Gain variable access\r\
    \n\t\t\tglobal failover\r\
    \n\r\
    \n\t\t\t#Clear all corresponding variables\r\
    \n\t\t\tset failover\r\
    \n\t\t\tset FailoverTarget\r\
    \n\t\t\tset FailoverThreshold\r\
    \n\t\t\tset FailoverInterval\r\
    \n\r\
    \n\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\tset execStatus [\$error (\"The \$1 configuration is invalid, please check the config file and try again.\")]\r\
    \n\t\t} else {\r\
    \n\t\t\t#If the function script is not installed\r\
    \n\t\t\tif ([/system script find name=mod-failover]=\"\") do={\r\
    \n\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\tset execStatus [\$error (\"The failover module script is missing, please install it and try again.\")]\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\t#Gain variable access\r\
    \n\t\t\t\tglobal gateway\r\
    \n\r\
    \n\t\t\t\t#If the gateway function is not present\r\
    \n\t\t\t\tif ([typeof \$gateway]=\"nothing\") do={\r\
    \n\t\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\t\tset execStatus [\$error (\"The failover module depends on the gateway selector module, please provision it and try again.\")]\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#Gain variable access\r\
    \n\t\t\t\t\tglobal WANNames\r\
    \n\r\
    \n\t\t\t\t\t#Initialize the failover counters string\r\
    \n\t\t\t\t\tglobal FailoverCounters \"\"\r\
    \n\r\
    \n\t\t\t\t\t#For every gateway\r\
    \n\t\t\t\t\tforeach WANName in=\$WANNames do={\r\
    \n\t\t\t\t\t\t#Append an initialization value to it\r\
    \n\t\t\t\t\t\tset FailoverCounters \"\$FailoverCounters,0\"\r\
    \n\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t#Convert the failover counters string to an array\r\
    \n\t\t\t\t\tset FailoverCounters [toarray \$FailoverCounters]\r\
    \n\r\
    \n\t\t\t\t\t#Declare helper flag\r\
    \n\t\t\t\t\tlocal cronStatus true\r\
    \n\r\
    \n\t\t\t\t\t#If the scheduler entry for the periodic failover checks exists\r\
    \n\t\t\t\t\tif ([/system scheduler find name=cron-failover]!=\"\") do={\r\
    \n\t\t\t\t\t\t#Fetch its status\r\
    \n\t\t\t\t\t\tset cronStatus [/system scheduler get value-name=disabled [find name=cron-failover]]\r\
    \n\r\
    \n\t\t\t\t\t\t#Remove it\r\
    \n\t\t\t\t\t\t/system scheduler remove [find name=\"cron-failover\"]\r\
    \n\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t#Re-add the scheduler entry\r\
    \n\t\t\t\t\t/system scheduler add name=\"cron-failover\" interval=\$FailoverInterval policy=read,write,policy,test disabled=\$cronStatus on-event={global failover;\$failover check}\r\
    \n\r\
    \n\t\t\t\t\t#Initialize function\r\
    \n\t\t\t\t\t/system script run mod-failover\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"dyndns\"\r\
    \n\tif (\$1=\"dyndns\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Notify\r\
    \n\t\t\$notify (\"Provisioning \$1.\")\r\
    \n\r\
    \n\t\t#Request variables from config\r\
    \n\t\tglobal DDNSService [\$parseConfig \"DDNSService\"]\r\
    \n\t\tglobal DDNSInterval [tonum [\$parseConfig \"DDNSInterval\"]]\r\
    \n\t\tglobal DDNSUsername [\$parseConfig \"DDNSUsername\"]\r\
    \n\t\tglobal DDNSPassword [\$parseConfig \"DDNSPassword\"]\r\
    \n\t\tglobal DDNSHostname [\$parseConfig \"DDNSHostname\"]\r\
    \n\r\
    \n\t\t#Validate that the mandatory variables are not empty\r\
    \n\t\tlocal cfgMandatoryVars {\"DDNSService\";\"DDNSInterval\";\"DDNSUsername\";\"DDNSPassword\";\"DDNSHostname\"}\r\
    \n\t\tlocal cfgMandatoryValues {\$DDNSService;\$DDNSInterval;\$DDNSUsername;\$DDNSPassword;\$DDNSHostname}\r\
    \n\t\tset execStatus [\$validateVars \$cfgMandatoryVars \$cfgMandatoryValues]\r\
    \n\r\
    \n\t\t#If any errors occured\r\
    \n\t\tif (\$execStatus<0) do={\r\
    \n\t\t\t#Gain variable access\r\
    \n\t\t\tglobal dyndns\r\
    \n\r\
    \n\t\t\t#Clear all corresponding variables\r\
    \n\t\t\tset dyndns\r\
    \n\t\t\tset DDNSService\r\
    \n\t\t\tset DDNSInterval\r\
    \n\t\t\tset DDNSUsername\r\
    \n\t\t\tset DDNSPassword\r\
    \n\t\t\tset DDNSHostname\r\
    \n\r\
    \n\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\tset execStatus [\$error (\"The \$1 configuration is invalid, please check the config file and try again.\")]\r\
    \n\t\t} else {\r\
    \n\t\t\t#If the function script is not installed\r\
    \n\t\t\tif ([/system script find name=mod-dyndns]=\"\") do={\r\
    \n\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\tset execStatus [\$error (\"The DynDNS updater module script is missing, please install it and try again.\")]\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\t#Declare helper flag\r\
    \n\t\t\t\tlocal cronStatus true\r\
    \n\r\
    \n\t\t\t\t#If the scheduler entry for the periodic DynDNS updates exists\r\
    \n\t\t\t\tif ([/system scheduler find name=cron-dyndns]!=\"\") do={\r\
    \n\t\t\t\t\t#Fetch its status\r\
    \n\t\t\t\t\tset cronStatus [/system scheduler get value-name=disabled [find name=cron-dyndns]]\r\
    \n\r\
    \n\t\t\t\t\t#Remove it\r\
    \n\t\t\t\t\t/system scheduler remove [find name=\"cron-dyndns\"]\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#Re-add the scheduler entry\r\
    \n\t\t\t\t/system scheduler add name=\"cron-dyndns\" interval=\$DDNSInterval disabled=\$cronStatus on-event={global dyndns;\$dyndns update}\r\
    \n\r\
    \n\t\t\t\t#Initialize function\r\
    \n\t\t\t\t/system script run mod-dyndns\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"resolver\"\r\
    \n\tif (\$1=\"resolver\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Notify\r\
    \n\t\t\$notify (\"Provisioning \$1.\")\r\
    \n\r\
    \n\t\t#If the function script is not installed\r\
    \n\t\tif ([/system script find name=mod-resolvefqdn]=\"\") do={\r\
    \n\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\tset execStatus [\$error (\"The FQDN resolver module script is missing, please install it and try again.\")]\r\
    \n\t\t} else {\r\
    \n\t\t\t#Initialize function\r\
    \n\t\t\t/system script run mod-resolvefqdn\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"livestream\"\r\
    \n\tif (\$1=\"livestream\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Notify\r\
    \n\t\t\$notify (\"Provisioning \$1.\")\r\
    \n\r\
    \n\t\t#Request variables from config\r\
    \n\t\tglobal LVStreamList [\$parseConfig \"LVStreamList\"]\r\
    \n\t\tglobal LVStreamFQDN [\$parseConfig \"LVStreamFQDN\"]\r\
    \n\t\tglobal LVStreamRulePrefix [\$parseConfig \"LVStreamRulePrefix\"]\r\
    \n\r\
    \n\t\t#Validate that the mandatory variables are not empty\r\
    \n\t\tlocal cfgMandatoryVars {\"LVStreamList\";\"LVStreamFQDN\";\"LVStreamRulePrefix\"}\r\
    \n\t\tlocal cfgMandatoryValues {\$LVStreamList;\$LVStreamFQDN;\$LVStreamRulePrefix}\r\
    \n\t\tset execStatus [\$validateVars \$cfgMandatoryVars \$cfgMandatoryValues]\r\
    \n\r\
    \n\t\t#If any errors occured\r\
    \n\t\tif (\$execStatus<0) do={\r\
    \n\t\t\t#Gain variable access\r\
    \n\t\t\tglobal livestream\r\
    \n\r\
    \n\t\t\t#Clear all corresponding variables\r\
    \n\t\t\tset livestream\r\
    \n\t\t\tset LVStreamList\r\
    \n\t\t\tset LVStreamFQDN\r\
    \n\t\t\tset LVStreamRulePrefix\r\
    \n\r\
    \n\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\tset execStatus [\$error (\"The \$1 configuration is invalid, please check the config file and try again.\")]\r\
    \n\t\t} else {\r\
    \n\t\t\t#If the function script is not installed\r\
    \n\t\t\tif ([/system script find name=mod-livestream]=\"\") do={\r\
    \n\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\tset execStatus [\$error (\"The live streaming module script is missing, please install it and try again.\")]\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\t#Gain variable access\r\
    \n\t\t\t\tglobal resolvefqdn\r\
    \n\r\
    \n\t\t\t\t#If the resolvefqdn function is not present\r\
    \n\t\t\t\tif ([typeof \$resolvefqdn]=\"nothing\") do={\r\
    \n\t\t\t\t\t#Throw error and adjust execution status\r\
    \n\t\t\t\t\tset execStatus [\$error (\"The live streaming module depends on the FQDN resolver module, please provision it and try again.\")]\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#Initialize function\r\
    \n\t\t\t\t\t/system script run mod-livestream\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If this part has been reached, it means that no valid arguments were caught\r\
    \n\t#Respond on console with help message\r\
    \n\tput (\"[Provision][Info]: incorrect arguments, try:\")\r\
    \n\tput (\"\\\$provision auto|purge|failover|gateways|dyndns|resolver|livestream\")\r\
    \n\r\
    \n\t#Exit with error\r\
    \n\tset execStatus -1\r\
    \n\treturn \$execStatus\r\
    \n}"
/system script add dont-require-permissions=no name=mod-gateway owner=admin policy=read,write source="#Gateway selector module script\r\
    \n#Script permissions: read, write\r\
    \n#Script dependencies: mod-provision\r\
    \n\r\
    \n#Function declaration\r\
    \nglobal gateway do={\r\
    \n\t#Error handling functions\r\
    \n\tlocal notify do={local msg \"[Gateway Selector][Info]: \$1\";put \$msg}\r\
    \n\tlocal warn do={local msg \"[Gateway Selector][Warn]: \$1\";put \$msg;log warn \$msg}\r\
    \n\tlocal error do={local msg \"[Gateway Selector][Error]: \$1\";put \$msg;return -1}\r\
    \n\tlocal execStatus 0\r\
    \n\t\r\
    \n\t#Gain variable access\r\
    \n\tglobal WANNames\r\
    \n\tglobal WANGateways\r\
    \n\tglobal WANGatewayPrefix\r\
    \n\tglobal BalancingRulePrefix\r\
    \n\r\
    \n\t#If function was called with argument \"switch\"\r\
    \n\tif (\$1=\"switch\" && [typeof \$2]!=\"nothing\" && [typeof \$3]=\"nothing\") do={\r\
    \n\t\tif (\$2=\"Balancer\") do={\r\
    \n\t\t\t#If there are no load balancing rules present\r\
    \n\t\t\tif ([/ip firewall mangle find comment~\$BalancingRulePrefix]=\"\") do={\r\
    \n\t\t\t\t#Throw error and exit\r\
    \n\t\t\t\tset execStatus [\$error (\"Cannot activate load balancing because there are no corresponding rules present.\")]\r\
    \n\t\t\t\treturn \$execStatus\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Declare helper flag\r\
    \n\t\t\tlocal WANOutage false\r\
    \n\r\
    \n\t\t\t#Iterate through the gateways\r\
    \n\t\t\tforeach WANGateway in=\$WANGateways do={\r\
    \n\t\t\t\t#If there's at least one non-operational gateway\r\
    \n\t\t\t\tif ([/ip route get value-name=distance [find gateway=\$WANGateway comment~\$WANGatewayPrefix]]>[len \$WANGateways]) do={\r\
    \n\t\t\t\t\t#Adjust the helper flag\r\
    \n\t\t\t\t\tset WANOutage true\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#If there's a WAN outage\r\
    \n\t\t\tif (\$WANOutage) do={\r\
    \n\t\t\t\t#If load balancing was not active prior the outage\r\
    \n\t\t\t\tif ([/ip firewall mangle find action=passthrough content=\"Failover\" comment~\$BalancingRulePrefix]=\"\") do={\r\
    \n\t\t\t\t\t#Create semaphore rule\r\
    \n\t\t\t\t\t/ip firewall mangle add chain=input action=passthrough disabled=yes content=\"Failover\" comment=\"\$BalancingRulePrefix\"\r\
    \n\r\
    \n\t\t\t\t\t#Throw warning\r\
    \n\t\t\t\t\t\$warn (\"Load balancing has been selected and will be activated once WAN outage has been resolved.\")\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#Throw warning\r\
    \n\t\t\t\t\t\$warn (\"Load balancing is already selected, but will be activated once the WAN outage has been resolved.\")\r\
    \n\t\t\t\t}\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\t#If load balancing is currently active\r\
    \n\t\t\t\tif ([len [/ip firewall mangle find disabled=no comment~\$BalancingRulePrefix]]>0) do={\r\
    \n\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\$notify (\"Load balancing is already active.\")\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#Activate the load balancing rules\r\
    \n\t\t\t\t\t/ip firewall mangle set disabled=no [find comment~\$BalancingRulePrefix]\r\
    \n\r\
    \n\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\$notify (\"Switched to: Load balancing\")\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Exit\r\
    \n\t\t\treturn \$execStatus\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Iterate through the gateways\r\
    \n\t\tforeach wanIndex,WANName in=\$WANNames do={\r\
    \n\t\t\t#If the requested gateway exists\r\
    \n\t\t\tif (\$2=\$WANName) do={\r\
    \n\t\t\t\t#Declare helper flags\r\
    \n\t\t\t\tlocal gatewayOperational true\r\
    \n\t\t\t\tlocal disabledBalancer false\r\
    \n\r\
    \n\t\t\t\t#Fetch its distance\r\
    \n\t\t\t\tlocal requestedGatewayDistance [/ip route get value-name=distance [find gateway=(\$WANGateways->\$wanIndex) comment~\$WANGatewayPrefix]]\r\
    \n\r\
    \n\t\t\t\t#If it's non-operational\r\
    \n\t\t\t\tif (\$requestedGatewayDistance>[len \$WANGateways]) do={\r\
    \n\t\t\t\t\t#Adjust for real distance\r\
    \n\t\t\t\t\tset requestedGatewayDistance (\$requestedGatewayDistance-[len \$WANGateways])\r\
    \n\r\
    \n\t\t\t\t\t#Adjust helper flag\r\
    \n\t\t\t\t\tset gatewayOperational false\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#If load balancing is currently active\r\
    \n\t\t\t\tif ([len [/ip firewall mangle find disabled=no comment~\$BalancingRulePrefix]]>0) do={\r\
    \n\t\t\t\t\t#Deactivate the load balancing rules\r\
    \n\t\t\t\t\t/ip firewall mangle set disabled=yes [find comment~\$BalancingRulePrefix]\r\
    \n\r\
    \n\t\t\t\t\t#Adjust helper flag\r\
    \n\t\t\t\t\tset disabledBalancer true\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#If load balancing was active prior a WAN outage\r\
    \n\t\t\t\tif ([/ip firewall mangle find action=passthrough content=\"Failover\" comment~\$BalancingRulePrefix]!=\"\") do={\r\
    \n\t\t\t\t\t#Clean up semaphore rule\r\
    \n\t\t\t\t\t/ip firewall mangle remove [find action=passthrough content=\"Failover\" comment~\$BalancingRulePrefix]\r\
    \n\r\
    \n\t\t\t\t\t#Adjust helper flag\r\
    \n\t\t\t\t\tset disabledBalancer true\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#If it's already the default gateway\r\
    \n\t\t\t\tif (\$requestedGatewayDistance=1) do={\r\
    \n\t\t\t\t\t#If load balancing was just disabled\r\
    \n\t\t\t\t\tif (\$disabledBalancer) do={\r\
    \n\t\t\t\t\t\t#If it's operational\r\
    \n\t\t\t\t\t\tif (\$gatewayOperational) do={\r\
    \n\t\t\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\t\t\$notify (\"Switched to: \$WANName\")\r\
    \n\t\t\t\t\t\t} else {\r\
    \n\t\t\t\t\t\t\t#Throw warning\r\
    \n\t\t\t\t\t\t\t\$warn (\"\$WANName has been selected and will be available once it regains WAN access.\")\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t} else {\r\
    \n\t\t\t\t\t\t#If it's operational\r\
    \n\t\t\t\t\t\tif (\$gatewayOperational) do={\r\
    \n\t\t\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\t\t\$notify (\"\$WANName is already the default gateway.\")\r\
    \n\t\t\t\t\t\t} else {\r\
    \n\t\t\t\t\t\t\t#Throw warning\r\
    \n\t\t\t\t\t\t\t\$warn (\"\$WANName is already selected, but will be available once it regains WAN access.\")\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t#Exit\r\
    \n\t\t\t\t\treturn \$execStatus\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#Iterate through the gateways\r\
    \n\t\t\t\tforeach WANGateway in=\$WANGateways do={\r\
    \n\t\t\t\t\t#Fetch their distance information\r\
    \n\t\t\t\t\tlocal gatewayDistance [/ip route get value-name=distance [find gateway=\$WANGateway comment~\$WANGatewayPrefix]]\r\
    \n\r\
    \n\t\t\t\t\t#If there are any non-operational ones\r\
    \n\t\t\t\t\tif (\$gatewayDistance>[len \$WANGateways]) do={\r\
    \n\t\t\t\t\t\t#Adjust for real distance\r\
    \n\t\t\t\t\t\tset gatewayDistance (\$gatewayDistance-[len \$WANGateways])\r\
    \n\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t#For every gateway's default route that has a distance less than requested one\r\
    \n\t\t\t\t\tif (\$gatewayDistance<\$requestedGatewayDistance) do={\r\
    \n\t\t\t\t\t\t#Increase their distance by one\r\
    \n\t\t\t\t\t\t/ip route set distance=([/ip route get value-name=distance [find gateway=\$WANGateway comment~\$WANGatewayPrefix]]+1) [find gateway=\$WANGateway comment~\$WANGatewayPrefix]\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#Set the minimum distance to one\r\
    \n\t\t\t\tlocal minDistance 1\r\
    \n\r\
    \n\t\t\t\t#If it's non-operational\r\
    \n\t\t\t\tif (!\$gatewayOperational) do={\r\
    \n\t\t\t\t\t#Set the minimum distance to the total number of gateways plus one\r\
    \n\t\t\t\t\tset minDistance ([len \$WANGateways]+1)\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#Set it as the default gateway\r\
    \n\t\t\t\t/ip route set distance=\$minDistance [find gateway=(\$WANGateways->\$wanIndex) comment~\$WANGatewayPrefix]\r\
    \n\r\
    \n\t\t\t\t#If it's operational\r\
    \n\t\t\t\tif (\$gatewayOperational) do={\r\
    \n\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\$notify (\"Switched to: \$WANName\")\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#Throw warning\r\
    \n\t\t\t\t\t\$warn (\"\$WANName has been selected and will be available once it regains WAN access.\")\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#Exit\r\
    \n\t\t\t\treturn \$execStatus\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#If this part has been reached, it means that no valid gateway was caught\r\
    \n\t\t#Throw error and exit\r\
    \n\t\tset execStatus [\$error (\"Cannot switch to \$2 because there's no such gateway declared in the config file.\")]\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"status\"\r\
    \n\tif (\$1=\"status\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Find the gateway of least distance\r\
    \n\t\tlocal currGateway \"\"\r\
    \n\t\tlocal currMinDistance ([len \$WANGateways]+1)\r\
    \n\t\tforeach wanIndex,WANName in=\$WANNames do={\r\
    \n\t\t\tif ([/ip route get value-name=distance [find gateway=(\$WANGateways->\$wanIndex) comment~\$WANGatewayPrefix]]<\$currMinDistance) do={\r\
    \n\t\t\t\tset currGateway \$WANName\r\
    \n\t\t\t\tset currMinDistance [/ip route get value-name=distance [find gateway=(\$WANGateways->\$wanIndex) comment~\$WANGatewayPrefix]]\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#If the gateway of least distance is non-operational\r\
    \n\t\tif (\$currMinDistance>[len \$WANGateways]) do={\r\
    \n\t\t\t#Throw warning\r\
    \n\t\t\t\$warn (\"All gateways are non-operational.\")\r\
    \n\t\t} else {\r\
    \n\t\t\t#If load balancing is currently active\r\
    \n\t\t\tif ([len [/ip firewall mangle find disabled=no comment~\$BalancingRulePrefix]]>0) do={\r\
    \n\t\t\t\t#Notify\r\
    \n\t\t\t\t\$notify (\"Currently via: Load balancing\")\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\t#Notify\r\
    \n\t\t\t\t\$notify (\"Currently via: \$currGateway\")\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If this part has been reached, it means that no valid arguments were caught\r\
    \n\t#Respond on console with help message\r\
    \n\tput (\"[Gateway Selector][Info]: incorrect arguments, try:\")\r\
    \n\tput (\"\\\$gateway <switch Balancer|<WAN Name>>|status\")\r\
    \n\r\
    \n\t#Exit with error\r\
    \n\tset execStatus -1\r\
    \n\treturn \$execStatus\r\
    \n}"
/system script add dont-require-permissions=no name=mod-failover owner=admin policy=read,write source="#Failover module script\r\
    \n#Script permissions: read, write\r\
    \n#Script dependencies: mod-provision, mod-gateway\r\
    \n\r\
    \n#Function declaration\r\
    \nglobal failover do={\r\
    \n\t#Error handling functions\r\
    \n\tlocal notify do={local msg \"[Failover][Info]: \$1\";put \$msg;log info \$msg}\r\
    \n\tlocal warn do={local msg \"[Failover][Warn]: \$1\";put \$msg;log warn \$msg}\r\
    \n\tlocal error do={local msg \"[Failover][Error]: \$1\";put \$msg;log error \$msg;return -1}\r\
    \n\tlocal execStatus 0\r\
    \n\r\
    \n\t#Gain variable access\r\
    \n\tglobal gateway\r\
    \n\tglobal WANNames\r\
    \n\tglobal WANGateways\r\
    \n\tglobal WANGatewayPrefix\r\
    \n\tglobal BalancingRulePrefix\r\
    \n\tglobal FailoverCounters\r\
    \n\tglobal FailoverTarget\r\
    \n\tglobal FailoverThreshold\r\
    \n\r\
    \n\t#If the gateway selector function is not present\r\
    \n\tif ([typeof \$gateway]=\"nothing\") do={\r\
    \n\t\t#Throw error and exit\r\
    \n\t\tset execStatus [\$error (\"The gateway selector function is missing. Please ensure that the module is installed and properly provisioned.\")]\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"check\"\r\
    \n\tif (\$1=\"check\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#WAN availability evaluator subfunction\r\
    \n\t\t#Inputs: <WAN index>\r\
    \n\t\tlocal evaluateAvailability do={\r\
    \n\t\t\t#WAN interface locator subfunction\r\
    \n\t\t\t#Inputs: <Gateway address>\r\
    \n\t\t\tlocal locateWANInterface do={\r\
    \n\t\t\t\t#Iterate through the ip address entries\r\
    \n\t\t\t\tforeach entry in=[/ip address find] do={\r\
    \n\t\t\t\t\t#Fetch the address information\r\
    \n\t\t\t\t\tlocal entryAddress [/ip address get value-name=address \$entry]\r\
    \n\r\
    \n\t\t\t\t\t#If it contains a subnet mask\r\
    \n\t\t\t\t\tif ([typeof [find \$entryAddress \"/\"]]=\"num\") do={\r\
    \n\t\t\t\t\t\t#Convert the mask to octet format\r\
    \n\t\t\t\t\t\tlocal entryMask [toip (255.255.255.255<<(32-[pick \$entryAddress ([find \$entryAddress \"/\"]+1) [len \$entryAddress]]))]\r\
    \n\r\
    \n\t\t\t\t\t\t#Calculate the host range\r\
    \n\t\t\t\t\t\tlocal firstHost ([toip ([pick \$entryAddress 0 ([find \$entryAddress \"/\"])]&(\$entryMask))]+1)\r\
    \n\t\t\t\t\t\tlocal lastHost ([toip ([pick \$entryAddress 0 ([find \$entryAddress \"/\"])]|(~\$entryMask))]-1)\r\
    \n\r\
    \n\t\t\t\t\t\t#If the requested address exists within the host range\r\
    \n\t\t\t\t\t\tif (\$1>=\$firstHost && \$1<=\$lastHost) do={\r\
    \n\t\t\t\t\t\t\t#Return the name of the corresponding interface\r\
    \n\t\t\t\t\t\t\treturn [/ip address get value-name=interface \$entry]\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t#If this part has been reached, it means that no valid interface was caught\r\
    \n\t\t\t\t#Exit\r\
    \n\t\t\t\treturn \"\"\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Error handling functions\r\
    \n\t\t\tlocal notify do={local msg \"[Failover][Info]: \$1\";put \$msg;log info \$msg}\r\
    \n\t\t\tlocal warn do={local msg \"[Failover][Warn]: \$1\";put \$msg;log warn \$msg}\r\
    \n\r\
    \n\t\t\t#Gain variable access\r\
    \n\t\t\tglobal WANNames\r\
    \n\t\t\tglobal WANGateways\r\
    \n\t\t\tglobal WANGatewayPrefix\r\
    \n\t\t\tglobal BalancingRulePrefix\r\
    \n\t\t\tglobal FailoverCounters\r\
    \n\t\t\tglobal FailoverTarget\r\
    \n\t\t\tglobal FailoverThreshold\r\
    \n\r\
    \n\t\t\t#Declare helper flag\r\
    \n\t\t\tlocal pingSuccessful false\r\
    \n\r\
    \n\t\t\t#If the gateway points to a valid interface\r\
    \n\t\t\tlocal WANInterface [\$locateWANInterface (\$WANGateways->\$1)]\r\
    \n\t\t\tif (\$WANInterface!=\"\") do={\r\
    \n\t\t\t\t#Ping the target through that interface and if it succeeds\r\
    \n\t\t\t\tif ([ping \$FailoverTarget count=1 interface=\$WANInterface]>0) do={\r\
    \n\t\t\t\t\t#Adjust helper flag\r\
    \n\t\t\t\t\tset pingSuccessful true\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#If the gateway is operational\r\
    \n\t\t\tif ([/ip route get value-name=distance [find gateway=(\$WANGateways->\$1) comment~\$WANGatewayPrefix]]<=[len \$WANGateways]) do={\r\
    \n\t\t\t\t#If the ping was successful\r\
    \n\t\t\t\tif (\$pingSuccessful) do={\r\
    \n\t\t\t\t\t#If its counter had picked up any failures prior\r\
    \n\t\t\t\t\tif ((\$FailoverCounters->\$1)>0) do={\r\
    \n\t\t\t\t\t\t#Set it to minus one\r\
    \n\t\t\t\t\t\tset (\$FailoverCounters->\$1) -1\r\
    \n\t\t\t\t\t} else {\r\
    \n\t\t\t\t\t\t#Reduce it by one\r\
    \n\t\t\t\t\t\tset (\$FailoverCounters->\$1) ((\$FailoverCounters->\$1)-1)\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#If its counter had not picked up any failures prior\r\
    \n\t\t\t\t\tif ((\$FailoverCounters->\$1)<=0) do={\r\
    \n\t\t\t\t\t\t#Set it to one\r\
    \n\t\t\t\t\t\tset (\$FailoverCounters->\$1) 1\r\
    \n\t\t\t\t\t} else {\r\
    \n\t\t\t\t\t\t#Increase it by one\r\
    \n\t\t\t\t\t\tset (\$FailoverCounters->\$1) ((\$FailoverCounters->\$1)+1)\r\
    \n\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t#If its counter met the threshold\r\
    \n\t\t\t\t\tif ((\$FailoverCounters->\$1)=\$FailoverThreshold) do={\r\
    \n\t\t\t\t\t\t#Throw a warning\r\
    \n\t\t\t\t\t\t\$warn (\"WAN outage detected on \".(\$WANNames->\$1).\".\")\r\
    \n\r\
    \n\t\t\t\t\t\t#Increase its default route distance\r\
    \n\t\t\t\t\t\t/ip route set distance=([/ip route get value-name=distance [find gateway=(\$WANGateways->\$1) comment~\$WANGatewayPrefix]]+[/len \$WANGateways]) [find gateway=(\$WANGateways->\$1) comment~\$WANGatewayPrefix]\r\
    \n\r\
    \n\t\t\t\t\t\t#If load balancing is currently active\r\
    \n\t\t\t\t\t\tif ([len [/ip firewall mangle find disabled=no comment~\$BalancingRulePrefix]]>0) do={\r\
    \n\t\t\t\t\t\t\t#Deactivate its rules\r\
    \n\t\t\t\t\t\t\t/ip firewall mangle set disabled=yes [find comment~\$BalancingRulePrefix]\r\
    \n\r\
    \n\t\t\t\t\t\t\t#Create semaphore rule\r\
    \n\t\t\t\t\t\t\t/ip firewall mangle add chain=input action=passthrough disabled=yes content=\"Failover\" comment=\"\$BalancingRulePrefix\"\r\
    \n\r\
    \n\t\t\t\t\t\t\t#Throw a warning\r\
    \n\t\t\t\t\t\t\t\$warn (\"Load balancing has been temporarily disabled until all gateways regain WAN access.\")\r\
    \n\t\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t\t#Declare helper flag\r\
    \n\t\t\t\t\t\tlocal WANOutage true\r\
    \n\r\
    \n\t\t\t\t\t\t#Iterate through the gateways\r\
    \n\t\t\t\t\t\tforeach WANGateway in=\$WANGateways do={\r\
    \n\t\t\t\t\t\t\t#If there's at least one that's operational\r\
    \n\t\t\t\t\t\t\tif ([/ip route get value-name=distance [find gateway=\$WANGateway comment~\$WANGatewayPrefix]]<=[len \$WANGateways]) do={\r\
    \n\t\t\t\t\t\t\t\t#Adjust the helper flag\r\
    \n\t\t\t\t\t\t\t\tset WANOutage false\r\
    \n\t\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t\t#If all gateways are non-operational\r\
    \n\t\t\t\t\t\tif (\$WANOutage) do={\r\
    \n\t\t\t\t\t\t\t#Throw a warning\r\
    \n\t\t\t\t\t\t\t\$warn (\"WAN outage detected on all gateways.\")\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t\t\r\
    \n\t\t\t\t}\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\t#If the ping was successful\r\
    \n\t\t\t\tif (\$pingSuccessful) do={\r\
    \n\t\t\t\t\t#Reset its counter to zero\r\
    \n\t\t\t\t\tset (\$FailoverCounters->\$1) 0\r\
    \n\r\
    \n\t\t\t\t\t#Decrease its default route distance\r\
    \n\t\t\t\t\t/ip route set distance=([/ip route get value-name=distance [find gateway=(\$WANGateways->\$1) comment~\$WANGatewayPrefix]]-[/len \$WANGateways]) [find gateway=(\$WANGateways->\$1) comment~\$WANGatewayPrefix]\r\
    \n\r\
    \n\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\$notify (\"Gateway \".(\$WANNames->\$1).\" regained WAN access.\")\r\
    \n\r\
    \n\t\t\t\t\t#Declare helper flag\r\
    \n\t\t\t\t\tlocal WANOutage false\r\
    \n\r\
    \n\t\t\t\t\t#Iterate through the gateways\r\
    \n\t\t\t\t\tforeach WANGateway in=\$WANGateways do={\r\
    \n\t\t\t\t\t\t#If there's at least one that's non-operational\r\
    \n\t\t\t\t\t\tif ([/ip route get value-name=distance [find gateway=\$WANGateway comment~\$WANGatewayPrefix]]>[len \$WANGateways]) do={\r\
    \n\t\t\t\t\t\t\t#Adjust the helper flag\r\
    \n\t\t\t\t\t\t\tset WANOutage true\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t}\r\
    \n\r\
    \n\t\t\t\t\t#If all gateways are operational\r\
    \n\t\t\t\t\tif (!\$WANOutage) do={\r\
    \n\t\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\t\$notify (\"All gateways have regained WAN access.\")\r\
    \n\r\
    \n\t\t\t\t\t\t#If load balancing was enabled prior the outage\r\
    \n\t\t\t\t\t\tif ([/ip firewall mangle find action=passthrough content=\"Failover\" comment~\$BalancingRulePrefix]!=\"\") do={\r\
    \n\t\t\t\t\t\t\t#Clean up semaphore rule\r\
    \n\t\t\t\t\t\t\t/ip firewall mangle remove [find action=passthrough content=\"Failover\" comment~\$BalancingRulePrefix]\r\
    \n\r\
    \n\t\t\t\t\t\t\t#Activate its rules\r\
    \n\t\t\t\t\t\t\t/ip firewall mangle set disabled=no [find comment~\$BalancingRulePrefix]\r\
    \n\r\
    \n\t\t\t\t\t\t\t#Notify\r\
    \n\t\t\t\t\t\t\t\$notify (\"Load balancing has been reactivated because it was previously enabled.\")\r\
    \n\t\t\t\t\t\t}\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t} else {\r\
    \n\t\t\t\t\t#In case the device is coming off of a reboot and the failed gateway's counter has not yet met or surpassed the threshold\r\
    \n\t\t\t\t\tif ((\$FailoverCounters->\$1)<\$FailoverThreshold) do={\r\
    \n\t\t\t\t\t\t#Set it to the threshold\r\
    \n\t\t\t\t\t\tset (\$FailoverCounters->\$1) \$FailoverThreshold\r\
    \n\t\t\t\t\t} else {\r\
    \n\t\t\t\t\t\t#Increase it by one\r\
    \n\t\t\t\t\t\tset (\$FailoverCounters->\$1) ((\$FailoverCounters->\$1)+1)\r\
    \n\t\t\t\t\t}\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#For every gateway\r\
    \n\t\tforeach wanIndex,WANName in=\$WANNames do={\r\
    \n\t\t\t#Evaluate its availability\r\
    \n\t\t\t\$evaluateAvailability \$wanIndex\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"toggle\"\r\
    \n\tif (\$1=\"toggle\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#If the scheduler entry for the periodic failover checks is not enabled\r\
    \n\t\tif ([/system scheduler get value-name=disabled [find name=cron-failover]]) do={\r\
    \n\t\t\t#Enable it\r\
    \n\t\t\t/system scheduler set disabled=no [find name=cron-failover]\r\
    \n\r\
    \n\t\t\t#Notify\r\
    \n\t\t\t\$notify (\"Enabled.\")\r\
    \n\t\t} else {\r\
    \n\t\t\t#For every gateway\r\
    \n\t\t\tforeach WANGateway in=\$WANGateways do={\r\
    \n\t\t\t\t#Which is non-operational\r\
    \n\t\t\t\tif ([/ip route get value-name=distance [find gateway=\$WANGateway comment~\$WANGatewayPrefix]]>[len \$WANGateways]) do={\r\
    \n\t\t\t\t\t#Restore its distance\r\
    \n\t\t\t\t\t/ip route set distance=([/ip route get value-name=distance [find gateway=\$WANGateway comment~\$WANGatewayPrefix]]-[/len \$WANGateways]) [find gateway=\$WANGateway comment~\$WANGatewayPrefix]\r\
    \n\t\t\t\t}\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#If load balancing was enabled prior the outage\r\
    \n\t\t\tif ([/ip firewall mangle find content=\"Failover\" comment~\$BalancingRulePrefix]!=\"\") do={\r\
    \n\t\t\t\t#Clean up semaphore rule\r\
    \n\t\t\t\t/ip firewall mangle remove [find action=passthrough content=\"Failover\" comment~\$BalancingRulePrefix]\r\
    \n\r\
    \n\t\t\t\t#Activate its rules\r\
    \n\t\t\t\t/ip firewall mangle set disabled=no [find comment~\$BalancingRulePrefix]\t\t\t\t\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Disable it\r\
    \n\t\t\t/system scheduler set disabled=yes [find name=cron-failover]\r\
    \n\r\
    \n\t\t\t#Notify\r\
    \n\t\t\t\$notify (\"Disabled.\")\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If function was called with argument \"status\"\r\
    \n\tif (\$1=\"status\" && [typeof \$2]=\"nothing\") do={\r\
    \n\t\t#Error handling functions\r\
    \n\t\tlocal notifyCon do={local msg \"[Failover][Info]: \$1\";put \$msg}\r\
    \n\r\
    \n\t\t#If the scheduler entry for the periodic failover checks is not enabled\r\
    \n\t\tif ([/system scheduler get value-name=disabled [find name=cron-failover]]) do={\r\
    \n\t\t\t#Notify\r\
    \n\t\t\t\$notifyCon (\"Failover status: Inactive.\")\r\
    \n\t\t} else {\r\
    \n\t\t\t#Notify\r\
    \n\t\t\t\$notifyCon (\"Failover status: Active.\")\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Declare helper string\r\
    \n\t\tlocal WANStatus\r\
    \n\r\
    \n\t\t#Iterate through the failover counters\r\
    \n\t\tforeach wanIndex,WANName in=\$WANNames do={\r\
    \n\t\t\t#If the counter value greater than zero\r\
    \n\t\t\tif ((\$FailoverCounters->\$wanIndex)>0) do={\r\
    \n\t\t\t\t#Append the downtime estimation to the message\r\
    \n\t\t\t\tset WANStatus (\"\$WANName: Offline\\t(down for at least \".((\$FailoverCounters->\$wanIndex)*[/system scheduler get value-name=interval [find name=cron-failover]]).\")\")\r\
    \n\t\t\t} else {\r\
    \n\t\t\t\tset WANStatus (\"\$WANName: Online\\t(up for at least \".((\$FailoverCounters->\$wanIndex)*(-1)*[/system scheduler get value-name=interval [find name=cron-failover]]).\")\")\r\
    \n\t\t\t}\r\
    \n\r\
    \n\t\t\t#Notify\r\
    \n\t\t\t\$notifyCon \$WANStatus\r\
    \n\t\t}\r\
    \n\r\
    \n\t\t#Exit\r\
    \n\t\treturn \$execStatus\r\
    \n\t}\r\
    \n\r\
    \n\t#If this part has been reached, it means that no valid arguments were caught\r\
    \n\t#Respond on console with help message\r\
    \n\tput (\"[Failover][Info]: incorrect arguments, try:\")\r\
    \n\tput (\"\\\$failover check|toggle|status\")\r\
    \n\r\
    \n\t#Exit with error\r\
    \n\tset execStatus -1\r\
    \n\treturn \$execStatus\r\
    \n}"
/tool romon set enabled=yes
 
Frostbyte
Frequent Visitor
Frequent Visitor
Topic Author
Posts: 92
Joined: Mon Dec 25, 2017 1:42 am

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Fri Oct 15, 2021 11:11 am

Apologies for the late response, I've been caught up with lots of work-related stuff again.. Thank you for your patience and understanding.

The only thing I saw in your detailed configuration, which might be interfering, could be this NAT masquerade rule:
"/ip firewall nat add action=masquerade chain=srcnat out-interface-list=wan"
So, at first, you could give it another try with that particular rule disabled and see if the problem gets resolved.

I believe that the answer you were given is certainly a misinformed one, as it clearly works for me (on multiple devices) and no other users have reported back with such a problem.
Sure - this parameter on the `ping` command might've been intended for ipv6, as other people have been saying, but this is the first time I'm seeing it not working on ipv4.

Lastly, this might be a complete shot in the dark, but could you give me a screenshot of your installed packages?
You can always check this from WinBox (System -> Packages).

Here is mine for reference:
Image
 
chakjer
just joined
Posts: 4
Joined: Thu Apr 22, 2021 7:41 pm

Re: RouterSCRIPTS - A collection of scripts for RouterBOARD devices

Thu Oct 28, 2021 12:14 am

Hi,
I found some workaround this problem.
First I added static routes for pinged hosts
/ip route
add distance=100 dst-address=8.8.4.4/32 gateway=GW2
add distance=100 dst-address=8.8.8.8/32 gateway=GW1
then modified mod_failover
75c75
< global FailoverTarget
---
> global FailoverTarget {8.8.8.8; 8.8.4.4}

84,85c84,85
< #Ping the target through that interface and if it succeeds
< if ([ping $FailoverTarget count=1 interface=$WANInterface]>0) do={
---
> foreach host in=$FailoverTarget do={
> if ([ping $host count=1 interface=$WANInterface]>0) do={
88a89,90
> }
It is a quick fix and ugly but I realized that my failover stop working.

Who is online

Users browsing this forum: No registered users and 12 guests