Búsqueda de dirección MAC en conmutadores Juniper

En la red local, a menudo necesita averiguar en qué puerto del conmutador se encuentra la dirección MAC específica del dispositivo. El problema se resuelve fácilmente si hay varios conmutadores en la red, pero cuando hay más de 30 de ellos, todo se vuelve mucho más complicado. Quiero compartir un pequeño script de Python que busca la dirección MAC deseada en la red y devuelve el nombre y el puerto del conmutador en el que está registrado este MAC.



La crítica constructiva es bienvenida. Detalles debajo del corte.

Si el diseño de la red se realiza correctamente, es decir, el conmutador raíz CORE , al que están conectados los conmutadores de distribución DS (conmutador de distribución), y a ellos, a su vez, los conmutadores de nivel de acceso AS (conmutador de acceso). Esta regla no siempre es cierta; los interruptores de acceso se pueden conectar en serie. En cualquier caso, el puerto del conmutador ascendente contiene todas las direcciones MAC de los dispositivos conectados al conmutador descendente.

Por ejemplo, si el dispositivo que nos interesa está conectado al conmutador AS3 , entonces, comenzando la búsqueda con CORE , encontraremos esta dirección en el puerto que conduce a DS1 . Al ir a DS1 , encontraremos este MAC en el puerto que conduce a AS2 , yendo a AS2 , veremos que nos lleva a AS3 , y solo en AS3 encontraremos el puerto específico al que está conectado el dispositivo de interés.

No quería hacerlo todo a mano, ordenar todos los interruptores en un bucle y determinar dónde está el enlace ascendente y dónde no, así que nació la siguiente solución, que quiero compartir.

Un poco de teoría


Para encontrar el MAC 08: 62: 66: c7: b3: 45 en el conmutador Juniper, ejecute el siguiente comando:

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

Si existe tal MAC, la respuesta será la siguiente:

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

La última columna será el nombre de la interfaz del conmutador en el que está registrado el MAC. Pero, ¿cómo entender a dónde conduce esta interfaz? Y aquí las descripciones de interfaz vienen al rescate. Estas son líneas en el archivo de configuración del conmutador que le permiten asignar etiquetas de texto a las interfaces.

El equipo

 show interfaces xe-0/0/23 descriptions 

mostrará lo siguiente:

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

En la configuración, indicamos que esta interfaz conduce al conmutador descendente:

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

Implementación


El guión propuesto hará lo siguiente:

  1. Conéctese a través de SSH al conmutador raíz
  2. comprobar en qué interfaz se encuentra la dirección MAC pasada en los parámetros;
  3. Verifique la descripción de esta interfaz;
  4. Si la interfaz conduce al conmutador, acceda al siguiente conmutador de la cadena de forma recursiva.

 #       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) 

Por lo tanto, el script pasará por todos los conmutadores de red, comenzando desde el núcleo, e intentará encontrar el MAC deseado. Para una operación exitosa, es suficiente mantener actualizadas las descripciones en las interfaces, y la topología puede ser de casi cualquier complejidad.

Un ejemplo de un script:

 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"} 

Si no hay MAC, obtenemos

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

La última línea es el switch y el puerto que nos interesa, pero al mismo tiempo podemos rastrear toda la ruta de búsqueda.

El código completo está debajo del spoiler, gracias por su atención.

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/es420013/


All Articles