Tuesday, October 21, 2008

Getting the CAM table from Catalyst Switches using SNMP

Cisco has an article that describes a method for getting the CAM table (aka MAC address forwarding table) for Cisco Catalyst switches using SNMP. I developed a Python implementation of the procedure described in the article. My implementation uses the Zenoss pynetsnmp library. You can get the latest version of pynetsnmp from the Zenoss subversion repository at http://dev.zenoss.com/svn/trunk/pynetsnmp. The script also depends on the Twisted framework.

Here is the code


#! /usr/bin/env python

# http://www.cisco.com/en/US/tech/tk648/tk362/technologies_tech_note09186a0080094a9b.shtml

from pynetsnmp.tableretriever import TableRetriever
from pynetsnmp.twistedsnmp import AgentProxy
from twisted.internet import reactor
from pprint import pprint
import sys
import binascii
import logging

# oids
vtpVlanState = '.1.3.6.1.4.1.9.9.46.1.3.1.1.2'
dot1dBasePortIfIndex = '.1.3.6.1.2.1.17.1.4.1.2'
ifName = '.1.3.6.1.2.1.31.1.1.1.1'
dot1dTpFdbAddress = '.1.3.6.1.2.1.17.4.3.1.1'
dot1dTpFdbPort = '.1.3.6.1.2.1.17.4.3.1.2'

deffered_count = [0]
bridgeport_ifname = {}
vlan_ifname_mac = {}

def error(msg, proxy):
print 'Error: %s' % msg
proxy.close()
reactor.stop()

def proc_fdb(result, proxy, vlan):
fdbport_dct = {}
for fdbport_key, fdbport_value in result[dot1dTpFdbPort].items():
# .1.3.6.1.2.1.17.4.3.1.2.0.208.211.106.71.251 = INTEGER: 113
fdb_key = fdbport_key.replace(dot1dTpFdbPort, '', 1)
fdbport_dct[fdb_key] = int(fdbport_value)
ifname_mac = {}
for fdbaddress_key, fdbaddress_value in result[dot1dTpFdbAddress].items():
# .1.3.6.1.2.1.17.4.3.1.1.0.208.211.106.71.251 =
# Hex-STRING: 00 D0 D3 6A 47 FB
fdbport_key = fdbaddress_key.replace(dot1dTpFdbAddress, '', 1)
bridgeport = fdbport_dct[fdbport_key]
if bridgeport_ifname.has_key(bridgeport):
ifname = bridgeport_ifname[bridgeport]
if not ifname_mac.has_key(ifname): ifname_mac[ifname] = []
ifname_mac[ifname].append(binascii.hexlify(fdbaddress_value))
else:
logging.debug('no ifname for bridgeport=%s' % bridgeport)
vlan_ifname_mac[vlan] = ifname_mac
proxy.close()
deffered_count[0] -= 1
if not deffered_count[0]:
reactor.stop()
pprint(vlan_ifname_mac)

def proc_vtpVlanState(result, proxy, create_proxy):
proxy.close()
ifindex_bridgeport = {}
for oid, ifindex in result[dot1dBasePortIfIndex].items():
ifindex_bridgeport[ifindex] = int(oid.split('.')[-1])
for oid, ifname in result[ifName].items():
ifindex = int(oid.split('.')[-1])
if ifindex_bridgeport.has_key(ifindex):
bridgeport_ifname[ifindex_bridgeport[ifindex]] = (ifindex, ifname)
else:
logging.debug('no bridge port for ifindex=%s' % ifindex)
def vlans():
for key in result[vtpVlanState].keys():
vlan = int(key.split('.')[-1])
if vlan < 1000: yield vlan
for vlan in vlans():
deffered_count[0] += 1
vlan_proxy = create_proxy(vlan)
vlan_proxy.open()
d = TableRetriever(vlan_proxy, [dot1dTpFdbAddress, dot1dTpFdbPort])()
d.addCallback(proc_fdb, vlan_proxy, vlan)
d.addErrback(error, vlan_proxy)

def main(create_proxy):
proxy = create_proxy()
proxy.open()
d = TableRetriever(proxy, [vtpVlanState, dot1dBasePortIfIndex, ifName])()
d.addCallback(proc_vtpVlanState, proxy, create_proxy)
d.addErrback(error, proxy)
reactor.run()

