0%

主模式第二包:main_inI1_outR1()

1. 序言

main_inI1_outR1()函数是ISAKMP协商过程中第二包的核心处理函数,同时也是响应端口的首包。这里我们主要说明main_inI1_outR1的处理流程,关于main_inI1_outR1的上下文环境暂不叙述,留给后面的文章进行更新。

ISAKMP协商报文的处理流程都比较复杂,一个函数有几百行都是很常见的,因此个人学习期间难免有遗漏和理解错误的地方,请大家多多批评指正。

2. main_inI1_outR1()处理流程图

image-20200520004303196

image-20200520004352037

3. main_inI1_outR1()源码

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/* State Transition Functions.
*
* The definition of state_microcode_table in demux.c is a good
* overview of these routines.
*
* - Called from process_packet; result handled by complete_v1_state_transition
* - struct state_microcode member "processor" points to these
* - these routine definitionss are in state order
* - these routines must be restartable from any point of error return:
* beware of memory allocated before any error.
* - output HDR is usually emitted by process_packet (if state_microcode
* member first_out_payload isn't ISAKMP_NEXT_NONE).
*
* The transition functions' functions include:
* - process and judge payloads
* - update st_iv (result of decryption is in st_new_iv)
* - build reply packet
*/

/* Handle a Main Mode Oakley first packet (responder side).
* HDR;SA --> HDR;SA
*/

/********************************
*main_inI1_outR1函数被process_packet函数调用
*它的返回结果由complete_v1_state_transition处理
*
*********************************/
stf_status
main_inI1_outR1(struct msg_digest *md)
{
#ifdef DMALLOC
if (_dm_initialized != 0) {
/* log unfreed pointers that have been added to the heap since mark */
dmalloc_log_changed(_dm_mark, 1, 0, 1);
dmalloc_log_stats ();
}
_dm_mark = dmalloc_mark() ;
_dm_initialized = 1;
#endif
/*接收到的数据包中的SA载荷部分*/
struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_SA];
struct state *st;
struct connection *c;
/*准备作为应答的SA载荷缓冲区*/
pb_stream r_sa_pbs;
lset_t policy_hint = 0;

/* we are looking for an OpenPGP Vendor ID sent by the peer */
bool openpgp_peer = FALSE;

/* Determin how many Vendor ID payloads we will be sending */
int next;
int numvidtosend = 1; /* we always send DPD VID */
/*至少会发送DPD的VID,因此初始值为1*/
#ifdef NAT_TRAVERSAL /*支持NAT-T,则增加一个nat-t探测的VID*/
if (md->quirks.nat_traversal_vid && nat_traversal_enabled) {
DBG(DBG_NATT, DBG_log("nat-t detected, sending nat-t VID"));
numvidtosend++;
}
#endif
/*如果预定义了PLUUO VID 或openpgp 对端, 增加VID 数量*/
#if SEND_PLUTO_VID || defined(openpgp_peer)
numvidtosend++;
#endif

#if defined(openpgp_peer)
{
struct payload_digest *p;
/*遍历接收到的数据包中的VID 载荷链表*/
for (p = md->chain[ISAKMP_NEXT_VID]; p != NULL; p = p->next)
{
int vid_len = sizeof(pgp_vendorid) - 1 < pbs_left(&p->pbs)
? sizeof(pgp_vendorid) - 1 : pbs_left(&p->pbs);

/*检查是否是pgp VID, 是则设置openpgp_peer 标志为TRUE*/
if (memcmp(pgp_vendorid, p->pbs.cur, vid_len) == 0)
{
openpgp_peer = TRUE;
DBG(DBG_PARSING,
DBG_log("we have an OpenPGP peer")
)
}
}
}
#endif

/*根据接收到的数据包的目的地址,目的端口,源地址,源端口查找连接结构*/
/* random source ports are handled by find_host_connection */
c = find_host_connection(ANY_MATCH, &md->iface->ip_addr, pluto_port500
, KH_IPADDR
, &md->sender
, md->sender_port, LEMPTY, POLICY_IKEV1_DISABLE, &policy_hint);

