1. 序言

在介绍第②包quick_inI1_outR1()函数之前,先说明下处理流程中的主要的功能:

  • 协商第二阶段的SA算法信息,包括AH协议、ESP协议、封装模式等重要参数。
  • 密钥材料交换,包括Nonce、KE(可选)。
  • 使用ID载荷来协商两端的保护子网范围。
  • 建立IPSec SA结构。
  • 报文的认证和加密。

从上述作用可以看出,quick_inI1_outR1()及后续函数几乎实现了第一阶段的所有基本交换(第一阶段里的重要载荷在此流程中基本都有实现),因此第二包处理流程算是IKEv1协商流程里最为复杂的流程了。这里只是做一个简单的笔记说明核心流程,无法涉及到完整的交换流程。此外响应端通过此次交换后会建立一个inbound sa,这部分流程尚未看明白处理逻辑(可能在于涉及到内核路由表等内容,目前还没有get到)。因此如果需要深入了解此流程,请参考源码实现。

2. quick_inI1_outR1()流程图

刚才已经说明第二个报文的处理流程比较复杂,实现的功能也较其他接口复杂了很多,从流程图上便可以看出:

3. 快速模式消息②数据包格式

下表中的报文格式有部分字段应该为变长类型,但是并未标出,这一点请注意。

4. 源码分析

4.1 quick_inI1_outR1()

quick_inI1_outR1()接口的作用包括:

  • 检验报文的完整性

    • HASH载荷(杂凑载荷)既可以用来检验报文的完整性,也可以用来实现源认证功能,两者实际上是一致的。它计算范围是除了ISAKMP头部以外的完整报文进行杂凑运算。计算方式为:

      H

      A

      S

      H

      =

      P

      R

      F

      (

      S

      K

      E

      Y

      I

      D

      a

      ,

      M

      s

      g

      I

      D

      N

      i

      S

      A

      N

      r

      [

      I

      D

      i

      I

      D

      r

      ]

      )

      HASH = PRF(SKEYID-a, MsgID | Ni | SA | Nr [ | IDi | IDr ] )

      HASH=PRF(SKEYID−a,MsgID∣Ni∣SA∣Nr[∣IDi∣IDr])
      还需要注意的是快速模式的三个报文的HASH载荷的运算模式并不相同。

  • 解析报文中的ID载荷

    • 快速模式中,身份标识ID载荷缺省定义为ISAKMP双方的协商地址。如果双方需要指定身份ID载荷,则需要按照一定的顺序进行传输:IDi + IDr。还需要注意的是协商隧道时配置的保护子网(感兴趣流)是通过ID载荷来传输并完成协商的。ID载荷可以传输IPv4和IPv6的主机地址、子网地址、地址范围。因此使用ID载荷来协商感兴趣流完全满足需求。使用emit_subnet_id()来将保护子网填充到ID载荷,使用decode_net_id()将ID载荷解析为保护子网地址.
  • 保存IV值,并调用后续处理

quick_inI1_outR1()函数并没有协商保护子网信息,而是在后续接口中进行的协商。(NAT-T相关略)

