2つのMikrotikインターネットチャネルを切り替えるためのユニバーサルスクリプト

約2.5年前、インターネットチャネルをバックアップチャネルに自動的に切り替えるというトピックに関する記事を書きまし 。 もちろん、今日までのスクリプトは「完全に」動作しますが、その外観といくつかのニュアンスは...

そのため、タスクはスクリプトを改善し、副作用を可能な限り排除することでした。 さあ、始めましょう。

画像

Mikrotik RB850Gx2を自由に使用して、スクリプトを作成します( RB450GおよびRB951G-2HnDモデルでパフォーマンスをテスト済みです)。

まず、新しいスクリプトを追加します
画像

スクリプトに名前を付けます。たとえば、 script-check-inet


その中で何を使用しますか?

したがって、まず最初に、必要な変数を定義しましょう。

:local firstInterface "pppoe-rostelecom"; :local secondInterface "eth2-MTS"; :local pingTo1 "8.8.8.8"; :local pingTo2 "217.112.35.75"; :local pingCount 5; :local stableConnectFrom 70; :local prefix ">>> "; :local firstInterfaceName $firstInterface; :local secondInterfaceName $secondInterface; 

ここで:
$ firstInterfaceは、メインラインPPPoE接続の名前です。
$ secondInterfaceは、冗長イーサネット接続の名前です。
$ pingTo1$ pingTo2は、「キック」するリソースのIPアドレスです。
$ pingCount -IPアドレスごとのpingの数
$ stableConnectFrom-インターネットチャネルの「安定性」を決定する割合。 たとえば、私たちの場合、pingパケットの30%以上が失われた場合( `$ pingStatus <$ stableConnectFrom`、以下でさらに説明します)、チャネルはバックアップに切り替えられます。
$プレフィックス -これを使用してログを表示し、目的のテキストを表示できるようにします。

$ firstInterfaceName-前の例にあった「[get $ firstInterface gateway]」の形式の構築を除外するために、インターフェイスの名前を「記憶」する変数

ARPテーブルをクリアせずにルート距離を変更すると、エラーが発生します。コード内にはこのテーブルをクリアするために呼び出す必要がある場所がいくつかあるため、関数内でコードをフォーマットします。

 :local clearArp do={ :local dumplist [/ip arp find] :foreach i in=$dumplist do={ /ip arp remove $i } :log info ($prefix . "ARP cleaned"); } 


さらに、コードの行を減らすために、 `/ ip route {}`構造を使用します。この構造内では、たとえば `/ interface pppoe-client ...`など、他のルートコマンドを呼び出すことはできません。 したがって、PPPoE接続を再接続する別の関数を作成します。

 # Function to reconnect PPPoE connection :local reconnectPPPoE do={ /interface pppoe-client set $nameInterface disable=yes; :delay 1s; /interface pppoe-client set $nameInterface disable=no; } 


私たちの場合、メインラインとしてRostelecomが使用されています。これは、内部ラインが正常に機能するにもかかわらず、インターネットを定期的にシャットダウンする「トリック」を持っています。 これは、RostelecomのSIPサーバーを使用して実験的に達成されましたが、決して落ちませんでした。
一般に、接続を再接続する機能を作成しました。 どうぞ

主要部分の前に、「愚か者に対する保護」を追加します。 突然インターフェイスがオフになるか、距離が正しくありません...

したがって、インターフェイスがアクティブかどうかを確認し、アクティブでない場合はアクティブにします。

 # Check FIRST interface /interface pppoe-client { :if ( [get $firstInterface disable] = true) do={ set $firstInterface disable=no; :delay 5s; } } # Check SECOND interface /interface ethernet { :if ( [get $secondInterface disable] = true) do={ set $secondInterface disable=no; } } 


