SNMP TRAP
为什么要使用SNMP trap
上篇数据中心网络设备管理(一)中介绍了如何使用snmp get 获取设备信息,网管要在尽量短的时间内获取到设备告警,使用snmp get来获取设备告警,需要网管设置轮询来实现,如果设置轮询时间为5min,告警就有可能在设备发生故障5分钟后才被网管发现,如果设置轮询时间颗粒度过短,就增加了网络流量,增大设备处理数据压力,而snmp trap为网管被动接收设备的告警信息,设备出现告警,会自动给网管发送告警消息。
SNMP trap 设备配置
以华为设备为例进行配置snmp trap
snmp-agent trap type base-trap //配置trap类型为base-trap
snmp-agent //使能snmp-agent
snmp-agent sys-info version all //兼容V1、V2C、V3版本
snmp-agent trap source Loopback0 //设置Trap源地址为Loopback0
snmp-agent mib-view included proxyping mib-2
snmp-agent target-host trap address udp-domain 网管ip params securityname 团体字 v2c
如何解析SNMP trap 报文
接下来进行抓包,分析SNMP trap报文内容,通过tcpdump进行抓包,查看包的形式
sudo tcpdump -i 网卡 udp port 162 -w snmp.pcap
使用wireshark及trapbuffer的一条数据进行分析
time device_name %%01CONFIGURATION/6/hwCfgChgNotify(t):CID=0x80cb000c-OID=1.3.6.1.4.1.2011.5.25.191.3.1;Configuration changed. (CurrentCfgChgSeqID=676, CfgChgSeqIDReveralCount=0, CfgChgTableMaxItem=10000, CfgBaselineTime=2020-01-16 时间)
variable-bindings为6项,对应分别对应
参数名称 | 参数含义 |
---|---|
TIMETICKS | 可以理解为时间戳 |
OID | MIB中的告警号标识 |
CurrentCfgChgSeqID | 系统配置变更标识。 |
CfgChgSeqIDReveralCount | 配置变更流水号反转次数。 |
CfgChgTableMaxItem | 系统可以保存的配置变更点数目。 |
CfgBaselineTime | 系统配置的基线时间。 |
明白trap消息格式后,就可以通过程序作为snmp trap的服务器,并根据snmp trap格式还原告警。我这里使用python进行实现
from pysnmp.carrier.asynsock.dispatch import AsynsockDispatcher
from pysnmp.carrier.asynsock.dgram import udp
from pyasn1.codec.ber import decoder
from pysnmp.proto import api
def cb_fun(self,transport_dispatcher, transport_domain, transport_address, whole_msg): # 处理Trap信息的函数,返回处理后的字典信息
while whole_msg:
msg_ver = int(api.decodeMessageVersion(whole_msg))
print(msg_ver)#打印下报文
def snmp_trap_receiver(self,if_ip,port): # 创建snmp_trap_receiver服务器,监听发送来的SNMP Trap信息
transport_dispatcher = AsynsockDispatcher() # 创建实例
transport_dispatcher.registerRecvCbFun(self.cb_fun) # 调用处理Trap信息的函数
# UDP/IPv4
transport_dispatcher.registerTransport(
udp.domainName, udp.UdpSocketTransport().openServerMode((if_ip,port)) # 绑定到本地地址与UDP/162号端口
)
transport_dispatcher.jobStarted(1) # 开始工作
print("[SNMP Trap Receiver Started]")
try:
transport_dispatcher.runDispatcher() # 运行
except Exception as e:
transport_dispatcher.closeDispatcher()
print(e)
if __name__ == '__main__':
snmp_trap_receiver(0.0.0.0,162)
在设备上造一条告警出来后,打印snmp trap报文,为下一步分析做准备
Message:
version=version-2c
community=
data=PDUs:
snmpV2-trap=SNMPv2TrapPDU:
request-id=37471
error-status=noError
error-index=0
variable-bindings=VarBindList:
VarBind:
name=1.3.6.1.2.1.1.3.0
=_BindValue:
value=ObjectSyntax:
application-wide=ApplicationSyntax:
timeticks-value=45719668
VarBind:
name=1.3.6.1.6.3.1.1.4.1.0
=_BindValue:
value=ObjectSyntax:
simple=SimpleSyntax:
objectID-value=1.3.6.1.4.1.2011.5.25.191.3.1
VarBind:
name=1.3.6.1.4.1.2011.5.25.191.1.1.0
=_BindValue:
value=ObjectSyntax:
simple=SimpleSyntax:
integer-value=676
VarBind:
name=1.3.6.1.4.1.2011.5.25.191.1.2.0
=_BindValue:
value=ObjectSyntax:
simple=SimpleSyntax:
integer-value=0
VarBind:
name=1.3.6.1.4.1.2011.5.25.191.1.3.0
=_BindValue:
value=ObjectSyntax:
simple=SimpleSyntax:
integer-value=10000
VarBind:
name=1.3.6.1.4.1.2011.5.25.191.1.4.0
=_BindValue:
value=ObjectSyntax:
simple=SimpleSyntax:
string-value=time
使用分割将这6项的值全部提取出来
def analysis(msg_ver):
if msg_ver in api.protoModules: # 如果版本兼容
p_mod = api.protoModules[msg_ver]
else: # 如果版本不兼容,就打印错误
print('Unsupported SNMP version %s' % msg_ver)
return
req_msg, whole_msg = decoder.decode(
whole_msg, asn1Spec=p_mod.Message(), # 对信息进行解码
)
print(req_msg)
req_pdu = p_mod.apiMessage.getPDU(req_msg)
if req_pdu.isSameTypeWith(p_mod.TrapPDU()):
if msg_ver == api.protoVersion1: # SNMPv1的特殊处理方法,可以提取更加详细的信息
var_binds = p_mod.apiTrapPDU.getVarBindList(req_pdu)
else: # SNMPv2c的处理方法
var_binds = p_mod.apiPDU.getVarBindList(req_pdu)
result_dict = {} # 每一个Trap信息,都会整理返回一个字典
for x in var_binds: # 打印详细Trap信息
result = {}
for x, y in x.items():
# print(x, y.prettyPrint()) # 最原始信息打印y = {ObjectName: 9} 1.3.6.1.2.1.1.3.0
# 处理信息到字典
if x == "name":
id = y.prettyPrint() # 把name写入字典的键
else:
bind_v = [x.strip() for x in y.prettyPrint().split("\n")]
for v in bind_v:
print(v)
if "=" in v:
result[v.split('=')[0]] = v.split('=')[1]
result_dict[id] = result
print(result_dict)
根据提取出来的值和snmp trap对应OID告警模板进行拼接,就可以还原设备告警了,本条告警模板如下:
Configuration changed. (CurrentCfgChgSeqID=[CurrentCfgChgSeqID], CfgChgSeqIDReveralCount=[CfgChgSeqIDReveralCount], CfgChgTableMaxItem=[CfgChgTableMaxItem], CfgBaselineTime=[CfgBaselineTime])
设备当前运行的配置发生改变。
其他告警模板可以在华为官网对应设备文档中找到,这样当设备发生故障或因某些原因导致系统进入不正常的工作状态时,就网管可以被动收到设备产生事件和告警信息。
参考:
https://bbs.csdn.net/topics/391964541?page=1
https://support.huawei.com/carrierindex/zh/anony/index.html
https://stackoverflow.com/questions/33752602/listen-traps-with-pysnmp