Recently I’ve moved to new house. And because I simply can’t be offline as I work over the net I’ve decided to give 3G a try.
I gave 3Ireland a chance at first. Unfortunately their “Up to 3.6Mb/s” is far from advertised. I didn’t expect it to reach speeds they advertise but something closer to 1Mb/s, but in practice I’ve got only 200-300Kb/s down and 50Kb/s up and the connection choked so often it wasn’t even funny. So the next day I have returned it and went to Vodafone.

Vodafone turned out to be more reliable in terms of stability and speed. It can fallback from 3G to GPRS if the signal gets too weak (and in some parts of my home it does). While on 3G I’m getting speeds of ~1Mb/s down and 400Kb/s up which is great, on GPRS it’s ~380Kb/s down and ~50-90Kb/s up.
Latency is not as bad as I’ve expected: 100-250ms for most servers.
So… Vodafone is nice but there’s a catch… 5GB traffic limit per month.. AND it’s aggregate traffic for upload and download!
With my day to day internet habits and work I’m using 300-1000MB each day, and no that’s without any P2P traffic.
As of yet Vodafone seemed unwilling to make any kind of deal to get a better package, and I’m not sure I’m prepared to pay hundreds euro per month for medicore broadband.
I guess I could always try out O2, and I may actually do that, because I’m sure I’ll resign from Vodafone Broadband before the 14days return warranty.

I’m trying to get a DSL at home, but that may take anything between few weeks and 3months. I guess I’ll be calling BT everyday to check when the installer will come and in the meantime I have to look for an office space with nice internet connection.

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

Since I’m working in a mixed environment (Windows and Linux) I’m expecting from every package I use to be multi-platform. I have started working on a Python script that would help me to locate a MAC address on a Motorola Canopy network, down to the Subscriber Module (SM).

I find PySNMP too complex, too difficult to learn in few minutes. It’s very low-level.
yapysnmp looks great, but it’s troublesome to compile (dependencies on outdated net-snmp).
SNMPy? Linux only….

So I’ve started writing my own code using popen and net-snmp binaries which should work on any platform.

I always like to keep things simple. Nice and easy for someone else to install and start using, so I’m always trying to keep the dependencies on 3rd party stuff to the minimum.

The idea I have is to utilize SQLObject + SQLite to keep track of the MAC addresses (incl. last seen info) so the MAC lookup will be very fast, and run a collector that would sweep through provided subnets collect MAC tables and save that information to the database.