Mikrotik:バックアップインターネットチャネルに切り替えるスクリプト

メインのインターネットが消えたときにバックアップインターネットに切り替え、メインのインターネットが再び機能し始めたらすぐに戻るためのスクリプトを共有したいと思います。 チャネルは一度に1つずつ使用可能で、ここでは負荷分散が行われないことをすぐに言わなければなりません。 両方のチャネルはPPP接続です(私の場合、1つは有線で、2つ目は3Gホイッスルです)。 スクリプトは最も柔軟な監視ツールとして特別に作成されました。他のオプション、特にチェックゲートウェイは、私にとって完全に正しいわけではないからです。



基本的な原則は単純です。VPNチャネルを上げても、インターネットがそれを介して機能することを意味するわけではありません。 いくつかの外部アドレスにpingを送信して確認します。 pingが作業の指標ではない場合に思い付くことができますが、スクリプトでは、状況に応じて他の検証方法を指定できます。 その他の機能:バックアップチャネルはモバイルネットワークであり、メインチャネルがない場合にのみ接続し、それ以外の時間はインターフェイスがオフになります。 メインチャネルに戻ると、その操作性が正しくチェックされます。 インターフェイスを使用したpingとは異なる手法。 さて、インターフェイスのルート距離は動的に変化し、常に等しくないため、チャネルが同時に機能することは可能ですが、トラフィックはそのうちの1つにのみ向けられます。



理解してください。プロバイダーまたはプロバイダーのいずれかが静的を提供する場合、スクリプトを簡単にやり直すことができます。



したがって、スクリプトが機能するために必要な設定を一貫して説明し、次に作業の主要なポイントを細かく説明します。 最後はスクリプト全体です。



ISP1(メイン)とISP2(バックアップ)の2つのPPP接続があるとします。両方とも設定され、別々に動作します。 dial-on-demand = noおよびadd-default-route = yesをそれらに設定し、ISP2のdefault-route-distanceパラメーターをISP1よりも1つ多く設定します 。 NATなどの標準的なものを設定し、リクエストの送信元と同じインターフェイス上で応答用のパケットと接続をマークし、マークされたパケットのルートを設定します。



準備する
/ip firewall mangle add action=mark-connection chain=forward connection-mark=no-mark \ in-interface=ISP1 new-connection-mark=ISP1 passthrough=no add action=mark-routing chain=prerouting connection-mark=ISP1 in-interface=\ bridge-local new-routing-mark=to_ISP1 passthrough=no add action=mark-connection chain=forward connection-mark=no-mark \ in-interface=ISP2 new-connection-mark=ISP2 passthrough=no add action=mark-routing chain=prerouting connection-mark=ISP2 in-interface=\ bridge-local new-routing-mark=to_ISP2 passthrough=no /ip firewall nat add action=masquerade chain=srcnat out-interface=ISP1 add action=masquerade chain=srcnat out-interface=ISP2 /ip route add distance=1 gateway=ISP1 routing-mark=to_ISP1 add distance=1 gateway=ISP2 routing-mark=to_ISP2
      
      





また、ルーターのローカルアドレスは192.168.xx.yy、サブネットは192.168.xx.0 / 24であると想定しています。 このデータは、インターフェースの名前と同様に、独自のものに変更する必要があります。 これは全体のセットアップではなく、すべての順序についてです。



変数
 global FailoverTimes; global FailoverLastTime; global FailoverLastBackTime; local ifMain "ISP1"; local ifRes "ISP2"; local scriptName "Failover"; local state 0; local pingNum 0; local pingRes; local routeDist; local routeDist2; local tmp; local ip { xxxx; yyyy; zzzz }; local pingSrcAddr 192.168.xx.yy;
      
      





変数を定義します: ifMainおよびifResにインターフェイスの名前を書き込み、 pingSrcAddrにルーターのローカルアドレス(必要な理由は後で明らかになります)、およびip配列のチャネルをチェックするためにpingする3つの外部アドレスを書き込みます。



単一インスタンス
 if ( [len [/system script job find where script=$scriptName]] > 1) do= { error "single instance" }; delay 15;
      
      





スクリプトのコピーを1つだけ実行します。 RouterOSの開始時に起動する場合は、接続を確立する時間を与えてください。



少しスキップして、主要部分に移りましょう。 スクリプトは、停止するかエラーが発生するまで無限に実行されます。 無限ループでは、 状態変数を使用して現在の状態を分析し、必要なアクションを実行します。 それらを考慮してください。



