Tan Sir 眼中,一切都是那么自然且和谐。

假设这篇是有读者的,我得先声明几件需要声明的事。

  • RFC-2408 是什么并不重要,因为假设过我们已经有所了解;
  • 开源程序库到底选了哪个也不重要,因为都是些基本语法实现的东西,没有原因看不懂。

但其实说点令人伤感的话,在布置这个大作业之前,《自顶向下》 哪怕一个章节我都没有完整地读过。计网基础不说是 0,也算是 std::numeric_limits<double>::epsilon() 了。

我也不知道为什么,也不知道怎么样,我就是在 Mayaqua 这个文件夹里面找到了 Memory.h 这个头文件,并且非常笃定里面会定义一些我阅读协议分析代码区块时,非常需要的一些定义类型,比如:

1. 莫名其妙的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct BUF
{
void *Buf;
UINT Size;
UINT SizeReserved;
UINT Current;
};

struct LIST
{
REF *ref;
UINT num_item, num_reserved;
void **p;
LOCK *lock;
COMPARE *cmp;
bool sorted;
UINT64 Param1;
};

2. 莫名其妙的 Header

ISAKMP 使用的过程中,毕竟它是一个协议嘛,而且定义的东西也不仅仅是一个来回而已,所以每次往返都需要某种格式。那么就自然考虑到,这个协议需要定义一个协议头,也就是 ISAKMP Header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                     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 !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

现在我也不知道 initiator / responder cookie 是做什么的,本来也不知道它是不是用 MD5 / SHA 等一系列摘要算法生成的,更不知道生成 cookie 的输入是主机的一些标识信息还是其他。

总之,它现在只是两个 64 位长的数据罢了。

Next Payload,现在我也不知道为什么有这个字段,也猜不到它是不是会构成一个 链式结构,总之这个字段放在这里就仅仅为了标识下一个 Payload 到底是什么类型(如果有的话),十分巧的是,我找到了 Cedar/Proto_IkePacket.h 这个头文件中的一些宏定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// IKE payload type
#define IKE_PAYLOAD_NONE 0 // No payload
#define IKE_PAYLOAD_SA 1 // SA payload
#define IKE_PAYLOAD_PROPOSAL 2 // Proposal payload
#define IKE_PAYLOAD_TRANSFORM 3 // Transform payload
#define IKE_PAYLOAD_KEY_EXCHANGE 4 // Key exchange payload
#define IKE_PAYLOAD_ID 5 // ID payload
#define IKE_PAYLOAD_CERT 6 // Certificate payload
#define IKE_PAYLOAD_CERT_REQUEST 7 // Certificate request payload
#define IKE_PAYLOAD_HASH 8 // Hash payload
#define IKE_PAYLOAD_SIGN 9 // Signature payload
#define IKE_PAYLOAD_RAND 10 // Random number payload
#define IKE_PAYLOAD_NOTICE 11 // Notification Payload
#define IKE_PAYLOAD_DELETE 12 // Deletion payload
#define IKE_PAYLOAD_VENDOR_ID 13 // Vendor ID payload
#define IKE_PAYLOAD_NAT_D 20 // NAT-D payload
#define IKE_PAYLOAD_NAT_OA 21 // NAT-OA payload
#define IKE_PAYLOAD_NAT_D_DRAFT 130 // NAT-D payload draft
#define IKE_PAYLOAD_NAT_OA_DRAFT 16 // NAT-OA payload draft
#define IKE_PAYLOAD_NAT_OA_DRAFT_2 131 // NAT-OA payload draft 2

这与 RFC-2408 上对 ISAKMP 定义的大体相仿,并且可以了解到,有些应该是 SoftEtherVPN 自己搞的。但是对此,暂时就不需要深究了。

Exchange Type 貌似是比较重要的字段,或许它有 identity protection / base / informatioinal 这些选择,但是 SoftEtherVPN 中实现的是 IKE,应该就只有 aggressive / authentication 两种了。

Flags 也是老熟人了,不过这里看起来只定义了 29(A), 30(C), 31(E) 位的样子,RFC 上的叙述又臭又长,看到:

1
2
3
4
// IKE header flag
#define IKE_HEADER_FLAG_ENCRYPTED 1 // Encryption
#define IKE_HEADER_FLAG_COMMIT 2 // Commit
#define IKE_HEADER_FLAG_AUTH_ONLY 4 // Only authentication

想必整个 Flags 占 8 位,里面的 E 就在最末一位,然后 A 就在倒数第三位吧?一定是这样的。

再后面是 Message ID,看不懂。不过,总体来看,有些契合同为 Cedar/Proto_IkePacket.h 中的:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct IKE_PACKET
{
UINT64 InitiatorCookie; // Initiator cookie
UINT64 ResponderCookie; // Responder cookie
UCHAR ExchangeType; // Exchange type
bool FlagEncrypted; // Encryption flag
bool FlagCommit; // Commit flag
bool FlagAuthOnly; // Flag only authentication
UINT MessageId; // Message ID
LIST *PayloadList; // Payload list
BUF *DecryptedPayload; // Decrypted payload
UINT MessageSize; // Original size
};

3. 莫名其妙的 Payload Header

第一反应肯定是 Payload 就老老实实当 Payload,为什么还有 Header,所以啊,这就是数据报又臭又长的原因所在吗?

由于 ISAKMP HeaderNext Header 的存在。首先,这个 Next Header 字段肯定不是为了标识下一个数据报的类型,尽管从 ISAKMP 这个协议角度来看,当前协议的确了解下一次从本机出发的协议,在正常情况下到底是什么类型,都有什么参数。但是我认为还是不能认为它知道下一个是什么。