stf_status
quick_inI1_outR1(struct msg_digest *md)
{
const struct state *const p1st = md->st;
struct connection *c = p1st->st_connection;
struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID];
struct verify_oppo_bundle b; /* HASH(1) in *//*使用第一阶段的算法、计算并检验报文的hash载荷*/
CHECK_QUICK_HASH(md
, quick_mode_hash12(hash_val, hash_pbs->roof, md->message_pbs.roof
, p1st, &md->hdr.isa_msgid, FALSE)
, "HASH(1)", "Quick I1"); /* [ IDci, IDcr ] in
* We do this now (probably out of physical order) because
* we wish to select the correct connection before we consult
* it for policy.
*/ if (id_pd != NULL)/*如果ID载荷存在*/
{
struct payload_digest *IDci = id_pd->next; /* ??? we are assuming IPSEC_DOI */ /* IDci (initiator is peer) */
if (!decode_net_id(&id_pd->payload.ipsec_id, &id_pd->pbs
, &b.his.net, "peer client"))/*获取到对端的网段*/
return STF_FAIL + INVALID_ID_INFORMATION; /* Hack for MS 818043 NAT-T Update */
if (id_pd->payload.ipsec_id.isaiid_idtype == ID_FQDN) {/*将单个地址转换为子网地址*/
loglog(RC_LOG_SERIOUS, "Applying workaround for MS-818043 NAT-T bug");
memset(&b.his.net, 0, sizeof(ip_subnet));
happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net));
}
/* End Hack for MS 818043 NAT-T Update */ b.his.proto = id_pd->payload.ipsec_id.isaiid_protoid;
b.his.port = id_pd->payload.ipsec_id.isaiid_port;
b.his.net.addr.u.v4.sin_port = htons(b.his.port); /* IDcr (we are responder) */ if (!decode_net_id(&IDci->payload.ipsec_id, &IDci->pbs
, &b.my.net, "our client"))
return STF_FAIL + INVALID_ID_INFORMATION; b.my.proto = IDci->payload.ipsec_id.isaiid_protoid;
b.my.port = IDci->payload.ipsec_id.isaiid_port;
b.my.net.addr.u.v4.sin_port = htons(b.my.port); #ifdef NAT_TRAVERSAL
/*
* 略
*/
#endif
}
else
{ /*载荷中不存在ID载荷,如果两端的地址类型不一致的化则返回错误
*
*如果不存在ID载荷,则使用协商地址作为保护子网
*/
/* implicit IDci and IDcr: peer and self */
if (!sameaddrtype(&c->spd.this.host_addr, &c->spd.that.host_addr))
return STF_FAIL;
/*默认使用IP地址当作ID*/
happy(addrtosubnet(&c->spd.this.host_addr, &b.my.net));
happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net));
b.his.proto = b.my.proto = 0;
b.his.port = b.my.port = 0;
}
b.step = vos_start;
b.md = md;
b.new_iv_len = p1st->st_new_iv_len;
save_new_iv(p1st, b.new_iv); /*
* FIXME - DAVIDM
* "b" is on the stack, for OPPO tunnels this will be bad, in
* quick_inI1_outR1_start_query it saves a pointer to it before
* a crypto (async op).
*/
return quick_inI1_outR1_authtail(&b, NULL);
}

4.2 quick_inI1_outR1_authtail()

quick_inI1_outR1_authtail()函数作用包括如下几个:

  • 根据子网信息查询连接

    这部分代码没有看懂。。按常理来说,接收此报文时已经确定了连接和状态信息,直接比较连接上的保护子网信息和SA载荷中的保护子网信息,确定是否匹配即可。但是openswan源码中的逻辑负责了很多,没有看明白这部分代码,先留一个疑问吧

  • 根据连接创建新的状态

  • 解析IPSec SA建议载荷

    • 解析SA载荷parse_ipsec_sa_body()

      这个接口是快速模式协商IPSec策略的核心接口,包括封装协议 (ESP | AH | IPCOM)、加密算法、认证算法(完整性算法)、隧道模式or传输模式等等,都是在此接口中进行协商的。此外,该函数也可以完成应答报文的SA载荷的封装。

      近700行的代码,不再另行说明了。

  • 解析Nonce载荷

  • 如果支持PFS,则解析KE载荷

    • 启动PFS功能(完美向前加密),则第二阶段需要再进行一次DH交换,因此需要重新计算生成KE载荷。PFS简单的说如果第一阶段的秘钥被破解(无论采用何种方式),由第一阶段密钥衍生的第二阶段密钥则不受影响。这就要求在第二阶段再次进行DH交换。
  • 构建密钥交换材料申请结构信息,包括:

    • 本端的KE载荷
    • 本端的Nonce载荷
