欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用Python的Pysnmp库打造简易网络管理工具(第6部分)

最编程 2024-02-21 12:12:19
...

一、SNMP简介

  简单网络管理协议SNMP(Simple Network Management Protocol)用于网络设备的管理。SNMP作为广泛应用于TCP/IP网络的网络管理标准协议,提供了统一的接口,从而实现了不同种类和厂商的网络设备之间的统一管理。
  SNMP协议分为三个版本:SNMPv1、SNMPv2c和SNMPv3。

  • SNMPv1是SNMP协议的最初版本,提供最小限度的网络管理功能。SNMPv1基于团体名认证,安全性较差,且返回报文的错误码也较少。
  • SNMPv2c也采用团体名认证。在SNMPv1版本的基础上引入了GetBulk和Inform操作,支持更多的标准错误码信息,支持更多的数据类型(Counter64、Counter32)。
  • SNMPv3主要在安全性方面进行了增强,提供了基于USM(User Security Module)的认证加密和基于VACM(View-based Access Control Model)的访问控制。SNMPv3版本支持的操作和SNMPv2c版本支持的操作一样。

1.1 SNMP系统组成

  SNMP系统由网络管理系统NMS(Network Management System)、SNMP Agent、被管对象Management object和管理信息库MIB(Management Information Base)四部分组成。


SNMP系统
  • NMS
      NMS是网络中的管理者,是一个采用SNMP协议对网络设备进行管理/监视的系统,运行在NMS服务器上。
    • NMS可以向设备上的SNMP Agent发出请求,查询或修改一个或多个具体的参数值。
    • NMS可以接收设备上的SNMP Agent主动发送的SNMP Traps,以获知被管理设备当前的状态。
  • SNMP Agent
      SNMP Agent是被管理设备中的一个代理进程,用于维护被管理设备的信息数据并响应来自NMS的请求,把管理数据汇报给发送请求的NMS。
    • SNMP Agent接收到NMS的请求信息后,通过MIB表完成相应指令后,并把操作结果响应给NMS。
    • 当设备发生故障或者其它事件时,设备会通过SNMP Agent主动发送SNMP Traps给NMS,向NMS报告设备当前的状态变化。
  • Managed Object
      Managed object指被管理对象。每一个设备可能包含多个被管理对象,被管理对象可以是设备中的某个硬件,也可以是在硬件、软件(如路由选择协议)上配置的参数集合。
  • MIB
      MIB是一个数据库,指明了被管理设备所维护的变量。MIB在数据库中定义了被管理设备的一系列属性:对象的名称、对象的状态、对象的访问权限和对象的数据类型等。MIB也可以看作是NMS和SNMP Agent之间的一个接口,通过这个接口,NMS对被管理设备所维护的变量进行查询/设置操作。
      MIB是以树状结构进行存储的,如下图。树的节点表示被管理对象,它可以用从根开始的一条路径唯一地识别,这条路径就称为OID,如system的OID为1.3.6.1.2.1.1,interfaces的OID为1.3.6.1.2.1.2。子树可以用该子树根节点的OID来标识。如以private为根节点的子树的OID为private的OID——{1.3.6.1.4}。
    MIB

1.2 SNMP查询操作

  SNMP查询是指NMS主动向SNMP Agent发送查询请求,如图1-3所示。SNMP Agent接收到查询请求后,通过MIB表完成相应指令,并将结果反馈给NMS。SNMP查询操作有三种:Get、GetNext和GetBulk。SNMPv1版本不支持GetBulk操作。

  • Get操作:NMS使用该操作从SNMP Agent中获取一个或多个参数值。
  • GetNext操作:NMS使用该操作从SNMP Agent中获取一个或多个参数的下一个参数值。
  • GetBulk操作:基于GetNext实现,相当于连续执行多次GetNext操作。在NMS上可以设置被管理设备在一次GetBulk报文交互时,执行GetNext操作的次数。

