阅读完本文你可以学到:
(1)SNMP 中 OBJECT IDENTIFIER 的 BER 编码与解码代码实现。
(2)在学习 OBJECT IDENTIFIER 编解码过程中的一些思考(思考过后,懂得当涉及对无符号数组进行传输编码时,可以给出一个较佳的方案)。
(3)snmp++-3.3.7 版本中函数 asn_parse_objid 存在的 bug。
一、理论知识
1、Tag
OBJECT IDENTIFIER 对应的 Tag 为 0x06,占用一个八位组。
2、Length
Length有三种形式:定长短格式、定长长格式和变长格式。(SNMP 的所有内置类型的 Length 段都采用定长格式)
定长短格式:采用定长方式,当长度不大于127个八位组时,Length 只在一个八位组中编码。此时,bit0~bit6 表示实际长度,最高位 bit7 为0(用于区分其他两种格式)。
定长长格式:采用定长方式,当长度大于127个八位组时,Length 在多个八位组中编码,此时第一个八位组低七位(bit0~bit6)表示的是 Length 所占的长度,第一个八位组的最高位 bit7 为1,第一个八位组的低七位不能全为 0(用于区分其他两种格式)。
变长格式:采用变长方式,Length 所在八位组固定编码为 0x80,但在 Value 编码结束后以两个 0x00 结尾。这种方式使得可以在编码没有完全结束的情况下,可以先发送部分消息给对方。
3、Value
所有的子标识符(OID 点分十进制中的一位数值)连续编码。(笔者注:内存大小对应的 OID 为 .1.3.6.1.2.1.25.2.2.0,其子标识符自顶到下依次为 1,3,6,1,2,1,25,2,2,0。)
每个子标识符由一个或多个字节组成。子标识符是由一个字节还是多个字节表示的区分规则是:每个字节最高有效位 bit7 表征了该子标识符是否为编码后的最后一个字节。bit7 为 0 表示这个字节是 OID 中最后一个编码字节;bit7 为 1 时表示该字节不是该子标识符最后一个编码(OID没有负值)。正因如此,对于数值大于 127 的子标识符必然要使用多于一个字节的编码。如对于数值为 128(0x80 = 1000 0000B)的子标识符,就应该使用两个字节编码:1000 0001 0000 0000,值为 81 00。
子标识符编码应尽可能地使用字节(也就少一个字节),规定 OID 的第一个子标识符和第二个子标识符经过运算组合后编码为一个字节。该运算法则为:(X * 40)+ Y = Z。
其中 X 表示第一个子标识符,Y 为第二个子标识符。X 的取值为(0,1,2)。另外之所以使用一个“magic number” 为 40,是因为首个子标识符为 0 或 1 的 OID 的第二个子标识符最大的是 39(这种不见得有多优雅的解决方案也确实减少了一个字节的编码等)。这就使得具有 N 个子标识符的 OID,第 i 个(2 <= i <= N - 1)编码值对应 OID 中的第(i + 1)个子标识符。正是由于上述方程的约束条件,使得方程的结果也是唯一的,即当 Z 为表5-3总第一列所列出的值时,就可以推导出前两个子 OID 的值。
表5-3 OID TLV示例(16 进制)
Z | X | Y |
0 <= Z <= 39 | 0 | Z |
40 <= Z <= 79 | 1 | Z - 40 |
Z >= 80 | 2 | Z - 80 |
二、思考——设计一个包格式,满足“发送端将一个无符号 int 数组中的内容发送到接收端,接收端接收并打印”?
最简单地方案可能是: 其他信息 + 整数1 + 整数2 + . . . + 整数 i + . . . + 整数 n。(其中,“其他信息”在这里的讨论中并不涉及。在这种方案中