DIY Loopdetect

問題の本質


イーサネットネットワークの最悪の悪化の1つは、いわゆるループです。 (主に人的要因による)ネットワークトポロジでリングが形成されるときに発生します。 たとえば、2つのスイッチポートがパッチコードで接続されていた(2つのスイッチが1つに置き換えられて、すべてを固定しているように見えない場合によく発生します)または、新しい回線でノードを起動し、古いものを切断するのを忘れました(結果は悲しく、特定するのが困難です)。 このようなループの結果、パケットが増加し始め、スイッチングテーブルが失われ、雪崩のようなトラフィックの増加が始まります。 このような状況では、ネットワーク機器がフリーズし、ネットワークが完全に混乱します。



これらのループに加えて、ポート(スイッチまたはネットワークカード)が燃え尽きると、受信したパケットがネットワークに返され始め、ほとんどの場合、接続が10Mでネゴシエートされ、ケーブルが切断されていてもリンクがアップすることは珍しくありません。 セグメントにポートが1つしかない場合、結果はそれほど嘆かわしいものではないかもしれませんが、依然として非常に敏感です(VistaおよびSevenのユーザーは特にひどく苦しんでいます)。 いずれにせよ、このようなことを行うと、短時間でも意図的にまたは誤ってループを作成することで、ネットワークセグメント全体を切断できるという事実を容赦なく戦い、理解する必要があります。



マテリエル


幸いなことに、ほとんどの最新のマネージドスイッチは、何らかの形でループ検出機能(loopdetect、stp)を備えており、さらにプロトコルのstpファミリにより、リングトポロジを具体的に構築できます(フォールトトレランスと信頼性を向上させるため)。 しかし、コインには裏返しがあり、1つの焼けたポートが通信なしでエリア全体から出ることがよくあります。 または、同じstpで、トポロジはすぐに再構築されず、この時点での接続は、もちろん、多くの要望を残します。 さらに、一部のメーカーは、ループ検出プロトコルの実装に非常に怠慢です。たとえば、DES-3016(glink)は、2つのポートを接続するだけではループをまったく決定できないと言います。



識別の原則


ループ検出(loopdetect)の原理は非常に簡単です。 ブロードキャストアドレス(すべてのユーザー向け)を使用して特別なパケットがネットワークに送信されます。戻った場合、このインターフェイスの背後のネットワークはループバックされていると考えられます。 次に行うことは、機器の種類と設定によって異なります。 ほとんどの場合、ポートは完全にまたは部分的に(別のVLANで)ブロックされ、イベントがログに記録され、SNMPトラップが送信されます。 これがシステム管理者と緊急サービスの出番です。



ネットワーク全体が管理可能であれば、ループを特定して排除することは難しくありません。 ただし、5〜6個のアンマネージドスイッチのチェーンが1つのポートに接続されているネットワークはそれほど多くありません。 このようなループを削除するには、多くの時間と労力がかかります。 検索プロセスは、ポートの順次無効化(有効化)に要約されます。 ループの存在を判別するには、優れた管理対象スイッチまたはスニファー(wireshark、tcpdump)のいずれかが使用されます。 最初の方法は、ロックのオンとオフの間に遅延があるため非常に危険です。最良の場合、ユーザーは単に遅れをとるだけであり、最悪の場合、ループ検出はラインでより高く動作し、より大きなセグメントが落ちるでしょう。 2番目のケースでは、ユーザーに危険はありませんが、ループの存在を判断することははるかに困難です(特に、ブロードキャストトラフィックがほとんどない小さなセグメントで)、それにもかかわらず、スニファーは定義上、受動的です。



自分でやる


前述のように、ループ検索のハードウェア実装で十分です。 ためらうことなく、wiresharkをオンにし、フィルターを構成して、スイッチの機能と方法を確認します。 実際には、すべてが単純です:宛先アドレスcf:00:00:00:00:00 、タイプ0x9000CTP )、および不明な機能番号256(見つかったドキュメントには2つだけが記載されています)でポートにイーサネットパケットが送信されます。 宛先アドレスはブロードキャストであるため、ネットワークにループがある場合は、このパケットのコピーをいくつか戻す必要があります。



最初にライブラリを決定しました:



さらに、すべてが簡単でシンプルです。 選択したインターフェイスでpcapy.open_liveクラスのインスタンスを作成し、それにフィルターを追加します。 定期的にパケットを送信する最初のループを作成し、その中に返されたパケットをキャプチャして処理する2番目のループを作成します。 キャプチャされたパケットが送信されたパケットと同一である場合、+ 1がカウンターに追加されます。 タイムアウトの期限が切れた後、パケットの複数のコピーが受信されると、サウンドが再生され、コンソールにループメッセージが表示されます。



結果のスクリプトは後で見つけることができます。

import pcapy, dpkt , sys import time , random, socket import pyaudio , wave def packetBody(length): rez = [] for x in range(0,length): rez.append(random.choice('0123456789abcdef') + random.choice('0123456789abcdef')) return rez class loopDetector: packetCount = 0 loopCount = 0 timeout = 1 def __init__(self,iface): self.iface = iface self.pcaper = pcapy.open_live(iface,100,1,500) self.Mac = '00:19:5b:'+':'.join(packetBody(3)) self.pcaper.setfilter('ether dst cf:00:00:00:00:00 and ether src %s' % self.Mac) wf = wave.open('alarm.wav', 'rb') self.pyA = pyaudio.PyAudio() self.stream = self.pyA.open(format = self.pyA.get_format_from_width(wf.getsampwidth()), channels = wf.getnchannels(), rate = wf.getframerate(), output = True) self.wfData = wf.readframes(100000) wf.close() def __del__(self): self.stream.stop_stream() self.stream.close() self.pyA.terminate() def PlayAlarm(self): self.stream.write(self.wfData) def Capture(self,hdr,data): if data == str(self.sPkt): self.packetReceived += 1 def Process(self): while 1: try: pktData = '00000001' + ''.join(packetBody(42)) self.sPkt = dpkt.ethernet.Ethernet(dst="cf0000000000".decode('hex'), src=''.join(self.Mac.split(':')).decode('hex'), type=36864,data=pktData.decode('hex')) endTime = time.time() + self.timeout print "Send packet to %s" % self.iface self.packetCount += 1 self.pcaper.sendpacket(str(self.sPkt)) self.packetReceived = 0 while time.time() < endTime: try: self.pcaper.dispatch(-1,self.Capture) except socket.timeout: pass if self.packetReceived > 1: self.loopCount += 1 print "Loop Detected. Duplication found %s" % self.packetReceived self.PlayAlarm() except KeyboardInterrupt: break print "Packets sent: ", self.packetCount , "Loops discovered : " , self.loopCount def main(): dev_list = {} n = 0 iface = '' for x in pcapy.findalldevs(): dev_list[n] = x n += 1 try: iface = dev_list[0] except KeyError: print "No device found" exit(1) if len(sys.argv) == 2: try: if sys.argv[1] in ['list','ls','all']: for x in dev_list: print 'Index:', x, 'Device name:' ,dev_list[x] return 0 else: iface = dev_list[int(sys.argv[1])] except KeyError: print "Invalid device id, trying use first" iface = dev_list[0] ld = loopDetector(iface) ld.Process() if __name__ == "__main__": main()
      
      







オリジナルとソースへのリンク



All Articles