本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
9. 协商总体概述
9.1 概述
pluto启动时,whack将ipsec.conf中的连接信息发送给pluto(rcv_whack.c, whack_handle()函数)进
行连接的初始化(connection.c, add_connection()函数,initiate_connection()函数)。如果连接
是静态的,也就是连接双方的地址是固定好的,则启动IKE协商过程(ipsec_doi.c,
ipsecdoi_initiate()函数);如果是动态的,即一方地址没固定,则只是进入监听状态,监听UDP500
、4500端口。进入协商状态时,接收到对方数据包时(demux.c, process_packet()函数),双方的状态
转换通过demux.c中的state_microcode_table[]数组进行转换,各状态下的处理函数主要在
ipsec_doi.c中定义, 描述如下:
第一阶段:
主模式:
Initiator Responder
----------- -----------
HDR, SA -->
main_outI1(发送初始化数据1)
<-- HDR, SA
main_inI1_outR1(接收到初始化数据1, 发送响应数据1)
HDR, KE, Ni -->
main_inR1_outI2(接收到响应数据1,发送初始化数据2)
<-- HDR, KE, Nr
main_inI2_outR2(接收到初始化数据2, 发送响应数据2)
HDR*, IDii, [ CERT, ] SIG_I -->
main_inR2_outI3(接收到响应数据2,发送初始化数据3)
<-- HDR*, IDir, [ CERT, ] SIG_R
main_inI3_outR3(接收到初始化数据3, 发送响应数据3)
main_inR3(接收到响应数据3, 完成主模式协商, 建立ISAKMP SA)
野蛮模式:
Initiator Responder
----------- -----------
HDR, SA, KE, Ni, IDii -->
aggr_outI1(发送初始化数据1)
<-- HDR, SA, KE, Nr, IDir,
[ CERT, ] SIG_R
aggr_inI1_outR1_psk(接收到初始化数据1, 发送响应数据1)
aggr_inI1_outR1_rsasig(根据认证方式分别用不同函数处理)
HDR, [ CERT, ] SIG_I -->
aggr_inR1_outI2(接收到响应数据1,发送初始化数据2)
aggr_inI2(接收到初始化数据2,完成野蛮模式协商, 建立ISAKMP SA)
第2阶段:
快速模式:
Initiator Responder
----------- -----------
HDR*, HASH(1), SA0, SA1, Ni,
[, KE ] [, IDci, IDcr ] -->
qouck_outI1(发送初始化数据1)
<-- HDR*, HASH(2), SA0, SA1, Nr,
[, KE ] [, IDci, IDcr ]
quick_inI1_outR1(接收到初始化数据1, 发送响应数据1)
HDR*, HASH(3) -->
quick_inR1_outI2(接收到响应数据1,发送初始化数据2)
quick_inI2 (接收到初始化数据2,完成快速模式协商,
建立IPSec SA)
XAUTH, MODE_CFG在第一阶段完成,第2阶段前进行。
为了描述协商, 用到的最重要的数据结构就是连接(connection)和状态(state), 连接是通过配置文
件ipsec.conf中的连接定义的, 如果双方地址都非空, 就是一个真实连接, 可直接发起; 否则就是连
接模板, 只有真正连接发生时根据模板填充原来为空的参数(实体化)后生成新的连接实体,但总体说
是一种静态的概念。而状态是用来描述具体连接过程的各种中间状态,是动态概念,即使是相同的连
接也可能因为使用不同的加密认证算法,不同的协议而生成不同的状态,因此我觉得pluto的状态和
连接的关系就类似进程和程序的关系。
9.2 协商数据格式
为更好理解协商过程,对协商数据格式是需要大致有所了解,但不用每个结构都详细说明,只要了解
了基本结构,具体到各种详细类型的查询只要看RFC就行了,而pluto中对各种协商载荷的数据结构在
lib/libopenswan/packet.c中定义。
标准的协商数据格式和取值在FC2408, 2409等文件中定义, 包括一个ISAKMP数据头和若干载荷, 载荷
数为1个到多个:
ISAKMP数据头格式:
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Initiator !
! Cookie !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Responder !
! Cookie !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Message ID !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
数据头包括:
8字节的发起方cookie, 不能都为0;
8字节的响应方cookie, 如果是初始协商, 为0;
1字节的下一个载荷类型, 取值如下, 当为0时表示是最后一个载荷:
Next Payload Type Value
NONE 0
Security Association (SA) 1
Proposal (P) 2
Transform (T) 3
Key Exchange (KE) 4
Identification (ID) 5
Certificate (CERT) 6
Certificate Request (CR) 7
Hash (HASH) 8
Signature (SIG) 9
Nonce (NONCE) 10
Notification (N) 11
Delete (D) 12
Vendor ID (VID) 13
RESERVED 14 - 127
Private USE 128 - 255
4位主版本, 对IKEv1, 取值为1;
4位次版本, 对IKEv1, 取值为0;
1字节交换类型, 取值如下:
Exchange Type Value
NONE 0
Base 1
Identity Protection 2
Authentication Only 3
Aggressive 4
Informational 5
ISAKMP Future Use 6 - 31
DOI Specific Use 32 - 239
Private Use 240 - 255
1字节的标志;
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|E|C|A| |
+-+-+-+-+-+-+-+-+
E: Encryption; C: Commit; A: AUthentication
4字节的消息ID;
4字节表示的数据包长度, 这是所有数据的总长, 包含ISAKMP数据头和所有载荷;
各种载荷的通用头结构为:
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! RESERVED ! Payload Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1字节的下一个载荷类型, 取值如上;
1字节保留, 必须为0;
2字节的载荷长度, 包含通用头和载荷数据本身;
各种类型载荷的数据格式在RFC2408中有详细定义.
举例:
以下是pluto的日志记录的IKE协商初始包的情况,这个数据包是由一个Windows的IPSec客户端发出的
:
| *received 183 bytes from 192.168.5.49:500 on eth2 (port=500)
| 11 bc 56 f2 25 e9 ff 1c 00 00 00 00 00 00 00 00
| 01 10 02 00 00 00 00 00 00 00 00 b7 0d 00 00 74
| 00 00 00 01 00 00 00 01 00 00 00 68 01 01 00 03
| 03 00 00 20 01 01 00 00 80 01 00 05 80 02 00 01
| 80 03 fd e9 80 04 00 02 80 0b 00 01 80 0c 70 80
| 03 00 00 20 02 01 00 00 80 01 00 05 80 02 00 02
| 80 03 fd e9 80 04 00 02 80 0b 00 01 80 0c 70 80
| 00 00 00 20 03 01 00 00 80 01 00 06 80 02 00 01
| 80 03 fd e9 80 04 00 02 80 0b 00 01 80 0c 70 80
| 0d 00 00 13 54 68 64 61 73 63 6f 6d 56 50 4e 30
| 31 30 30 00 00 00 14 44 85 15 2d 18 b6 bb cd 0b
| e8 a8 46 95 79 dd cc
| **parse ISAKMP Message:
| initiator cookie:
| 11 bc 56 f2 25 e9 ff 1c
| responder cookie:
| 00 00 00 00 00 00 00 00
| next payload type: ISAKMP_NEXT_SA
| ISAKMP version: ISAKMP Version 1.0
| exchange type: ISAKMP_XCHG_IDPROT
| flags: none
| message ID: 00 00 00 00
| length: 183
| processing packet with exchange type=ISAKMP_XCHG_IDPROT (2)
| np=1 and sd=0x80d8fd0
| ***parse ISAKMP Security Association Payload:
| next payload type: ISAKMP_NEXT_VID
| length: 116
| DOI: ISAKMP_DOI_IPSEC
| np=13 and sd=0x80d95ec
| ***parse ISAKMP Vendor ID Payload:
| next payload type: ISAKMP_NEXT_VID
| length: 19
| np=13 and sd=0x80d95ec
| ***parse ISAKMP Vendor ID Payload:
| next payload type: ISAKMP_NEXT_NONE
| length: 20
从数据包中先看ISAKMP头信息:
| 11 bc 56 f2 25 e9 ff 1c 00 00 00 00 00 00 00 00
| 01 10 02 00 00 00 00 00 00 00 00 b7
发起者cookie: 11 bc 56 f2 25 e9 ff 1c
响应者cookie: 00 00 00 00 00 00 00 00, 全0表示这是协商起始包
下一载荷: 01, 表示是SA载荷
主版本号: 1
次版本号: 0
交换类型: 02, 表示Identity Protection, 身份保护
标志: 0
消息ID: 00 00 00 00
数据长度: 0x00000b7, 183字节, 从长度不能看出到底有多少载荷, 必须一个一个分析直到遇到下一
载荷字段值为0的载荷, 表示是最后一个载荷
以下是各级载荷的数据信息:
第1载荷: SA载荷
| 0d 00 00 74
| 00 00 00 01 00 00 00 01 00 00 00 68 01 01 00 03
| 03 00 00 20 01 01 00 00 80 01 00 05 80 02 00 01
| 80 03 fd e9 80 04 00 02 80 0b 00 01 80 0c 70 80
| 03 00 00 20 02 01 00 00 80 01 00 05 80 02 00 02
| 80 03 fd e9 80 04 00 02 80 0b 00 01 80 0c 70 80
| 00 00 00 20 03 01 00 00 80 01 00 06 80 02 00 01
| 80 03 fd e9 80 04 00 02 80 0b 00 01 80 0c 70 80
SA载荷结构定义如下(RFC2408):
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! RESERVED ! Payload Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Domain of Interpretation (DOI) !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! !
~ Situation ~
! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SA的详细格式说明在RFC2407中定义:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! RESERVED ! Payload Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Domain of Interpretation (IPSEC) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Situation (bitmap) !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Labeled Domain Identifier !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Secrecy Length (in octets) ! RESERVED !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ Secrecy Level ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Secrecy Cat. Length (in bits) ! RESERVED !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ Secrecy Category Bitmap ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Integrity Length (in octets) ! RESERVED !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ Integrity Level ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Integ. Cat. Length (in bits) ! RESERVED !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ Integrity Category Bitmap ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
保留字段必须为0
下一载荷: 0x0d, Vendor ID, 提供者ID
长度: 0x0074, 116字节
DOI: 0x00000001, 表示是IPSec DOI, 为0表示ISAKMP DOI
状况(situation): 00 00 00 01 , 表示是SIT_IDENTITY_ONLY, 只包含标志信息,, 由于没支持
SIT_SECRECY(0x02)和 SIT_INTEGRITY(0x04), 因此不包含Labeled Domain Identifier域
Labeled Domain Identifier: 空
秘密长度: 0x00 00 00 68, 十进制104, 表示ID部分104字节, 含4字节长度
后面100字节应该是秘密信息, 不过似乎pluto没有定义situation更多信息,只分析到DOI:
/* lib/libopenswan/packet.c */
static field_desc isasa_fields[] = {
{ ft_enum, 8/BITS_PER_BYTE, "next payload type", &payload_names },
{ ft_mbz, 8/BITS_PER_BYTE, NULL, NULL },
{ ft_len, 16/BITS_PER_BYTE, "length", NULL },
{ ft_enum, 32/BITS_PER_BYTE, "DOI", &doi_names },
// 没继续解析situation信息
{ ft_end, 0, NULL, NULL }
};
第2载荷: Vendor ID
| 0d 00 00 13 54 68 64 61 73 63 6f 6d 56 50 4e 30
| 31 30 30
IPSec提供者ID载荷格式如下:
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload ! RESERVED ! Payload Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! !
~ Vendor ID (VID) ~
! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
保留字段必须为0
下一载荷: 0x0d, Vendor ID, 提供者ID
长度: 0x0013, 19字节(含通用载荷头4字节)
VID: 54 68 64 61 73 63 6f 6d 56 50 4e 30 31 30 30
第3载荷: Vendor ID
| 00 00 00 14 44 85 15 2d 18 b6 bb cd 0b
| e8 a8 46 95 79 dd cc
下一载荷: 0x00, 表示已经是最后一个载荷了
长度: 0x0014, 20字节(含通用载荷头4字节)
VID: 44 85 15 2d 18 b6 bb cd 0b e8 a8 46 95 79 dd cc
只要了解了协商数据的结构格式,就可以自己分析其他协商数据的格式,打开pluto的日志全记录,
pluto会把接收和发送出的所有数据都打印到日志中供分析。
9.3 载荷字段的读取和构造
由前面介绍IKE协商数据的载荷是一个一个顺序读取的, 而不是一下子全部读出为一个结构, 而发出
过程则是反向过程, 因此pluto需要的是循环顺序读取或构造载荷结构, 定义的统一的库函数来实现
,具体代码在lib/libopenswan/packet.c中。
9.3.1 读取
/* "parse" a network struct into a host struct.
*
* This code assumes that the network and host structure
* members have the same alignment and size! This requires
* that all padding be explicit.
*
* If obj_pbs is supplied, a new pb_stream is created for the
* variable part of the structure (this depends on their
* being one length field in the structure). The cursor of this
* new PBS is set to after the parsed part of the struct.
*
* This routine returns TRUE iff it succeeds.
*/
// 该函数从网络数据包中读取一个结构
// struct_ptr是目标结构, sd是对该结构的描述, ins是输入数据
bool
in_struct(void *struct_ptr, struct_desc *sd
, pb_stream *ins, pb_stream *obj_pbs)
{
err_t ugh = NULL;
// 当前输入数据位置
u_int8_t *cur = ins->cur;
if (ins->roof - cur < (ptrdiff_t)sd->size)
{
// 输入数据的长度不足以构造一个目标结构, 失败
// pluto经常使用这样的错误处理,不是用整数的错误号来标识错误,而是用错误描述字符串来
// 描述错误, 如果是空指针, 就说明操作成功了, 非空, 字符串就是说明错误原因, 还是面向
// 人而不是机器进行错误处理
ugh = builddiag("not enough room in input packet for %s"
" (remain=%d, sd->size=%d)"
, sd->name, ins->roof - cur, sd->size);
}
else
{
// 最大可能结束位置, 由于可能有填充数据, 所以实际结束位置可能会变化
u_int8_t *roof = cur + sd->size; /* may be changed by a length field */
// 输出的数据的结构起点
u_int8_t *outp = struct_ptr;
bool immediate = FALSE;
field_desc *fp;
// 循环对目标结构的每个域的描述
for (fp = sd->fields; ugh == NULL; fp++)
{
// 该域的长度
size_t i = fp->size;
passert(ins->roof - cur >= (ptrdiff_t)i);
passert(cur - ins->cur <= (ptrdiff_t)(sd->size - i));
passert(outp - (cur - ins->cur) == struct_ptr);
#if 0
DBG(DBG_PARSING, DBG_log("%d %s"
, (int) (cur - ins->cur), fp->name == NULL? "" : fp->name));
#endif
// 域类型
switch (fp->field_type)
{
case ft_mbz: /* must be zero */
// 必须为0的域, 如保留(RESERVED)域
for (; i != 0; i--)
{
if (*cur++ != 0)
// 如果这些域中有非0值存在, 生成错误
{
ugh = builddiag("byte %d of %s must be zero, but is not"
, (int) (cur - ins->cur), sd->name);
break;
}
*outp++ = '\0'; /* probably redundant */
}
break;
// 自然数类型
case ft_nat: /* natural number (may be 0) */
// 长度类型
case ft_len: /* length of this struct and any following crud */
// 长度/值类型
case ft_lv: /* length/value field of attribute */
// 枚举类型
case ft_enum: /* value from an enumeration */
// 宽松枚举类型
case ft_loose_enum: /* value from an enumeration with only some names known
*/
// 属性格式/值类型
case ft_af_enum: /* Attribute Format + value from an enumeration */
// 宽松属性格式/值类型
case ft_af_loose_enum: /* Attribute Format + value from an enumeration
*/
case ft_set: /* bits representing set */
{
u_int32_t n = 0;
// 一个字节一个字节的转换, 相当于完成了htons(l)的功能, 很多情况是长度/类型值
for (; i != 0; i--)
n = (n << BITS_PER_BYTE) | *cur++;
// 以下检查一下值的合法性和一些标志情况
switch (fp->field_type)
{
case ft_len: /* length of this struct and any following crud */
case ft_lv: /* length/value field of attribute */
{
// 读取长度值
// 有的属性长度值包含长度字段本身,有的不包含
u_int32_t len = fp->field_type == ft_len? n
: immediate? sd->size : n + sd->size;
// 检查长度值是否合法
if (len < sd->size)
{
ugh = builddiag("%s of %s is smaller than minimum"
, fp->name, sd->name);
}
// 检查输入数据的实际长度和标称的长度是否匹配
else if (pbs_left(ins) < len)
{
ugh = builddiag("%s of %s is larger than can fit"
, fp->name, sd->name);
}
else
{
// 定义新的数据结束位置
roof = ins->cur + len;
}
break;
}
case ft_af_loose_enum: /* Attribute Format + value from an enumeration
*/
if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
immediate = TRUE;
break;
case ft_af_enum: /* Attribute Format + value from an enumeration
*/
if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
immediate = TRUE;
/* FALL THROUGH */
case ft_enum: /* value from an enumeration */
// 检查类型值是否合法
if (enum_name(fp->desc, n) == NULL)
{
ugh = builddiag("%s of %s has an unknown value: %lu"
, fp->name, sd->name, (unsigned long)n);
}
/* FALL THROUGH */
case ft_loose_enum: /* value from an enumeration with only some
names known */
break;
case ft_set: /* bits representing set */
// 检查标志是否正确
if (!testset(fp->desc, n))
{
ugh = builddiag("bitset %s of %s has unknown member(s): %s"
, fp->name, sd->name, bitnamesof(fp->desc, n));
}
break;
default:
break;
}
// 根据域长度获取实际的数值,分8/16/32位数
i = fp->size;
switch (i)
{
case 8/BITS_PER_BYTE:
*(u_int8_t *)outp = n;
break;
case 16/BITS_PER_BYTE:
*(u_int16_t *)outp = n;
break;
case 32/BITS_PER_BYTE:
*(u_int32_t *)outp = n;
break;
default:
bad_case(i);
}
// 跳到下一域字段
outp += i;
break;
}
// 原始字段, 直接复制域长度的数据
case ft_raw: /* bytes to be left in network-order */
for (; i != 0; i--)
{
*outp++ = *cur++;
}
break;
// 结束标识
case ft_end: /* end of field list */
passert(cur == ins->cur + sd->size);
if (obj_pbs != NULL)
{
init_pbs(obj_pbs, ins->cur, roof - ins->cur, sd->name);
obj_pbs->container = ins;
obj_pbs->desc = sd;
obj_pbs->cur = cur;
}
ins->cur = roof;
// 打印解析的DEBUG信息, 返回成功
DBG(DBG_PARSING
, DBG_prefix_print_struct(ins, "parse ", struct_ptr, sd, TRUE));
return TRUE;
default:
bad_case(fp->field_type);
}
}
}
// 错误记录, 返回错误
/* some failure got us here: report it */
openswan_loglog(RC_LOG_SERIOUS, ugh);
return FALSE;
}
9.3.2 输出
/* "emit" a host struct into a network packet.
*
* This code assumes that the network and host structure
* members have the same alignment and size! This requires
* that all padding be explicit.
*
* If obj_pbs is non-NULL, its pbs describes a new output stream set up
* to contain the object. The cursor will be left at the variable part.
* This new stream must subsequently be finalized by close_output_pbs().
*
* The value of any field of type ft_len is computed, not taken
* from the input struct. The length is actually filled in when
* the object's output stream is finalized. If obj_pbs is NULL,
* finalization is done by out_struct before it returns.
*
* This routine returns TRUE iff it succeeds.
*/
// 将实际内部结构转换为数据包中的字段
// struct_ptr是输入结构, sd是结构描述, outs是输出结果
bool
out_struct(const void *struct_ptr, struct_desc *sd
, pb_stream *outs, pb_stream *obj_pbs)
{
err_t ugh = NULL;
// 读取数据的起始位置
const u_int8_t *inp = struct_ptr;
// 输出起始点
u_int8_t *cur = outs->cur;
DBG(DBG_EMITTING
, DBG_prefix_print_struct(outs, "emit ", struct_ptr, sd, obj_pbs==NULL));
// 检查输出缓冲中是否有足空间
if (outs->roof - cur < (ptrdiff_t)sd->size)
{
ugh = builddiag("not enough room left in output packet to place %s"
, sd->name);
}
else
{
bool immediate = FALSE;
pb_stream obj;
// 域描述指针
field_desc *fp;
obj.lenfld = NULL; /* until a length field is discovered */
obj.lenfld_desc = NULL;
// 遍历结构的所有域
for (fp = sd->fields; ugh == NULL; fp++)
{
// 域长度
size_t i = fp->size;
passert(outs->roof - cur >= (ptrdiff_t)i);
passert(cur - outs->cur <= (ptrdiff_t)(sd->size - i));
passert(inp - (cur - outs->cur) == struct_ptr);
#if 0
DBG(DBG_EMITTING, DBG_log("%d %s"
, (int) (cur - outs->cur), fp->name == NULL? "" : fp->name);
#endif
// 按域类型处理
switch (fp->field_type)
{
case ft_mbz: /* must be zero */
// 全0域
inp += i;
for (; i != 0; i--)
*cur++ = '\0';
break;
case ft_nat: /* natural number (may be 0) */
case ft_len: /* length of this struct and any following crud */
case ft_lv: /* length/value field of attribute */
case ft_enum: /* value from an enumeration */
case ft_loose_enum: /* value from an enumeration with only some names known
*/
case ft_af_enum: /* Attribute Format + value from an enumeration */
case ft_af_loose_enum: /* Attribute Format + value from an enumeration */
case ft_set: /* bits representing set */
{
u_int32_t n = 0;
// 长度/属性值的描述
switch (i)
// 先根据字段长度获取具体的数值
{
case 8/BITS_PER_BYTE:
n = *(const u_int8_t *)inp;
break;
case 16/BITS_PER_BYTE:
n = *(const u_int16_t *)inp;
break;
case 32/BITS_PER_BYTE:
n = *(const u_int32_t *)inp;
break;
default:
bad_case(i);
}
switch (fp->field_type)
{
case ft_len: /* length of this struct and any following crud */
case ft_lv: /* length/value field of attribute */
if (immediate)
break; /* not a length */
/* We can't check the length because it will likely
* be filled in after variable part is supplied.
* We do record where this is so that it can be
* filled in by a subsequent close_output_pbs().
*/
passert(obj.lenfld == NULL); /* only one ft_len allowed */
obj.lenfld = cur;
obj.lenfld_desc = fp;
break;
case ft_af_loose_enum: /* Attribute Format + value from an enumeration
*/
if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
immediate = TRUE;
break;
case ft_af_enum: /* Attribute Format + value from an enumeration
*/
if ((n & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
immediate = TRUE;
/* FALL THROUGH */
case ft_enum: /* value from an enumeration */
if (enum_name(fp->desc, n) == NULL)
{
ugh = builddiag("%s of %s has an unknown value: %lu"
, fp->name, sd->name, (unsigned long)n);
}
/* FALL THROUGH */
case ft_loose_enum: /* value from an enumeration with only some
names known */
break;
case ft_set: /* bits representing set */
if (!testset(fp->desc, n))
{
ugh = builddiag("bitset %s of %s has unknown member(s): %s"
, fp->name, sd->name, bitnamesof(fp->desc, n));
}
break;
default:
break;
}
// 将每个字节填充到输出结构中
while (i-- != 0)
{
cur[i] = (u_int8_t)n;
n >>= BITS_PER_BYTE;
}
// 长度统计增加
inp += fp->size;
cur += fp->size;
break;
}
case ft_raw: /* bytes to be left in network-order */
// 原始数据, 直接拷贝
for (; i != 0; i--)
*cur++ = *inp++;
break;
case ft_end: /* end of field list */
// 结束处理
passert(cur == outs->cur + sd->size);
// 填充这个数据包字节流结构信息
obj.container = outs;
obj.desc = sd;
obj.name = sd->name;
obj.start = outs->cur;
obj.cur = cur;
obj.roof = outs->roof; /* limit of possible */
/* obj.lenfld and obj.lenfld_desc already set */
if (obj_pbs == NULL)
{
// 收尾操作
close_output_pbs(&obj); /* fill in length field, if any */
}
else
{
/* We set outs->cur to outs->roof so that
* any attempt to output something into outs
* before obj is closed will trigger an error.
*/
outs->cur = outs->roof;
// 将数据包字节流结构返回
*obj_pbs = obj;
}
// 返回成功
return TRUE;
default:
bad_case(fp->field_type);
}
}
}
// 错误, 记录日志, 返回错误
/* some failure got us here: report it */
loglog(RC_LOG_SERIOUS, ugh); /* ??? serious, but errno not relevant */
return FALSE;
}
...... 待续 ......