if (c == NULL)/*如果没找到*/
{
pb_stream pre_sa_pbs = sa_pd->pbs;
lset_t policy = preparse_isakmp_sa_body(&pre_sa_pbs);
/*
* If there is XAUTH VID, copy it to policies.
*/
if (md->quirks.xauth_vid == TRUE)
{
policy |= POLICY_XAUTH;
}
/* See if a wildcarded connection can be found.
* We cannot pick the right connection, so we're making a guess.
* All Road Warrior connections are fair game:
* we pick the first we come across (if any).
* If we don't find any, we pick the first opportunistic
* with the smallest subnet that includes the peer.
* There is, of course, no necessary relationship between
* an Initiator's address and that of its client,
* but Food Groups kind of assumes one.
*/
{
// 可能是那种未定义对方地址的动态连接, 将源地址条件置空,重新查找连接,
// 可能会找到多个连接结构,返回的是一个链表
struct connection *d;
d = find_host_connection(ANY_MATCH, &md->iface->ip_addr, pluto_port500
, KH_ANY
, (ip_address*)NULL
, md->sender_port, policy, POLICY_IKEV1_DISABLE, &policy_hint);
// 遍历链表
for (; d != NULL; d = d->IPhp_next)
{
// GROUP 类型连接不考虑
if (d->kind == CK_GROUP)
{
/* ignore */
}
else
{
// 如果连接类型为模板型而且没定义OE, 该连接可用于处理该数据包, 连接找到,中断循环
if (d->kind == CK_TEMPLATE && !(d->policy & POLICY_OPPO))
{
/* must be Road Warrior: we have a winner */
c = d;
break;
}

/* Opportunistic or Shunt: pick tightest match */
// 比较数据包源地址是否匹配连接中对方地址的定义
if (addrinsubnet(&md->sender, &d->spd.that.client)
// 而且目前可用连接为空,或目前可用连接的地址范围比新找到的连接范围大, 更新可用连接
&& (c == NULL || !subnetinsubnet(&c->spd.that.client, &d->spd.that.client)))
c = d;
}
}
}
// 如果没找到连接, 该数据包处理不了, 返回忽略该数据包
if (c == NULL)
{
loglog(RC_LOG_SERIOUS, "initial Main Mode message received on %s:%u"
" but no connection has been authorized%s%s"
, ip_str(&md->iface->ip_addr), ntohs(portof(&md->iface->ip_addr))
, (policy != LEMPTY) ? " with policy=" : ""
, (policy != LEMPTY) ? bitnamesof(sa_policy_bit_names, policy) : "");

if(policy_hint & POLICY_IKEV1_DISABLE) {
md->note = INVALID_MAJOR_VERSION;
return STF_FAIL;
}

/* XXX notification is in order! */
return STF_IGNORE;
}/* 否则如果不是模板类型连接(动态连接), 返回忽略*/
else if (c->kind != CK_TEMPLATE)
{
loglog(RC_LOG_SERIOUS, "initial Main Mode message received on %s:%u"
" but \"%s\" forbids connection"
, ip_str(&md->iface->ip_addr), pluto_port500, c->name);
/* XXX notification is in order! */
return STF_IGNORE;
}
else
{
/* Create a temporary connection that is a copy of this one.
* His ID isn't declared yet.
*/
DBG(DBG_CONTROL, DBG_log("instantiating \"%s\" for initial Main Mode message received on %s:%u"
, c->name, ip_str(&md->iface->ip_addr), pluto_port500));
// 将模板连接进行实例化, 根据模板连接新生成一个新连接结构, 填充对方地址
c = rw_instantiate(c, &md->sender
, NULL, NULL);
}
} else {
/* we found a non-wildcard conn. double check if it needs instantiation anyway (eg vnet=) */
if ((c->kind == CK_TEMPLATE) && c->spd.that.virt) {
DBG(DBG_CONTROL, DBG_log("local endpoint has virt (vnet/vhost) set without wildcards - needs instantiation"));
c = rw_instantiate(c,&md->sender,NULL,NULL);
}
}

#ifdef XAUTH
if(c->spd.this.xauth_server || c->spd.this.xauth_client)
{
numvidtosend++;
}
#endif
/* Set up state */
// 新分配状态结构
md->st = st = new_state();
#ifdef XAUTH
passert(st->st_oakley.xauth == 0);
#endif
st->st_connection = c;
st->st_remoteaddr = md->sender;
st->st_remoteport = md->sender_port;
st->st_localaddr = md->iface->ip_addr;
st->st_localport = md->iface->port;
st->st_interface = md->iface;

/* IKE version numbers -- used mostly in logging */
st->st_ike_maj = md->maj;
st->st_ike_min = md->min;

set_cur_state(st); /* (caller will reset cur_state) */
st->st_try = 0; /* not our job to try again from start */
st->st_policy = c->policy & ~POLICY_IPSEC_MASK; /* only as accurate as connection */
// 状态类型为R0(接收到初始化包)
change_state(st, STATE_MAIN_R0);

// 复制对方的cookie
memcpy(st->st_icookie, md->hdr.isa_icookie, COOKIE_SIZE);

// 生成本地的cookie
get_cookie(FALSE, st->st_rcookie, COOKIE_SIZE, &md->sender);

// 将新状态插入到状态哈希表
insert_state(st); /* needs cookies, connection, and msgid (0) */

st->st_doi = ISAKMP_DOI_IPSEC;
st->st_situation = SIT_IDENTITY_ONLY; /* We only support this */

