在通信基础知识:SNMP协议PDU报文格式解析一文中,对SNMP协议格式进行了简单介绍。为进一步学习和掌握SNMP协议,本文对SNMP协议报文再进行了深入分析,使自己看到snmp报文时,可对其进行解析。
分析SNMP协议报文,除了需要掌握SNMP报文格式外,需要掌握两点知识——ASN.1和BER。
SNMP协议用于两台设备之间的信息传递,而这两台设备也就是两个系统各种各样。为实现信息传递,就必须对传输数据进行规范和约定,使其与具体的设 备和系统独立开来。为此,SNMP采用了ASN.1(Abstract Syntax Notation One)——抽象语法标记。
ASN.1是一种ISO/ITU-T标准,描述了一种对数据进行表示、编码、传输和解码的数据格式。它提供了一整套正规的格式用于描述对象的结构, 而不管语言上如何执行及这些数据的具体指代,也不用去管到底是什么样的应用程序。ISO 协议套中的应用层协议使用了 ASN.1 来描述它们所传输的 PDU,包括 SNMP协议。
ASN.1有多种标准编码规则。在SNMP协议中,ASN.1采用了BER(Basic Encoding Rule)标准化编码规则对传输数据进行编码。BER编码规则使用TLV 方式:{Type,Length,Value}。消息中所表达的信息都用一个 Type 域标记,一个Legnth 限定值,然后是Value。
因此,我们可以这样理解SNMP报文:“信息的描述基于ASN.1 语法”,“信息的编码基于BER编码规则”。
ASN.1 提供了一些基本的预定义数据类型。其中,SNMP报文中经常用到的ASN.1数据类型如下表所示。
ASN.1基本数据类型
数据类型解释:
INTEGER:整数类型,表示一个有符号的任意精度的标量,它的编码是可移植,平台无关的。
OCTER STRING ,八位位组串,保存字节数组。
NULL,空类型,实际上是”占位符”, 它是含有空白选项的选择修改器所特有。
OBJECT IDENTIFIER,对象标识符类型即SNMP中的OID。
SEQUENCE,序列,是一种用来把相关数据元素收集为一个独立的可解码元素的简单方法。
在具体系统中,需要用具体的编码规则将ASN.1语法表示的抽象数据转换成具体的比特流。SNMP使用的编码方法是BER(Basic Encoding Rule)。BER的数据都由三个域构成:标识域(Type)+长度域(length)+值域(value),即TLV。
标识域指明数据的类型,占用1个字节,常见的类型有:BOOL(0×01)、INT(0×02)、OCTSTR(0×04)、 NULL(0×05)、OBJID(0×06)、ENUM(0x0A)、SEQ(0×30)、SETOF(0×31)、IPADDR(0×40)、 COUNTER(0×41)、GAUGE(0×42)、TIMETICKS(0×43)、OPAQUE(0×44)。
长度域指明值域的长度,不定长,一般为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。
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
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
根据SNMP数据报文格式(详见通信基础知识:SNMP协议PDU报文格式解析,或者见下面示意图)和上面介绍的BER编码规则,我们对上面的SNMP数据报文进行分解。
SNMP报文位于UDP报文的数据域,称为PDU,上面的就是从UDP报文数据域里提取出来的十六进制SNMP报文。上面讲了SNMP的PDU采用BER编码,即TLV方式。需要注意的是:BER编码采用层层TLV编码方式,即整个SNMP报文是TLV方式,然后对其值域又采用TLV,如此下去。
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