I wrote some code a few years ago that I have been running to do per-route checking along with distance adjustments (weighting for different lines).
You can use any RouterOS command for the route check by creating a comment on the route that starts with "ROUTE_CHECK:". This allows you to use specific routing tables and have compound statements. On my systems, I ping two addresses and sum their status (which gives either a 0, 1, or 2) and anything other than 0 will be success. If it returns 0, it will either be disabled or have the distance adjusted depending on the variables minDistance/maxDistance being there. All of this can be tied together to give "priorities" for lines that are up, such that there can always be a last resort available.
This is all run via the scheduler every 5s.
add check-gateway=ping comment="ROUTE_CHECK: :global minDistance 5; :global maxDistance 55; return ([/ping PUBLICIP1 count=1 interface=OutsideComcast routing-table=Comcast]+[/ping PUBLICIP2 count=1 interface=OutsideComcast routing-table=Comcast])" distance=5 gateway=GATEWAYMASKED routing-mark=PhoneInternet
add check-gateway=ping comment="ROUTE_CHECK: :global minDistance 10; :global maxDistance 50; return ([/ping PUBLICIP1 count=1 interface=OutsideVerizon routing-table=Verizon]+[/ping PUBLICIP2 count=1 interface=OutsideVerizon routing-table=Verizon])" distance=10 gateway=GATEWAYMASKED routing-mark=PhoneInternet
add check-gateway=ping comment="ROUTE_CHECK: :global minDistance 5; :global maxDistance 50; return ([/ping PUBLICIP1 count=1 interface=OutsideVerizon routing-table=Verizon]+[/ping PUBLICIP2 count=1 interface=OutsideVerizon routing-table=Verizon])" distance=5 gateway=GATEWAYMASKED pref-src=SRCMASKED
add check-gateway=ping comment="ROUTE_CHECK: :global minDistance 10; :global maxDistance 55; return ([/ping PUBLICIP1 count=1 interface=OutsideComcast routing-table=Comcast]+[/ping PUBLICIP2 count=1 interface=OutsideComcast routing-table=Comcast])" distance=10 gateway=GATEWAYMASKED
If anyone wants to try it - add this scheduler/script. I'm pasting a more readable version below as well.
/system script add name=route_check owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive source=":foreach k in [/ip route find comment~\"ROUTE_CHECK\"] do={\
\n:local actualroute [:tostr [/ip route get \$k]]\
\n:local disabled [/ip route get \$k disabled]\
\n:local comment [/ip route get \$k comment]\
\n:local distance [/ip route get \$k distance]\
\n:local cmd [:pick \$comment 13 [:len \$comment]]\
\n:local parsedcmd [:parse \"\$cmd\"]\
\n:global minDistance \"\"\
\n:global maxDistance \"\"\
\n:global stepDistance 5\
\n:local status [\$parsedcmd]\
\n:set minDistance [:tonum \$minDistance]\
\n:set maxDistance [:tonum \$maxDistance]\
\n#:put \"STATUS: \$status \$[:type \$status]\"\
\n:if ([:type \$status] != \"num\") do={\
\n\t:error \"\$comment returned non-number type (need return on expression\?)\"\
\n}\
\nif ([:type \$minDistance] != \"nil\" && [:type \$maxDistance] != \"nil\") do={\
\n\tif (\$minDistance >= \$maxDistance) do={\
\n\t\t:error \"minDistance >= maxDistance\"\
\n\t}\
\n\t:local newDistance \$distance\
\n\tif (\$status = 0) do={\
\n\t\t:set newDistance (\$distance + \$stepDistance)\
\n\t}\
\n\tif (\$status != 0) do={\
\n\t\t:set newDistance (\$distance - \$stepDistance)\
\n\t}\
\n\tif (\$newDistance < \$minDistance) do={ :set newDistance \$minDistance; }\
\n\tif (\$newDistance > \$maxDistance) do={ :set newDistance \$maxDistance; }\
\n\tif (\$newDistance != \$distance) do={\
\n\t\t/log warning \"distance (\$status): \$distance [\$minDistance, \$maxDistance] -> \$newDistance - \$actualroute\"\
\n\t\t/ip route set \$k distance=\$newDistance\
\n\t}\
\n} else {\
\n\t:if (\$status = 0 && !\$disabled) do {\
\n \t\t/ip route disable \$k\
\n\t\t/log warning \"Command: \$cmd status=\$status\"\
\n\t\t/log warning \"distance (\$status): \$distance DISABLE - \$actualroute\"\
\n\t} else {\
\n\t\t:if (\$status != 0 && \$disabled) do {\
\n\t\t\t/ip route enable \$k\
\n\t\t\t/log warning \"distance (\$status): \$distance ENABLE - \$actualroute\"\
\n\t\t}\
\n\t}\
\n}\
\n}"
/system scheduler add interval=5s name=route_check on-event=route_check policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive start-date=feb/09/2016 start-time=03:39:20
:foreach k in [/ip route find comment~"ROUTE_CHECK"] do={
:local actualroute [:tostr [/ip route get $k]]
:local disabled [/ip route get $k disabled]
:local comment [/ip route get $k comment]
:local distance [/ip route get $k distance]
:local cmd [:pick $comment 13 [:len $comment]]
:local parsedcmd [:parse "$cmd"]
:global minDistance ""
:global maxDistance ""
:global stepDistance 5
:local status [$parsedcmd]
:set minDistance [:tonum $minDistance]
:set maxDistance [:tonum $maxDistance]
#:put "STATUS: $status $[:type $status]"
:if ([:type $status] != "num") do={
:error "$comment returned non-number type (need return on expression?)"
}
if ([:type $minDistance] != "nil" && [:type $maxDistance] != "nil") do={
if ($minDistance >= $maxDistance) do={
:error "minDistance >= maxDistance"
}
:local newDistance $distance
if ($status = 0) do={
:set newDistance ($distance + $stepDistance)
}
if ($status != 0) do={
:set newDistance ($distance - $stepDistance)
}
if ($newDistance < $minDistance) do={ :set newDistance $minDistance; }
if ($newDistance > $maxDistance) do={ :set newDistance $maxDistance; }
if ($newDistance != $distance) do={
/log warning "distance ($status): $distance [$minDistance, $maxDistance] -> $newDistance - $actualroute"
/ip route set $k distance=$newDistance
}
} else {
:if ($status = 0 && !$disabled) do {
/ip route disable $k
/log warning "Command: $cmd status=$status"
/log warning "distance ($status): $distance DISABLE - $actualroute"
} else {
:if ($status != 0 && $disabled) do {
/ip route enable $k
/log warning "distance ($status): $distance ENABLE - $actualroute"
}
}
}
}