static stf_status
quick_inI1_outR1_authtail(struct verify_oppo_bundle *b
, struct adns_continuation *ac)
{
struct msg_digest *md = b->md;
struct state *const p1st = md->st;
struct connection *c = p1st->st_connection;
ip_subnet *our_net = &b->my.net
, *his_net = &b->his.net;
struct end our, peer;
struct hidden_variables hv; zero(&our); zero(&peer);
our.host_type = KH_IPADDR;
our.client = b->my.net;
our.port = b->my.port;
our.protocol = b->my.proto;
our.has_client = TRUE; peer.host_type = KH_IPADDR;
peer.client = b->his.net;
peer.port = b->his.port;
peer.protocol = b->his.proto;
peer.has_client = TRUE; /*log信息*/ /* Now that we have identities of client subnets, we must look for
* a suitable connection (our current one only matches for hosts).
*/ struct connection *p = find_client_connection(c, &our, &peer);/*根据两端的保护子网来查询连接*/
... ... /* now that we are sure of our connection, create our new state */
{
struct state *const st = duplicate_state(p1st); /* first: fill in missing bits of our new state object
* note: we don't copy over st_peer_pubkey, the public key
* that authenticated the ISAKMP SA. We only need it in this
* routine, so we can "reach back" to p1st to get it.
*/
if (st->st_connection != c)
{
struct connection *t = st->st_connection; st->st_connection = c;
set_cur_connection(c);
connection_discard(t);
} st->st_try = 0; /* not our job to try again from start */ st->st_msgid = md->hdr.isa_msgid; st->st_new_iv_len = b->new_iv_len;
set_new_iv(st, b->new_iv); set_cur_state(st); /* (caller will reset) */
md->st = st; /* feed back new state */ st->st_peeruserprotoid = b->his.proto;
st->st_peeruserport = b->his.port;
st->st_myuserprotoid = b->my.proto;
st->st_myuserport = b->my.port; change_state(st, STATE_QUICK_R0); insert_state(st); /* needs cookies, connection, and msgid */ /* copy hidden variables (possibly with changes) */
st->hidden_variables = hv; /* copy the connection's
* IPSEC policy into our state. The ISAKMP policy is water under
* the bridge, I think. It will reflect the ISAKMP SA that we
* are using.
*/
st->st_policy = (p1st->st_policy & POLICY_ID_AUTH_MASK)
| (c->policy & ~POLICY_ID_AUTH_MASK); #ifdef NAT_TRAVERSAL
...
#endif passert(st->st_connection != NULL);
passert(st->st_connection == c); /* process SA in */
{
struct payload_digest *const sapd = md->chain[ISAKMP_NEXT_SA];
pb_stream in_pbs = sapd->pbs; /* parse and accept body, setting variables, but not forming
* our reply. We'll make up the reply later on.
*
* note that we process the copy of the pbs, so that
* we can process it again in the cryptotail().
*/
st->st_pfs_group = &unset_group;
RETURN_STF_FAILURE(parse_ipsec_sa_body(&in_pbs
, &sapd->payload.sa
, NULL
, FALSE, st));
} /* Ni in *//*Nonce载荷存储在state上*/
RETURN_STF_FAILURE(accept_v1_nonce(md, &st->st_ni, "Ni")); /* [ KE ] in (for PFS) *//*KE载荷存储在state上*/
RETURN_STF_FAILURE(accept_PFS_KE(md, &st->st_gi
, "Gi", "Quick Mode I1")); /*本端的KE和NONCE载荷哪里进行的填充???*/ passert(st->st_pfs_group != &unset_group); passert(st->st_connection != NULL); {/*根据发起端的KE和Nonce载荷,生成本端的ke和Nonce材料*/
struct qke_continuation *qke = alloc_thing(struct qke_continuation
, "quick_outI1 KE"); stf_status e;
enum crypto_importance ci; ci = pcim_ongoing_crypto;
if(ci < st->st_import) ci = st->st_import; qke->md = md;
pcrc_init(&qke->qke_pcrc);
qke->qke_pcrc.pcrc_func = quick_inI1_outR1_cryptocontinue1; if (st->st_pfs_group != NULL) {/*支持PFS???*/
e = build_ke(&qke->qke_pcrc, st, st->st_pfs_group, ci);
} else {
e = build_nonce(&qke->qke_pcrc, st, ci);
} passert(st->st_connection != NULL); return e;
}
}
}

4.3 quick_inI1_outR1_cryptocontinue1()

quick_inI1_outR1_cryptocontinue1()函数的作用如下:

  • 提取计算得到的Nonce载荷
  • 如果启动PFS功能,则计算DH密钥信息
  • 如果未启动PFS功能,则进行应答报文封装操作
static void
quick_inI1_outR1_cryptocontinue1(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r
, err_t ugh)
{
struct qke_continuation *qke = (struct qke_continuation *)pcrc;
struct msg_digest *md = qke->md;
struct state *const st = state_with_serialno(qke->qke_pcrc.pcrc_serialno);/*根据序号查找状态*/
stf_status e; set_cur_state(st); /* we must reset before exit */
st->st_calculating=FALSE;
set_suspended(st, NULL); /* we always calcualte a nonce */
unpack_nonce(&st->st_nr, r);/*提取Nonce值*/ if (st->st_pfs_group != NULL) {/*如果支持PFS,则需要进行第二次DH协商*/
struct dh_continuation *dh = alloc_thing(struct dh_continuation
, "quick outR1 DH"); unpack_KE(st, r, &st->st_gr); /* set up second calculation */
dh->md = md;
set_suspended(st, md);
pcrc_init(&dh->dh_pcrc);
dh->dh_pcrc.pcrc_func = quick_inI1_outR1_cryptocontinue2;
e = start_dh_secret(&dh->dh_pcrc, st
, st->st_import
, RESPONDER
, st->st_pfs_group->group); /* In the STF_INLINE, quick_inI1_outR1_cryptocontinue1 has already
* called complete_v1_state_transition and it has freed *dh. It
* called quick_inI1_outR1_cryptocontinue2 which did the release_md too.
*/
if(e != STF_SUSPEND && e != STF_INLINE) {
if(dh->md != NULL) {
complete_v1_state_transition(&qke->md, e);
if(dh->md) release_md(qke->md);
}
} } else {/*无需第二次DH协商*/
/* but if PFS is off, we don't do a second DH, so
* just call the continuation after making something up.
*/
struct dh_continuation dh; dh.md=md; e = quick_inI1_outR1_cryptotail(&dh, NULL);
if(e == STF_OK) { if(dh.md != NULL) {
/* note: use qke-> pointer */
complete_v1_state_transition(&qke->md, e);
if(dh.md)
release_md(qke->md);
}
}
}
reset_cur_state();
}