しかし、それだけではありません! 次に、設定されたルート距離を確認します。

 /ip route { # Set objects to variables :set firstInterface [find dst-address="0.0.0.0/0" gateway=$firstInterfaceName]; :set secondInterface [find dst-address="0.0.0.0/0" gateway=$secondInterfaceName]; # Check routes :if ( [get $firstInterface distance] != 2 ) do={ set $firstInterface distance=2; :log info ($prefix . "Distance for " . $firstInterfaceName . " corrected"); } :if ( [get $secondInterface distance] != 1 && [get $secondInterface distance] != 3) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . $secondInterfaceName . " corrected"); } # ... body ... } 


メイン部分は `/ ip route`で動作するため、「愚か者に対する保護」を最初に追加しました。
この例では、 `dstで2つのルートを使用します。 アドレスが「0.0.0.0 / 0」に等しい、インターフェイスの名前と「距離」によるゲートウェイ。

スクリプトの機能:

まず、各ルートのオブジェクトを変数に割り当てて、記述するコードを減らします。
次に、メインルート(Rostelecom)の距離を確認します。 値「2」を使用します。

その後、バックアップルートの距離を確認します-「1」または「3」の距離の値を取ることができます。 これにより、メインチャネルの距離を変更する必要がないため、距離を変更するタスクが簡素化されます。

距離を確認し、正しい距離を設定しました。 次は?

pingチェック


インターフェイスは非アクティブである可能性があるため、初期状態では、ping検証だけでなくインターフェイスステータスの構成も使用します。

 /ip route { # ... :local pingStatus \ ((( [/ping $pingTo1 interface=$firstInterfaceName count=$pingCount] + \ [/ping $pingTo2 interface=$firstInterfaceName count=$pingCount] ) / ($pingCount * 2)) * 100); :if ( [get $firstInterface active] = false or $pingStatus < $stableConnectFrom) do={ # ... } # ... } 


チャネルの「安定性」を識別するためにパーセンテージを入力することが決定されたため、ローカル変数 `$ pingStatus`が追加されました。これにより、成功したパケットのパーセンテージがそれらの総数に保存されます

つまり、インターフェイスがオフになっている場合、または成功したパケットの割合が「$ stableConnectFrom」変数で指定したものよりも低い場合、インターネットは「ドライ」であると見なされます。

「[/ ping $ pingTo1 interface = $ firstInterfaceName count = $ pingCount]」という行に注意してください。ここで、「interface = ...」は、pingを確認するためにハード指定されたインターフェイスです。 メインのものが「落ちる」ので、その名前を示します。 この方法により、ファイアウォールタブで特定のIPアドレスを持つ特定のチャネルのトラフィックブロックルールを追加する必要がなくなります。

この記事のコメンテーター( icCEおよびmafet )は、1つのリソースに対するpingが頻繁に消えることがあることに注意を喚起したため、「キック」用に2つのリソースを導入しました。

  :if ( [get $firstInterface active] = false or $pingStatus < $stableConnectFrom) do={ :log info ($prefix . "FIRST NO INTERNET!!!"); # Change distance :if ( [get $secondInterface distance] != 1 ) do={ set $secondInterface distance=1; :log info ($prefix . "Distance for " . $secondInterfaceName . " changed"); $clearArp; } $reconnectPPPoE nameInterface=$firstInterfaceName; } 


最初に、インターネットがないというログを表示してから、バックアップチャンネルの距離を変更し、関数を呼び出してARPテーブルを消去します。
その後、PPPoE接続を再接続する機能を呼び出します。 (変数を生成しないように)1つの場所で接続の名前を指定するため、関数はインターフェイスの名前を含む変数の採用を考慮して記述されます。 したがって、 `nameInterface`パラメーターで関数を呼び出すときに、必要なPPPoEインターフェイスの名前を渡します。

インターネットが「現れた」、またはすべてを取り戻す方法


この時点で、 `if else`関数を呼び出します:

 /ip route { # ... :if ( [get $firstInterface active] = false or $pingStatus < $stableConnectFrom) do={ # ... } else={ :log info ($prefix . "FIRST INTERNET CONNECTED"); # Change distance :if ( [get $secondInterface distance] != 3 ) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . $secondInterfaceName . " changed"); $clearArp; } } # ... } 


機能の最初の部分と同様に、ネットワークの可用性に関するメッセージを表示した後、バックアップチャネルの距離を変更し、ARPテーブルを消去します。

実行方法



シェダーでレコードを作成します
画像

画像


「名前」フィールドに、混乱しないようにエントリの名前を入力します。
フィールド「開始時刻」に「00:00:00」を設定して、真夜中に開始します。
間隔-30秒
[イベント時]フィールドにスクリプトの名前を入力します-` script-check-inet`
OKをクリックします。

実際、それだけです!

ネタバレの下には完全なスクリプトコードがあります。

完全なスクリプトコード
 # Set local variables :local firstInterface "pppoe-rostelecom"; :local secondInterface "eth2-MTS"; :local pingTo1 "8.8.8.8"; :local pingTo2 "217.112.35.75"; :local pingCount 5; :local stableConnectFrom 70; :local prefix ">>> "; # Local variables :local firstInterfaceName $firstInterface; :local secondInterfaceName $secondInterface; # Function to cleaning ARP table :local clearArp do={ :local dumplist [/ip arp find] :foreach i in=$dumplist do={ /ip arp remove $i } :log info ($prefix . "ARP cleaned"); } # Function to reconnect PPPoE connection :local reconnectPPPoE do={ /interface pppoe-client set $nameInterface disable=yes; :delay 1s; /interface pppoe-client set $nameInterface disable=no; } :log info ($prefix . "START PING to $pingTo1 & $pingTo2"); # Check FIRST interface /interface pppoe-client { :if ( [get $firstInterface disable] = true) do={ set $firstInterface disable=no; :delay 5s; } } # Check SECOND interface /interface ethernet { :if ( [get $secondInterface disable] = true) do={ set $secondInterface disable=no; } } /ip route { # Set objects to variables :set firstInterface [find dst-address="0.0.0.0/0" gateway=$firstInterfaceName]; :set secondInterface [find dst-address="0.0.0.0/0" gateway=$secondInterfaceName]; # Check routes :if ( [get $firstInterface distance] != 2 ) do={ set $firstInterface distance=2; :log info ($prefix . "Distance for " . $firstInterfaceName . " corrected"); } :if ( [get $secondInterface distance] != 1 && [get $secondInterface distance] != 3) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . $secondInterfaceName . " corrected"); } # Get ping successfully packets. In percent :local pingStatus \ ((( [/ping $pingTo1 interface=$firstInterfaceName count=$pingCount] + \ [/ping $pingTo2 interface=$firstInterfaceName count=$pingCount] ) / ($pingCount * 2)) * 100); # Check Internet :if ( [get $firstInterface active] = false or $pingStatus < $stableConnectFrom) do={ :log info ($prefix . "FIRST NO INTERNET!!!"); # Change distance :if ( [get $secondInterface distance] != 1 ) do={ set $secondInterface distance=1; :log info ($prefix . "Distance for " . $secondInterfaceName . " changed"); $clearArp; } $reconnectPPPoE nameInterface=$firstInterfaceName; } else={ :log info ($prefix . "FIRST INTERNET CONNECTED"); # Change distance :if ( [get $secondInterface distance] != 3 ) do={ set $secondInterface distance=3; :log info ($prefix . "Distance for " . $secondInterfaceName . " changed"); $clearArp; } } } :log info ($prefix . "END PING"); 



UPD


当初、スクリプトはMikrotik RB450GおよびRB951G-2HnDデバイス ( `mipsbe`カーネル)で記述およびテストされていましたが、 RB850Gx2モデル( ` powerpc`カーネル)で起動すると、 の行でエラーが発生しました: `[/ ping $ pingTo interface = [get $ firstInterfaceゲートウェイ]カウント= 5] `、つまり、[get $ firstInterface gateway]` inside` [/ ping ...] `の処理の失敗。

追加の変数 `$ firstInterfaceName`を導入したため、この設計を変更し、リストされたすべてのデバイスで動作するスクリプトを実現しました。

UPD 2


スクリプトコードが再び更新されました。 esudnikは、アクティブなネットワーク接続で多数の失われたパケットの問題に注意を喚起しました。 したがって、追加の変数( `stableConnectFrom`)を導入し、ラインの「品質」の割合を決定するために使用しました。

この例では、変数の値は「70」に等しくなります。 つまり、正常に送信されたパケットの割合が70%未満の場合、スクリプトはバックアップチャネルをアクティブにします。

UPD 3


icCEが指摘したように、ファームウェア6.36rc10でスクリプトを使用すると、エラーが発生します。

  :local pingStatus \ (( [/ping $pingTo1 interface=$firstInterfaceName count=$pingCount] + \ [/ping $pingTo2 interface=$firstInterfaceName count=$pingCount] ) / ($pingCount * 2)) * 100; 


ファームウェアは「 * 100 」を好みません。 問題の解決策は簡単でした-計算された値を追加の括弧で囲んで、

  :local pingStatus \ ((( [/ping $pingTo1 interface=$firstInterfaceName count=$pingCount] + \ [/ping $pingTo2 interface=$firstInterfaceName count=$pingCount] ) / ($pingCount * 2)) * 100); 


記事のスクリプトテキストが更新されました。

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


All Articles