Pesquisa de endereço MAC em comutadores Juniper

Na rede local, muitas vezes você precisa descobrir em qual porta do switch o endereço MAC específico do dispositivo está localizado. O problema é facilmente resolvido se houver vários comutadores na rede, mas quando houver mais de 30 deles, tudo se tornará muito mais complicado. Quero compartilhar um pequeno script Python que procure o endereço MAC desejado na rede e retorne o nome e a porta do switch no qual esse MAC está registrado.



Críticas construtivas são bem-vindas. Detalhes sob o corte.

Se o design da rede for feito corretamente, ou seja, o comutador raiz CORE , ao qual os comutadores de distribuição DS (Switch de distribuição) estão conectados e a eles, por sua vez, os comutadores de nível AS (comutador de acesso). Essa regra nem sempre é verdadeira; os comutadores de acesso podem ser conectados em série. De qualquer forma, a porta do switch upstream contém todos os endereços MAC dos dispositivos conectados ao switch downstream.

Por exemplo, se o dispositivo de nosso interesse estiver conectado ao comutador AS3 , iniciando a pesquisa com CORE , encontraremos esse endereço na porta que leva ao DS1 . Indo para o DS1 , encontraremos esse MAC na porta que leva ao AS2 , ao AS2 , veremos que ele nos leva ao AS3 , e somente no AS3 encontraremos a porta específica à qual o dispositivo de interesse está conectado.

Eu não queria fazer tudo manualmente, classificar todos os comutadores em um loop e determinar onde fica o uplink e onde não está, então nasceu a próxima solução, que eu quero compartilhar.

Um pouco de teoria.


Para localizar o MAC 08: 62: 66: c7: b3: 45 no switch Juniper, execute o seguinte comando:

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

Se houver esse MAC, a resposta será a seguinte:

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

A última coluna será o nome da interface do switch no qual o MAC está registrado. Mas como entender para onde essa interface leva? E aqui as descrições de interface são úteis. Essas são linhas no arquivo de configuração do comutador que permitem atribuir rótulos de texto às interfaces.

A equipe

 show interfaces xe-0/0/23 descriptions 

mostrará o seguinte:

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

Na configuração, indicamos que essa interface leva ao switch downstream:

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

Implementação


O script proposto fará o seguinte:

  1. Conecte-se via SSH ao switch raiz
  2. verifique em qual interface o endereço MAC passado nos parâmetros está localizado;
  3. Marque Descrição desta interface;
  4. se a interface levar ao comutador, acesse o próximo comutador na cadeia recursivamente.

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

Assim, o script passará por todos os comutadores de rede, iniciando no kernel, e tentará encontrar o MAC desejado. Para uma operação bem-sucedida, basta manter as descrições das interfaces atualizadas, e a topologia pode ter quase qualquer complexidade.

Um exemplo de 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"} 

Se não houver MAC, obtemos

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

A última linha é o switch e a porta que nos interessam, mas, ao mesmo tempo, podemos rastrear todo o caminho de pesquisa.

O código completo está sob o spoiler, obrigado por sua atenção.

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


All Articles