SNMP的学习笔记
(ccnupq http://blog.csdn.net/ccnupq/)
1 BRE编码(Basic Encoding Rule)
基本编码规则使用使用TLV 方式,即Type,Length,Value消息中所表达的信息都用一个Type 域标记一个Legnth 限定值然后是Value ,ASN.1是一种用来描述系统之间传递的信息格式的语言规范被广泛用在通信协议的规格定义中一句话理解ASN.1 和BER即是:“信息的描述基于ASN.1 语法”和“信息的编码基于BER方法”
2 SNMP报文的过程中我们经常用到的是下面的数据类型标识号
各种数据类型的解释
l INTEGER 一个变量虽然定义为整型,但也有多种形式。有些整型变量没有范围限制,有些整型变量定义为特定的数值(例如,IP的转发标志就只有允许转发时的或者不允许转发时的这两种),有些整型变量定义一个特定的范围(例如,UDP和TCP的端口号就从0到65535)。
l OCTER STRING 0或多个8 bit字节,每个字节值在0~255之间。对于这种数据类型和下一种数据类型的BER编码,字符串的字节个数要超过字符串本身的长度。这些字符串不是以NULL结尾的字符串。
l DisplayString 0或多个8bit字节,但是每个字节必须是ASCII码。在MIB-II中,所有该类型的变量不能超过255个字符(0个字符是可以的)。
l OBJECT IDENTIFIER
l NULL 代表相关的变量没有值。例如,在get或get-next操作中,变量的值就是NULL,因为这些值还有待到代理进程处去取。
l IpAddress 4字节长度的OCTER STRING,以网络序表示的IP地址。每个字节代表IP地址的一个字段。
l PhysAddress OCTER STRING类型,代表物理地址(例如以太网物理地址为6个字节长度)。
l Counter 非负的整数,可从0递增到232—1(4294976295)。达到最大值后归0。
l Gauge 非负的整数,取值范围为从0到4294976295(或增或减)。达到最大值后锁定直到复位。例如,MIB中的tcpCurrEstab就是这种类型的变量的一个例子,它代表目前在ESTABLISHED或CLOSE_WAIT状态的TCP连接数。
l TimeTicks 时间计数器,以0.01秒为单位递增,但是不同的变量可以有不同的递增幅度。所以在定义这种类型的变量的时候,必须指定递增幅度。例如,MIB中的sysUpTime变量就是这种类型的变量,代表代理进程从启动开始的时间长度,以多少个百分之一秒的数目来表示。
l SEQUENCE 这一数据类型与C程序设计语言中的“structure”类似。一个SEQUENCE包括0个或多个元素,每一个元素又是另一个ASN.1数据类型。例如,MIB中的UdpEntry就是这种类型的变量。它代表在代理进程侧目前“激活”的UDP数量(“激活”表示目前被应用程序所用)。在这个变量中 包含两个元素:
Ø IpAddress类型中的udpLocalAddress,表示IP地址。
Ø INTEGER类型中的udpLocalPort,从0到65535,表示端口号。
l SEQUENDEOF 这是一个向量的定义,其所有元素具有相同的类型。如果每一个元素都具有简单 的数据类型,例如是整数类型,那么我们就得到一个简单的向量(一个一维向量)。但是我们将 看到,SNMP在使用这个数据类型时,其向量中的每一个元素是一个SEQUENCE(结构)。因而可以将它看成为一个二维数组或表。
l Opague 不透明类型或未知类型
3 SNM规定了5种协议数据单元PDU
(也就是SNMP报文),用来在管理进程和代理之间的交换。
l get-request操作:从代理进程处提取一个或多个参数值
l get-next-request操作:从代理进程处提取紧跟当前参数值的下一个参数值
l set-request操作:设置代理进程的一个或多个参数值
l get-response操作:返回的一个或多个参数值。这个操作是由代理进程发出的,它是前面三种操作的响应操作。
l trap操作:代理进程主动发出的报文,通知管理进程有某些事情发生。
前面的3种操作是由管理进程向代理进程发出的,后面的2个操作是代理进程发给管理进程的,为了简化起见,前面3个操作今后叫做get、get-next和set操作。下图描述了SNMP的这5种报文操作。请注意,在代理进程端是用熟知端口161俩接收get或set报文,而在管理进程端是用熟知端口162来接收trap报文。
SNMP的5种报文操作
4 SNMP报文格式
SNMP报文格式
解释如下:
4.1 Version(版本号):
该字段是TLV 格式的数据块包含3个字节,表示报文的版本号
内容为02-01-XX 其中02 字节是数据类型,对应了表格1 中的Integer 整型数据01是数据长度:1个字节。XX 是数据值,它的取值分别对应SNMP的三种不同的版本。对应关系如下
XX=0 对应SNMPv1
XX=1 对应SNMPv2c
XX=2 对应SNMPv2
XX=3 对应SNMPv3
4.2 Community(团体名)
字段也是TLV 格式的数据块,其LV 取决于实际的团体名,例如某个SNMPv1 的网管报文的团体名为字符串public, 则编码的二进制为 04-06-70 -75-62-6c-69-63。
04对应表格1 的Octet string 即字符串型数据。
06对应后面的长度是6个字节。
70-75-62-6c-69-63是“public“的ASCII码值。
从这里可以看出SNMPv1 的团体名是以明文表示的当SNMP。报文被非法截获后团体名就泄漏出去了对网络管理的安全将产生一定的威胁。SNMPv3在网络管理的安全性方面有改进采用MD5 SHA 的认证方法保证安全SNMP 报文的协议数据单元PDU 结构分两类一般网管报文的PDU 和Trap
4.3 一般的PDU报文(非Trap)
报文的 PDU 分别介绍如下:
一般的SNMP 报文的PDU 结构如图2 所示
4.3.1 PDU Type:
SNMP 的Get-Request、Get-Response 等基本命令集的操作类型。
根据PDU的类型,填入0~4中的一个数字(非Trap报文填写0-3),其对应关系如下表所示意图。
PDU类型
PDU类型 |
名称 |
0 |
get-request |
1 |
get-next-request |
2 |
get-response |
3 |
set-request |
4 |
trap |
4.3.2 Request ID :
采用02-04-XX-XX-XX-XX 的TLV 数据块用于网管站和被管设备之间传递的SNMP 报文的操作和应答的匹配,这和CAMS服务器与设备之间交互的Radius 报文ID 的功能基本相同,之所以使用报文ID机制来匹配报文,根本原因在于UDP 通信的不可靠特点:基于UDP 的应用程序均应该依靠这种方法维持通信的正常秩序。
4.3.3 Error-status:
采用 02-01-00 的TLV 数据块用来指示错误类型一般情况下V总是0
差错状态描述
差错状态 |
名字 |
说明 |
0 |
noError |
一切正常 |
1 |
tooBig |
代理无法将回答装入到一个SNMP报文之中 |
2 |
noSuchName |
操作指明了一个不存在的变量 |
3 |
badValue |
一个set操作指明了一个无效值或无效语法 |
4 |
readOnly |
管理进程试图修改一个只读变量 |
5 |
genErr |
某些其他的差错 |
4.3.4 Error-index:
02-01-XX 的TLV 数据块。用来表示产生错误的变量。当出现noSuchName、badValue或readOnly的差错时,由代理进程在回答时设置的一个整数,它指明有差错的变量在变量列表中的偏移。
var-bindings 字段包含众多的变量对偶var_n表示变量n 的标识,value_n 表示变量n 的取值。其中的var_n 基本上就是SNMP的管理变量,是SNMP 操作的基本对象,也即下文所述的OID(ObjectIdentifier),每个OID 标识一个管理变量OID符合TLV 格式,但是OID 的V部分不再符合TLV格式。其编码方式在下节讲述。Value_n 表示OID 的取值部分,其编码方法是TLV 格式的
4.4 Trap 报文
Trap 网管报文的PDU 结构如图所示
4.4.1 PDU Type(PDU类型)
Trap报文的PUD类型必填写4
4.4.2 enterprise(企业)
填入trap报文的网络设备的对象标识符。此对象标识符肯定是在图3的对象命名树上的enterprise结点{1.3.6.1.4.1}下面的一棵子树上。
4.4.3 agent-address
代理的IP地址
4.4.4 generic-trap
共分为下表中的7种。
trap类型描述
trap类型 |
名字 |
说明 |
0 |
coldStart |
代理进行了初始化 |
1 |
warmStart |
代理进行了重新初始化 |
2 |
linkDown |
一个接口从工作状态变为故障状态 |
3 |
linkUp |
一个接口从故障状态变为工作状态 |
4 |
authenticationFailure |
从SNMP管理进程接收到具有一个无效共同体的报文 |
5 |
egpNeighborLoss |
一个EGP相邻路由器变为故障状态 |
6 |
enterpriseSpecific |
代理自定义的事件,需要用后面的“特定代码”来指明 |
当使用上述类型2、3、5时,在报文后面变量部分的第一个变量应标识响应的接口。
4.4.5 specific-code (特定代码)
指明代理自定义的时间(若trap类型为6),否则为0。
4.4.6 timestamp(时间戳)
指明自代理进程初始化到trap报告的事件发生所经历的时间,单位为10ms。例如时间戳为1908表明在代理初始化后1908ms发生了该时间。
4.4.7 variable-bindings(变量绑定)
Trap 报文的variable-bindings 和普通的SNMP 报文的含义相同。实际上核心内容就是OID
5 基本编码规则(BRE)
在具体系统中,我们需要用具体的编码规则将ASN.1语法表示的抽象数据转换成具体的比特流。SNMP使用的编码方法是BER(Basic Encoding Rule)。BER的数据都由三个域构成:标识域(tag)+长度域(length)+值域(value)。
5.1 标识域(tag)的编码规则
标识域指明数据的类型,占用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)。
5.2 长度域的编码规则
长度域指明值域的长度,不定长,一般为一到三个字节。其格式可分为短格式(后面的值域没有超过127长)和长格式,如下所示
短格式的表示方法:
0(1bit) |
长度(7bit) |
长格式的表示方法:
1(1bit) |
K(7bit) |
K个八位组长度(K Byte) |
例:
length=30=>1E(30没有超过127,长度域为0001 1110)
length=169=>81 A9(169超过127,长度域为 1000 0001 1010 1001,169是后9位的值,前八位的第一个1表示这是长格式的表示方法,前八位的后七位表示后面有多少个字节表示针对的长度,这里,是000 0001,后面有一个字节表示真正的长度,1010 1001是169,后面的值有169个字节长。)
length=1500=>82 05 DC(1000 0010 0000 0101 1101 1100,先看第一个字节,表示长格式,后面有2个字节表示长度,这两个字节是0000 0101 1101 1100 表示1500)
5.3 值域的编码规则
5.3.1 整型Integer的编码
integer::=0x02 length{byte}* (*表示重复)
整型数据值域用补码表示,去掉多余的零(正数)或一(负数)。值域最高位为符号位。例:
1500=>02 02 05 DC 21500= >02 02 FA 24
5.3.2 对象标识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 3 40+3=43);子对象标识810超过127,根据规则(2)、(3)、(4)将其拆分为两个字节86 2A (810=11 0010 1010==>1000 0110 0010 1010);整个MIB被编码为:0x06 0x06 0x2b 0x06 0x01
0x86 0x2a 0x01。
5.3.3 sequence组合类型的编码
sequence::=0x30 length{asndata}*
如:30 05 02 01 10 05 00表示一个sequence结构,内含两个成员,其中一个为整型,另一个为空类型(NULL)。
5.3.4 空类型的编码
null::=0x05 0x00
5.3.5 字符串类型的编码
string::=0x04 length{byte}*
例如:04 06 70 75 62 6c 69 63表示字符串public
实例:
30 3c 02 01 00 04
06 70 75 62 6c 69 63 a2 2f 02 01 10 02 01 00 02
01 00 30 24 30 22 06 08 2b 06 01 02 01 01 01 00
04 16 53 54 41 52 20 53 77 69 74 63 68 20 53 32
38 30 30 20 56 34 2e 30
根据第2节BER编码规则和3.1节SNMP数据报格式,我们对数据进行分解:
SNMP Length=60 <=30 3c
Version=1 <= 02 01 00
Community=public<=04 06 70 75 62 6c 69 63
PDU Type=Get response length=2f <=a2 2f
Request ID=6 <= 02 01 10
Error status=(No error) <= 02 01 00
Error index=0 <= 02 01 00
Variable List length=24<=30 24
Item1 Length=22<=30 22
Object={1.3.6.1.2.1.1.1.0}<=06 08 2b 06 01 02 01 01 01 00
Value=STAR Switch S2800 V4.0<=04 16 53 54 41 52 20…
整个报文的含义为SNMP GetReply sysDescr=
STAR Switch S2800 V4.0
6 常见PDU的实现过程
驻留在被管设备上的AGENT从UDP端口161接受来自网管站的串行化报文,经解码、团体名验证、分析得到管理变量在MIB树中对应的节点,从相应的模块中得到管理变量的值,再形成响应报文,编码发送回网管站。网管站得到响应报文后,再经同样的处理,最终显示结果。
下面根据RFC1157详细介绍Agent接受到报文后采取的动作:
首先解码生成用内部数据结构表示的报文,解码依据ASN.1的基本编码规则,如果在此过程中出现错误导致解码失败则丢弃该报文,不做进一步处理。
第二步:将报文中的版本号取出,如果与本Agent支持的SNMP版本不一致,则丢弃该报文,不做进一步处理。当前北研的数据通信产品只支持SNMP版本1。
第三步:将报文中的团体名取出,此团体名由发出请求的网管站填写。如与本设备认可的团体名不符,则丢弃该报文,不做进一步处理,同时产生一个陷阱报文。SNMPv1只提供了较弱的安全措施,在版本3中这一功能将大大加强。
第四步:从通过验证的ASN.1对象中提出协议数据单元PDU,如果失败,丢弃报文,不做进一不处理。否则处理PDU,结果将产生一个报文,该报文的发送目的地址应同收到报文的源地址一致。
根据不同的PDU,SNMP协议实体将做不同的处理:
6.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相同。
6.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 = "9.1.2.3" ),
( ipRouteNextHop.9.1.2.3 = "99.0.0.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 = "10.0.0.51" ),
( ipRouteNextHop.10.0.0.51 = "89.1.1.42" ),
( 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 = "10.0.0.99" ),
( ipRouteNextHop.10.0.0.99 = "89.1.1.42" ),
( 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。这个响应通知网管站对表的遍历已经完成。
6.3 GetResponse PDU
GetResponse PDU只有当受到getRequest GetNextRequest SetRequest才由协议实体产生,网管站收到这个PDU后,应显示其结果。
6.4 SetRequest PDU
SetRequest PDU除了PDU类型标识以外,和GetRequest相同,当需要对被管变量进行写操作时,网管站侧的协议实体将生成该PDU。
对SetRequest的响应将根据下面情况分别处理:
1. 如果是关于一个只读变量的设置请求,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为noSuchName, error index的值是错误变量在变量list中的位置。
2. 如果被管设备上的协议实体收到的PDU中的变量对偶中的值,类型、长度不符和要求,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为badValue, error index的值是错误变量在变量list中的位置。
3. 如果需要产生的GetReponse报文长度超过了本地限制,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为tooBig, error index的值是0。
4. 如果是其他原因导致SET失败,则收到该PDU的协议实体产生一个GetReponse报文,并置error status为genErr, error index的值是错误变量在变量list中的位置。
如果不符合上面任何情况,则agent将把管理变量设置收到的PDU中的相应值,这往往可以改变被管设备的运行状态。同时产生一个GetResponse PDU,其中error status置为noError,error index的值为0。
6.5 Trap PDU
Trap PDU的有如下的形式
产生trap的系统的OBJECT IDENTIFIER
|
系统的IP地址 |
普通类型 |
特定类型 |
时戳 |
变量对偶表 |
Trap是被管设备遇到紧急情况时主动向网管站发送的消息。网管站收到trap PDU后要将起变量对偶表中的内容显示出来。一些常用的trap类型有冷、热启动,链路状态发生变化等。