Macfind voor cisco

Het komt echt heel vaak voor dat iemand, of ik zelf, wil weten waar een ip/mac adres zich bevindt op het netwerk.
Dus je logt in op de core en zoekt op welke poort dat mac adres zit (bij een IP eerst opzoeken in de ip-arp lijst) en kijkt dan wat er achter die poort zit, waarschijnlijk een volgende switch. Dan log je in op die switch en kijkt weer op welke poort dat mac adres zich bevindt. Dat kan een access poort zijn, dan zijn we er al, of weer een volgende switch en dan kijken we weer op die swich. En zo zijn we snel een switch of 5 verder en heel wat commando's verder. Dat kan ook geautomatiseerd.

Dit script doet al die handelingen en kan ook met etherchannels overweg en met verschillende IOS versies. Echter al die soorten poorten en de schrijfwijzes (Fastethernet 0/1, Fa0/1, Te1/1/4, Gi0/1, etc) maakt het script wel gevoelig voor omvallen al gaat het al erg lang goed. Het script meldt met de optie -d wel alle stappen dus debuggen is goed te doen. Ik wil er maar mee zeggen dat het misschien niet altijd foutloos werkt in elke omgeving.
Ook dit script gebruik ik vele malen per dag en ik ben blij dat ik die stap geautomatiseeerd heb, hopelijk kan ik anderen en ook een plezier mee doen (wat ik ook graag zou horen!).

Als er een ip adres wordt mee gegeven dan zal het script op het eerste device proberen het mac adres te vinden in de ip-arp lijst, dat eerste device moet in dat geval dan wel de core router/L3 switch zijn.
Netmiko wordt gebruikt als extra module:
python3 -m pip install netmiko
Het is voor het script wel belangrijk dat alle switches een hostname hebben die begint met de prefix zoals in de variabelen staat, bijvoorbeeld SW....
Verder is het kwestie van de core opgeven als startpunt en de juiste credentials voor alle switches. Elke volgende switch die bevraagd wordt wordt getoond met een "." in de voortgang.
Het script kan overweg met een mac adres in de dotted format, dubbele punt format of windows dhcp format.
Er is ook commentaar toegevoegd ter verduidelijking zodat aanpassen wat eenvoudiger is.

Code

#!/usr/bin/python3
# file    : macfind.py
# author  : Wouter Barendsen
# created : 2015-03-01 09:22
# modified: 2024-02-11 17:06:29

import netmiko,sys,re

### Editable variables ##################################
coreSw="192.168.2.254"
swPrefix="SW"
adminUser="user"
adminWw="pass"
 
### Variables ###########################################
macAddress=""
debug=False

def hellup():
  print("macfind.py v1.7  Wouter Barendsen 2015-2024                      ")
  print("  examples:                                                      ")
  print("    macfind.py 00:11:22:33:44:55     Find mac                    ")
  print("    macfind.py 1.2.3.4               Find mac with this IP       ")
  print("    macfind.py -d 00:11:22:33:44:55  Find mac and show debug info")
  print("")
  sys.exit(1)

def checkMac(macAddress):
  oke=False
  # return dotted format, Mac al oke?, return Mac
  match = re.search(r'[0-9a-fA-F]{4}.[0-9a-fA-F]{4}.[0-9a-fA-F]{4}',macAddress)
  if match:
    oke=True
  match = re.search(r'([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2}):([0-9a-fA-F]{2})',macAddress)
  if match:
    # dubbelepuntformat, wijzig naar dotted
    macAddress=match.group(1)+match.group(2)+"."+match.group(3)+match.group(4)+"."+match.group(5)+match.group(6)
    oke=True
  match = re.search(r'([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})',macAddress)
  if match:
    # windows formaat, wijzig naar dotted
    macAddress=match.group(1)+"."+match.group(2)+"."+match.group(3)
    oke=True
  if oke:
    return(macAddress.lower())
  else:
    return("")