状態0
  if ($state = 0) do= { do { if ($pingNum >= 3) do= { set $pingNum 0; } if ([ping ($ip->$pingNum) count=1] = 0) do= { set $pingRes [ping ($ip->0) count=2]; set $pingRes ($pingRes+[ping ($ip->1) count=2]); set $pingRes ($pingRes+[ping ($ip->2) count=2]); if ($pingRes = 0) do= { set $FailoverLastTime "$[/system clock get date] $[/system clock get time]"; set $FailoverTimes ([tonum $FailoverTimes] + 1) set $state 1; log info "$scriptName: state changed 0->1"; } } set $pingNum ($pingNum + 1); if ($state = 0) do= { delay 15 }; } while ($state = 0); }
      
      





状態0-メインチャネルが機能しているとき。 15秒ごとに1回、指定された3つのアドレスの1つを順番にチェックし、回答がない場合は3つのアドレスすべてをチェックします。 聴覚障害者-バックアップチャネルへの移行を開始します。 配列内のアドレスは3であることが厳密に示されています。そうでない場合は、修正する必要があります。



状態1
  if ($state = 1) do= { if ( [/interface l2tp-client get $ifMain default-route-distance] > 10) do= { /interface ppp-client set $ifRes default-route-distance=1; } /interface enable $ifRes; beep frequency=2000 length=250ms; delay 500ms; beep frequency=2000 length=250ms; delay 500ms; delay 6; /interface disable $ifMain; set $routeDist ([/interface ppp-client get $ifRes default-route-distance] + 1); /interface l2tp-client set $ifMain default-route-distance=$routeDist; /interface enable $ifMain; set $state 2; log info "$scriptName: state changed 1->2"; }
      
      





状態1-チャネルの切り替え。 どの特定のPPP接続が使用されるかが重要です。 この例では、ISP1はl2tp-clientであり、ISP2はppp-clientです。 他の場合は、 default-route-distanceの行でそれらを修正する必要があります



バックアップチャネルをオンにした後、7秒間待機します。 これは私にとって十分な時間であり、その間に3G接続が立ち上がります。 この間、現在の接続と新しい接続はタイムアウトでハングしますが、メインVPNはまだ切断されておらず、宛先到達不能ルーターの応答は最小限に抑えられます。



アマチュア向けのサウンド表示は、夜間に動作する場合があります。 そうでない場合は、削除します。



さらに、メインチャネルはオフになり、そのデフォルトルート距離はバックアップチャネルより1大きく設定され、再びオンになります。 このため、予約を介してインターネットに干渉することなく、メインチャネルの復帰を待つことができます。



今後、メインチャネルに切り替えて予約を切断すると、 デフォルトルート距離が1ずつ増加します。ルート距離を切り替えるたびに、PPP接続が順次増加します。 それらが行き過ぎないようにするために、ここで現在の値がチェックされ、10を超えると1のリセットが発生します(たとえば、理論的には最大約250)。



状態2
  if ($state = 2) do= { do { if ( [len [interface find where name=$ifMain and running] ] = 1) do= { set $pingRes [ping ($ip->0) src-address=$pingSrcAddr count=2]; set $pingRes ($pingRes+[ping ($ip->1) src-address=$pingSrcAddr count=2]); set $pingRes ($pingRes+[ping ($ip->2) src-address=$pingSrcAddr count=2]); if ($pingRes > 0) do= { set $state 3; log info "$scriptName: state changed 2->3"; } } if ($state = 2) do= { delay 15 }; } while ($state = 2); }
      
      





状態2-メインチャネルの復元を待機しています。 保護区の状態がおもしろくないことは注目に値します。 彼が接続しなかった場合、何もすることはなく、彼のためのすべての条件が作成されます。実際、私たちはメインチャンネルにのみ興味があります。



ここでは、メインチャネルのVPNが引き上げられることが予想され、その後、アクティブな予約を使用して外部アドレスへのpingが試行されます。 それは難しくなりますが、正しいです。 ping xx.xx.xx.xx interface = $ ifMainを記述した場合、開発者によると、これは機能する場合と機能しない場合があります。 ここで、pingはルーターのローカルアドレスから使用されます。 それは常にそこにあると仮定され、そうでなければルーターが必要です。 プロバイダーが動的に提供するため、メインチャネルの外部アドレスは使用しませんでした。 ルートが非アクティブ(ルート距離がバックアップよりも大きい)場合でも、メインチャネルを介してこのようなpingを送信するようにルーターに指示する方法を見つけます。