1.2.1 工作原理

  不同版本的SNMP查询操作的工作原理基本一致,唯一的区别是SNMPv3版本增加了身份验证和加密处理。下面以SNMPv2c版本的Get操作为例介绍SNMP查询操作的工作原理。假定NMS想要获取被管理设备MIB节点sysContact的值,使用可读团体名为public,过程如下所示:

  • NMS:向SNMP Agent发送Get请求报文。报文中各字段的设置如下:版本号为所使用的SNMP版本;团体名为public;PDU中PDU类型为Get类型,绑定变量填入MIB节点名sysContact。
  • SNMP Agent:首先对报文中携带版本号和团体名进行认证,认证成功后,SNMP Agent根据请求查询MIB中的sysContact节点,得到sysContact的值并将其封装到Response报文中的PDU,向NMS发送响应;如果查询不成功,SNMP Agent会向NMS发送出错响应。


    SNMP 查询

1.2.2 SNMPv1和SNMPv2c查询报文格式

SNMPv1和SNMPv2c查询报文

字段解释

  • 版本:表示SNMP的版本
    • SNMPv1为0。
    • SNMPv2c为1。
  • 团体名:用于在SNMP Agent与NMS之间完成认证,字符串形式,用户可自行定义。团体名包括“read”和“write”两种,
    • 执行SNMP查询操作时,采用“read”团体名进行认证;
    • 执行SNMP设置操作时,则采用“write”团体名进行认证。
  • Request ID:用于匹配请求和响应,SNMP给每个请求分配全局唯一的ID。
  • Non repeaters/Max repetitions:GetBulk操作基于GetNext操作实现,相当于多次执行GetNext操作,这两个参数用于设置执行GetNext操作次数。
  • Error status:用于表示在处理请求时出现的状况。
  • Error index:差错索引。当出现异常情况时,提供变量绑定列表(Variable bindings)中导致异常的变量的信息。
  • Variable bindings:变量绑定列表,由变量名和变量值对组成。

1.2.3 SNMPv3报文格式

SNMPv3报文格式

字段解释

  • 版本:表示SNMP的版本
    • SNMPv3为3。
    • MsgID:请求报文的序列号。
    • MaxSize:消息发送者所能够容纳的消息最大字节,同时也表明了发送者能够接收到的最大字节数。
  • Flags:消息标识位,占一个字节,有三个特征位:reportableFlag,privFlag和authFlag。
    • reportableFlag=1,在能够导致Report PDU生成的情况下,SNMPv3报文接收方必须向发送方发送Report PDU;reportableFlag=0,SNMPv3报文接收方不发送Report PDU。只有在SNMP PDU部分不能被解密时(比如由于密钥错误导致解密失败等)才会用到Report。
    • privFlag=1,对SNMPv3报文进行加密;privFlag=0,不对SNMPv3报文进行加密。
    • authFlag=1,对SNMPv3报文进行鉴权;authFlag=0,不对SNMPv3报文进行鉴权。
    • 除了privFlag=1,authFlag=0的情况外,其他任意组合都可以,所以在配置SNMPv3的安全级别的时候需要注意:如果用户组是privacy级别,用户和告警主机就必须是privacy级别;用户组是authentication级别,用户和告警主机可以是privacy或者authentication级别。
  • SecurityModel:消息采用的安全模型,发送方和接收方必须采用相同的安全模型。
  • SecurityParameters:安全参数,包含SNMP实体引擎的相关信息、用户名、鉴权参数、加密参数等安全信息。
  • Context EngineID:SNMP唯一标识符,和PDU类型一起决定应该发往哪个应用程序。
  • Context Name:用于确定Context EngineID对被管理设备的MIB视图。
    SNMPv3报文的SNMP PDU的格式与SNMPv2c的一致。SNMPv3版本的报文可以使用鉴权机制,会对Context EngineID、Context Name和SNMP PDU进行加密。

1.3 SNMP设置操作

  SNMP设置是指NMS主动向SNMP Agent发送对设备进行Set操作的请求,如下图示。SNMP Agent接收到Set请求后,通过MIB表完成相应指令,并将结果反馈给NMS。


SNMP设置操作

  不同版本的SNMP Set操作的工作原理基本一致,唯一的区别是SNMPv3版本增加了身份验证和加密处理。下面以SNMPv3版本的Set操作为例介绍SNMP Set操作的工作原理。