if __name__=='__main__':
if len(sys.argv) != 4:
print 'Useage: main.py hostname version community'
else:
hostname, version, community = sys.argv[1:]
def create_proxy(community_index=None):
if community_index:
_community = community + '@%s' % community_index
else:
_community = community
return AgentProxy(hostname,
snmpVersion=version,
community=_community)
main(create_proxy)



Here is an example of the output. You can see the VLAN number, the ifIndex, the interface name, and the list of MAC addresses associated with each interface.


$ ./main.py switch1 2c public
{1: {(10125, 'Gi0/25'): ['0016cbaebd91'],
(10127, 'Gi0/27'): ['0015c55fc0e1'],
(10128, 'Gi0/28'): ['00e08602ef22'],
(10129, 'Gi0/29'): ['0013723bdd0d'],
(10130, 'Gi0/30'): ['0050568a2f5d',
'005056a908ab',
'0050564936d4',
'005056a9543f',
'0050568a104d',
'000c297d74aa',
'0050568a51b3',
'005056ab2cff',
'0050568a7804',
'005056ab3e2e'],
(10132, 'Gi0/32'): ['0016359fcba1'],
(10133, 'Gi0/33'): ['0050568a0e85',
'0050568a7dfb',
'0050568a5553',
'0050568a1fb2',
'0050568a1cfb',
'0050568a79c2',
'0050568a58f9',
'00505642a408',
'0050568a7595'],
(10134, 'Gi0/34'): ['0050568a59cc',
'0050568a5afb',
'0050568a5afe',
'0050568a1996',
'0050568a623e',
'0050568a5e9c',
'005056ab463d',
'0050568a68a6',
'0050568a2086',
'0050568a5269',
'0050568a5899',
'0050568a0558',
'0050568a2bb3',
'0050568a4f2e',
'0050564a7903',
'0050568a721d',
'0050568a2937',
'0050568a6bbe',
'0050568a5a49',
'0050568a259b',
'0050568a5ab2',
'0050568a68d0',
'005056ab34a8',
'0050568a1cd3'],
(10135, 'Gi0/35'): ['0050568a269a',
'0050568a5662',
'005056ab0b11',
'0050568a0647',
'0050568a4454',
'005056ab0635',
'0050568a7b13',
'0050568a5b15',
'005056ab6513',
'0050568a44ac',
'0050564ee608'],
(10136, 'Gi0/36'): ['0015c5f55b24'],
(10137, 'Gi0/37'): ['005056ab0a52',
'005056ab6673',
'005056446cac',
'0050568a627c',
'0050568a54c6',
'0050568a2831',
'0050568a67c2'],
(10138, 'Gi0/38'): ['0003ba1340ad'],
(10139, 'Gi0/39'): ['000c298e5c1b', '000c2981b3cb', '0015c5e0b865'],
(10140, 'Gi0/40'): ['005056ab7c2e',
'005056ab2f3b',
'0050568a1815',
'005056ab74a6',
'005056ab20be',
'0050564a6368',
'005056ab0bd3',
'005056a916cc',
'005056a97caa',
'005056ab7f68',
'005056ab51ac',
'005056ab4b36',
'005056ab6560',
'005056a94f6a',
'0050568a5b4a',
'005056ab2821',
'005056ab3ca6'],
(10141, 'Gi0/41'): ['00304899fa5c'],
(10145, 'Gi0/45'): ['001f12258106'],
(10147, 'Gi0/47'): ['0050568a3d37',
'0050568a7ead',
'0050568a4b2e',
'005056ab2fdc',
'005056ab632a',
'005056a9018b',
'0050568a42a0',
'005056ab3d3b',
'00505645ff39',
'0050568a52ce',
'0050568a55d8',
'005056ab4898',
'005056a96d0b',
'0050568a00b6',
'0050568a4a18',
'0050568a4978',
'005056a9754c'],
(10148, 'Gi0/48'): ['0019e70b4840', '0019e70b4802']},
2: {},
3: {},
10: {}}

Monday, October 20, 2008

Zenoss

I am a software developer at Zenoss. We use Net-SNMP, RRDtool, and Python in our open-source network management product. From time to time, I come up with small snippets of code that might be useful to a large audience. I'll post those snippets here.