チューニング
 /ip firewall mangle add action=mark-routing chain=output comment=Failover_script_rule \ dst-address=!192.168.xx.0/24 new-routing-mark=to_ISP1 passthrough=no \ protocol=icmp src-address=192.168.xx.yy /ip route rule add action=lookup-only-in-table routing-mark=to_ISP1 src-address=\ 192.168.xx.yy/32 table=to_ISP1
      
      





ここで使用されるpingトラフィックは非標準です。 これは、ルーター自体から外部アドレスへの出力トラフィックです。 通常、このような場合、ルーターはパケットがsrc-addressに向かうインターフェースのアドレスを取得します 。 ルーターのローカルアドレスをsrc-addressとして指定することにより、lokalkaが座っているのと同じNATに対してそれを取り出すように見えます。 さらに、このようなトラフィックはメインチャネルのルーティングマークでタグ付けされ、パケットはラベル付きのルートのためにメインチャネルを通過します。



2番目のルールも必要です。 これがないと、メインチャンネルが突然再び落ちた場合、 to_ISP1とマークされたpingでも、バックアップチャンネルラベルなしでルートに沿って移動し、メインチャンネルに正しく戻りません。 これは、RouterOSがどのように機能するかです。チャネルが接続されていない場合、ルートはマークされていても無効になります。 少し明確にするために、 状態 = 2を想定します。メインチャネルはアップしていますが、トラフィックは通過しません。 この場合、pingに6秒かかります。 したがって、この時点でメインチャネルがオフになっている場合、pingはリザーブを通過し始めます。 2番目のルールはこれを除外します。



ルーターからLANへのpingはマークされておらず、通常どおり動作することに注意してください。



状態3
  if ($state = 3) do= { /interface disable $ifRes; set $routeDist ([/interface l2tp-client get $ifMain default-route-distance] + 1); /interface ppp-client set $ifRes default-route-distance=$routeDist; set $state 0; set $FailoverLastBackTime "$[/system clock get date] $[/system clock get time]"; log info "$scriptName: state changed 3->0"; beep frequency=500 length=500ms; }
      
      





状態3-メインチャネルへの移行。 pingがメインチャネルを通過し始めたら、バックアップVPNをオフにするだけで、メインVPNが使用されます。 次に、バックアップのdefault-route-distanceをメインよりも1大きく変更し、音声信号を送ります。 PPP接続のタイプに注意を払い、必要に応じて変更します。



この時点で、サイクルは閉じて状態0に戻ります。



ここで、スクリプトを実行するときに、現在の状態を確認する方法について説明します。



初期状態
 set $routeDist [/interface l2tp-client get $ifMain default-route-distance]; set $routeDist2 [/interface ppp-client get $ifRes default-route-distance]; if ($routeDist < $routeDist2) do= { if ( [/interface get $ifMain running] = true) do= { set $state 0; } else= { set $state 1; } } else= { if ( [/interface get $ifMain disabled] = true) do= { /interface enable $ifMain; } if ($routeDist > $routeDist2 and [/interface get $ifRes disabled] = false) do= { set $state 2; } else= { set $state 3; } } log info "$scriptName: initial state $state";
      
      





ここでは、ロジックも一見複雑です。 ISP1が実行されているかどうか、ISP2が実行されているかどうか、およびデフォルトのルート距離関係を持つ3つのパラメーターが分析されます。 初期状態1および3は非標準であり、構成が正しくないことを示していますが、この場合、不必要な切り替えによっても、スクリプト自体がすべてを復元します。



除外条件
除外した条件がもう1つあります。 ほとんどの場合、ほとんどの人にはほとんど必要ありません。 私のISP1は、IPではなく名前でVPNを接続してこの名前を解決するために、同じプロバイダーのDNSを使用する必要があります。 ローカルアドレスに解決されます。 また、特定のDNSを示す解決でスクリプトを支援しない場合、ISP1ネットワークが利用可能になった後でも、接続しません。 ドメイン名は解決されませんが、DNSリザーブは引き続き使用されます。 これは余分です。 状態:



  if ($state = 2) do= { do { if (([ping DNSip1 count=1] > 0) or ([ping DNSip2 count=1] > 0)) do= { set $tmp 0; do { resolve VPNaddress server=DNSip1; } on-error= { }; do { resolve VPNaddress server=DNSip2; } on-error= { }; do { resolve VPNaddress } on-error= { set $tmp 1; }; if ($tmp = 0) do= { set $state 3; log info "$scriptName: state changed 2->3"; delay 5; } } if ($state = 2) do= { delay 15 }; } while ($state = 2); }
      
      





