本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn:
[email protected]
来源: http://yfydz.cublog.cn
13. 主模式协商
IKE协商处理函数基本都在programs/pluto/ipsec_doi.c中定义。
13.1 过程
主模式:
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)
连接发起函数为ipsecdoi_initiate()函数, 在其中调用了main_outI1(), aggr_outI1()和quick_outI1()函数分别发起主模式,野蛮模式和快速模式的协商。
13.2 发起方的第一个初始包
/* Initiate an Oakley Main Mode exchange.
* --> HDR;SA
* Note: this is not called from demux.c
*/
static stf_status
main_outI1(int whack_sock
, struct connection *c
, struct state *predecessor
, lset_t policy
, unsigned long try
, enum crypto_importance importance)
{
// 生成新状态空间
struct state *st = new_state();
pb_stream reply; /* not actually a reply, but you know what I mean */
pb_stream rbody;
// 以下设置状态参数
/* set up new state */
// 状态相关连接
st->st_connection = c;
// 设置状态中的本地和对端地址端口信息和网卡位置信息
set_state_ike_endpoints(st, c);
set_cur_state(st); /* we must reset before exit */
// IPSEC策略合法策略范围
st->st_policy = policy & ~POLICY_IPSEC_MASK;
// 状态和whack通信的域套接口
st->st_whack_sock = whack_sock;
// 尝试次数
st->st_try = try;
// 状态的类型: STATE_MAIN_I1
st->st_state = STATE_MAIN_I1;
st->st_import = importance;
// 初始化本地cookie,8字节的随机数
get_cookie(TRUE, st->st_icookie, COOKIE_SIZE, &c->spd.that.host_addr);
// 将新状态插入到状态哈希表
insert_state(st); /* needs cookies, connection, and msgid (0) */
// 如果有IPSEC策略, 增加pending结构, pending结构的作用以后再详细分析
if (HAS_IPSEC_POLICY(policy))
add_pending(dup_any(whack_sock), st, c, policy, 1
, predecessor == NULL? SOS_NOBODY : predecessor->st_serialno);
// 是完全新的协商还是更新的协商
if (predecessor == NULL)
openswan_log("initiating Main Mode");
else
openswan_log("initiating Main Mode to replace #%lu", predecessor->st_serialno);
/* set up reply */
// 初始化响应包
init_pbs(&reply, reply_buffer, sizeof(reply_buffer), "reply packet");
/* HDR out */
{
// 填充ISAKMP头
struct isakmp_hdr hdr;
zero(&hdr); /* default to 0 */
// 版本
hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
// 下一个头: SA
hdr.isa_np = ISAKMP_NEXT_SA;
// 交换类型
hdr.isa_xchg = ISAKMP_XCHG_IDPROT;
// cookie
memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);
/* R-cookie, flags and MessageID are left zero */
// 填充到reply结构到rbody, reply这里只是起个中介作用
if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody))
{
reset_cur_state();
return STF_INTERNAL_ERROR;
}
}
/* SA out */
{
// 填充SA信息
u_char *sa_start = rbody.cur;
// 策略索引
int policy_index = POLICY_ISAKMP(policy
, c->spd.this.xauth_server
, c->spd.this.xauth_client);
/* if we have an OpenPGP certificate we assume an
* OpenPGP peer and have to send the Vendor ID
*/
int np = (SEND_PLUTO_VID || c->spd.this.cert.type == CERT_PGP) ?
ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
// 填写SA结构到rbody
if (!out_sa(&rbody
, &oakley_sadb[policy_index], st, TRUE, FALSE, np))
{
openswan_log("outsa fail");
reset_cur_state();
return STF_INTERNAL_ERROR;
}
/* save initiator SA for later HASH */
passert(st->st_p1isa.ptr == NULL); /* no leak! (MUST be first time) */
clonetochunk(st->st_p1isa, sa_start, rbody.cur - sa_start
, "sa in main_outI1");
}
if (SEND_PLUTO_VID || c->spd.this.cert.type == CERT_PGP)
{
char *vendorid = (c->spd.this.cert.type == CERT_PGP) ?
pgp_vendorid : pluto_vendorid;
// 填充提供者ID结构
if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_vendor_id_desc, &rbody
, vendorid, strlen(vendorid), "Vendor ID"))
return STF_INTERNAL_ERROR;
}
/* ALWAYS Announce our ability to do Dead Peer Detection to the peer */
// 填充DPD
if (!out_modify_previous_np(ISAKMP_NEXT_VID, &rbody))
return STF_INTERNAL_ERROR;
// 填充DPD提供者信息
if( !out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_vendor_id_desc
, &rbody, dpd_vendorid, dpd_vendorid_len, "V_ID"))
return STF_INTERNAL_ERROR;
#ifdef NAT_TRAVERSAL
DBG(DBG_NATT, DBG_log("nat traversal enabled: %d", nat_traversal_enabled));
if (nat_traversal_enabled) {
// 如果NAT穿越有效, 添加NAT穿越VID信息
/* Add supported NAT-Traversal VID */
if (!nat_traversal_add_vid(ISAKMP_NEXT_NONE, &rbody)) {
reset_cur_state();
return STF_INTERNAL_ERROR;
}
}
#endif
#ifdef XAUTH
if(c->spd.this.xauth_client || c->spd.this.xauth_server)
{
// 如果是XAUTH的客户端或服务器, 填充XAUTH的VID信息
if(!out_vendorid(ISAKMP_NEXT_NONE, &rbody, VID_MISC_XAUTH))
{
return STF_INTERNAL_ERROR;
}
}
#endif
// 结构收尾
close_message(&rbody);
close_output_pbs(&reply);
clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply)
, "reply packet for main_outI1");
/* Transmit */
// 发送数据包
send_packet(st, "main_outI1", TRUE);
/* Set up a retransmission event, half a minute henceforth */
// 删除状态结构原来的事件(insert_state时初始化的事件)
delete_event(st);
// 重新设置状态的超时事件: 10秒无响应时重新发送
event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st);
if (predecessor != NULL)
{
// 更新pending结构
update_pending(predecessor, st);
whack_log(RC_NEW_STATE + STATE_MAIN_I1
, "%s: initiate, replacing #%lu"
, enum_name(&state_names, st->st_state)
, predecessor->st_serialno);
}
else
{
whack_log(RC_NEW_STATE + STATE_MAIN_I1
, "%s: initiate", enum_name(&state_names, st->st_state));
}
// 复位当前状态
reset_cur_state();
return STF_OK;
}
13.3 响应方收到发起方初始化包, 发送第一响应包
/* 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_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
*/
stf_status
main_inI1_outR1(struct msg_digest *md)
{
// 接收到的数据包的SA载荷
struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_SA];
struct state *st;
struct connection *c;
// 准备发送的SA载荷缓冲区
pb_stream r_sa_pbs;
/* we are looking for an OpenPGP Vendor ID sent by the peer */
// 对方是否是openpgp比标志
bool openpgp_peer = FALSE;
/* Determin how many Vendor ID payloads we will be sending */
int next;
// 发送的VID数量,至少会发送DPD的VID
int numvidtosend = 1; /* we always send DPD VID */
#ifdef NAT_TRAVERSAL
// NAT穿越有效, VID数量增加
if (md->quirks.nat_traversal_vid && nat_traversal_enabled) {
DBG(DBG_NATT, DBG_log("nat-t detected, sending nat-t VID"));
numvidtosend++;
}
#endif
#if SEND_PLUTO_VID || defined(openpgp_peer)
// 如果预定义了PLUUO VID或openpgp对端, 增加VID数量
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标志为真
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(&md->iface->ip_addr, pluto_port
, &md->sender
, md->sender_port);
if (c == NULL)
{
// 如果没找到
/* 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(&md->iface->ip_addr, pluto_port
, (ip_address*)NULL
, md->sender_port);
// 遍历链表
for (; d != NULL; d = d->hp_next)
{
if (d->kind == CK_GROUP)
{
// 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"
, ip_str(&md->iface->ip_addr), ntohs(portof(&md->iface->ip_addr)));
/* 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_port, 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.
*/
c = rw_instantiate(c, &md->sender
, NULL, NULL);
}
}
// 到此处已经有了可用的连接结构
#ifdef XAUTH
if(c->spd.this.xauth_server || c->spd.this.xauth_client)
{
// 如果本地是XAUTH客户端或服务器,VID数量增加
numvidtosend++;
}
#endif
/* Set up state */
// 新分配状态结构
md->st = st = new_state();
#ifdef XAUTH
passert(st->st_oakley.xauth == 0);
#endif
// 添加状态结构中的相关数据,下面的操作和main_outI1很类似了
// 连接
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;
// 设置为当前状态
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(接收到初始化包)
st->st_state = 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 */
// 复制特殊标志: xauth_ack_msgid, modecfg_pull_mode, nat_traversal_vid
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 md->reply and emit HDR before calling it.
*/
// 以下开始填充要发送的回应包信息
/* HDR out.
* We can't leave this to comm_handle() because we must
* fill in the cookie.
*/
{
struct isakmp_hdr r_hdr = md->hdr;
// 填充ISAKMP头结构信息
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, &md->reply, &md->rbody))
return STF_INTERNAL_ERROR;
}
/* start of SA out */
{
struct isakmp_sa r_sa = sa_pd->payload.sa;
// 填充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));
if (SEND_PLUTO_VID || openpgp_peer)
{
char *vendorid = (openpgp_peer) ?
pgp_vendorid : pluto_vendorid;
// 填充提供者ID
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
*/
/* Announce our ability to do RFC 3706 Dead Peer Detection */
next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
// 填充DPD提供者VID
if( !out_generic_raw(next, &isakmp_vendor_id_desc
, &md->rbody, dpd_vendorid
, dpd_vendorid_len, "DPP Vendor ID"))
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)
{
// 填充XAUTH VID
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_CONTROLMORE, DBG_log("sender checking NAT-t: %d and %d"
, nat_traversal_enabled
, md->quirks.nat_traversal_vid));
if (md->quirks.nat_traversal_vid && nat_traversal_enabled) {
// // 填充NAT穿越VID
next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
/* reply if NAT-Traversal draft is supported */
st->hidden_variables.st_nat_traversal = nat_traversal_vid_to_method(md->quirks.nat_traversal_vid);
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;
}
...... 待续 ......