阅读完本文你可以学到:
(1)BER 中 INTEGER 的编码规则(其中1、2、3主要引自《ASN.1编码规则详解.doc》(作者不详,该文档可在CSDN资源中搜索到))。
(2)SNMP 中 INTEGER 的编码及解码实现(主要参考 net-snmp源码和 snmp++源码)。本文仅对编码相关函数进行了详细的解释。理解它,或许是我们走向自己实现 SNMP 协议的第一步。
特别声明:
(1)感谢《ASN.1编码规则详解.doc》作者所做的工作。
(2)感谢所有为 net-snmp 及 snmp++ 作出贡献的 coders 及相关人士。
(3)文中的所有观点仅供参考。
一、BER 中 INTEGER 的编码规则
1、BER是什么
BER(Basic Encoding Rules)是 ASN.1 中最早定义的编码规则,在讨论编码规则时,我们是基于正确的抽象描述上。BER 传输语法的格式一直是 TLV 三元组<Type, Length, Value>。TLV 每个域都是一系列八位组,对于组合结构,其中V还可以是 TLV 三元组。BER 传输语法 是基于八位组(为了避免不同系统上的混淆,没有采用 Byte 为单位)的自定界的 编码,因为其中 L 明确界定了八位组的长度。BER 是大端编码的,其八位组的高位比特在左手边。(注:术语“大端”和“小端”表示多字节值的哪一端(小端或大端))存储在该值的起始地址。引自《UNP卷一》)。
2、Tag
INTEGER 对应的 Tag 为 0x02,占用一个八位组。
3、Length
Length有三种形式:定长短格式、定长长格式和变长格式。
定长短格式:采用定长方式,当长度不大于127个八位组时,Length 只在一个八位组中编码。
定长长格式:采用定长方式,当长度大于127个八位组时,Length 在多个八位组中编码,此时第一个八位组低七位(bit0~bit6)表示的是 Length 所占的长度,第一个八位组的最高位 bit7 为1。
变长格式:采用变长方式,Length 所在八位组固定编码为 0x80,但在 Value 编码结束后以两个 0x00 结尾。这种方式使得可以在编码没有完全结束的情况下,可以先发送部分消息个对方。
4、INTEGER 类型的值编码
按照编码规则,如果一个数值编码后大于等于两个八位组,则要求编码后的结果从 MSB(Most Significant Bit 最高有效位)开始的9个位不能是连续的0或1。
该规则是为了尽最大的可能减少 INTEGER 编码后所占的八位组个数。
可以这样理解(仅供参考)(以32位 Windows 系统上的 int 类型举例):
在 32位的 Windows 上,一个 int 类型的值占 4个字节,即占 32位。那么它对应的八位组个数就是 32 / 8 = 4。现在我想对 1进行 BER 编码,那么应该这样做:1 的十六进制为 0x00000001,如果按照通常的编码,那它编码后的结果就是 0x00000001(对应 4个八位组),但是 BER 则要求编码后的结果从 MSB开始的9个位不能是连续的0或1,这就要求我们去除一些东西。如果按照 BER 的编码规则,那它编码后的结果就是 0x01(对应 1个八位组)。
相应地,在解码时,如果待解码数值的 MSB 为 0,则如果有需要的话,它的前面应填充 0;如果待解码数值的 MSB 为 1,则如果有需要的话,它的前面应填充 1。
二、SNMP中INTEGER的编码与解码实现(在 VS2013 下演示)
1. BER(Basic Encoding Rules)所处的位置,如图1-1:
图1-1
2. INTEGER 的 BER 编码与解码代码,并给出测试程序。
/************************************************************************/
/* asn1.h */
/************************************************************************/
#pragma once
typedef unsigned char u_char;
typedef unsigned long u_long;
#define ASN_LONG_LEN 0x80
u_char* asn_build_int(u_char *data, size_t *datalength, const u_char type, long integer);
u_char* _asn_build_header(u_char *data, size_t *datalength, const u_char type, size_t length);
u_char* _asn_build_length(u_char *data, size_t *datalength, size_t length);
u_char* asn_parse_int(u_char* const data, size_t *datalength, u_char *type, long *intp);
u_char* _asn_parse_length(u_char *data, u_long *length);
/************************************************************************/
/* asn1.cpp */
/*****