如何用pysnmp v3实现指定VLAN查询Cisco交换机MAC地址表?
解决Cisco交换机SNMPv3获取全VLAN MAC地址表的问题
问题背景
我正尝试通过SNMPv3从Cisco交换机获取完整MAC地址表,发现Cisco需逐个VLAN查询,与其他厂商不同。现有pysnmp bulk命令仅能获取VLAN 1的MAC表,尝试新方法时触发错误,寻求解决方案。
等效SNMPv2c命令:
snmpwalk -v2c -c letsconfigRO@10 10.0.0.101 1.3.6.1.2.1.17.4.3.1.1
当前可用代码(仅获取VLAN 1)
auth = cmdgen.UsmUserData(userName=snmpv3Username, authKey=snmpv3Password, authProtocol=authProtocol, privKey=privKey, privProtocol=privProtocol) errorIndication, errorStatus, errorIndex, varBinds = cmdGen.bulkCmd( auth, cmdgen.UdpTransportTarget((host, 161)), 0, 1000, cmdgen.MibVariable(walk_oid), lookupMib=False, lexicographicMode=False )
这段代码可正常执行,但仅返回VLAN 1的MAC地址表。
尝试的错误代码及问题
# Create SNMP engine instance snmp_engine = SnmpEngine() # Create SNMPv3 user data object user = UsmUserData(userName=snmpv3Username, authKey=snmpv3Password, authProtocol=authProtocol, privKey=privKey, privProtocol=privProtocol) # Define SNMPv3 target target = UdpTransportTarget(('10.0.0.101', 161)) # Define VLAN ID vlan_id = 10 # Define OID for retrieving MAC addresses for a specific VLAN oid = ObjectType(ObjectIdentity('BRIDGE-MIB', 'dot1dTpFdbPort')) # Define OID for retrieving VLAN membership information vmVlan_oid = ObjectType(ObjectIdentity('CISCO-VLAN-MEMBERSHIP-MIB', 'vmVlan', vlan_id)) # Perform bulkwalk operation to retrieve the MAC address table iterator = bulkCmd( snmp_engine, user, target, ContextData(), oid, lexicographicMode=False, maxRepetitions=500, acInfo=(vmVlan_oid, None) )
执行时触发错误:
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'ObjectType'
解决方案
错误原因
acInfo参数需要的是上下文引擎ID的元组,而非ObjectType对象。Cisco交换机查询特定VLAN的MAC表时,需将VLAN ID作为**上下文名称(Context Name)**传入,而非通过acInfo传递MIB对象;同时原代码漏掉了bulkCmd必填的non-repeaters和max-repetitions位置参数,导致参数错位。
修正后的单VLAN查询代码
from pysnmp.hlapi import SnmpEngine, UsmUserData, UdpTransportTarget, ContextData, ObjectType, ObjectIdentity, bulkCmd snmp_engine = SnmpEngine() # 配置SNMPv3认证信息 user = UsmUserData( userName=snmpv3Username, authKey=snmpv3Password, authProtocol=authProtocol, privKey=privKey, privProtocol=privProtocol ) target = UdpTransportTarget(('10.0.0.101', 161)) vlan_id = 10 # 将VLAN ID转为字符串作为SNMP上下文名称,这是Cisco识别目标VLAN的关键 context_data = ContextData(contextName=str(vlan_id)) # 指定查询MAC表的OID(BRIDGE-MIB::dot1dTpFdbPort) oid = ObjectType(ObjectIdentity('BRIDGE-MIB', 'dot1dTpFdbPort')) # 执行bulk查询,注意参数顺序:non-repeaters、max-repetitions需放在OID之前 iterator = bulkCmd( snmp_engine, user, target, context_data, 0, # non-repeaters:固定为0 500, # max-repetitions:单次返回的最大条目数 oid, lookupMib=False, lexicographicMode=False ) # 解析并输出结果 for errorIndication, errorStatus, errorIndex, varBinds in iterator: if errorIndication: print(errorIndication) elif errorStatus: print(f"{errorStatus.prettyPrint()} at {errorIndex and varBinds[int(errorIndex)-1][0] or '?'}") else: for varBind in varBinds: # 从OID的最后6个节点提取MAC地址并格式化 mac_oid_nodes = varBind[0].getOid()[-6:] mac_address = ':'.join(f'{int(node):02x}' for node in mac_oid_nodes) port = varBind[1].prettyPrint() print(f"VLAN {vlan_id} - MAC: {mac_address}, Port: {port}")
批量获取所有VLAN的MAC表
若要获取所有VLAN的MAC表,可先通过CISCO-VLAN-MEMBERSHIP-MIB::vmVlan获取所有存在的VLAN ID,再循环遍历每个VLAN执行上述查询:
# 第一步:获取所有VLAN ID vlan_oid = ObjectType(ObjectIdentity('CISCO-VLAN-MEMBERSHIP-MIB', 'vmVlan')) vlan_iterator = bulkCmd( snmp_engine, user, target, ContextData(), 0, 500, vlan_oid, lookupMib=False ) vlans = set() for errorIndication, errorStatus, errorIndex, varBinds in vlan_iterator: if not errorIndication and not errorStatus: for varBind in varBinds: vlans.add(varBind[1].prettyPrint()) # 第二步:遍历每个VLAN查询MAC表 for vlan in vlans: context_data = ContextData(contextName=vlan) mac_iterator = bulkCmd( snmp_engine, user, target, context_data, 0, 500, ObjectType(ObjectIdentity('BRIDGE-MIB', 'dot1dTpFdbPort')), lookupMib=False, lexicographicMode=False ) # 解析当前VLAN的MAC表数据(代码同单VLAN查询的结果解析部分) print(f"\n--- VLAN {vlan} MAC Address Table ---") for errorIndication, errorStatus, errorIndex, varBinds in mac_iterator: if errorIndication: print(errorIndication) elif errorStatus: print(f"{errorStatus.prettyPrint()} at {errorIndex and varBinds[int(errorIndex)-1][0] or '?'}") else: for varBind in varBinds: mac_oid_nodes = varBind[0].getOid()[-6:] mac_address = ':'.join(f'{int(node):02x}' for node in mac_oid_nodes) port = varBind[1].prettyPrint() print(f"MAC: {mac_address}, Port: {port}")
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