4.4 quick_inI1_outR1_cryptotail()

quick_inI1_outR1_cryptotail()函数的作用如下:

  • 构建应答报文

    • ISAKMP头部
    • HASH载荷
    • SA载荷
    • Nonce载荷
    • KE载荷
    • ID载荷
  • 计算报文的哈希值

  • 生成密钥材料compute_keymats

    • 不同协议生成不同的keymats, 如AH、ESP分别生成不同的keymats。

    • 计算公式:

      K

      E

      Y

      M

      A

      T

      =

      P

      R

      F

      (

      S

      K

      E

      Y

      I

      D

      d

      ,

      p

      r

      o

      t

      o

      c

      o

      l

      S

      P

      I

      N

      i

      b

      N

      r

      b

      )

      KEYMAT = PRF(SKEYID—d, protocol | SPI | Ni-b | Nr-b)

      KEYMAT=PRF(SKEYID—d,protocol∣SPI∣Ni−b∣Nr−b)

    • 实现中将所有算法需要的密钥长度全部相加,通过反馈连接方法从而生成所需长度的密钥材料。

  • 建立入ipsec sa: install_inbound_ipsec_sa

    • 最最关键的部分没看懂…
  • 加密报文

static stf_status
quick_inI1_outR1_cryptotail(struct dh_continuation *dh
, struct pluto_crypto_req *r)
{
struct msg_digest *md = dh->md;
struct state *st = md->st;
struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID];
struct payload_digest *const sapd = md->chain[ISAKMP_NEXT_SA];
struct isakmp_sa sa = sapd->payload.sa;
pb_stream r_sa_pbs;
u_char /* set by START_HASH_PAYLOAD: */
*r_hashval, /* where in reply to jam hash value */
*r_hash_start; /* from where to start hashing */ /* Start the output packet.
*
* proccess_packet() would automatically generate the HDR*
* payload if smc->first_out_payload is not ISAKMP_NEXT_NONE.
* We don't do this because we wish there to be no partially
* built output packet if we need to suspend for asynch DNS.
*
* We build the reply packet as we parse the message since
* the parse_ipsec_sa_body emits the reply SA
*/ /* HDR* out */
echo_hdr(md, TRUE, ISAKMP_NEXT_HASH); /* HASH(2) out -- first pass *//*填充HASH载荷并清零hash数据部分*/
START_HASH_PAYLOAD(md->rbody, ISAKMP_NEXT_SA); passert(st->st_connection != NULL); /* sa header is unchanged -- except for np *//*SA载荷头部未发生改变,直接填充即可*/
sa.isasa_np = ISAKMP_NEXT_NONCE;
if (!out_struct(&sa, &isakmp_sa_desc, &md->rbody, &r_sa_pbs))
return STF_INTERNAL_ERROR; /* parse and accept body, this time recording our reply *//*再次匹配SA载荷,然后将
* 匹配的SA载荷填充到r_sa_pbs中*/
RETURN_STF_FAILURE(parse_ipsec_sa_body(&sapd->pbs
, &sapd->payload.sa
, &r_sa_pbs
, FALSE, st)); /**** packet payload: HDR SA Nr [, KE ] [, IDci, IDcr ] ****/
passert(st->st_pfs_group != &unset_group); if ((st->st_policy & POLICY_PFS) && st->st_pfs_group == NULL) {
loglog(RC_LOG_SERIOUS, "we require PFS but Quick I1 SA specifies no GROUP_DESCRIPTION");
return STF_FAIL + NO_PROPOSAL_CHOSEN; /* ??? */
} openswan_log("responding to Quick Mode proposal {msgid:%08x}", st->st_msgid);
{
char instbuf[END_BUF];
struct connection *c = st->st_connection;
struct spd_route *sr = &c->spd; format_end(instbuf, sizeof(instbuf),&sr->this,&sr->that,TRUE, LEMPTY);
openswan_log(" us: %s", instbuf); format_end(instbuf, sizeof(instbuf),&sr->that,&sr->this,FALSE, LEMPTY); openswan_log(" them: %s", instbuf);
} /**** finish reply packet: Nr [, KE ] [, IDci, IDcr ] ****/ {
int np;
#ifdef IMPAIR_UNALIGNED_R1_MSG
char *padstr=getenv("PLUTO_UNALIGNED_R1_MSG"); if(padstr) {
np = ISAKMP_NEXT_VID;
} else
#endif
if(st->st_pfs_group != NULL) {
np = ISAKMP_NEXT_KE;
} else if(id_pd != NULL) {
np = ISAKMP_NEXT_ID;
} else {
np = ISAKMP_NEXT_NONE;
} /* Nr out */
if (!justship_nonce(&st->st_nr, &md->rbody, np, "Nr"))
return STF_INTERNAL_ERROR; #ifdef IMPAIR_UNALIGNED_R1_MSG
if(padstr) {
pb_stream vid_pbs;
int padsize;
padsize = strtoul(padstr, NULL, 0); openswan_log("inserting fake VID payload of %u size", padsize); if(st->st_pfs_group != NULL) {
np = ISAKMP_NEXT_KE;
} else if(id_pd != NULL) {
np = ISAKMP_NEXT_ID;
} else {
np = ISAKMP_NEXT_NONE;
} if (!out_generic(np,
&isakmp_vendor_id_desc, &md->rbody, &vid_pbs))
return STF_INTERNAL_ERROR; if (!out_zero(padsize, &vid_pbs, "Filler VID"))
return STF_INTERNAL_ERROR; close_output_pbs(&vid_pbs);
}
#endif
} /* [ KE ] out (for PFS) */
if (st->st_pfs_group != NULL && r!=NULL) {
if (!justship_KE(&st->st_gr
, &md->rbody
, id_pd != NULL? ISAKMP_NEXT_ID : ISAKMP_NEXT_NONE))
return STF_INTERNAL_ERROR; finish_dh_secret(st, r);
if(!r->pcr_success) {
return STF_FAIL + INVALID_KEY_INFORMATION;
}
} /* [ IDci, IDcr ] out */
if (id_pd != NULL) {
struct isakmp_ipsec_id *p = (void *)md->rbody.cur; /* UGH! */ if (!out_raw(id_pd->pbs.start, pbs_room(&id_pd->pbs), &md->rbody, "IDci"))
return STF_INTERNAL_ERROR;
p->isaiid_np = ISAKMP_NEXT_ID; p = (void *)md->rbody.cur; /* UGH! */ if (!out_raw(id_pd->next->pbs.start, pbs_room(&id_pd->next->pbs), &md->rbody, "IDcr"))
return STF_INTERNAL_ERROR;
p->isaiid_np = ISAKMP_NEXT_NONE;
} #ifdef TPM
{
pb_stream *pbs = &md->rbody;
size_t enc_len = pbs_offset(pbs) - sizeof(struct isakmp_hdr); TCLCALLOUT_crypt("preHash", st,pbs,sizeof(struct isakmp_hdr),enc_len);
r_hashval = tpm_relocateHash(pbs);
}
#endif /* Compute reply HASH(2) and insert in output */
(void)quick_mode_hash12(r_hashval, r_hash_start, md->rbody.cur
, st, &st->st_msgid, TRUE); /* Derive new keying material */
compute_keymats(st); /* Tell the kernel to establish the new inbound SA
* (unless the commit bit is set -- which we don't support).
* We do this before any state updating so that
* failure won't look like success.
*/
if (!install_inbound_ipsec_sa(md->pst, st))
return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ /* encrypt message, except for fixed part of header */ if (!encrypt_message(&md->rbody, st))
{
delete_ipsec_sa(st, TRUE);
return STF_INTERNAL_ERROR; /* ??? we may be partly committed */
} DBG(DBG_CONTROLMORE, DBG_log("finished processing quick inI1"));
return STF_OK;
}