def checkSwitch(switch,param):
  macAddress=""
  # Als parameters is een IP dan eerst mac zoeken in ip arp
  match=re.search(r"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}",param)
  if match:
    ip=match.group(0)
    # evt nog checken op of elk byte kleiner is dan 256 maar aan de andere kant als ip niet in ip arp staat dan exit.
    ssh_connection = netmiko.ConnectHandler(device_type='cisco_ios',ip=switch,username=adminUser,password=adminWw)
    ssh_connection.enable()
    command="show ip arp | include "+ip
    result = ssh_connection.send_command(command, delay_factor=2)
    ssh_connection.disconnect()
    if ip in result:
      #filter mac
      match=re.search(r'[0-9a-f]{4}\.[0-9a-f]{4}\.[0-9a-f]{4}',result)
      if match:
        macAddress=match.group(0)
        if debug: print("DEBUG: IP adres gevonden in ip-arp. mac address is",macAddress)
    else:
      print("ERROR: IP niet gevonden in ip-arp !")
      sys.exit(1)
  #print("DEBUG: param: -"+param+"-"+macAddress+"-")
  if macAddress=="": 
    macAddress=checkMac(param)
  if macAddress=="":
    print(f"ERROR, {param} is geen geldig mac adres")
    sys.exit(1)
  if debug: print("DEBUG:",macAddress)
  # connect naar switch
  if debug: print("DEBUG: Connect to",switch)
  if not debug: print(".",end="",flush=True)
  ssh_connection = netmiko.ConnectHandler(device_type='cisco_ios',ip=switch,username=adminUser,password=adminWw)
  ssh_connection.enable()
  command="show mac address-table address "+macAddress+" | incl "+macAddress
  if debug: print("DEBUG:",command)
  result = ssh_connection.send_command(command, delay_factor=2)
  if debug: print("DEBUG: ",result)
  if result == "":
    print("Mac adres",macAddress,"niet gevonden op",switch)
    sys.exit(0)
  # port is laatste deel
  match = re.search(r' ([0-9a-zA-Z/\-]+) *$',result)
  if match:
    port=match.group(1)
    if debug: print("DEBUG: port: -"+port+"-")
  # filter poort en check of portchannel of poort, als portchannel dan pak 1 poort
  if "Po" in port:
    # poort is etherchannel, Portxx or Port-channelxx
    # sh etherchanel [port] port | incl ^Port: 
    # haal nummer van "Portxx" of "Port-channelxx"
    match=re.search(r'(\d+)',port)
    if match:
      po=match.group(1)
      if debug: print("DEBUG: Channel:",po)
    else:
      print("ERROR: Channel nr niet kunnen filteren")
      sys.exit(1)
    if not debug: print(".",end="",flush=True)
    command="sh etherchannel "+po+" port | incl ^Port:"
    result = ssh_connection.send_command(command, delay_factor=3)
    lines=result.split('\n')
    if debug: print("DEBUG: poort uit channel regel 1:",lines[0])
    port=lines[0].split()[1]
    if debug: print("DEBUG: port uit channel:",port)
  # sh cdp nei [port], regel begint met [swPrefix] is ok, pak eerste woord tot . of spatie. Als geen neigbor dan edge poort en einde
  if debug: print("DEBUG: Neighbors check: sh cdp nei "+port)
  command="sh cdp nei "+port
  if not debug: print(".",end="",flush=True)
  result = ssh_connection.send_command(command, delay_factor=2)
  ssh_connection.disconnect()
  if debug: print("DEBUG: ",result)
  LineList=result.split('\n')
  PartOne=False
  neighbor=""
  for Line in LineList:
    if PartOne == True:
      # laatste deel van regel is switch/poort
      neighbor=DeelEen+Line
      DeelEen=""
      PartOne=False
    if Line.startswith(swPrefix) and len(Line) < 25:
      # deel 1 van nei
      PartOne=True
      DeelEen=Line 
    if Line.startswith(swPrefix) and len(Line) >= 25:
      neighbor=Line
  if neighbor != "":
    buurman=neighbor.split('.')[0] 
    if len(buurman)>25:                     # als buurman hele regel is dan geen domein achter switch dus splitten op spatie
      buurman=neighbor.split(' ')[0]
    if debug: print("DEBUG: Neighbor",buurman)
    checkSwitch(buurman,macAddress)
  else:
    print("")
    print("Endpoint gevonden:",macAddress,"is at",switch,port)
 
if len(sys.argv) < 2:
  # minstens 1 parameter nodig, mac of ip en anders help aanroepen
  hellup()

if len(sys.argv) >= 2:
  # 2 parameters
  for param in sys.argv:
    if param == "-d":
      debug=True
    if param == "-h":
      hellup()
  macAddress=sys.argv[1]
 
checkSwitch(coreSw,macAddress)

Output

$ macfind.py b827.ebd3.9929
....
Endpoint gevonden: b827.ebd3.9929 is at SW02 Gi0/6

Als je het wat vindt dan hoor ik dat graag.




Geef hieronder uw commentaar, mening of aanvullingen op deze pagina en lees de eventuele meldingen van andere lezers.

naam:
email:
spamcheck: 1999+1=...
opmerking:
   



Website door Wouter Barendsen, 2005-2024