snmp学习笔记

更新时间:2024-05-21 06:49:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

1 SNMP简介

SNMP(Simple Network Management Protocol,简单网络管理协议)是网络中管理设备和被管理设备之间的通信规则,它定义了一系列消息、方法和语法,用于实现管理设备对被管理设备的访问和管理。SNMP具有以下优势:

? 自动化网络管理。网络管理员可以利用SNMP平台在网络上的节点检索信息、

修改信息、发现故障、完成故障诊断、进行容量规划和生成报告。

? 屏蔽不同设备的物理差异,实现对不同厂商产品的自动化管理。SNMP只提供

最基本的功能集,使得管理任务分别与被管设备的物理特性和下层的联网技术相对独立,从而实现对不同厂商设备的管理,特别适合在小型、快速和低成本的环境中使用。

1.1.1 SNMP的工作机制SNMP网络元素分为NMS和Agent两种。

?

NMS(Network Management Station,网络管理站)是运行SNMP客户端程序的工作站,能够提供非常友好的人机交互界面,方便网络管理员完成绝大多数的网络管理工作。

? Agent是驻留在设备上的一个进程,负责接收、处理来自NMS的请求报

文。在一些紧急情况下,如接口状态发生改变等,Agent也会主动通知NMS。

NMS是SNMP网络的管理者,Agent是SNMP网络的被管理者。NMS和Agent之间通过SNMP协议来交互管理信息。

1.1 NMS AGENT和MIB关系

一套完整的SNMP系统主要包括管理信息库(MIB)、管理信息结构(SMI)及SNMP报文协议。 (1)管理信息库MIB:任何一个被管理的资源都表示成一个对象,称为被管理的对象。MIB是被管理对象的集合。它定义了被管理对象的一系列属性:对象的名称、对象的访问权限和对象的数据类型等。每个SNMP设备(Agent)都有自己的MIB。MIB也可以看作是NMS(网管系统)和Agent之间的沟通桥梁。它们之间的关系如图1所示。

1.2 MIB树结构

MIB文件中的变量使用的名字取自ISO和ITU管理的对象标识符(object identifier)名字空间。它是一种分级树的结构。如图2所示,第一级有三个节点:ccitt、iso、iso-ccitt。低级的对象ID分别由相关组织分配。一个特定对象的标识符可通过由根到该对象的路径获得。一般网络设备取iso节点下的对象内容。如名字空间ip结点下一个名字为ipInReceives的MIB变量被指派数字值3,因而该变量的名字为:

iso.org.dod.internet.mgmt.mib.ip.ipInReceives

相应的数字表示(对象标识符OID,唯一标识一个MIB对象)为: 1.3.6.1.2.1.4.3

当网络管理协议在报文中使用MIB变量时,每个变量名后还要加一个后缀,以作为该变量的一个实例。如ipInReceives的实例数字表示为:1.3.6.1.2.1.4.3.0.

需要注意的是,MIB中的管理对象的OID有些需要动态确定,如IP路由表,为了指明地址202.120.86.71的下一站路由(next hop),我们可以引用这样的实例:

iso.org.dod.internet.mgmt.mib.ip. ipRouteTable.ipRouteEntry.ipRouteNextHop.202.120.86.71, 相应的数字表示为:1.3.6.1.2.1.4.21.1.7.202.120.86.71

对于这种动态对象标识的实例,由于无法转换为预先指定的Readkey名称,与飞邻的产品架构冲突(需要动态生成可变Readkey),暂不考虑支持。

(2)管理信息结构(SMI)

SMI定义了SNMP框架所用信息的组织、组成和标识,它还为描述MIB对象和描述协议怎样交换信息奠定了基础。

1.3

SMI定义的数据类型:

◆简单类型(simple)

Integer:整型是-2,147,483,648~2,147,483,647的有符号整数 octet string: 字符串是0~65535个字节的有序序列

OBJECT IDENTIFIER: 来自按照ASN.1规则分配的对象标识符集 ◆ 简单结构类型(simple-constructed)

SEQUENCE 用于列表。这一数据类型与大多数程序设计语言中的“structure”类似。一个SEQUENCE包括0个或更多元素,每一个元素又是另一个ASN.1数据类型

SEQUENCE OF type 用于表格。这一数据类型与大多数程序设计语言中的“array”类似。一个表格包括0个或更多元素,每一个元素又是另一个ASN.1数据类型。

◆ 应用类型(application-wide)

IpAddress: 以网络序表示的IP地址。因为它是一个32位的值,所以定义为4个字节; counter:计数器是一个非负的整数,它递增至最大值,而后回零。在SNMPv1中定义的计数器是32位的,即最大值为4,294,967,295;

Gauge :也是一个非负整数,它可以递增或递减,但达到最大值时保持在最大值,最大值为232-1; time ticks:是一个时间单位,表示以0.01秒为单位计算的时间;

DisplayString和OCTET STRING这两种类型并没有什么差别,在net-snmp中,它们都是用的ASN_OCTET_STR类型返回。但是在值的显示上,它们是有区别的。前者按一般的字符串形式显示,如commonNEVendorInfo.0:-->Linux。后者按照十六进制字节流显示(每个字节间以空格分隔),

2 SNMP的5种协议数据单元:

SNMP规定了5种协议数据单元PDU(也就是SNMP报文),用来在管理进程和代理之间的交换。 get-request操作:从代理进程处提取一个或多个参数值。

get-next-request操作:从代理进程处提取紧跟当前参数值的下一个参数值。 set-request操作:设置代理进程的一个或多个参数值。

get-response操作:返回的一个或多个参数值。这个操作是由代理进程发出的,它是前面三种操作的响应操作。trap操作:代理进程主动发出的报文,通知管理进程有某些事情发生。

前面的3种操作是由管理进程向代理进程发出的,后面的2个操作是代理进程发给管理进程的,为了简化起见,前面3个操作今后叫做get、get-next和set操作。图1描述了SNMP的这5种报文操作。请注意,在代理进程端是用熟知端口161来接收get或set报文,而在管理进程端是用熟知端口162来接收trap报文。

SNMP报文

SNMP报文结构如下:(编码之前) 版本号 团体名 协议数据单元PDU SNMP共有5种报文,所以其PDU也有5中,第七点会详细介绍SNMP的5种协议数据单元。 前面的3种操作是由管理进程向代理进程发出的,后面的2个操作是代理进程发给管理进程的,为了简化起见,前面3个操作今后叫做get、get-next和set操作。图1描述了SNMP的这5种报文操作。请注意,在代理进程端是用熟知端口161俩接收get或set报文,而在管理进程端是用熟知端口162来接收trap报文。

图1 SNMP的5种报文操作

图2是封装成UDP数据报的5种操作的SNMP报文格式。可见一个SNMP报文共有三个部分组成,即公共SNMP首部、get/set首部、trap首部、变量绑定。

(1)公共SNMP首部 共三个字段: ? 版本

写入版本字段的是版本号减1,对于SNMP(即SNMPV1)则应写入0。 ?共同体(community)

共同体就是一个字符串,作为管理进程和代理进程之间的明文口令,常用的是6个字符“public”。 ? PDU类型