DNSip1、DNSip2、VPNaddressの代わりに、必要なデータを置き換えます。 以下のすべての状態は、それぞれ+1シフトします。





これは基本的にすべて、6.26およびRB951G-2HnDで開発およびデバッグされています。 他のバージョンでは-私は約束しません、そしてチームの前に ':'がないことを残念に思います。



私の構成では、このスクリプトと組み合わせて、1分に1回、スケジュールに従って実行される別のスクリプトが機能します。 彼は、このスクリプトが実行されているかどうかを確認し、変更されたときにメールでIPアドレスをさらに送信します。 これは小さな例ですが、最初の部分のみです:



スクリプトモニター
 global FailoverDisabled; if ( [len [/system script job find where script="Failover"]] = 0 and $FailoverDisabled != 1) do= { do { execute script="Failover"; } on-error= { log info "$scriptName: Failed to execute Failover" }; }
      
      





グローバル変数は、フェールオーバースクリプトの起動を無効にすることができます。 また、スケジュールにより、ルーターが予期せず再起動した場合、スクリプトは自動的に再起動されます。



フェールオーバースクリプト全体
 global FailoverTimes; global FailoverLastTime; global FailoverLastBackTime; local ifMain "ISP1"; local ifRes "ISP2"; local scriptName "Failover"; local state 0; local pingNum 0; local pingRes; local routeDist; local routeDist2; local tmp; local ip { xxxx; yyyy; zzzz }; local pingSrcAddr 192.168.xx.yy; if ( [len [/system script job find where script=$scriptName]] > 1) do= { error "single instance" }; delay 15; set $routeDist [/interface l2tp-client get $ifMain default-route-distance]; set $routeDist2 [/interface ppp-client get $ifRes default-route-distance]; if ($routeDist < $routeDist2) do= { if ( [/interface get $ifMain running] = true) do= { set $state 0; } else= { set $state 1; } } else= { if ( [/interface get $ifMain disabled] = true) do= { /interface enable $ifMain; } if ($routeDist > $routeDist2 and [/interface get $ifRes disabled] = false) do= { set $state 2; } else= { set $state 3; } } log info "$scriptName: initial state $state"; do { if ($state = 0) do= { do { if ($pingNum >= 3) do= { set $pingNum 0; } if ([ping ($ip->$pingNum) count=1] = 0) do= { set $pingRes [ping ($ip->0) count=2]; set $pingRes ($pingRes+[ping ($ip->1) count=2]); set $pingRes ($pingRes+[ping ($ip->2) count=2]); if ($pingRes = 0) do= { set $FailoverLastTime "$[/system clock get date] $[/system clock get time]"; set $FailoverTimes ([tonum $FailoverTimes] + 1) set $state 1; log info "$scriptName: state changed 0->1"; } } set $pingNum ($pingNum + 1); if ($state = 0) do= { delay 15 }; } while ($state = 0); } # endof if state = 0 if ($state = 1) do= { if ( [/interface l2tp-client get $ifMain default-route-distance] > 10) do= { /interface ppp-client set $ifRes default-route-distance=1; } /interface enable $ifRes; beep frequency=2000 length=250ms; delay 500ms; beep frequency=2000 length=250ms; delay 500ms; delay 6; /interface disable $ifMain; set $routeDist ([/interface ppp-client get $ifRes default-route-distance] + 1); /interface l2tp-client set $ifMain default-route-distance=$routeDist; /interface enable $ifMain; set $state 2; log info "$scriptName: state changed 1->2"; } if ($state = 2) do= { do { if ( [len [interface find where name=$ifMain and running] ] = 1) do= { set $pingRes [ping ($ip->0) src-address=$pingSrcAddr count=2]; set $pingRes ($pingRes+[ping ($ip->1) src-address=$pingSrcAddr count=2]); set $pingRes ($pingRes+[ping ($ip->2) src-address=$pingSrcAddr count=2]); if ($pingRes > 0) do= { set $state 3; log info "$scriptName: state changed 2->3"; } } if ($state = 2) do= { delay 15 }; } while ($state = 2); } # endof if state = 2 if ($state = 3) do= { /interface disable $ifRes; set $routeDist ([/interface l2tp-client get $ifMain default-route-distance] + 1); /interface ppp-client set $ifRes default-route-distance=$routeDist; set $state 0; set $FailoverLastBackTime "$[/system clock get date] $[/system clock get time]"; log info "$scriptName: state changed 3->0"; beep frequency=500 length=500ms; } # bad programming protection delay 1; } while= ( true );
      
      






All Articles