• SNMP学习笔记之SNMP TRAP简介、流程以及使用Python实现接受Trap信息


    0x00 SNMP TRAP简介

    SNMP(Simple Network Management Protocol) trap是一种很有用,但是也容易让人难以理解的协议。

    虽然名字叫做简单网络管理协议,但实际上并不是字面上的意思,尤其是看到.1.3.6.1.2.1.1.1.0这样一串串诡异的数字时候,就会有点让人崩溃。

    不管怎么说,现在所有的网络设备的都需要支持SNMP。而且现在还有很多的开源的网络管理系统,所以就有利于我们来学习和理解SNMP。

    SNMP trap是由被管理的设备主动的向管理服务器发送设备的异常情况,可以看成是管理服务器被动的去接收的过程。

    所以会有很多的工具会把snmptrap集成到自己的工具中,对网络设备进行监控。


    把SNMP trap集成的到Nagios当中的,大体的工作流程是:


    1. 由snmptrapd来接收网络设备发出的trap
    2. snmptrapd调用snmptt(snmp trap translator 翻译器)
    3. snmptt中定义了每一种trap的级别,以及什么样的trap才有必要写入到syslog中
    4. SEC(simple event correlator是一个事件收集器)从syslog中读取每一个事件,并把调用用户的脚本snmptraphandling.py 来处理。
    5. snmptraphandling.py 会调用根据主机名和trap的级别来调用相当的Nagios命令行。

    这个过程包含了很多层,所以感觉很复杂,还是深入进去看看snmptrapd是怎么工作的。从而尽可能简化snmptrap的使用方法。

    snmptrapd来自开源软件Net-SNMP. Net-SNMP会有很多的用途,当然使用trap也是一个很有用的用途。当snmptrapd接收到trap以后,可以调用用户自定义的脚本或者命令行来处理trap。如果想使用这个功能就需要在snmptrapd的配置文件中设置traphandle。 snmptrapd接收到的trap信息格式是:


    1. 数据包来源的主机名字。
    2. 数据包来源的ip地址。
    3. 数据包中的内容。

    traphandle的工作就是读取这些内容并进行处理就可以了。

    Trap信息例子:

    cisco2611.lon.altinity
    192.168.10.20
    RFC1213-MIB::sysUpTime.0 0:18:14:45.66
    SNMPv2-MIB::snmpTrapOID.0 IF-MIB::linkDown
    RFC1213-MIB::ifIndex.2 2
    RFC1213-MIB::ifDescr.2 "Serial0/0"
    RFC1213-MIB::ifType.2 ppp
    OLD-CISCO-INTERFACES-MIB::locIfReason.2 "administratively down"
    SNMP-COMMUNITY-MIB::snmpTrapAddress.0 192.168.10.20
    SNMP-COMMUNITY-MIB::snmpTrapCommunity.0 "public"
    SNMPv2-MIB::snmpTrapEnterprise.0 CISCO-SMI::ciscoProducts.186

    因为snmptt对trap信息中的OID进行了翻译,如果不进行翻译的话,那么trap信息的样子应该是

    cisco2611.lon.altinity
    192.168.10.20
    .1.3.6.1.2.1.1.3.0 0:18:13:59.95
    .1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.6.3.1.1.5.3
    .1.3.6.1.2.1.2.2.1.1.2 2
    .1.3.6.1.2.1.2.2.1.2.2 "Serial0/0"
    .1.3.6.1.2.1.2.2.1.3.2 ppp
    .1.3.6.1.4.1.9.2.2.1.1.20.2 "administratively down"
    .1.3.6.1.6.3.18.1.3.0 192.168.10.20
    .1.3.6.1.6.3.18.1.4.0 "public"
    .1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.9.1.186

    这就说明了,snmptt拥有自己的配置文件,文件中包含了OID和对应的属性名,并且是使用OID来进行索引的。如果snmptt在配置文件中无法找到对应的OID的话,那么snmptt也就无法对信息进行翻译,我们看到的信息内容也就是原始的格式了。

    snmptt使用的配置文件叫做MIBs(Management Information Base管理信息基础),在MIBs以OID为索引的key,可以快速查找到对应的文字形式和警告的级别。每一个MIB都有自己固定的定义格式,其中会包含一个宏(用来说明要显示信息的内容)。

    可是为什么要有MIBs的存在呢?直接由snmptrapd来完成OID到信息的翻译不可以么?
    1. MIBs是可以由用户自己来定义,使用的。因为各厂商对trap信息定义的内容肯定是不同的,所以想要snmp有一定的扩展性,那么就必须要支持用户自定义MIBs文件。这也就是MIBs存在的原因。
    2. 如果由snmptrapd自己来完成翻译也是可以的,因为如果每次都去通过检索OID对应的消息的话,那对性能的影响是非常的大的,因此snmptrapd就需要读入MIBs。问题也就出现了,如果更新MIBs话,就要把snmptrapd重新启动,必定会影响到trap消息的接收。所以把snmptt作为一个独立的deamon存在是很正确的选择。

    最后要做的事情就是添加traphandle了,handle会影响到snmptrap的接收性能,所以处理的速度要快,因为handle有可能1秒中会有数百次的调用。
     这样以来上面的处理流程就可以进行简化的:
    1. snmptrapd接收到trap的信息
    2. snmptrapd调用handle(如果不是调用现有的命令的话,在handle中就可以完成所有的处理了,就没有下面的处理了。又节省了一步,哈哈)
    3. handle中再调用Nagios的命令行就可以了

    这样一看流程就简单许多了,以后再使用snmptrap的时候也可以参考这个处理的流程

    0x01 SNMP TRAP流程

    SNMP Trap流程可参考这篇文章

    0x02 SNMP Trap接受用Python实现

    下载 安装pysnmp-4.2.5.tar.gz (md5)

    https://pypi.python.org/pypi/pysnmp/

    from pysnmp.carrier.asynsock.dispatch import AsynsockDispatcher
    from pysnmp.carrier.asynsock.dgram import udp, udp6
    from pyasn1.codec.ber import decoder
    from pysnmp.proto import api
    from test_case_common import *
     
    def cbFun(transportDispatcher, transportDomain, transportAddress, wholeMsg):
        while wholeMsg:
            msgVer = int(api.decodeMessageVersion(wholeMsg))
            if msgVer in api.protoModules:
                pMod = api.protoModules[msgVer]
            else:
                print('Unsupported SNMP version %s' % msgVer)
                return
            reqMsg, wholeMsg = decoder.decode(
                wholeMsg, asn1Spec=pMod.Message(),
                )
            print('Notification message from %s:%s: ' % (
                transportDomain, transportAddress
                )
            )
            reqPDU = pMod.apiMessage.getPDU(reqMsg)
            if reqPDU.isSameTypeWith(pMod.TrapPDU()):
                if msgVer == api.protoVersion1:
                    print('Enterprise: %s' % (
                        pMod.apiTrapPDU.getEnterprise(reqPDU).prettyPrint()
                        )
                    )
                    print('Agent Address: %s' % (
                        pMod.apiTrapPDU.getAgentAddr(reqPDU).prettyPrint()
                        )
                    )
                    print('Generic Trap: %s' % (
                        pMod.apiTrapPDU.getGenericTrap(reqPDU).prettyPrint()
                        )
                    )
                    print('Specific Trap: %s' % (
                        pMod.apiTrapPDU.getSpecificTrap(reqPDU).prettyPrint()
                        )
                    )
                    print('Uptime: %s' % (
                        pMod.apiTrapPDU.getTimeStamp(reqPDU).prettyPrint()
                        )
                    )
                    varBinds = pMod.apiTrapPDU.getVarBindList(reqPDU)
                else:
                    varBinds = pMod.apiPDU.getVarBindList(reqPDU)
                print('Var-binds:')
                for oid, val in varBinds:
                    a = oid.prettyPrint().strip()
                    b = val.prettyPrint().strip().split('
    ')
                    print a
                    for line in b:
                        item = line.strip()
                        if item.startswith('string-value'):
                            print 'string-value='+item.replace('string-value=0x','').decode('hex')
                        else:
                            print item
        return wholeMsg
     
    if __name__ == '__main__':
        transportDispatcher = AsynsockDispatcher()
     
        transportDispatcher.registerRecvCbFun(cbFun)
     
        # UDP/IPv4
        transportDispatcher.registerTransport(
            udp.domainName, udp.UdpSocketTransport().openServerMode(('0.0.0.0', 162))
        )
     
        # UDP/IPv6
        transportDispatcher.registerTransport(
            udp6.domainName, udp6.Udp6SocketTransport().openServerMode(('::1', 162))
        )
     
        transportDispatcher.jobStarted(1)
     
        try:
            # Dispatcher will never finish as job#1 never reaches zero
            transportDispatcher.runDispatcher()
        except:
            transportDispatcher.closeDispatcher()
            raise
  • 相关阅读:
    codeforces 459 C. Pashmak and Buses(思维)
    poj 2763 Housewife Wind(树链剖分+单点查询+区间修改)
    codeforces E. Phone Talks(dp)
    BZOJ 1036: [ZJOI2008]树的统计Count(树链剖分+单点更新+区间求和+区间求最大值)
    SPOJ
    hdu 3966 Aragorn's Story(树链剖分+区间修改+单点查询)
    codeforces H. Queries for Number of Palindromes(区间dp)
    codeforces 245 D. Restoring Table(位运算+思维)
    codeforces 402 D. Upgrading Array(数论+贪心)
    codeforces 793 D. Presents in Bankopolis(记忆化搜索)
  • 原文地址:https://www.cnblogs.com/JetpropelledSnake/p/9870336.html
Copyright © 2020-2023  润新知