This little program is what I’ve mentioned earlier on my blog. It connects to all devices it can find in specified IP ranges via SMTP and gathers their MAC tables.
The plan is to keep this data in a DB and update it once in a while (20min would seem appropriate ). Then you can easily pin point what route do packets take on the L2 of the network and what Subscriber Module (SM) has a client using specific IP address/MAC.
This can be very useful in Motorola Canopy based networks with open architecture.

Package still under development.

Currently to make it work you need:

* Motorola Canopy based network
* Net-SNMP binaries (snmpwalk)
* Python 2.5
* SQLObject
* IPy
* SQLite

1) Download and install Net-SNMP binaries (or compile it if you wish) and make sure the executables are within PATH.

2) Install Python 2.5

3) To make your life easier download and run ez_setup.py to install latest distutils.

4) From the command line type: easy_install SQLObject
which should download and install SQLObject (and most likely SQLite as well - if not download SQLite and install it)

5) From the command line type: easy_install IPy

6) Now you can use following script

macsearch.py

Python [Show Plain Code]:
  1. #!/usr/bin/env python
  2.  
  3. #  Copyright (c) 2007, Bartosz Ptaszynski
  4. #
  5. #  macsearch.py is free software; you can redistribute it and/or modify
  6. #  it under the terms of the GNU General Public License as published by
  7. #  the Free Software Foundation; either version 2 of the License, or
  8. #  (at your option) any later version.
  9.  
  10. """
  11.  
  12. @author: Bartosz Ptaszynski
  13. @copyright: (C) 2007 Bartosz Ptaszynski
  14. @license: GNU General Public License (GPL)
  15. """
  16.  
  17. # Standard Library imports
  18. from os import popen
  19. from sys import argv
  20. from optparse import OptionParser
  21. from datetime import datetime
  22.  
  23. # 3rd Party Library imports
  24. from sqlobject import *
  25. from IPy import IP
  26.  
  27.  
  28. class MAC(SQLObject):
  29.     ip = StringCol(length=16)
  30.     mac = StringCol(length=18)
  31.     device_type = StringCol(length=6)
  32.     last_seen = DateTimeCol(default=datetime.now())
  33.     site_name = StringCol(default="")
  34.  
  35. class Subnet(SQLObject):
  36.     subnet = StringCol(length=20, alternateID=True)
  37.  
  38. class MACDiscoveryAgent(object):   
  39.     snmp_site_name = " SNMPv2-MIB::sysName.0"
  40.     snmp_macs = " enterprises.161.19.3.3.4.1.1"
  41.     snmp_device_type = " enterprises.161.19.3.3.1.7.0"
  42.    
  43.     def __init__(self, community="Canopy", snmp_version="2c", db_uri="sqlite:/:memory:"):
  44.         self.community = community
  45.         self.snmp_version = snmp_version
  46.         self.snmpwalk = str("snmpwalk -c "+community+" -v "+snmp_version+" ")
  47.         self.db_uri = db_uri
  48.         self._db_connect()
  49.  
  50.     def _init_db(self):
  51.         try:
  52.             MAC.createTable()
  53.             Subnet.createTable()           
  54.             return True
  55.         except:
  56.             return False
  57.        
  58.     def _db_connect(self):
  59.         """ Connect to the Database
  60.         """
  61.         sqlhub.processConnection = connectionForURI(self.db_uri)
  62.         self._init_db()
  63.  
  64.     def _db_addMAC(self, ip, mac, device_type, site_name):
  65.         """ Add a MAC to the Database, refresh the last_seen if it already exists
  66.         """
  67.         oldmac = MAC.selectBy(mac=mac, ip=ip, device_type=device_type)
  68.  
  69.         try:
  70.             if oldmac.last_seen:
  71.                 oldmac.last_seen = datetime.now()
  72.         except AttributeError:       
  73.             m = MAC(ip=ip, mac=mac, device_type=device_type, site_name=site_name)
  74.             #print m
  75.    
  76.     def _db_removeSubnet(self, subnet):
  77.         """ Removes a subnet that the MAC lookup should sweep from the Database
  78.         """
  79.         pass
  80.  
  81.     def _db_removeMAC(self, mac):
  82.         """ Completely removes a MAC address from the database
  83.         """
  84.         pass
  85.    
  86.     def _db_lookupMAC(self, mac):
  87.         """ Looks up a MAC address in the Database
  88.         """
  89.         results = MAC.selectBy(mac=mac)
  90.         return results
  91.    
  92.     def _normalizeMAC(self, mac):
  93.         # TODO: use regexp here
  94.         if "-" in mac:
  95.             mac = mac.split("-")
  96.         elif ":" in mac:
  97.             mac = mac.split(":")
  98.         elif " " in mac:
  99.             mac = mac.split(" ")
  100.         mac = .join(mac)           
  101.         mac = mac.upper()
  102.         return mac
  103.  
  104.     def _getMACs(self, ip):
  105.         """ Get all MAC addresses from the device
  106.         """
  107.         print ip
  108.         device_type = popen( str(self.snmpwalk + ip + self.snmp_device_type) )
  109.         device_type = device_type.read()
  110.         device_type = device_type[52:-2] # TODO: I should use regexp here!
  111.         macs = popen( str(self.snmpwalk + ip + self.snmp_macs) )
  112.         macs = macs.readlines()
  113.         site_name = popen( str(self.snmpwalk + ip + self.snmp_site_name) )
  114.         site_name = site_name.read()
  115.         for mac in macs:
  116.             self._db_addMAC(ip=ip, mac=self._normalizeMAC(mac[-19:-2]), device_type=device_type, site_name=site_name[32:]) # TODO: use regexp!
  117.  
  118.     def addSubnet(self, subnet):
  119.         """ Add a subnet that the MAC lookup should sweep to the Database
  120.         """
  121.         try:
  122.             Subnet(subnet=subnet)
  123.             return True
  124.         except:
  125.             return False
  126.  
  127.     def sweep(self):
  128.         for subnet in Subnet.select():
  129.             for ip in IP(subnet.subnet):
  130.                 self._getMACs(str(ip))
  131.        
  132.     def lookupMAC(self, mac):
  133.         """ Lookup a MAC address in the Database
  134.  
  135.             If successful it returns IP address of the SM with provided MAC address.
  136.             If not successful it returns a list with all APs and BHs that have provided MAC address.
  137.             It’s a list of dictionaries. This list has a format [ {’device_type’: string, ‘ip’, string}, … ]
  138.            
  139.         """
  140.         mac = self._normalizeMAC(mac)
  141.         results = self._db_lookupMAC(mac)
  142.         for result in results:
  143.             if "SM" in str(result.device_type):
  144.                 return result
  145.         return results
  146.        
  147. # Example usage below…
  148.  
  149. m = MACDiscoveryAgent()
  150. m.addSubnet("192.168.105.44/30")
  151. print "Sweeping the network…"
  152. m.sweep()
  153. print "Sweeping completed."
  154. print "Looking up the MAC address…"
  155. res = m.lookupMAC(‘00 E0 4C A0 28 CC’)
  156. if type(res) is MAC:
  157.     print "Found it:", res.device_type,":", res.ip, ":",res.site_name,": last seen:", res.last_seen
  158. else:
  159.     print "Couldn’t find the SM, But found following items with this MAC in their MAC table"
  160.     for i in res:
  161.         print i.device_type, ":", i.ip, ":", i.site_name, ": last seen ", i.last_seen

One Response to “macsearch.py - MAC search script for Motorola Canopy networks”

  1. Robert "Apofis"

    Hi
    I have to say that it’s a nice piece of code but it’s not perfect ;)

    Greetings from Wrocław

    Robert

    July 6th, 2007 | 7:10 pm

Leave a Reply