5. 其他接口源码分析

5.1 decode_net_id()

decode_net_id()函数的作用:

  • 解析报文中的ID载荷,并将主机地址子网地址地址范围转换为子网信息。

    这里解析的子网信息用于协商感兴趣流(保护子网)参数

    • 如果类型为“ID_IPV4_ADDR”或者“ID_IPV6_ADDR”,则说明为单个主机地址,解析后转换为子网地址,掩码长度为32位;
    • 如果类型为"ID_IPV4_ADDR_SUBNET"或者“ID_IPV6_ADDR_RANGE”,则表明ID载荷数据部分是子网信息,包含两部分:网络地址子网掩码。通过网络地址和子网掩码共同确定保护子网信息。
    • 如果类型为“ID_IPV4_ADDR_RANGE”或者“ID_IPV6_ADDR_RANGE”,则同样表明ID载荷数据是一个地址范围,包含两部分内容:起始地址终止地址。需要注意的时,这里目前仅支持标准的子网范围,而非任意子网范围,这点需要注意(详情参见rangetosubnet())。
static bool
decode_net_id(struct isakmp_ipsec_id *id
, pb_stream *id_pbs
, ip_subnet *net
, const char *which)
{
const struct af_info *afi = NULL; /* Note: the following may be a pointer into static memory
* that may be recycled, but only if the type is not known.
* That case is disposed of very early -- in the first switch.
*/
const char *idtypename = enum_show(&ident_names, id->isaiid_idtype);
/*
* 子网ID可能为单个地址、子网、子网范围
*
*/
switch (id->isaiid_idtype)
{
case ID_IPV4_ADDR:
case ID_IPV4_ADDR_SUBNET:
case ID_IPV4_ADDR_RANGE:
afi = &af_inet4_info;
break;
case ID_IPV6_ADDR:
case ID_IPV6_ADDR_SUBNET:
case ID_IPV6_ADDR_RANGE:
afi = &af_inet6_info;
break;
case ID_FQDN:
loglog(RC_COMMENT, "%s type is FQDN", which);
return TRUE; default:
/* XXX support more */
loglog(RC_LOG_SERIOUS, "unsupported ID type %s"
, idtypename);
/* XXX Could send notification back */
return FALSE;
} switch (id->isaiid_idtype)
{
case ID_IPV4_ADDR:/*ID载荷为单个地址*/
case ID_IPV6_ADDR:
{
ip_address temp_address;
err_t ughmsg; ughmsg = initaddr(id_pbs->cur, pbs_left(id_pbs), afi->af, &temp_address); if (ughmsg != NULL)
{
loglog(RC_LOG_SERIOUS, "%s ID payload %s has wrong length in Quick I1 (%s)"
, which, idtypename, ughmsg);
/* XXX Could send notification back */
return FALSE;
}
if (isanyaddr(&temp_address))
{
loglog(RC_LOG_SERIOUS, "%s ID payload %s is invalid (%s) in Quick I1"
, which, idtypename, ip_str(&temp_address));
/* XXX Could send notification back */
return FALSE;
}
happy(addrtosubnet(&temp_address, net));/*将单个地址解析为保护子网地址*/
DBG(DBG_PARSING | DBG_CONTROL
, DBG_log("%s is %s", which, ip_str(&temp_address)));
break;
} case ID_IPV4_ADDR_SUBNET:/*如果ID为子网信息*/
case ID_IPV6_ADDR_SUBNET:
{
ip_address temp_address, temp_mask;
err_t ughmsg; if (pbs_left(id_pbs) != 2 * afi->ia_sz)/*子网信息包括IP和掩码,因此长度*2 */
{
loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1"
, which, idtypename);
/* XXX Could send notification back */
return FALSE;
}
ughmsg = initaddr(id_pbs->cur
, afi->ia_sz, afi->af, &temp_address);/*解析子网地址*/
if (ughmsg == NULL)
ughmsg = initaddr(id_pbs->cur + afi->ia_sz
, afi->ia_sz, afi->af, &temp_mask);/*解析子网掩码*/
if (ughmsg == NULL)
ughmsg = initsubnet(&temp_address, masktocount(&temp_mask)
, '0', net);
if (ughmsg == NULL && subnetisnone(net))
ughmsg = "contains only anyaddr";
if (ughmsg != NULL)
{
loglog(RC_LOG_SERIOUS, "%s ID payload %s bad subnet in Quick I1 (%s)"
, which, idtypename, ughmsg);
/* XXX Could send notification back */
return FALSE;
}
DBG(DBG_PARSING | DBG_CONTROL,
{
char temp_buff[SUBNETTOT_BUF]; subnettot(net, 0, temp_buff, sizeof(temp_buff));
DBG_log("%s is subnet %s", which, temp_buff);
});
break;
} case ID_IPV4_ADDR_RANGE:
case ID_IPV6_ADDR_RANGE:
{
ip_address temp_address_from, temp_address_to;
err_t ughmsg; if (pbs_left(id_pbs) != 2 * afi->ia_sz)
{
loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1"
, which, idtypename);
/* XXX Could send notification back */
return FALSE;
}/*解析子网地址*/
ughmsg = initaddr(id_pbs->cur, afi->ia_sz, afi->af, &temp_address_from);
if (ughmsg == NULL)/*解析子网掩码*/
ughmsg = initaddr(id_pbs->cur + afi->ia_sz
, afi->ia_sz, afi->af, &temp_address_to);
if (ughmsg != NULL)
{
loglog(RC_LOG_SERIOUS, "%s ID payload %s malformed (%s) in Quick I1"
, which, idtypename, ughmsg);
/* XXX Could send notification back */
return FALSE;
} ughmsg = rangetosubnet(&temp_address_from, &temp_address_to, net);
if (ughmsg == NULL && subnetisnone(net))
ughmsg = "contains only anyaddr";
if (ughmsg != NULL)
{
char temp_buff1[ADDRTOT_BUF], temp_buff2[ADDRTOT_BUF]; addrtot(&temp_address_from, 0, temp_buff1, sizeof(temp_buff1));
addrtot(&temp_address_to, 0, temp_buff2, sizeof(temp_buff2));
loglog(RC_LOG_SERIOUS, "%s ID payload in Quick I1, %s"
" %s - %s unacceptable: %s"
, which, idtypename, temp_buff1, temp_buff2, ughmsg);
return FALSE;
}
DBG(DBG_PARSING | DBG_CONTROL,
{
char temp_buff[SUBNETTOT_BUF]; subnettot(net, 0, temp_buff, sizeof(temp_buff));
DBG_log("%s is subnet %s (received as range)"
, which, temp_buff);
});
break;
}
} /* set the port selector */
setportof(htons(id->isaiid_port), &net->addr); DBG(DBG_PARSING | DBG_CONTROL,
DBG_log("%s protocol/port is %d/%d", which, id->isaiid_protoid, id->isaiid_port)
) return TRUE;
}