好吧,其实这个 Next Header 是为了指示本次数据报中,位于 Payload 中的类型。这也就是为什么 Payload 还有自己的 Header。其实就是类似嵌套嘛。(但个人以为这和外层协议包裹内层不完全相同,无论是从数据结构的逻辑上,还是从 ISAKMP 这个协议体给人感受到的语义上)

a. 通用的

较为通用的协议头—— Generic Header

1
2
3
4
5
                     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 !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

我不知道为什么就一下子在 Cedar/Proto_IkePacket.h 里面找到了这段定义:

1
2
3
4
5
6
struct IKE_COMMON_HEADER
{
UCHAR NextPayload;
UCHAR Reserved;
USHORT PayloadSize;
} GCC_PACKED;

b. SA Payload

通过与 RFC-2408 “反复” 比较,这个 SA_HEADER 也算是和代码定义的差不多吧,就是差了点东西。

1
2
3
4
5
6
7
8
9
10
11
                     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 ~
! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1
2
3
4
5
6
// IKE SA payload header
struct IKE_SA_HEADER
{
UINT DoI; // DOI value
UINT Situation; // Situation value
} GCC_PACKED;

轻松地观察到只要在这个 IKE_SA_HEADER 前面加上 IKE_COMMON_HEADER 就可以变成 RFC-24083.4 Security Association Payload 所示图了。

从这里,我进一步了解到什么叫做 Generic Header

但是……

c. 通用的为什么是通用的?

不知道为什么我就定义到了 Cedar/Proto_IkePacket.c 这个源文件了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Build a bit array from the payload list
BUF *IkeBuildPayloadList(LIST *o) {
BUF *b;
UINT i;

/* balabala */

b = NewBuf();
for (i = 0;i < LIST_NUM(o);i++) {
IKE_PACKET_PAYLOAD *p = LIST_DATA(o, i);
IKE_PACKET_PAYLOAD *next = NULL;
IKE_COMMON_HEADER h;
BUF *tmp;

if (i < (LIST_NUM(o) - 1))
next = LIST_DATA(o, i + 1);

Zero(&h, sizeof(h));
if (next != NULL)
h.NextPayload = next->PayloadType;
else
h.NextPayload = IKE_PAYLOAD_NONE;

tmp = IkeBuildPayload(p);
if (tmp != NULL) {
h.PayloadSize = Endian16(tmp->Size + (USHORT)sizeof(h));
WriteBuf(b, &h, sizeof(h));
WriteBuf(b, tmp->Buf, tmp->Size);
FreeBuf(tmp);
}
}

SeekBuf(b, 0, 0);
return b;
}

这里简单做一些不负责任的说明。

首先,LISTPayloadLIST,通过 LIST_DATA 这个函数,可以基于给定数字,给出 Payload。这也是为什么:

1
2
3
IKE_PACKET_PAYLOAD *p = LIST_DATA(o, i);
/* some code */
next = LIST_DATA(o, i + 1);

的原因。

其次,我们还知道了一个用脚趾头想都能想出来的道理:

1
2
如果 LIST_DATA 中的 Payload 遍历完了,
就应该把指针指向一个代表 “空” 的事物,以表示链终止。

这在程序里面表示为:

1
2
3
4
if (next != NULL)
h.NextPayload = next->PayloadType;
else
h.NextPayload = IKE_PAYLOAD_NONE;

最后,写入是由顺序的,COMMON_HEADER 在前,XXX_HEADER 在后(注意,这之后的都是 Payload Header)。
这也就是为什么 Cedar/Proto_IkePacket.h 中的所有 Payload Header 都不像 RFC-2408 中描述的那样完整。

具体在代码中是这样体现的:

1
2
3
4
tmp = IkeBuildPayload(p);
h.PayloadSize = Endian16(tmp->Size + (USHORT)sizeof(h));
WriteBuf(b, &h, sizeof(h));
WriteBuf(b, tmp->Buf, tmp->Size);

通过这段程序……

  • 岂不是轻松得知一堆 Payload Header 的定义?
  • 岂不是立刻找到一堆 SoftEtherVPNRFC-2408 的区别之处?

比如:

1
2
3
4
5
6
7
8
9
                     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 !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Proposal # ! Protocol-Id ! SPI Size !# of Transforms!
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! SPI (variable) !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

中的 “Proposal #”,在 Cedar/Proto_IkePacket.h 中却是:

1
2
3
4
5
6
7
struct IKE_PROPOSAL_HEADER
{
UCHAR Number; // Number
UCHAR ProtocolId; // Protocol ID
UCHAR SpiSize; // Length of SPI
UCHAR NumTransforms; // Transform number
} GCC_PACKED;

当然,这种不同是可以理解的。因为没有必要为了与 RFC 相同而做许多不必要的变量名限制。

可是,这种就比较不同了:

1
2
3
4
5
// IKE generic data payload
struct IKE_PACKET_DATA_PAYLOAD
{
BUF *Data; // Generic data
};

RFC 中完全不存在这么一种通用载荷 Format。这只是因为比如 KE / Hash / SIG / Nonce Payload 们大体格式都是一样的,只是装填的数据不同。

然而这种不同,还和其算法有关,因此不好特化。一旦泛化到了极致,Format 就差不多了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
                     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 !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! !
~ Key Exchange Data ~
! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

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 !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! !
~ Hash Data ~
! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

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 !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! !
~ Signature Data ~
! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

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 !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! !
~ Nonce Data ~
! !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

之后的之后再看。

end.