/* copy the quirks we might have accumulated */
// 复制特殊标志
copy_quirks(&st->quirks,&md->quirks);

if ((c->kind == CK_INSTANCE) && (c->spd.that.host_port_specific))
{
openswan_log("responding to Main Mode from unknown peer %s:%u"
, ip_str(&c->spd.that.host_addr), c->spd.that.host_port);
}
else if (c->kind == CK_INSTANCE)
{
openswan_log("responding to Main Mode from unknown peer %s"
, ip_str(&c->spd.that.host_addr));
}
else
{
openswan_log("responding to Main Mode");
}

/* parse_isakmp_sa also spits out a winning SA into our reply,
* so we have to build our reply_stream and emit HDR before calling it.
*/

/* HDR out.
* We can't leave this to comm_handle() because we must
* fill in the cookie.
*/
// 以下开始填充要发送的回应包信息

zero(reply_buffer);
init_pbs(&reply_stream, reply_buffer, sizeof(reply_buffer), "reply packet");
{
// 填充ISAKMP头部
struct isakmp_hdr r_hdr = md->hdr;

r_hdr.isa_flags &= ~ISAKMP_FLAG_COMMIT; /* we won't ever turn on this bit */
memcpy(r_hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE);
r_hdr.isa_np = ISAKMP_NEXT_SA;
if (!out_struct(&r_hdr, &isakmp_hdr_desc, &reply_stream, &md->rbody))
return STF_INTERNAL_ERROR;
}

// 填充SA 结构信息
/* start of SA out */
{
struct isakmp_sa r_sa = sa_pd->payload.sa;

/* if we to send any VID, then set the NEXT payload correctly */
r_sa.isasa_np = numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_struct(&r_sa, &isakmp_sa_desc, &md->rbody, &r_sa_pbs))
return STF_INTERNAL_ERROR;
}

/* SA body in and out */
/*解析对方的SA,根据他的建议载荷来确定本地是否支持,如果支持则回复支持的算法信息,并构建应答SA*/
RETURN_STF_FAILURE(parse_isakmp_sa_body(&sa_pd->pbs, &sa_pd->payload.sa
, &r_sa_pbs, FALSE, st));

/*填充VID*/
if (SEND_PLUTO_VID || openpgp_peer)
{
char *vendorid = (openpgp_peer) ?
pgp_vendorid : pluto_vendorid;

next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_generic_raw(next, &isakmp_vendor_id_desc, &md->rbody
, vendorid, strlen(vendorid), "Vendor ID"))
return STF_INTERNAL_ERROR;
}

/*
* NOW SEND VENDOR ID payloads
*/
/*填充DPD*/
/* Announce our ability to do RFC 3706 Dead Peer Detection */
next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if( !out_vid(next, &md->rbody, VID_MISC_DPD))
return STF_INTERNAL_ERROR;

#ifdef XAUTH
/* If XAUTH is required, insert here Vendor ID */
if(c->spd.this.xauth_server || c->spd.this.xauth_client)
{
next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_vendorid(next, &md->rbody, VID_MISC_XAUTH))
return STF_INTERNAL_ERROR;
}
#endif
#ifdef NAT_TRAVERSAL
DBG(DBG_NATT, DBG_log("sender checking NAT-T: %d and %d"
, nat_traversal_enabled
, md->quirks.nat_traversal_vid));
/*填充NAT-T的VID*/
if (md->quirks.nat_traversal_vid && nat_traversal_enabled) {

next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
/* reply if NAT-Traversal draft is supported */
/*nat_traversal_vid_to_method:将VID转换为对应的标准*/
st->hidden_variables.st_nat_traversal = nat_traversal_vid_to_method(md->quirks.nat_traversal_vid);
/*填充VID载荷,这里的NAT-T VID选择的是对方发来的最大的NAT-T 的VID,也就是说选用最新的NAT-T标准*/
if ((st->hidden_variables.st_nat_traversal) && (!out_vendorid(next,
&md->rbody, md->quirks.nat_traversal_vid))) {
return STF_INTERNAL_ERROR;
}
}
#endif


#ifdef DEBUG
/* if we are not 0 then something went very wrong above */
if(numvidtosend != 0) {
openswan_log("payload alignment problem please check the code in main_inI1_outR1 (num=%d)", numvidtosend);
}
#endif
/*应答报文封装完毕,填充长度字段*/
close_message(&md->rbody);

/* save initiator SA for HASH */
clonereplacechunk(st->st_p1isa, sa_pd->pbs.start, pbs_room(&sa_pd->pbs), "sa in main_inI1_outR1()");

return STF_OK;
}

4. parse_isakmp_sa_body()源码

暂略。

欢迎关注我的其它发布渠道