假定NMS想要设置被管理设备MIB节点sysName的值为HUAWEI,过程如下所示:

  1. NMS:向Agent发送不带安全参数的Set请求报文,向SNMP Agent获取Context EngineID、Context Name和安全参数(SNMP实体引擎的相关信息)。
    SNMP Agent:响应NMS的请求,并向NMS反馈请求的参数。
  2. NMS:再次向SNMP Agent发送Set请求,报文中各字段的设置如下:
  • 版本:SNMPv3
  • 报文头数据:指明采用鉴权、加密方式。
  • 安全参数:NMS通过配置的算法计算出鉴权参数和加密参数。将这些参数和获取的安全参数填入相应字段。
  • PDU:将获取的Context EngineID和Context Name填入相应字段,PDU类型设置为Set,绑定变量填入MIB节点名sysName和需要设置的值HUAWEI,并使用已配置的加密算法对PDU进行加密。
  1. SNMP Agent:首先对报文中携带版本号和团体名进行认证,认证成功后,SNMP Agent根据请求设置管理变量在管理信息库MIB中对应的节点,设置成功后向NMS发送响应;如果设置不成功,Agent会向NMS发送出错响应。

1.3.1 SNMP Set操作的报文

  SNMPv1和SNMPv2c的Set操作报文格式如下图所示。一般情况下,SNMPv3的Set操作信息是经过加密封装在SNMP PDU中,其格式与SNMPv2c的Set操作报文格式一致。

SNMP Set操作的报文

字段解释

  • Request ID:用于匹配请求和响应,SNMP给每个请求分配全局唯一的ID。
  • Error status:用于表示在处理请求时出现的状况。
  • Error index:差错索引。当出现异常情况时,提供变量绑定列表(Variable bindings)中导致异常的变量的信息。
  • Variable bindings:变量绑定列表,由变量名和变量值对组成。

1.4 SNMP Traps

  SNMP Traps是指SNMP Agent主动将设备产生的告警或事件上报给NMS,以便网络管理员及时了解设备当前运行的状态。
  SNMP Agent上报SNMP Traps有两种方式:Trap和Inform。SNMPv1版本不支持Inform。Trap和Inform的区别在于,SNMP Agent通过Inform向NMS发送告警或事件后,NMS需要回复InformResponse进行确认。


SNMP Traps

1.4.1 SNMPv2版本Trap/Inform操作报文格式

SNMPv2版本Trap/Inform操作报文

字段解释

  • Request ID:用于匹配请求和响应,SNMP给每个请求分配全局唯一的ID。
  • Variable bindings:变量绑定列表,由变量名和变量值对组成。
    SNMPv3的Trap操作或inform操作是经过加密封装在SNMP PDU中,其格式与SNMPv2c的Trap操作或inform操作的报文格式一致。

二、使用Pysnmp发送SNMPv2c报文

2.1 测试环境

  在Ensp中搭建网络环境,在R2上启用SNMP作为SNMP agent,Linux主机作为NMS;为方便观察SNMP报文格式,在R2使用SNMP的版本为v2c。

SNMP 测试环境

R2 SNMP配置

snmp-agent
snmp-agent community read  public
snmp-agent community write  private
snmp-agent sys-info version v2c
snmp-agent target-host trap address udp-domain 192.168.10.112 udp-port 162 params securityname public v2c
snmp-agent trap source Ethernet0/0/0
snmp-agent trap enable feature-name SNMP

2.2 Python实现Get操作

通过下面的Python脚本获取R2的系统信息与当前的主机名

#!/usr/bin/python3.4
# -*- coding=utf-8 -*-


from pysnmp.hlapi import *

#varBinds是列表,列表中的每个元素的类型是ObjectType(该类型的对象表示MIB variable)
errorIndication, errorStatus, errorindex, varBinds = next(
    getCmd(SnmpEngine(),
          CommunityData('public'),#配置community
          UdpTransportTarget(('172.16.10.2',161)),#配置目的地址和端口号
          ContextData(),
          ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0')),#读取的OID,获取系统信息
          ObjectType(ObjectIdentity('1.3.6.1.2.1.1.5.0'))#读取的OID,获取主机名
          )
    )

if errorIndication:
    print(errorIndication)
elif errorStatus:
    print('%s at %s' % (
            errorStatus.prettyPrint(),
            errorindex and varBinds[int(errorindex)-1][0] or '?'
        )
    )

for varBind in varBinds:
    print(varBind)#打印返回的结果!