根据PDU的类型,填入0~4中的一个数字,其对应关系如表2所示意图。 表2 PDU类型 PDU类型 0 1 2 3 4 名称 get-request get-next-request get-response set-request trap (2)get/set首部 ?请求标识符(request ID) 这是由管理进程设置的一个整数值。代理进程在发送get-response报文时也要返回此请求标识符。管理进程可同时向许多代理发出get报文,这些报文都使用UDP传送,先发送的有可能后到达。设置了请求标识符可使管理进程能够识别返回的响应报文对于哪一个请求报文 ?差错状态(error status) 由代理进程回答时填入0~5中的一个数字,见表3的描述 表3 差错状态描述 差错状态 0 名字 noError 说明 一切正常 代理无法将回答装入到一个SNMP报文之中 操作指明了一个不存在的变量 一个set操作指明了一个无效值或无效语法 管理进程试图修改一个只读变量 某些其他的差错 1 tooBig 2 noSuchName 3 badValue 4 5 readOnly genErr ?差错索引(error index)

当出现noSuchName、badValue或readOnly的差错时,由代理进程在回答时设置的一个整数,它指明有差错的变量在变量列表中的偏移。 (3)trap首部 ?企业(enterprise)

填入trap报文的网络设备的对象标识符。此对象标识符肯定是在图3的对象命名树上的enterprise结点{1.3.6.1.4.1}下面的一棵子树上。 ? trap类型

此字段正式的名称是generic-trap,共分为表4中的7种。

trap类型 0 1 2 名字 coldStart warmStart linkDown 说明 代理进行了初始化 代理进行了重新初始化 一个接口从工作状态变为故障状态 3 4 linkUp authenticationFailure 一个接口从故障状态变为工作状态 从SNMP管理进程接收到具有一个无效共同体的报文 5 6 egpNeighborLoss enterpriseSpecific 一个EGP相邻路由器变为故障状态 代理自定义的事件,需要用后面的“特定代码”来指明 当使用上述类型2、3、5时,在报文后面变量部分的第一个变量应标识响应的接口。 ?特定代码(specific-code)

指明代理自定义的时间(若trap类型为6),否则为0。 ?时间戳(timestamp)

指明自代理进程初始化到trap报告的事件发生所经历的时间,单位为10ms。例如时间戳为1908表明在代理初始化后1908ms发生了该时间。 (4)变量绑定(variable-bindings)

指明一个或多个变量的名和对应的值。在get或get-next报文中,变量的值应忽略。

管理变量的表示

管理变量表示管理对象类型在某一时刻的值(或称该类型的实例),SNMP以管理变量作为操作对象。

管理变量的表示方法是这样规定的:形如x.y,其中x是管理对象的object identifer。y是能唯一确定对象类型值的一组数字,在非表型变量中为0,在表型变量中是这个表的索引,比如接口表中的接口号,或路由表中的目的网络地址等等。如:在MIB文件里定义了ipAdEntNetMask这一管理对象,其object identifier为1.3.6.1.1.5.6.1.3它是个路由表中的一项,它的一个实例就是路由表中某一行的子网掩码,如果这行的索引、目的网络地址为129.102.1.0。则这个变量名是:1.3.6.1.1.5.6.1.3.129.102.1.0。在以后的说明中,为了方便,把唯一确定管理变量的一组数字,也就是x.y中的y称作实例。

3 SNMP的运行过程

驻留在被管设备上的AGENT从UDP端口161接受来自网管站的串行化报文,经解码、团体名验证、分析得到管理变量在MIB树中对应的节点,从相应的模块中得到管理变量的值,再形成响应报文,编码发送回网管站。网管站得到响应报文后,再经同样的处理,最终显示结果。 下面根据RFC1157详细介绍Agent接受到报文后采取的动作:

首先解码生成用内部数据结构表示的报文,解码依据ASN.1的基本编码规则,如果在此过程中出现错误导致解码失败则丢弃该报文,不做进一步处理。

第二步:将报文中的版本号取出,如果与本Agent支持的SNMP版本不一致,则丢弃该报文,不做进一步处理。当前北研的数据通信产品只支持SNMP版本1。

第三步:将报文中的团体名取出,此团体名由发出请求的网管站填写。如与本设备认可的团体名不符,则丢弃该报文,不做进一步处理,同时产生一个陷阱报文。SNMPv1只提供了较弱的安全措施,在版本3中这一功能将大大加强。

第四步:从通过验证的ASN.1对象中提出协议数据单元PDU,如果失败,丢弃报文,不做进一不处理。否则处理PDU,结果将产生一个报文,该报文的发送目的地址应同收到报文的源地址一致。 根据不同的PDU,SNMP协议实体将做不同的处理:

3.1.1 1.1 GetRequest PDU 第一种情况:如果PDU中的变量名在本地维护的MIB树中不存在,则接受到这个PDU的协议实体将向发出者发送一个GetResponse报文,其中的PDU与源PDU只有一点不同:将

ERROR-STATUS置为noSuchName,并在ERROR-INDEX中指出产生该变量在变量LIST中的位置。

第二种情况:如果本地协议实体将产生的响应报文的长度大于本地长度限制,将向该PDU的发出者发送一个GetResponse报文,该PDU除了ERROR-STATUS置为tooBig,ERROR-INDEX置为0以外,与源PDU相同。

第三种情况:如果本地协议实体因为其他原因不能产生正确的响应报文,将向该PDU的发出者发送一个GetResponse报文,该PDU除了ERROR-STATUS置为genErr,ERROR-INDEX置为出错变量在变量LIST中的位置,其余与源PDU相同。

第四中情况:如果上面的情况都没有发生,则本地协议实体向该PDU的发出者发送一个GetResponse报文,该PDU中将包含变量名和相应值的对偶表,ERROR-STATUS为noError,ERROR-INDEX为0,request-id域的值应与收到PDU的request-id相同。

3.1.2 1.2 GetNextRequest PDU GetNextRequest PDU的最重要的功能是表的遍历,这种操作受到了前面所说的管理变量的表示方法的支持,从而可以访问一组相关的变量,就好象他们在一个表内。 下面通过一个例子解释表遍历的过程: 被管设备维护如下路由表:

Destination NextHop Metric 10.0.0.99 89.1.1.42 5 9.1.2.3 99.0.0.3 3 10.0.0.51 89.1.1.42 5

假设网管站欲取得这张路由表的信息,该表的索引是目的网络地址。

网管站向被管设备发送一个GetNextRequest PDU,其中的受管对象的标识如下

GetNextRequest ( ipRouteDest, ipRouteNextHop, ipRouteMetric1 )

SNMP agent响应如下GetResponse PDU:

