MAC-Adressensuche auf Juniper-Switches

Im lokalen Netzwerk müssen Sie häufig herausfinden, an welchem ​​Port des Switches sich die spezifische MAC-Adresse des Geräts befindet. Das Problem ist leicht zu lösen, wenn mehrere Switches im Netzwerk vorhanden sind. Wenn jedoch mehr als 30 Switches vorhanden sind, wird alles viel komplizierter. Ich möchte ein kleines Python-Skript freigeben, das nach der gewünschten MAC-Adresse im Netzwerk sucht und den Namen und den Port des Switches zurückgibt, auf dem dieser MAC registriert ist.



Konstruktive Kritik ist willkommen. Details unter dem Schnitt.

Wenn das Netzwerkdesign korrekt ausgeführt wird, dh der CORE- Root-Switch, mit dem die DS- Verteilungsschalter (Verteilungsschalter) verbunden sind, und mit diesen wiederum die Zugriffsebenenschalter AS (Zugriffsschalter). Diese Regel gilt nicht immer, Zugriffsschalter können in Reihe geschaltet werden. In jedem Fall enthält der Upstream-Switch-Port alle MAC-Adressen der an den Downstream-Switch angeschlossenen Geräte.

Wenn beispielsweise das für uns interessante Gerät mit dem AS3- Switch verbunden ist, finden wir diese Adresse beim Starten der Suche mit CORE an dem Port, der zu DS1 führt . Wenn wir zu DS1 gehen , finden wir diesen MAC an dem Port, der zu AS2 führt , und zu AS2 . Wir werden sehen, dass er uns zu AS3 führt , und nur bei AS3 finden wir den spezifischen Port, an den das interessierende Gerät angeschlossen ist.

Ich wollte nicht alles von Hand machen, alle Schalter in einer Schleife sortieren und feststellen, wo sich der Uplink befindet und wo nicht. Daher wurde die nächste Lösung geboren, die ich teilen möchte.

Ein bisschen Theorie.


Führen Sie den folgenden Befehl aus, um den MAC 08: 62: 66: c7: b3: 45 auf dem Juniper-Switch zu finden:

show ethernet-switching table | match 08:62:66:c7:b3:45 

Wenn es einen solchen MAC gibt, lautet die Antwort wie folgt:

 vlan151 08:62:66:c7:b3:45 D - xe-0/0/23.0 

Die letzte Spalte ist der Name der Schnittstelle des Switches, auf dem der MAC registriert ist. Aber wie kann man verstehen, wohin diese Schnittstelle führt? Und hier kommen Schnittstellenbeschreibungen zur Rettung. Dies sind Zeilen in der Switch-Konfigurationsdatei, mit denen Sie Schnittstellen Textbeschriftungen zuweisen können.

Das Team

 show interfaces xe-0/0/23 descriptions 

zeigt Folgendes:

 Interface Admin Link Description xe-0/0/23 up up SW>DS1 

In der Konfiguration geben wir an, dass diese Schnittstelle zum Downstream-Switch führt:

 set interfaces xe-0/0/23 description SW>DS1 

Implementierung


Das vorgeschlagene Skript führt Folgendes aus:

  1. Stellen Sie über SSH eine Verbindung zum Root-Switch her
  2. Überprüfen Sie, auf welcher Schnittstelle sich die in den Parametern übergebene MAC-Adresse befindet.
  3. Überprüfen Sie die Beschreibung dieser Schnittstelle.
  4. Wenn die Schnittstelle zum Switch führt, greifen Sie rekursiv auf den nächsten Switch in der Kette zu.

 #       searchpass = [] #main      MAC-    checkswitch,           MAC-.       searchpass   json. def main(argv): mac_addr = argv[0] checkswitch('CORE',mac_addr) for switch in searchpass: print (json.dumps(switch, ensure_ascii=False)) if __name__ == "__main__": main(sys.argv[1:]) #   MAC- def checkswitch(hostname,mac_addr): try: #    ,  host    returnvalue = {} returnvalue['host']=hostname #sendCommand      SSH     MAC-    answer = sendCommand(hostname,'show ethernet-switching table | match '+mac_addr) #   ,      #vlan151 08:62:66:c7:b3:45 D - xe-0/0/23.0 if(answer!=0): iface = answer.split()[4] returnvalue['iface']=iface # description ,    2  .0       #xe-0/0/23 up up SW>DS01 answer = sendCommand(hostname,'show interfaces '+iface[:-2]+' descriptions | last 1 | no-more') iface = answer.split() # description   ,      ,     SW>.  ,   3 ,         . if(len(iface)>2): iface=iface[3] returnvalue['description']=iface else: returnvalue['description']='none' searchpass.append(returnvalue) if (iface[:3]=='SW>'): checkswitch(iface[3:],mac_addr) else: returnvalue['iface']='none' searchpass.append(returnvalue) except Exception as e: print(e) 