运行结果如下


image.png

  在R2接口上抓包结果如下,Linux主机向R2的161端口发送SNMP get-request报文,可以看到SNMP使用的版本为v2c,设置的团体名为public,随机生成了一个request-id,变量绑定列表(Variable bindings),即要查询的OID,但Value为空;值得注意的是这些信息都是明文传输的,为了安全在实际环境中应使用SNMPv3。


SNMPv2c Get-request

在R2发送的Get-response报文中可以看到request-ip与get-request一致,对变量绑定列表(Variable bindings)中的Value值进行了填充。
SNMPv2c get-response

2.3 Python实现Getbulk操作

通过下面的Python脚本获取R2的接口信息。

#!/usr/bin/python3.4
# -*- coding=utf-8 -*-

from pysnmp.entity.rfc3413.oneliner import cmdgen

cmdGen = cmdgen.CommandGenerator()

errorIndication, errorStatus, errorindex, varBindTable = cmdGen.bulkCmd(
    cmdgen.CommunityData('public'),#配置community
    cmdgen.UdpTransportTarget(('172.16.10.2',161)),0,25,'1.3.6.1.2.1.2.2.1.2',)
     #配置IP地址和端口号;#0为non-repeaters 和  25为max-repetitions(一个数据包中最多25个条目,和显示无关)

if errorIndication:
    print(errorIndication)
elif errorStatus:
    print('%s at %s' % (
            errorStatus.prettyPrint(),
            errorindex and varBinds[int(errorindex)-1][0] or '?'
        )
    )

for varBindTableRow in varBindTable:
    for name, val in varBindTableRow:
        print('OID:%s = %s' % (name, val))

运行结果如下:


image.png

在R2接口抓包结果如下,getBuikRequest相比get-request设置了一个max-repetitions字段,表明最多执行get操作的次数。Variable bindings中请求的OID条目只有一条。


SNMPv2c getBulkRequest

在get-response中,由于getBuikRequest中的get操作设置为25,因此Variable bindings中回复的OID条目有25条,其中包含有实际数据的只有17条。
SNMPv2c get-response

2.4 Python实现Set操作

下面Python脚本用于设置R2的主机名为SNMPv2R2。

#!/usr/bin/python3.4
# -*- coding=utf-8 -*-

from pysnmp.entity.rfc3413.oneliner import cmdgen
from pysnmp.proto import rfc1902

cmdGen = cmdgen.CommandGenerator()

errorIndication, errorStatus, errorindex, varBinds = cmdGen.setCmd(
    cmdgen.CommunityData('private'),#写入Community
    cmdgen.UdpTransportTarget(('172.16.10.2',161)),#IP地址和端口号
    ('1.3.6.1.2.1.1.5.0',rfc1902.OctetString('SNMPv2R2'))#OID和写入的内容,需要进行编码!
)

if errorIndication:
    print(errorIndication)
elif errorStatus:
    print('%s at %s' % (
            errorStatus.prettyPrint(),
            errorindex and varBinds[int(errorindex)-1][0] or '?'
        )
    )
for name,val in varBinds:
    print('%s = %s' % (name.prettyPrint(),val.prettyPrint()))#打印修改的结果

运行结果如下


image.png

在路由器上可以看到主机名有R2变为了SNMPv2R2。


R2

在R2接口上抓包结果如下,由于是write操作,set-request中的团体名为private,而Variable bindings中OID条目中的Value的值也不为空,设置成了SNMPv2R2。
set-request

get-response数据包内容与set-request中无异。


get-response

2.5 Python实现Trap 接收

下面Python脚本用于接收,R2发送的Trap,并做简单解析。

#!/usr/bin/python3.4
# -*- coding=utf-8 -*-

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

def analysis(info): # 分析处理过后的Trap字典信息,当配置改变时,使用python打印出信息
    #print("info",info)
    if info["1.3.6.1.6.3.1.1.4.1.0"]['objectID-value'] == '1.3.6.1.4.1.2011.5.25.191.3.1':
        print(info["1.3.6.1.6.3.1.1.4.1.0"]['objectID-value'],"configurations have been changed")