5.2 emit_subnet_id()

emit_subnet_id()函数的作用:

  • 隧道的保护子网地址转换为ID载荷内容,然后封装到报文中。

这个函数默认使用保护子网地址填充ID载荷(usehost===FALSE)。此函数与decode_net_id()共同完成保护子网地址的转换工作。

*填充的是隧道端口IP还是子网的信息?
*保护子网是如何协商的???
*/
static bool
emit_subnet_id(struct end *e
, u_int8_t np
, ip_address endpoint
, u_int8_t protoid
, u_int16_t port
, pb_stream *outs)
{
struct isakmp_ipsec_id id;
pb_stream id_pbs;
ip_address ta;
unsigned char *tbp;
size_t tal;
const struct af_info *ai;
bool usehost = FALSE;
ip_subnet clientnet; clientnet = e->client; if(!e->has_client) {
/* we propose the IP address of the interface that we are using. */
/*
* we could instead propose 0.0.0.0->255.255.255.255 and let the other
* end narrow the TS, but if one wants that, it is easy to just specify
* in the configuration file: rightsubnet=0.0.0.0/0.
*
* When there is NAT involved, we may really want a tunnel to the
* address that this end point thinks it is. That works only when
* virtual_ip includes the IP involved.
*
*/
addrtosubnet(&endpoint, &clientnet);
} ai = aftoinfo(subnettypeof(&clientnet));
passert(ai != NULL); id.isaiid_np = np;
id.isaiid_idtype = (usehost ? ai->id_addr : ai->id_subnet);/*确定使用主机ID还是子网ID*/
id.isaiid_protoid = protoid;
id.isaiid_port = port; if (!out_struct(&id, &isakmp_ipsec_identification_desc, outs, &id_pbs))
return FALSE; networkof(&clientnet, &ta);/*获取保护子网*/
tal = addrbytesptr(&ta, &tbp);
if (!out_raw(tbp, tal, &id_pbs, "client network"))/*填充保护子网信息*/
return FALSE; if(!usehost)
{
maskof(&clientnet, &ta);/*获取保护子网掩码*/
tal = addrbytesptr(&ta, &tbp);
if (!out_raw(tbp, tal, &id_pbs, "client mask"))/*填充保护子网掩码信息*/
return FALSE;
} close_output_pbs(&id_pbs);
return TRUE;
}