GetResponse (( ipRouteDest.9.1.2.3 = \( ipRouteNextHop.9.1.2.3 = \( ipRouteMetric1.9.1.2.3 = 3 )) 网管站继续:

GetNextRequest ( ipRouteDest.9.1.2.3, ipRouteNextHop.9.1.2.3, ipRouteMetric1.9.1.2.3 ) agent响应:

GetResponse (( ipRouteDest.10.0.0.51 = \( ipRouteNextHop.10.0.0.51 = \( ipRouteMetric1.10.0.0.51 = 5 ))

值得注意的是agent必须能够确定下一个管理变量名,以保证所有变量能被取到且只被取到一次。 网管站继续:

GetNextRequest ( ipRouteDest.10.0.0.51, ipRouteNextHop.10.0.0.51, ipRouteMetric1.10.0.0.51 ) agent 响应:

GetResponse (( ipRouteDest.10.0.0.99 = \( ipRouteNextHop.10.0.0.99 = \( ipRouteMetric1.10.0.0.99 = 5 )) 网管站继续

GetNextRequest ( ipRouteDest.10.0.0.99, ipRouteNextHop.10.0.0.99, ipRouteMetric1.10.0.0.99 )

这时因为路由表中所有的行都被取遍,agent因返回路由表对象的下一字典后继即该管理对象在MIB树中的后序遍历的直接后继。这里应是nettoMediaIndex,管理对象的OBJECT IDENTIFIER。这个响应通知网管站对表的遍历已经完成。

3.1.3 1.3 GetResponse PDU GetResponse PDU只有当受到getRequest GetNextRequest SetRequest才由协议实体产生,网管站收到这个PDU后,应显示其结果。

3.1.4 1.4 SetRequest PDU SetRequest PDU除了PDU类型标识以外,和GetRequest相同,当需要对被管变量进行写操作时,网管站侧的协议实体将生成该PDU。 对SetRequest的响应将根据下面情况分别处理:

如果是关于一个只读变量的设置请求,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为noSuchName, error index的值是错误变量在变量list中的位置。

如果被管设备上的协议实体收到的PDU中的变量对偶中的值,类型、长度不符和要求,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为badValue, error index的值是错误变量在变量list中的位置。

如果需要产生的GetReponse报文长度超过了本地限制,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为tooBig, error index的值是0。

如果是其他原因导致SET失败,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为genErr, error index的值是错误变量在变量list中的位置。

如果不符合上面任何情况,则agent将把管理变量设置收到的PDU中的相应值,这往往可以改变被管设备的运行状态。同时产生一个GetResponse PDU,其中error status置为noError,error index的值为0。

3.1.5 1.5 Trap PDU Trap PDU的有如下的形式 产生trap的系统的OBJECT IDENTIFIER 系统的IP地址 普通类型 特定类型 时戳 变量对偶表

Trap是被管设备遇到紧急情况时主动向网管站发送的消息。网管站收到trap PDU后要将起变量对偶表中的内容显示出来。一些常用的trap类型有冷、热启动,链路状态发生变化等。

4 net-snmp的配置文件snmpd.conf配置说明

snmpd.conf用来配置代理和管理端通信时的参数。snmpd.conf的配置项很多,但是真正常用的就那么几个,该文件中用到了4条指令:com2sec, group, view, access。

4.1 1.com2sec

命令格式一:com2sec [-Cn context] sec.name source community

功能: map an SNMPv1 or SNMPv2c community string to a security name,即将实体(community)字符串映射为安全的

名字,v1 和v2版本都是明文密码,也就无所谓安全性了。v3中增加了安全性。 参数说明:-Cn context:可选的,在v3版本中使用 sec.name:内部映射的名字,字符串,后面用到 community:实体字符串,外部使用的名字

source:可以访问的ip地址范围,默认值\,即任何ip都可以访问。 也可以限制ip范围,有两种方式:IP/MASK和IP/BITS IP/MASK:10.10.10.0/255.255.255.0

IP/BITS :10.10.10.0/24:只有ip地址在10.10.10.0 mask为24个1,即255.255.255.0 命令格式二:com2sec6 [-Cn context] sec.name source community 针对ipv6,其他和com2sec相同

命令格式三:com2secunix [-Cn context] sec.name sockpath community 只限本地socket可以访问

4.2 2. group

命令格式:group groupName securityModel sec.name

功能:将sec.name映射到一个group中,组中具有相同的存取权限。组中至少有两个 sec.name,如果只有一个sec.name,也会被解释为两个。即有两个相同的 参数说明:groupName:组名

securityModel:v1、v2c、usm、tsm、ksm其一

4.3 3. view

命令格式:view viewName type oid[mask]

功能:定义一个view,表示整个OID树的子树,同一个子树可以定义多个view 参数说明:viewName:view名字

type:有两个值:include 和 exclude oid:可以访问的oid [mask]:对oid的mask

例如:view all include 1.3.6.1.2.1.4 0xf0

0xf0:1111 0000,即访问的oid的前4位必须是1.3.6.1,否则不能访问,即 可以访问1.3.6.1下所有的子oid

4.4 4. access

命令格式:access groupName context model level prefx read write notify 功能:设置访问某一个view的存取权限 参数说明:groupName:控制存取权限的组名

context:v1和v2c版本,context必须设置为\ model:v1、v2c、usm、tsm、ksm

最后3中是v3版本的授权模式,usm(User-Based Security Model)是默认授权模式,tsm用于SSH or DTLS,ksm用于支持 Kerberos

level:可以去3种值:noauth、auth、priv noauth:允许无权限访问(v1,v2c使用) auth:必须有权限才能访问 priv:强制加密访问

prefx:exact or prefix

read、write、notify:指明某一个view的权限是否可以GET*, SET*、TRAP/INFORM,如果该view不能read,则置none

com2sec命令,它的基本语法是“com2sec NAME SOURCE COMMUNITY”。这里要提一下SNMP的安全策略,其实SNMP的安全性并不好,在这个协议中使用COMMUNITY这个东西来做访问控制的。简单来理解就是现在有一个帮会的一个人找去了总舵,那么这个人怎么跟总舵相认呢?他们直接约定一个“暗号”,例如见面就说“床前明月光”,那么总舵的前台就会带你去“床前明月光”想对应的地方。现在回过头来看这行配置,假如有以下一句话

com2sec magusu default magus 意思就是做一个映射,把magus这个COMMUNITY串和magusu这个名字做好一对映射,那么以后在调用SNMP的时候,只要你声明你就是magus,那么就可以获得相应的权限了。有了映射以后下面需要做的是建立一个从magusu到组的一个映射,用到的命令是group,它的基本语法是“group NAME MODEL SECURITY”

group magusg v2c magusu

上面这句话的意思就是把magusu放到组magusg里面,用的协议是v2c,插一句,SNMP现在通常用的有3个版本。那好了,组建好了下来要做什么呢?就是要建立一个VIEW,VIEW的基本语法是“view NAME TYPE SUBTREE [MASK]”

view magusv included .1.3.6.1.4.1.9129

像上面这句话的意思就是赋予magusv这个view能够查看1.3.6.1.4.1.9129下面所有节点的权限。最后要做的是设定哪些组的人,哪些view的用户可以做什么具体的事情,用access来设置,基本语法是“access NAME CONTEXT MODEL LEVEL PREFX READ WRITE NOTIFY”。

access magusg “” any noauth exact magusv magusv none

由于例子中用的是v2c版本的协议,所以CONTEXT必须为空,MODEL也就是协议的版本号,在这里例子里面可以是any也可以是v2c,还是由于v2c的缘故,所以LEVEL是noauth;READ、WRITE和NOTIFY分别需要指定一个view或者什么都不指定;在这里的设置就是magusv这个view可以对前面设置好的节点进行读和写操作。

到此,服务端的配置已经完成,重启一下snmpd就可以了,下面看看如何通过SNMP得到我们想要的数据。

snmpwalk -v 2c -c magus localhost 1.3.6.1.4.1.9129.1.2.2

snmpwalk可以便利指定节点下的所有子节点,-v 参数指定SNMP的协议版本,这个跟我们之前配置服务器的版本是要一致的,也就是2c,然后-c参数就指定了community的字符串,也就是刚才定义的magus,然后就是主机地址,由于我在本机测试,所以用的localhost,实际中可以填上IP,最后就是OID的值。搞定!

对于刚刚接触SNMP的朋友来说,理解community的作用是关键,其实说的通俗点就是接头暗号,而且还是个明文的“暗号”。

在新版本中,其配置文件增加了新的更加简便的配置方法,但是依然兼容以上的配置方法。

5 SNMP协议 PDU报文格式分析(BER编码)

SNMP使用ASN.1(Abstract Syntax Notation One)来定义SNMP报文格式和

MIB(Management Information Base)变量的名称。

ASN.1是一种描述数据和数据特征的正式语言,它和数据的存储及编码无关。根据ASN.1

标准定义,数据类型分为:简单数据类型: boolean、null、integer、real、octerstring、object identifier、ipaddress、time ticks等。构造数据类型:sequence、sequence of set、set ofchoice等。构造数据类型提供一种或多种简单数据类型进行复合的方法。 基本编码规则BER

在具体系统中,我们需要用具体的编码规则将ASN.1语法表示的抽象数据转换成具体的比特流。

SNMP使用的编码方法是BER(Basic Encoding Rule)。BER的数据都由三个域构成:标识

域(tag)+长

度域(length)+值域(value)。

5.1 标识域

标识域指明数据的类型,占用1个字节,常见的 类型有:

BOOL(0x01); INT(0x02);

OCTSTR(0x04); NULL(0x05); OBJID(0x06); ENUM(0x0A); SEQ(0x30); SETOF(0x31); IPADDR (0x40); COUNTER (0x41); GAUGE(0x42); TIMETICKS(0x43); OPAQUE(0x44); GET(0xA0);

GETNEXT(0xA1); GETResp(0xA2); SET(0xA3); TRAP(0xA4);

数据类型解释:

? INTEGER:整数类型,表示一个有符号的任意精度的标量,它的编码是可移植,平台无关的。

? ? ? ?

OCTER STRING ,八位位组串,保存字节数组。

NULL,空类型,实际上是”占位符”, 它是含有空白选项的选择修改器所特有。 OBJECT IDENTIFIER,对象标识符类型即SNMP中的OID。

SEQUENCE,序列,是一种用来把相关数据元素收集为一个独立的可解码元素的简单方法。

5.2 长度域

长度域指明值域的长度,不定长,一般为一到三个字节。其格式可分为短格式和长格式.长度域采用短/长指示器(Short/Long Form)来标明长度指示符是否是单个字节,指示器在bit8上。如果短/长指示器是0,则为短限定格式,低7位包含的就是数据的长度值,长度值在0到127之间;如果短/长指示器是1,则为长限定格式,其低7位的值表示后面紧跟的长度指示值的字节数,而后续字节拼接起来的值就是数据字段的长度,即数据长度。 例:

length:30 => 1E length:169 => 81 A9 length:1500 => 82 05 DC

长度域指明值域的长度,不定长,一般为1到3个字节。根据编码的实际长度,ASN.1定义了两种长度编码(length encoding)方法,长编码和短编码。

编码字节的最高位代表的是短编码还是长编码;而低7位则形成一个长度立即数。

<1>. 短编码。

在短编码中,值域的长度必须小于128字节。长度立即数用来表示值域的长度。例如,对于一个长度为65 (0×41)的值域进行编码,其长度编码字节只需简单的设置为0×41即可。因为其最高位是0,则解码器可以判断出这是短编码,而且长度是65。

<2>. 长编码。

在长编码中,定义了附加的抽象数据来对长度进行编码,它仅适用于所有长度为128字节或以上的值域。在这种模式下,长度立即数存储的是为了表示长度所需的字节数。这个长度必须以big-endian格式进行编码。(其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。)。

例如,为一个长度为47310 (0xB8CE)的值域进行编码,因为它的长度大于127, 所以要采用长编码方式。实际的长度需要两个字节来表示。则,长度编码字节为0×82; 然后用big-endian格式存储的长度值为0xB8 0xCE。则全部长度编码为 0×82 B8 CE。

值域(value)的编码

1>整型Integer的编码 integer::=0×02 length {value}* (*表示重复)

整型数据值域用补码表示,去掉多余的零(正数)或一(负数)。值域最高位为符号位。例: 整数1500 => 02 02 05 DC;整数21500 => 02 02 FA 24。 2> 对象标识ObjectID编码

SNMP网管(即管理端)维护的所有管理信息库(MIB)对象采用ObjectID表示(即OID),如1.3.6.1.2.1.1.1表示MIB库中的设备描述即SysDesc变量,其编码规则如下: objectID::=0×06 length {subidentifier}* (1) subidentifier::= {leadingbyte}* lastbyte (2) leadingbyte::=1 7bitvalue (3) lastbyte::=0 7bitvalue (4) 首两个ID被合并为一个字节X*40+Y (5)

虽然规则很多,但由于大多数子对象标识在0~127,只需按规则(1)、(5)即可;当子对象标识大于127,则按规则(2)、(3)、(4)将其分解为多个字节,最后一个字节的高位为零,其余字节的高位为一。如1.3.6.1.810.1,根据规则(5),首两个子对象标识1.3被合并为2B(1 3 40+3=43);子对象标识810超过127,根据规则(2)、(3)、(4)将其拆分为两个字节86 2A (810=11 0010 1010==>1000 0110 0010 1010);整个OID被编码为:0×06 0×06 0x2b 0×06 0×01 0×86 0x2a 0×01。 3> sequence组合类型的编码 sequence::=0×30 length{asndata}*

如:30 05 02 01 10 05 00,表示一个sequence结构,内含两个成员,其中一个为整型,另一个为空类型(NULL)。 4>空类型的编码 null::=0×05 0×00 5>字符串类型的编码 string::=0×04 length{byte}*

例如:04 06 70 75 62 6c 69 63,表示字符串public

5.3 值域

值域保存的是数据的实际编码。虽然ASN.1定义了很多数据类型,但大多数类型可由整形、对象标

识、空、串等基本数据类型和sequence构造类型表示。例如有符号整数和无符号整数、TimeTicks、Gauge、Counter统一用整数表示。

5.4 整型Integer

integer::=0x02 length {byte}* (*表示重复)

整型数据值域用补码表示,去掉多余的零(正数)或一(负数)。值域最高位为符号位。例:

1500 => 02 02 05 DC(第一个02是integer,第二个02是length,第三、第四是1500的值) -1500 => 02 02 FA 24 =>>-1500=>1000 0101 1101 1100=>1111 1010 0010 0011 => 1111 1010 0010 0100=>FA 24

5.5 对象标识ObjectID

SNMP服务器维护的所有管理信息库(MIB)对象采用ObjectID表示,如,1.3.6.1.2.1.1.1表示

MIB库中的设备描述SysDesc变量,其编码规则如下: objectID::=0x06 length {subidentifier}* (1) subidentifier::={leadingbyte}* lastbyte (2) leadingbyte::=1 7bitvalue (3) lastbyte::=0 7bitvalue (4)

首两个ID被合并为一个字节X*40+Y (5)

虽然规则很多,但由于大多数子对象标识在0~127,只需按规则(1)、(5)即可;当子对象标识大于127,则按规则(2)、(3)、(4)将其分解为多个字节,最后一个字节的高位为零,其余字节的高位为一。

如:1.3.6.1.810.1,根据规则(5),首两个子对象

标识1.3被合并为2B(1*40+3=43);子对象标识810超过127 ,根据规则(2)、(3)、(4)将其拆分为两个字节86 2A(810=11 0010 1010 ==> 1000 0110 00101010);整个MIB被编码为:0x06 0x06 0x2b 0x06 0x010x86 0x2a 0x01。

5.6 sequence组合类型

sequence::=0x30 length {asndata}*

如:30 05 02 01 10 05 00表示一个sequence结构,内含两个成员,其中一个为整型,另一个为空类型(NULL)。 其它类型

null::=0x05 0x00

string::=0x04 length {byte}*

SMI定义的OID返回值的数据类型。

SMI定义了一定数量的OID返回的数据类型。这些数据类型包括: Integer 整型

Signed 32bit Integer (values between -2147483648 and 2147483647). 有符号32位整数(值范围: -2147483648 - +2147483648) Integer32

Same as Integer. 与Integer相同。 UInteger32

Unsigned 32bit Integer (values between 0 and 4294967295). 无符号32位整数(值范围:0-4294967295). Octet String

Arbitrary binary or textual data, typically limited to 255 characters in length. 任意二进制或文本数据,通常长度限制在255个字符内。 Object Identifier An OID. 一个OID。 Bit String

Represents an enumeration of named bits. This is an unsigned datatype. 表示取名的位的枚举。这是一个无符号的数据类型。 IpAddress

An IP address. 一个IP地址。 Counter32

Represents a non-negative integer which monotonically increases until it reaches a maximum value of 32bits-1 (4294967295 dec), when it wraps around and starts increasing again from zero. 表示一个非负的整数(可递增到32位最大值-1),然后恢复并从0开始递增。 Counter64

Same as Counter32 but has a maximum value of 64bits-1. 与Counter32相同,最大值为64位的最大值-1。 Gauge32

Represents an unsigned integer, which may increase or decrease, but shall never exceed a maximum value. 表示无符号整数,可增加或减少,但是不超过最大值。 TimeTicks

Represents an unsigned integer which represents the time, modulo 2?32 (4294967296 dec), in hundredths of a second between two epochs. 表示代表数据的一个无符号整数,2^32取模(4294967296),两个值之间为百分之一秒。 Opaque

Provided solely for backward-compatibility, its no longer used. 提供向下兼容,不再使用的数据类型 NsapAddress

Represents an OSI address as a variable-length OCTET STRING. 表示一个

用变长八进制字符窗表示的OSI地址。

5.7 SNMP报文

SNMP报文格式

SNMP共有五种报文: Get_Request(0xA0)

Get_Next_Request(0xA1) Get_Response(0xA2) Set_Request(0xA3) Trap(0xA4),其结构如下:

SNMP_Message::=SEQUENCE{version Integer,community OcterString, pdu SNMP_PDUs}

SNMP_PDUs::=CHOICE{get_request PDU,get_next_request PDU,get_response PDU,set_request PDU,trap TrapPDU}

PDU::=SEQUENCE{request-id Integer,error-status Integer,error index Integer,variable-bindings VarBindList}

TrapPDU∷=SEQUENCE{enterprise ObjectID,agent_addr IPAddr, trap_type Integer, specific_type Integer,time TimeTicks, variable-bindings VarBindList}

6 对net-snmp中netsnmp_variable_list的理解

文档参见:http://www.net-snmp.org/dev/agent/structvariable__list.html snmp_variable_list是variable_list的typedef

其实snmp_variable_list相当于是snmp_variable_list类型链表的某一个元素,而不是整个链表。它里面放了一个snmp变量的相关信息。

snmp_variable_list里的next_variable是指向这个链表的下一个snmp_variable_list元素的指针。

name是这个元素对应的oid的指针。name指向的是一个int类型的数组,这个数组里面放的就是这个元素的oid

name_length是name指的那个装有oid的int型数组的长度。说白了就是这个元素的oid的长度

type是这个元素的类型。常用的有:ASN_OBJECT_ID,ASN_INTEGER,ASN_OCTET_STR,ASN_FLOAT,ASN_INTEGER64,ASN_TIMETICKS,ASN_IPADDRESS,ASN_COUNTER,ASN_GAUGE等等。

val是一个netsnmp_vardata类型的变量。netsnmp_vardata的文档在:

http://www.net-snmp.org/dev/agent/unionnetsnmp__vardata.html。netsnmp_vardata里面integer是指向ASN_INTEGER型结果的指针,string是指向ASN_OCTET_STR类型结果的指针,objid是指向ASN_OBJECT_ID类型的指针等等。

val_len是存放这个元素的值的内存的大小。如果这个值是一个ASN_OCTET_STR的元素,那么这个元素的长度就是val_len/sizeof(char),同样的,如果这个值是一个oid,那么存放这个oid的int类型数组的长度就是val_len/sizeof(int)

name_loc是一个oid类型的数组,这个数组存放的其实就是这个元素的oid。

name_loc和name前者是一个指向这个元素oid的指针,后者是存放这个元素oid的数组。这两者都可以取到这个元素的oid

buf是个u_char类型的数组。如果这个元素的值的在内存中的长度小于40(有百分之九十的可能性是这样),那么buf里面就放的这个元素的值。如果这个元素的值在内存中的长度大于40,buf里面就不知道放的什么乱七八糟的了。对于ASN_TIMETICKS类型的值来说,buf里面放的是一个int类型的指针,指针的值是这个元素的值的地址,*(vars->buf)这样可以取出这个值。对于ASN_OCTET_STR类型的值来说,如果长度小于40,那么buf里买面就放的这个字符串。对于ASN_OBJECT_ID类型的值来说,buf里面装的是oid的值,只不过是这也样子的:.1.3.6.1.6.3.16.2.2.1在buf里面的值

是.1.0.0.0.3.0.0.0.6.0.0.0.1.0.0.0.6.0.0.0.3.0.0.0.16.0.0.0.2.0.0.0.2.0.0.0.1.0.0.0

7 mib2c

表 1- 1 mib2c所使用的配置文件 File Name mib2c.scalar.conf mib2c.int_watch.conf mib2c.mfd.conf mib2c.iterate.conf mib2c. iterate_access.conf mib2c.create-dataset.conf mib2c.table_data.conf mib2c.container.conf mib2c.array-user.conf scalar Usage int scalar Table(推荐) Table,要获取的数据在Agent外部 Table,要获取的数据在Agent内部 Example agent/mibgroup/if-mib/ifTable/ifTable*.c agent/mibgroup/mibII/vacm_context.c examples/netSnmpHostsTable* agent/mibgroup/examples/data_set.c Table,要获取的数 据在Agent内部,并对表排序 产生头文件 TRAP OLD API(4.X) mib2c.column_defines.conf 产生头文件 mib2c.column_enums.conf mib2c.notify.conf mib2c.old-api.conf

在確定PATH設定好後,執行mib2c時, 通常會遇到訊息說沒有安裝Perl SNMP Module, 這是因為我們在configure時有設了prefix的關係, 導致perl找不到share library。

解決的方法是將net-snmp的lib目錄設定給ldconfig知道即可。

# echo /opt/net-snmp/lib > /etc/ld.so.conf.d/net-snmp.conf # ldconfig -v

3.

最後就是mib2c操作方法, 據mib2c的manpage裡說明,

mib2c的功用是產生template code給agent來擴充功能使用。 Usage為

mib2c [-h] -c CONFIGFILE [-I PATH] [-f OUTNAME] [-i][q][-S VAR=VAL] MIBNODE

而mib2c的使用方法是以mib node為parameter來開始生成template code。 所以必須先將mib file放置在$HOME/.mibs/或/opt/net-snmp/share/snmp/mibs, 然後在環境變數中設定MIBS,

export MIBS=+NET-SNMP-TUTORIAL-MIB

接著麻煩的在這裡,

你必須依著你的的mib node來選定所產生的template code的CONFIGFILE。 下面是CONFIGFILE所支援的類型: 如果你要寫些scalars所需的code,請用:

mib2c -c mib2c.scalar.conf MIBNODE

如果你要用int scalars,請用:

mib2c -c mib2c.int_watch.conf MIBNODE

一般來講Table是建議採用MDF:

mib2c -c mib2c.mfd.conf MIBNODE

如果Table Data是在外部時:

mib2c -c mib2c.iterate.conf MIBNODE

如果Table Data是在agent裡:

mib2c -c mib2c.create-dataset.conf MIBNODE

承上,如果還需排序時:

mib2c -c mib2c.array-user.conf MIBNODE

如果是要產生Table上任一Column的header時:

mib2c -c mib2c.column_defines.conf MIBNODE

如果是要產生Column上任一Enum的header時:

mib2c -c mib2c.column_enums.conf MIBNODE

7.1 mib2c.mfd.conf

在用来存放源码的目录下执行mic2c -c mib2c.mfd.conf表节点(节点名一定是Table结尾的),选择全部使用默认生成代码。

如果有需要 mib2c -c subagent.m2c 表节点名,能帮你生成扩展agent的启动文件。mib2c -c mfd-makefile.m2m 表节点名能帮你生成makefile文件模板。

关于MFD模板生成的文件修改: xxxxTable_data_access.c文件如下:

xxxxTable_init_data函数用来初始化数据,如不需要可以修改。 xxxxTable_container_init函数初始化一些参数.其中:

cache->timeout 缓存过期时间,不使用缓存可以设置为0或-1 xxxxTable_container_load函数可以用来填入数据,其中: //snmp_log(LOG_ERR, \ //return MFD_ERROR;

填入值之前需要判断是否超过范围,如果足够大的空间,可以忽略。

while (1) {}死循环用来逐行加入数据。 xxxxTable_container_free函数用来释放内容

xxxxTable_data_get.c文件主要实现get操作,如果某个字段get不到,检查这里的实现。 xxxxTable_data_set.c文件主要实现set操作 xxxxTable_oids.h文件表结构的宏定义

xxxxTable.h .c文件是表结构的数据结构体以及request的相关操作实现

执行xxxsubagent需要设置环境量export LD_LIBRARY_PATH=/usr/local/lib,否则会提示:error while loading shared libraries: libnetsnmpagent.so.25: cannot open shared object file: No such file or directory

或者直接将libnetsnmpagent.so.25 等等与可执行文件放一起。或者设置LD_LIBRARY_PATH到/etc/profile

使用net-snmp-config --base-cflags 和 net-snmp-config --agent-libs 可以帮组我们配置gcc的编译选项。

除了使用agentx模式,还可以将源码放入net-snmp源码这中一起编译到snmpd中。 将源码放到xxxxxxxxxx/agent/mibgroup/以模块名命名的目录下(没有试过是不是非要这样),执行: ./configure \\

--with-default-snmp-version=\ --with-sys-contact=\ --with-sys-location=\ --with-logfile=\ --with-persistent-directory=\ --disable-debugging \\ --disable-embedded-perl \\ --without-perl-modules \\ --without-rpm \\ --enable-mini-agent \\ --enable-static \\

--with-mib-modules=\模块名称\

--enable-debugging-with-mib-modules=loogson //打开调试信息 之后 make & make install

7.2 扩展 agent

到这里你已经应该正确的写出了一个私有的MIB文件,你需要把它放置到系统的MIB文件库中。我的系统的MIB库路径是/usr/share/snmp/mibs/。

然后开始学习使用MIB2C这个强大的工具,它的作用是把你写好的一个MIB文件,转化成一个或多个在net-snmp下可以使用的.c/.h文件。 一个简单的例子:当你写成了一个叫做ucdDemoPublic的私有MIB文件。 mib2c -c mib2c.scalar.conf ucdDemoPublic就可以生成ucdDemoPublic.c和ucdDemoPublic.h

MIB2C有很多版本,也有很多参数可供选择,可以在netsnmp的官网上查到:http://net-snmp.sourceforge.net/docs/man/mib2c.html

重要的参数是 –c 后面的那个.conf值: mib2c.mfd.conf mib2c.scalar.conf mib2c.int_watch.conf mib2c.iterate.conf mib2c.create-dataset.conf mib2c.array-user.conf mib2c.column_defines.conf mib2c.column_enums.conf 一个重要的问题:

在使用mib2c时,需要配置一个mib2c的配置文件:snmp.conf 我的配置文件路径是:/var/net-snmp/snmp.conf

在配置文件中要加入类似下面的语句,表示我要把XXX-MIB.txt这个mib文件转化成c文件。

mibfile /usr/share/snmp/mibs/XXX-MIB.txt 简单变量的代码生成

用mib2c.scalar.conf生成简单MIB变量的XXX.c和XXX.h,如上例。生成MIB之后需要自己根据具体参数的含义,修改get或set时的动作。在XXX.c中包含两个重要的函数:init_XXX()和handle_XXX(),需要在init_XXX中修改oid,在handle_XXX中的case MODE_GET和case MODE_SET_ACTION中设置GTE和SET的函数。

Table变量的代码生成

用mib2c.mfd.conf生成table类型的MIB文件,生成的文件比较多,包括:XXX.c/.h;XXX_data_access.c/.h; XXX_data_get.c/.h; XXX_data_set.c/.h; XXX_enums.h; XXX_interface.c/.h; XXX_oids.h;

Snmpget的修改:需要修改的是XXX_data_access.c中的XXX__container_load函数,修改XXXTable中的变量值。

Snmpset的修改:需要修改的是:

XXX_data_set.c:XXXTable_commit中设置

rowreq_ctx->column_set_flags的值,表示已经修改了XXXTable中的某一项。

XXX_data_set.c:中所有的XXX_set.c中指定自己想要的值。 代码集成

生成了正确的.c/.h文件之后,需要把代码继承到snmpd中,有两种比较靠谱的方法:

1、 把所扩展的agent MIB,静态的集成到snmpd中。方法:把所生成的.c/.h文件拷贝到/net-snmp/agent/mibgroup下面,然后重新 ./configure --with-mib-modules=\好了你的私有MIB已经被静态集成到snmpd里面了,最后用命令行snmpget;snmpset试验一下。

2、 是动态加载的方式,自己写一个makefile,把所有生成的.c/.h编译成为一个.so,把这个.so放到一个特定的路径下面,并在snmpd.conf文件中指明.so的位置。

1)编译的时候除指定-fPIC -shared -O0参数之外,还需要指定= -I. `net-snmp-config --cflags`,在最后链接成.so的时候还需要指定`net-snmp-config --libs`参数

2)在需要在snmpd.conf文件中指定.so的位置:如 dlmod exampleTable /usr/lib/exampleTable.so 3)在运行./snmpd的时候需要指定所需的.so如: ./snmpd –f –L –Dexample,DLmod, 这样也可以实现对agent的动态扩展。

7.3 扩展trap

net-snmp的trap也是可以扩展的,我用的方法是把写好的trap代码(.c)放

到snmp/snmplib下面,修改一下snmplib的Makefile,make;make install,即可 其代码的样例如下:

#include

#include config_require(util_funcs) int TrapSendTest() {

netsnmp_session session,*ss;

netsnmp_pdu *pdu; long sysuptime;

char csysuptime[20]; int status = 0;

oid oid_sysuptime[] = { 1,3,6,1,2,1,1,3,0 }; char *cp1 = \

snmp_sess_init(&session);

session.version = SNMP_VERSION_2c; session.peername = cp1; session.remote_port = 162;

session.community = (unsigned char*)cp2;

session.community_len = strlen((char *)session.community); session.retries = 3;

session.timeout = 2000; session.sessid = 0;

SOCK_STARTUP;

ss = snmp_add(&session,netsnmp_transport_open_client(\ session.peername), NULL, NULL); if (ss == NULL) {

snmp_sess_perror(\ SOCK_CLEANUP; }

pdu = snmp_pdu_create(SNMP_MSG_TRAP2); sysuptime = get_uptime();

sprintf(csysuptime, \

status = snmp_add_var(pdu, oid_sysuptime, OID_LENGTH(oid_sysuptime), 't', csysuptime); if(status != 0) {

snmp_sess_perror(\return -1; }

status = snmp_send(ss, pdu); if (status == 0) {

snmp_sess_perror(\ snmp_free_pdu(pdu); return -2; }

snmp_close(ss);

snmp_shutdown(\return 0; }

在实际用的时候,改一改参数就OK了。 Makefile的修改:

在.o和.lo编译目标列表尾部加入你所加入的文件名即可:XXX.o和XXX.lo, 这样你扩展的trap就静态加入snmpd中了

Snmpd的调用:snmpd的监听程序和你所扩展的trap程序是不能顺序运行的,否则会互相影响。所以要fork一下,父子进程分别运行。

7、调试方法

1、可以使用strace -f -L ./snmpd的方法跟踪snmpd调用的系统调用,来猜测snmpd的运行流程

2、由于snmpd是后台运行的,可以用syslog.

8 Net-snmp安装

8.1 下载net-snmp源码

可以在http://www.net-snmp.org/download.html下载net-snmp相应的版本源码。

8.2 Perl5编译

CFLAGS='-m64 -mtune=nocona'

./Configure -des -A ccflags=-fPIC make

make install

/usr/lib64/perl5/CORE/config.h

8.3 编译

tar-xzf net-snmp-5.7.3.tar.gz cdnet-snmp-5.7.3

1. ./configure --with-mib-modules=agentx

configure的配置参数介绍

./configure --with-perl-modules --prefix=/usr/local/net-snmp --enable-mfd-rewrites --with-default-snmp-version=\

--with-sys-contact=\ --with-sys-location=\\

--with-logfile=\ --with-persistent-directory=\其中配置选项含义如下:

Perl-modules:这个是mib2c依赖的关键模块。 prefix:net-snmp将要安装的路径

enable-mfd-rewrites:允许用新的 MFD重写可用的 mid模块 with-default-snmp-version:默认的 SNMP版本 with-sys-contact:可以配置该设备的联系人 with-sys-location:该设备的位置 with-logfile:日志文件路径

with-persistent-directory:不变数据存储目录 ) --with-mib-modules添加需要的代理 2. make

3. make install

4. cp ./EXAMPLE.conf /usr/local/net-snmp/share/snmp/snmpd.conf

根据自己需求修改/usr/local/net-snmp/share/snmp/snmpd.conf文件,修改snmpd.conf将在后面介绍。 把我们的Test-MIB加载到MIB中

echo \

8.4 启动snmpd进程

? 方法一 cd agent

./snmpd -f –L

在启动snmpd的时候,使用:snmpd -d –L,则可以在终端显示数据码流的信息以及代码中的printf信息。如果加上-D选项,如:snmpd -d -L -Dxxx,那么在代码中的 DEBUGMSGTL((\\;如果第一个参数有和xxx匹配上,那么就会输出initializing ? 方法二

执行命令echo export LD_LIBRARY_PATH=/usr/local/lib >> .bashrc 则可以在任何目录启动snmpd–f –L ? 方法三

/usr/share/sbin/snmpd –c /usr/local/share/snmp/snmpd.conf &

1. 测试net-snmp安装是否正确

在另外 一个终端执行

snmpget -v 2c -c public localhost system.sysUpTime.0 安装正确则会显示如下

注意:

如果configure的时候指定的—prefix是其他的路径,例如: ./configure --prefix=/usr/local/net-snmp需要如下操作 ? 建立软连接

ln -s /usr/local/net-snmp/bin/* /usr/local/bin/

ln -s /home/qshy/net-snmp-5.7.3/include/net-snmp /usr/local/include/ ? 导出环境变量

export LD_LIBRARY_PATH=/usr/local/net-snmp/lib:$LD_LIBRARY_PATH export PATH=/usr/local/net-snmp/bin:/usr/local/net-snmp/sbin:$PATH

2. 编辑/etc/rc.local添加为自启动

添加下面一行:

/usr/local/sbin/snmpd -c /usr/local/share/snmp/snmpd.conf & 3. 设置环境变量,编辑/etc/profile

在export前添加下面一行

PATH=/usr/local/net-snmp/bin:/usr/local/sbin:$PATH

8.5 配置snmpd.conf文件

vim /usr/local/net-snmp/share/snmp/snmpd.conf 修改内容如下

注释掉agentAddress udp:127.0.0.1:161 添加agentAddress udp:161

注释掉rocommunity public default -V systemonly 添加rocommunity public 添加如下配置

com2sec local default public group MyROGroup v2c local view all included .1 80

access MyROGroup \

如果是按照子代理的方式扩展mib则需要开启master agentx配置

验证服务是否启动成功,同时查看161端口 启动成功

# lsof -i:161 ,如果终端显示如下图,NAME的地方要显示*而不要localhost, 则服务已经

8.6 编写MIB文件

cd/usr/local/share/snmp/mibs touchTest-MIB.txt Test-MIB.txt文件内容 -- Test-MIB.txt

Test-MIB DEFINITIONS ::= BEGIN

IMPORTS

OBJECT-GROUP, MODULE-COMPLIANCE, NOTIFICATION-GROUP FROM SNMPv2-CONF

enterprises, Integer32, Unsigned32, OBJECT-TYPE, MODULE-IDENTITY, NOTIFICATION-TYPE FROM SNMPv2-SMI DisplayString FROM SNMPv2-TC;

-- October 09, 2002 at 14:50 GMT -- 1.3.6.1.4.1.16535 Test MODULE-IDENTITY

LAST-UPDATED \ ORGANIZATION \ CONTACT-INFO \ DESCRIPTION \

::= { enterprises 16535 }

-- Node definitions

-- This part will include all details about the Test. -- 1.3.6.1.4.1.16535.1

Time OBJECT IDENTIFIER ::= { Test 1 }

-- 1.3.6.1.4.1.16535.1.1 GetTime OBJECT-TYPE

SYNTAX DisplayString (SIZE (0..100)) MAX-ACCESS read-only STATUS current DESCRIPTION

\::= { Time 1 } END

-- Test-MIB.txt

1. 导出MIBS环境变量 export MIBS=all

就会将安装net-snmp的路径下的snmp/mibs目录的所有mibs文件包含进来了。

8.7 验证MIB

snmptranslate -IR -Tp Test MIB正确则显示

8.8 生成.c和.h源码

cd agent/mibgroup/

方法一:

mib2c -c mib2c.scalar.conf Test-MIB::Test 方法二:

执行env MIBS=\会引导你逐渐生成Test.h和Test.c, 先选2再选1,过程如下:

[root@localhost mibs]# env MIBS=\writing to -

mib2c has multiple configuration files depending on the type of code you need to write. You must pick one depending on your need.

You requested mib2c to be run on the following part of the MIB tree: OID: Test

numeric translation: .1.3.6.1.4.1.16535 number of scalars within: 1 number of tables within: 0 number of notifications within: 0

First, do you want to generate code that is compatible with the ucd-snmp 4.X line of code, or code for the newer Net-SNMP 5.X code base (which provides a much greater choice of APIs to pick from):

1) ucd-snmp style code 2) Net-SNMP style code

Select your choice : 2

********************************************************************** GENERATING CODE FOR SCALAR OBJECTS:

**********************************************************************

It looks like you have some scalars in the mib you requested, so I will now generate code for them if you wish. You have two choices for scalar API styles currently. Pick between them, or choose not to generate any code for the scalars:

1) If you're writing code for some generic scalars (by hand use: \

2) If you want to magically \scalars

(by hand use: \

3) Don't generate any code for the scalars

Select your choice: 1

using the mib2c.scalar.conf configuration file to generate your code. writing to Test.h writing to Test.c

********************************************************************** * NOTE WELL: The code generated by mib2c is only a template. *YOU* * * must fill in the code before it'll work most of the time. In many * * cases, spots that MUST be edited within the files are marked with * * /* XXX */ or /* TODO */ comments. * ********************************************************************** running indent on Test.h running indent on Test.c

mib2c已经统计出我们的mib库包含1个scalar变量,0个table变量,0个通知变量,Scalar就是包含我们常用的整型,字符串,时间等等数据类型。

则在当前目录下生成Test.c和Test.h文件 ? /*

* Note: this file originally auto-generated by mib2c using * $ */

#ifndef TEST_H #define TEST_H /*

* function declarations */

void init_Test(void); Netsnmp_Node_Handler handle_GetTime;

#endif /* TEST_H */ ? /*

* Note: this file originally auto-generated by mib2c using * $ */

#include #include

#include

生成的Test.c文件如下: 生成的Test.h如下:

#include \

/** Initializes the Test module */ void

init_Test(void) {

const oid GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };

DEBUGMSGTL((\

netsnmp_register_scalar(netsnmp_create_handler_registration (\ OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY)); } int

handle_GetTime(netsnmp_mib_handler *handler,

netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { /*

* We are never called for a GETNEXT if it's registered as a * \ */

/*

* a instance handler also only hands us one request at a time, so * we don't need to loop over a list of requests; we'll only get one. */

switch (reqinfo->mode) {

case MODE_GET:

snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, /*

* XXX: a pointer to the scalar's data */ , /*

* XXX: the length of the data in bytes */ ); break;

default: /*

* we should never get here, so this is a really bad error */

snmp_log(LOG_ERR, \reqinfo->mode);

return SNMP_ERR_GENERR; }

return SNMP_ERR_NOERROR; }

修改Test.c文件代码

修改后Test.c文件代码如下: /*

* Note: this file originally auto-generated by mib2c using * $ */

#include #include

#include #include \#include

/** Initializes the Test module */ void

init_Test(void) {

const oid GetTime_oid[] = { 1, 3, 6, 1, 4, 1, 16535, 1, 1 };

DEBUGMSGTL((\

netsnmp_register_scalar(netsnmp_create_handler_registration (\ OID_LENGTH(GetTime_oid), HANDLER_CAN_RONLY)); } int

handle_GetTime(netsnmp_mib_handler *handler,

netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) {

/*

* We are never called for a GETNEXT if it's registered as a * \ */ /*

* a instance handler also only hands us one request at a time, so * we don't need to loop over a list of requests; we'll only get one. */

time_t t;

switch (reqinfo->mode) { case MODE_GET: time(&t);

char szTime[100];

snprintf(szTime,100,\

snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, /*

* XXX: a pointer to the scalar's data */ szTime, /*

* XXX: the length of the data in bytes */ strlen(szTime)); break;

default: /*

* we should never get here, so this is a really bad error */

snmp_log(LOG_ERR, \reqinfo->mode);

return SNMP_ERR_GENERR; }

return SNMP_ERR_NOERROR; }

这里要讲一下int agentx_subagent=0;这个参数的意义

/* =0表示该程序要作为主代理,即不需要snmpd进程的支持,程序自身可以独立运行(需要自己的配置文件Vim

/usr/local/share/snmp/example-demon.conf,与上配置一样),此时能读

到的mib库只有自己定义的那部分,;=1表示作为子代理,需要snmpd先运行(在配置文件snmpd.conf中添加master agentx,使snmpd作为主代理运行,),此时在运行我们的进程,即可以读到原来的mib库和自己添加的mib库 */

8.9 生成子代理可执行文件

net-snmp-config --compile-subagentTestTest.c

执行

./snmpd -f -L -D

./Test

如果执行Test找不到库文件,执行如下:

export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH 或者

export LD_LIBRARY_PATH=/home/qshy/net-snmp-5.7.3/agent/.libs:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=/home/qshy/net-snmp-5.7.3/snmplib/.libs:$LD_LIBRARY_PATH

8.10 测试

snmpget -v2c -c public localhost 1.3.6.1.4.1.16535.1.1.0(最后.0必须要) 如果成功显示如下:

snmpset用法:

snmpset -v2c -c private localhost Type 12345 TYPE: one of i, u, t, a, o, s, x, d, b, n

i: INTEGER, u: unsigned INTEGER, t: TIMETICKS,

a: IPADDRESS o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING, b: BITS U: unsigned int64, I: signed int64, F: float, D: double

参考:

http://www.cnblogs.com/shipfi/archive/2008/01/07/1028916.html http://www.cnblogs.com/shipfi/articles/1033365.html http://blog.csdn.net/hepeng597/article/details/8782868 http://blog.csdn.net/sealyao/article/details/4545082

http://jingyan.http://www.wodefanwen.com//article/f79b7cb37c6d239144023ed6.html

agent/mibgroup/if-mib/ifTable/ifTable*.c

9 GDB调试net-snmp的snmpd

1、打开configure,发现CFLAG中都带有-g选项,默认都产生了调试信息。-O2选项可能对产生影响,我们去掉所有CFLAG中的-O2选项。 ./configure

查看Makefile,CFLAGS中有-g选项,而且没有-O2选项。

CFLAGS = -g -Dlinux -I/usr/include/rpm

2、设置断点

我们在snmp_agent.c中设置一个断点,可以用下面的方式: (gdb) b 源文件名:行号 在设置断点的报错: (gdb) b snmp_agent.c:123

No source file named snmp_agent.c.

查看agent/Makfile,发现net-snmp生成了多个共享库文件(.so文件)。snmpd调用了其中的多个.so文件,而共享库只有当程序运行才开始加载的。 于是可以这么做:

gdb snmpd (gdb)break main (gdb)run -f -Lo

然后再设置断点就可以了。

(gdb) b snmp_agent.c:123

本文来源:https://www.bwwdw.com/article/agd7.html

Top