def cbFun(transportDispatcher, transportDomain, transportAddress, wholeMsg):#处理Trap信息的函数
    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#打印发送TRAP的源信息
            )
        )
        reqPDU = pMod.apiMessage.getPDU(reqMsg)
        #print(reqPDU)

        if reqPDU.isSameTypeWith(pMod.TrapPDU()):
            if msgVer == api.protoVersion1:# SNMPv1的特殊处理方法,可以提取更加详细的信息
                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:# SNMPv2c的处理方法
                varBinds = pMod.apiPDU.getVarBindList(reqPDU)
            result_dict = {}  # 每一个Trap信息,都会整理返回一个字典
            for x in varBinds:  # 打印详细Trap信息
                result = {}
                for x, y in x.items():
                    #print(x, y.prettyPrint())  # 最原始信息打印
                    # 处理信息到字典
                    if x == "name":
                        id = y.prettyPrint()  # 把name写入字典的键
                    else:
                        bind_v = [x.strip() for x in y.prettyPrint().split(":")]
                        for v in bind_v:
                            if v == '_BindValue':
                                continue
                            else:
                                result[v.split('=')[0]] = v.split('=')[1]
                result_dict[id] = result

            # 把字典传到分析模块进行分析
            analysis(result_dict)
    return wholeMsg

transportDispatcher = AsynsockDispatcher()#创建实例
transportDispatcher.registerRecvCbFun(cbFun)#调用处理Trap信息的函数
# UDP/IPv4
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openServerMode(('192.168.10.112', 162))#绑定到本地地址与UDP/162号端口
)

transportDispatcher.jobStarted(1)#开始工作

try:
    transportDispatcher.runDispatcher()#运行
except:
    transportDispatcher.closeDispatcher()
    raise

先运行该脚本,之后再R2上手动将一个接口shutdown,结果如下:


shutdown
image.png

接口上抓包结果如下,此时团体名用的是public,data部分表明是trap。


SNMPv2c Trap

三、使用Pysnmp发送SNMPv3报文

3.1 实验环境

由于Ensp中的通用路由器认证算法只支持des56,而pysnmp不支持该算法,因此使用AR路由器配置SNMPv3。

snmpv3实验

路由器SNMPv3配置

snmp-agent
snmp-agent sys-info version v3
snmp-agent group v3 testgroup privacy
snmp-agent usm-user v3 testuser testgroup authentication-mode sha huawei123 privacy-mode aes128 huawei123
snmp-agent trap source Ethernet0/0/0

3.2 Python 实现Get操作

使用下面Python脚本发送snmpv3 get报文获取设备系统信息。

#!/usr/bin/python3
# -*- coding=utf-8 -*-

from pysnmp.entity import engine, config
from pysnmp.carrier.asynsock.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
from io import StringIO

# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()#添加SNMP引擎实例

# Setup transport endpoint and bind it with security settings yielding
# a target name (choose one entry depending of the transport needed).
# UDP/IPv4
config.addSocketTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)

# Error/response reciever
def cbFun(sendRequestHandle,
          errorIndication, errorStatus, errorIndex,
          varBindTable, cbCtx):#接收信息并处理
    global oid_list#全局清单
    oid_list = []#创建oid_list全局清单
    if errorIndication:#错误打印
        print(errorIndication)
    elif errorStatus:#错误打印
        print('%s at %s' % (
            errorStatus.prettyPrint(),
            errorIndex and varBindTable[-1][int(errorIndex)-1] or '?'
            )
        )
    else:
        for oid, val in varBindTable:
            o = StringIO()
            print(oid,file=o)
            oid_get = o.getvalue().strip()#通过print到StringIO进行转码,然后读回
            o.close()
            v = StringIO()
            print(val,file=v)
            val_get = v.getvalue().strip()#通过print到StringIO进行转码,然后读回
            v.close()
            oid_list.append((oid_get,val_get))#把oid和val的对添加到全局清单oid_list