Daher durchläuft das Skript alle Netzwerk-Switches, beginnend mit dem Kernel, und versucht, den gewünschten MAC zu finden. Für einen erfolgreichen Betrieb reicht es aus, die Beschreibungen der Schnittstellen auf dem neuesten Stand zu halten, und die Topologie kann nahezu beliebig komplex sein.

Ein Beispiel für ein Skript:

 python findmac.py 00:17:fc:21:e8:f9 {"host": "CORE", "iface": "xe-0/0/23.0", "description": "SW>DS1"} {"host": "DS1", "iface": "xe-0/0/11.0", "description": "SW>AS2"} {"host": "AS2", "iface": "xe-1/0/1.0", "description": "SW>AS3"} {"host": "AS3", "iface": "ge-0/0/26.0", "description": "none"} 

Wenn es keinen MAC gibt, bekommen wir

 {"host": "CORE", "iface": "none"} 

Die letzte Zeile ist der Switch und Port, der uns interessiert, aber gleichzeitig können wir den gesamten Suchpfad verfolgen.

Der vollständige Code befindet sich unter dem Spoiler. Vielen Dank für Ihre Aufmerksamkeit.

findmac.py
 import paramiko import time import sys import json import threading import logging login = 'user1' password = 'pass1234' searchpass = [] port = 22 class LogPipe(threading.Thread): def __init__(self, level): threading.Thread.__init__(self) self.daemon = False self.level = level self.fdRead, self.fdWrite = os.pipe() self.pipeReader = os.fdopen(self.fdRead) self.start() def fileno(self): return self.fdWrite def run(self): for line in iter(self.pipeReader.readline, ''): logging.log(self.level, line.strip('\n')) self.pipeReader.close() def close(self): os.close(self.fdWrite) def execute_ssh_command(host, port, username, password, command): try: # Create the SSH client. ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Connect to the host. ssh.connect(host, port, username, password, look_for_keys=False) # Send the command (non-blocking) stdin, stdout, stderr = ssh.exec_command(command) # Wait for the command to terminate while not stdout.channel.exit_status_ready() and not stdout.channel.recv_ready(): time.sleep(1) stdoutstring = stdout.readlines() stderrstring = stderr.readlines() return stdoutstring, stderrstring finally: if ssh is not None: # Close client connection. ssh.close() def sendCommand (hostname,command): returnvalue = 0 logging.info('Host '+hostname+', command: '+command) Try: #add .mydomain for FQDN (stdoutstring, stderrstring) = execute_ssh_command(hostname+'.mydomain', port, login, password, command+'\n') if (len(stdoutstring)>0): logging.info(stdoutstring[0]) if (len(stderrstring)>0): logging.info(stderrstring[0]) except Exception as e: return returnvalue else: returnvalue = stdoutstring[0] finally: return returnvalue def checkswitch(hostname,mac_addr): try: returnvalue = {} returnvalue['host']=hostname answer = sendCommand(hostname,'show ethernet-switching table | match '+mac_addr) if(answer!=0): iface = answer.split()[4] returnvalue['iface']=iface #cut .0 prefix in the interface name answer = sendCommand(hostname,'show interfaces '+iface[:-2]+' descriptions | last 1 | no-more') iface = answer.split() if(len(iface)>2): iface=iface[3] returnvalue['description']=iface else: returnvalue['description']='none' searchpass.append(returnvalue) if (iface[:3]=='SW>'): checkswitch(iface[3:],mac_addr) else: returnvalue['iface']='none' searchpass.append(returnvalue) except Exception as e: logging.info(e) def main(argv): mac_addr = argv[0] #configure log logging.basicConfig(filename='/var/log/findmac.log', level=logging.INFO, format='%(asctime)s %(message)s') logging.info('Find MAC: '+mac_addr) checkswitch('CORE',mac_addr) for switch in searchpass: print (json.dumps(switch, ensure_ascii=False)) if __name__ == "__main__": main(sys.argv[1:]) 

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


All Articles