6. 小结

快速模式的第二个报文流程相对其他报文复杂了很多,尚有很多关键部分没有完全没有理解。每有会意,再做更新,如果有get到的,请分享下共同进步。

快速模式第二包: quick_inI1_ouR1()的更多相关文章

  1. 快速模式第一包: quick_outI1()

    文章目录 1. 序言 2. quick_outI1()流程图 3. quick_outI1()源码分析 4. quick_outI1_continue()源码分析 5. quick_outI1_tai ...

  2. IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包

    IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 文章目录 IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 1. IKEv2 协商总体框架 2. 第二包流程图 3. ...

  3. openswan中ISAKMP交互过程关键函数接口

    1. ISAKMP交互过程中关键函数接口 下面分别说明不同的阶段和模式下的函数接口以及对应的报文. 2. 第一阶段(Phase I)主模式函数接口 发送端 响应端 main_outI1 主模式第一包 ...

  4. IPsec 9个包分析(主模式+快速模式)

    第一阶段:ISAKMP协商阶段 1.1 第一包 包1:发起端协商SA,使用的是UDP协议,端口号是500,上层协议是ISAKMP,该协议提供的是一个框架,里面的负载Next payload类似模块,可 ...

  5. 快速模式第三包:quick_inR1_outI2()

    快速模式第三包:quick_inR1_outI2() 文章目录 快速模式第三包:quick_inR1_outI2() 1. 序言 2. quick_inR1_outI2()的处理流程 3. 快速模式第 ...

  6. 韩顺刚-tcp报文头协议详细分析第一包数据:序号是0,发送数据的长度是0,因为没有收到对端的数据,所以确认号是0, Syn的标志位设置成1,这里没有发送的数据,只发送TCP的20个字节的头部

    TCP报文段首部格式 大部分TCP报文头部都是20个字节,有的数据包要加上选项. 上面一行代表4个字节,源端口和目的端口都是2个字节. TCP协议是面向字节流的协议 TCP是一段一段分块的发送数据的 ...

  7. openswan IPSec专栏目录锦集

    为了方便查阅现有的文章,特准备一个目录页供后续查询使用 专栏序言 1. 基础知识 openswan任务调度基础知识之信号 2. openswan环境搭建 openswan框架和编译时说明 opensw ...

  8. 低功耗蓝牙BLE之连接事件、连接参数和更新方法

    转自:http://blog.csdn.net/zzfenglin/article/details/51304084 连接事件 在一个连接当中,主设备会在每个连接事件里向从设备发送数据包.一个连接事件 ...

  9. TCP中异常关闭链接的意义 异常关闭的情况

    终止一个连接的正常方式是发送FIN. 在发送缓冲区中 所有排队数据都已发送之后才发送FIN,正常情况下没有任何数据丢失. 但我们有时也有可能发送一个RST报文段而不是F IN来中途关闭一个连接.这称为 ...