def snmpv3_get(ip='',user='',hash_meth=None,hash_key=None,cry_meth=None,cry_key=None,oid=''):
    hashval = None
    cryval = None
    model = None

    config.addTargetAddr(#添加目标,'yourDevice'(OID与处理方法),'my-creds'(用户,密码,安全模型),目的IP与端口号
        snmpEngine, 'yourDevice',
        udp.domainName, (ip, 161),
        'my-creds'
    )
    #========================下面的操作在判断安全模型==========================
    #NoAuthNoPriv
    if hash_meth == None and cry_meth == None:
        hashval = config.usmNoAuthProtocol
        cryval = config.usmNoPrivProtocol
        model = 'noAuthNoPriv'
    #AuthNoPriv
    elif hash_meth != None and cry_meth == None:
        if hash_meth == 'md5':
            hashval = config.usmHMACMD5AuthProtocol
        elif hash_meth == "sha":
            hashval = config.usmHMACSHAAuthProtocol
        else:
            print('哈希算法必须是md5 or sha!')
            return
        cryval = config.usmNoPrivProtocol
        model = 'authNoPriv'
    #AuthPriv
    elif hash_meth != None and cry_meth != None:
        if hash_meth == 'md5':
            hashval = config.usmHMACMD5AuthProtocol
        elif hash_meth == 'sha':
            hashval = config.usmHMACSHAAuthProtocol
        else:
            print('哈希算法必须是md5 or sha!')
            return
        if cry_meth == '3des':
            cryval = config.usm3DESEDEPrivProtocol
        elif cry_meth == 'des':
            cryval = config.usmDESPrivProtocol
        elif cry_meth == 'aes128':
            cryval = config.usmAesCfb128Protocol
        elif cry_meth == 'aes192':
            cryval = config.usmAesCfb192Protocol
        elif cry_meth == 'aes256':
            cryval = config.usmAesCfb256Protocol
        else:
            print('加密算法必须是3des, des, aes128, aes192 or aes256 !')
            return
        model = 'authPriv'
    #提供的参数不符合标准时给出提示
    else:
        print('三种USM: NoAuthNoPriv, AuthNoPriv, AuthPriv.。请选择其中一种。')
        return
    #========================判断安全模型结束==========================
    config.addV3User(#添加用户与他的密钥
        snmpEngine, user,
        hashval, hash_key,
        cryval, cry_key
    )
    config.addTargetParams(snmpEngine, 'my-creds', user, model)#创建'my-creds',里边有用户和安全模型

    # Prepare and send a request message
    cmdgen.GetCommandGenerator().sendReq(
        snmpEngine,
        'yourDevice',#创建'yourDevice',有OID和处理方法cbFun
        ( (oid, None), ),
        cbFun
    )

    # Run I/O dispatcher which would send pending queries and process responses
    snmpEngine.transportDispatcher.runDispatcher()#运行实例
    return oid_list#返回oid_list

if __name__ == '__main__':
    ip = "172.16.40.2"
    user ="testuser"
    hk="sha"
    cm = "huawei123"
    hm="aes128"
    ck="huawei123"
    oid="1.3.6.1.2.1.1.1.0"
    for item in snmpv3_get(ip,user,hk,cm,hm,ck,oid):
        print('OID: ', item[0], '\nVALUE: ', item[1])#从oid_list读取并且打印信息

  • 运行结果
运行结果

抓包结果如下,首先发送get-resques进行SNMPv3认证请求,随机生成一个msgID,认证模式为USM,msgflgs中Reportable置1要求对方发送report,其他为置0,表示不进行加密与鉴权;另外安全参数,认证参数、加密参数都为空,此时不携带get请求数据。


get-request

路由器给NMS回复report,msgID与resquest一致,Msgflgs中各位都置0,同时回复使用的安全引擎,认证与加密参数为空,不进行认证与加密,因此能看到data中的数据。


report

得到响应参数后NMS发送PDU,此时数据包中msgflags中各标志为都置1,表示需要回复,同时进行加密与鉴权;安全引擎设置为上一个report包中的参数,也可以看到用户名为testuser,认证参数与加密参数都填有内容,data部分则为加密信息。
PDU

AR1收到请求后进行回复,数据包中msgflags标志位中除reportable外其他位都置1,表示不需要回复,同时进行加密与鉴权。同样也可以看到认证用户为testuser,认证参数与加密参数都有填充,data部分也是同样加密。


PDU

参考:
什么是SNMP - 华为 (huawei.com)
AR100-S V300R003 MIB参考 - 华为 (huawei.com)
SNMP library for Python — SNMP library for Python 4.4 documentation (pysnmp.readthedocs.io)

推荐阅读