随机推荐

  1. Windows 系统安装 git基础小白的简单操作包含基本输入命令

    首先  去官网下载git应用 https://git-scm.com/downloads  点击前往 安装的操作也比较简单,一直点击下一步 安装完成之后   在空白桌面点击是否有   Git  Gui ...

  2. SVG和Canvas的区别?

    什么是SVG? SVG(可缩放矢量图形)编辑可缩放矢量图形是基于可扩展标记语言(标准通用标记语言的子集),用于描述二维矢量图形的一种图形格式.它由万维网联盟制定,是一个开放标准. 什么是 Canvas ...

  3. vue 源码详解(二): 组件生命周期初始化、事件系统初始化

    vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...

  4. 利用system generator 生成vivado ip—以低通滤波器举例

    前段时间自学了matlab和vivado联合推出的system generator工具,用来做数字信号处理,十分好用且使开发更便捷,下面举个例子来供大家一起学习下. 首先打开matlab命令行,输入s ...

  5. Compile Java Codes in Linux Shell instead of Ant Script

    The following is frequently used ant script, compile some java source codes with a libary path, then ...

  6. [数据结构]ODT(珂朵莉树)实现及其应用,带图

    [数据结构]ODT(珂朵莉树)实现及其应用,带图 本文只发布于博客园,其他地方若出现本文均是盗版 算法引入 需要一种这样的数据结构,需要支持区间的修改,区间不同值的分别操作. 一般的,我们会想到用线段 ...

  7. SpringBoot开发三

    需求介绍-SpringMVC 服务层的三层架构:表现层,业务层,数据层,浏览器访问服务器先访问表现层,期待表现层返回一些数据,表现层呢就访问业务层处理业务,而业务层在处理业务的时候会调用数据层请求数据 ...

  8. DLL劫持漏洞

    写文章的契机还是看沙雕群友挖了十多个DLL劫持的漏洞交CNVD上去了... 就想起来搜集整理一下这部分 0x01 前言 DLL(Dynamic Link Library)文件为动态链接库文件,又称&q ...

  9. noip9

    T1 本次考试最水的一道题,然而我sb,前一个小时,找了一大堆跟题目无关的性质,干脆打了个20pts的表,然后就走了,最后几分钟才看出来,匆匆码出来,结果段错误,然后考试就结束了. 好吧,段错误是UB ...

  10. NOIP 模拟 7 考试总结

    T1 超级大水题,用 \(kmp\) 和 \(hash\) 均能过,但都忘了,结果只打了个暴力.难受.板子题,题解就不放了 Code #include<bits/stdc++.h> #de ...