本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源: http://yfydz.cublog.cn

15. 快速模式协商

IKE野蛮协商处理函数基本都在programs/pluto/ikev1_quick.c中定义, 属于IKEv1协商的第2阶段, 协商完成后建立起两个方向的IPSEC SA用来保护实际的数据传输。

15.1 过程

快速模式:

        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)
 
连接发起函数为ipsecdoi_initiate()函数, 在其中调用了main_outI1(), aggr_outI1()和quick_outI1()函数分别发起主模式,野蛮模式和快速模式的协商。另外也可以从complete_state_transition()函数(这里应该是主要的发起点), unpending()函数中发起。
 
15.2 发起方的第一个初始包

stf_status
quick_outI1(int whack_sock
     , struct state *isakmp_sa
     , struct connection *c
     , lset_t policy
     , unsigned long try
     , so_serial_t replacing)
{
// 根据第一阶段协商出的ISAKMP SA状态复制出一个新的状态用于第2阶段
    struct state *st = duplicate_state(isakmp_sa);
// 分配快速密钥交换结构
    struct qke_continuation *qke = alloc_thing(struct qke_continuation
            , "quick_outI1 KE");
    stf_status e;
// 新状态的whack套接口
    st->st_whack_sock = whack_sock;
// 新状态的连接
    st->st_connection = c;
    passert(c != NULL);
    if(st->st_calculating) {
 return STF_IGNORE;
    }
// 设置新状态为当前状态
    set_cur_state(st); /* we must reset before exit */
// 策略
    st->st_policy = policy;
// 尝试次数
    st->st_try = try;
// 协议端口信息
    st->st_myuserprotoid = c->spd.this.protocol;
    st->st_peeruserprotoid = c->spd.that.protocol;
    st->st_myuserport = c->spd.this.port;
    st->st_peeruserport = c->spd.that.port;
// 消息ID
    st->st_msgid = generate_msgid(isakmp_sa);
// 状态类型为QUICK_I1
    st->st_state = STATE_QUICK_I1;
// 将新状态插入系统状态哈希表
    insert_state(st); /* needs cookies, connection, and msgid */

// 记录快速模式初始化信息, replacing为要替代的老的ISAKMP SA的序号
    if (replacing == SOS_NOBODY)
 openswan_log("initiating Quick Mode %s {using isakmp#%lu}"
       , prettypolicy(policy)
       , isakmp_sa->st_serialno);
    else
 openswan_log("initiating Quick Mode %s to replace #%lu {using isakmp#%lu}"
       , prettypolicy(policy)
       , replacing
       , isakmp_sa->st_serialno);
    /*
     * See if pfs_group has been specified for this conn,
     * if not, fallback to old use-same-as-P1 behaviour
     */
#ifdef IKE_ALG
// 查找该连接所用的PFS组, PFS: 完美向前加密
    if (st->st_connection)
     st->st_pfs_group = ike_alg_pfsgroup(st->st_connection
      , st->st_policy);
    if (!st->st_pfs_group)
#endif
    /* If PFS specified, use the same group as during Phase 1:
     * since no negotiation is possible, we pick one that is
     * very likely supported.
     */
     st->st_pfs_group = policy & POLICY_PFS? isakmp_sa->st_oakley.group : NULL;
// 填充快速模式密钥交换结构qke参数
// 第2阶段状态
    qke->st = st;
// 第一阶段状态
    qke->isakmp_sa = isakmp_sa;
// 替换的老状态
    qke->replacing = replacing;
// 继续处理函数
    qke->qke_pcrc.pcrc_func = quick_outI1_continue;
   
    if(policy & POLICY_PFS) {
// 如果设置了PFS策略, 每次都要重新更新加密密钥, 进行密钥交换处理
 e=build_ke(&qke->qke_pcrc, st, st->st_pfs_group, st->st_import);
    } else {
// 否则构造NONCE载荷, 使用原来的密钥
 e=build_nonce(&qke->qke_pcrc, st, st->st_import);
    }
    reset_globals();
    return e;
}
   
// 继续函数, 完成了密钥处理后的执行函数
static void
quick_outI1_continue(struct pluto_crypto_req_cont *pcrc
       , struct pluto_crypto_req *r
       , err_t ugh)
{
    struct qke_continuation *qke = (struct qke_continuation *)pcrc;
    struct state *const st = qke->st;
    stf_status e;
    DBG(DBG_CONTROLMORE
 , DBG_log("quick outI1: calculated ke+nonce, sending I1"));
    st->st_calculating = FALSE;
    /* XXX should check out ugh */
    passert(ugh == NULL);
    passert(cur_state == NULL);
    passert(st != NULL);
// 设置当前状态
    set_cur_state(st); /* we must reset before exit */
// 继续完成outI1操作
    e = quick_outI1_tail(pcrc, r);
// 复位全局变量
    reset_globals();
}

// outI1结尾操作,构造数据包
static stf_status
quick_outI1_tail(struct pluto_crypto_req_cont *pcrc
   , struct pluto_crypto_req *r)
{
// 快速模式密钥交换结构
    struct qke_continuation *qke = (struct qke_continuation *)pcrc;
// 状态
    struct state *st = qke->st;
// 连接
    struct connection *c = st->st_connection;
// 第一阶段状态, 描述ISAKMP SA, 用于保护第2阶段的协商
    struct state *isakmp_sa = qke->isakmp_sa;
    pb_stream reply; /* not really a reply */
    pb_stream rbody;
    u_char /* set by START_HASH_PAYLOAD: */
 *r_hashval, /* where in reply to jam hash value */
 *r_hash_start; /* start of what is to be hashed */
// 检查是否有内部机(是×××网关)
    bool has_client = c->spd.this.has_client || c->spd.that.has_client ||
        c->spd.this.protocol || c->spd.that.protocol ||
        c->spd.this.port || c->spd.that.port;
// 这个有意义么?不刚从st->st_connection赋值过来的嘛
    st->st_connection = c;
#ifdef NAT_TRAVERSAL
    if (isakmp_sa->hidden_variables.st_nat_traversal & NAT_T_DETECTED) {
// 如果检测到了NAT穿越, 更新端口
       /* Duplicate nat_traversal status in new state */
       st->hidden_variables.st_nat_traversal = isakmp_sa->hidden_variables.st_nat_traversal;
       if (isakmp_sa->hidden_variables.st_nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME)) {
    has_client = TRUE;
       }
       nat_traversal_change_port_lookup(NULL, st);
    }
    else {
       st->hidden_variables.st_nat_traversal = 0;
    }
#endif
    /* set up reply */
// 初始化回应包缓冲区
    init_pbs(&reply, reply_buffer, sizeof(reply_buffer), "reply packet");
    /* HDR* out */
    {
 struct isakmp_hdr hdr;
// 填充ISAKMP头结构
 hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
// 下一载荷是哈希载荷
 hdr.isa_np = ISAKMP_NEXT_HASH;
// 快速描述交换
 hdr.isa_xchg = ISAKMP_XCHG_QUICK;
// 消息ID
 hdr.isa_msgid = st->st_msgid;
// 数据是加密的
 hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION;
// 拷贝双方的cookie
 memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);
 memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE);
 if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody))
 {
     reset_cur_state();
     return STF_INTERNAL_ERROR;
 }
    }
    /* HASH(1) -- create and note space to be filled later */
// 构造哈希载荷
    START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_SA);
    /* SA out */
    /* Emit SA payload based on a subset of the policy bits.
     * POLICY_COMPRESS is considered iff we can do IPcomp.
     */
    {
 lset_t pm = POLICY_ENCRYPT | POLICY_AUTHENTICATE;
// 检查是否能进行IP压缩处理
 if (can_do_IPcomp)
     pm |= POLICY_COMPRESS;
// 填充SA载荷, 下一载荷是NONCE载荷
 if (!out_sa(&rbody
      , &ipsec_sadb[(st->st_policy & pm) >> POLICY_IPSEC_SHIFT]
      , st, FALSE, FALSE, ISAKMP_NEXT_NONCE))
 {
     reset_cur_state();
     return STF_INTERNAL_ERROR;
 }
    }
    {
 int np;
 if(st->st_policy & POLICY_PFS) {
// 如果使用PFS, NONCE载荷的下一载荷是KE载荷, 重新协商密钥
     np = ISAKMP_NEXT_KE;
 } else {
// 否则如果如果带内部机,下一载荷是ID载荷,否则不再带载荷
     if(has_client) {
  np = ISAKMP_NEXT_ID;
     } else {
  np = ISAKMP_NEXT_NONE;
     }
 }
 /* Ni out */
// 填充NONCE载荷
 if (!ship_nonce(&st->st_ni, r, &rbody
   , np
   , "Ni"))
     {
  reset_cur_state();
  return STF_INTERNAL_ERROR;
     }
    }
    /* [ KE ] out (for PFS) */
    if (st->st_pfs_group != NULL)
    {
// PFS情况下填充KE载荷
 if (!ship_KE(st, r, &st->st_gi
       , &rbody
       , has_client? ISAKMP_NEXT_ID : ISAKMP_NEXT_NONE))
 {
     reset_cur_state();
     return STF_INTERNAL_ERROR;
 }
    }
    /* [ IDci, IDcr ] out */
    if (has_client)
    {
// 填充ID载荷, 包括发起方ID和响应方ID
 /* IDci (we are initiator), then IDcr (peer is responder) */
 if (!emit_subnet_id(&c->spd.this.client
       , ISAKMP_NEXT_ID
       , st->st_myuserprotoid
       , st->st_myuserport, &rbody)
     || !emit_subnet_id(&c->spd.that.client
          , ISAKMP_NEXT_NONE
          , st->st_peeruserprotoid
          , st->st_peeruserport, &rbody))
 {
     reset_cur_state();
     return STF_INTERNAL_ERROR;
 }
    }
#ifdef NAT_TRAVERSAL
    if ((st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATOA)
 && (!(st->st_policy & POLICY_TUNNEL))
 && (st->hidden_variables.st_nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME))) {
 /** Send NAT-OA if our address is NATed */
// 如果需要进行NAT穿越,填充NAT信息ID载荷
 if (!nat_traversal_add_natoa(ISAKMP_NEXT_NONE, &rbody, st)) {
     reset_cur_state();
     return STF_INTERNAL_ERROR;
 }
    }
#endif
    /* finish computing  HASH(1), inserting it in output */
// 完成快速模式的哈希计算
    (void) quick_mode_hash12(r_hashval, r_hash_start, rbody.cur
 , st, &st->st_msgid, FALSE);
    /* encrypt message, except for fixed part of header */
// 初始化阶段2的初始化向量
    init_phase2_iv(isakmp_sa, &st->st_msgid);
    st->st_new_iv_len = isakmp_sa->st_new_iv_len;
    set_new_iv(st, isakmp_sa->st_new_iv);
// 加密数据
    if (!encrypt_message(&rbody, st))
    {
 reset_cur_state();
 return STF_INTERNAL_ERROR;
    }
    /* save packet, now that we know its size */
    clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply)
 , "reply packet from quick_outI1");
    /* send the packet */
// 发送数据包
    send_packet(st, "quick_outI1", TRUE);
// 重新调度状态事件, 超时重发
    delete_event(st);
    event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st);

// 输出信息
    if (qke->replacing == SOS_NOBODY)
 whack_log(RC_NEW_STATE + STATE_QUICK_I1
     , "%s: initiate"
     , enum_name(&state_names, st->st_state));
    else
 whack_log(RC_NEW_STATE + STATE_QUICK_I1
     , "%s: initiate to replace #%lu"
     , enum_name(&state_names, st->st_state)
     , qke->replacing);
    return STF_OK;
}

15.3 响应方收到发起方初始化包, 发送第一响应包

/* Handle first message of Phase 2 -- Quick Mode.
 * HDR*, HASH(1), SA, Ni [, KE ] [, IDci, IDcr ] -->
 * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ]
 * (see RFC 2409 "IKE" 5.5)
 * Installs inbound IPsec SAs.
 * Although this seems early, we know enough to do so, and
 * this way we know that it is soon enough to catch all
 * packets that other side could send using this IPsec SA.
 *
 * Broken into parts to allow asynchronous DNS for TXT records:
 *
 * - quick_inI1_outR1 starts the ball rolling.
 *   It checks and parses enough to learn the Phase 2 IDs
 *
 * - quick_inI1_outR1_authtail does the rest of the job
 *   unless DNS must be consulted.  In that case,
 *   it starts a DNS query, salts away what is needed
 *   to continue, and suspends.  Calls
 *   + quick_inI1_outR1_start_query
 *   + quick_inI1_outR1_process_answer
 *
 * - quick_inI1_outR1_continue will restart quick_inI1_outR1_authtail
 *   when DNS comes back with an answer.
 *
 * A big chunk of quick_inI1_outR1_authtail is executed twice.
 * This is necessary because the set of connections
 * might change while we are awaiting DNS.
 * When first called, gateways_from_dns == NULL.  If DNS is
 * consulted asynchronously, gateways_from_dns != NULL the second time.
 * Remember that our state object might disappear too!
 *
 *
 * If the connection is opportunistic, we must verify delegation.
 *
 * 1. Check that we are authorized to be SG for
 *    our client.  We look for the TXT record that
 *    delegates us.  We also check that the public
 *    key (if present) matches the private key we used.
 *    Eventually, we should probably require DNSsec
 *    authentication for our side.
 *
 * 2. If our client TXT record did not include a
 *    public key, check the KEY record indicated
 *    by the identity in the TXT record.
 *
 * 3. If the peer's client is the peer itself, we
 *    consider it authenticated.  Otherwise, we check
 *    the TXT record for the client to see that
 *    the identity of the SG matches the peer and
 *    that some public key (if present in the TXT)
 *    matches.  We need not check the public key if
 *    it isn't in the TXT record.
 *
 * Since p isn't yet instantiated, we need to look
 * in c for description of peer.
 *
 * We cannot afford to block waiting for a DNS query.
 * The code here is structured as two halves:
 * - process the result of just completed
 *   DNS query (if any)
 * - if another query is needed, initiate the next
 *   DNS query and suspend
 */
stf_status
quick_inI1_outR1(struct msg_digest *md)
{
// 相关状态, 这个状态是阶段1协商完的状态, 即描述ISAKMP SA的状态结构
    const struct state *const p1st = md->st;
    struct connection *c = p1st->st_connection;
// 输入数据包的ID载荷链表
    struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID];
    struct verify_oppo_bundle b;
    /* HASH(1) in */
// 计算并检查输入包的哈希值,错误的话返回
    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.
     */
// ID载荷非空
    if (id_pd != NULL)
    {
 /* ??? we are assuming IPSEC_DOI */
 /* IDci (initiator is peer) */
// 解码发起方ID载荷, 对方是快速模式发起方
 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 */
// 如果ID是FQDN格式(域名),将IP子网参数置0
        if (id_pd->payload.ipsec_id.isaiid_idtype == ID_FQDN)
           memset(&b.his.net, 0, sizeof(ip_subnet));
        if (id_pd->payload.ipsec_id.isaiid_idtype == ID_FQDN)
           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) */
// 解码响应方ID载荷,本地是快速模式响应方
 if (!decode_net_id(&id_pd->next->payload.ipsec_id, &id_pd->next->pbs
 , &b.my.net, "our client"))
     return STF_FAIL + INVALID_ID_INFORMATION;
// 本地的协议和端口参数
 b.my.proto = id_pd->next->payload.ipsec_id.isaiid_protoid;
 b.my.port = id_pd->next->payload.ipsec_id.isaiid_port;
 b.my.net.addr.u.v4.sin_port = htons(b.my.port);
    }
    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;
// 将连接地址作为bundle的地址参数
 happy(addrtosubnet(&c->spd.this.host_addr, &b.my.net));
 happy(addrtosubnet(&c->spd.that.host_addr, &b.his.net));
// 协议端口参数置为0
 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);
// 认证结束处理
    return quick_inI1_outR1_authtail(&b, NULL);
}
 
static stf_status
quick_inI1_outR1_authtail(struct verify_oppo_bundle *b
        , struct adns_continuation *ac)
{
// 消息摘要
    struct msg_digest *md = b->md;
// 阶段1的状态
    struct state *const p1st = md->st;
// 阶段1连接
    struct connection *c = p1st->st_connection;
    ip_subnet *our_net = &b->my.net
 , *his_net = &b->his.net;
    /* Now that we have identities of client subnets, we must look for
     * a suitable connection (our current one only matches for hosts).
     */
    {
// 根据子网地址(subnet),协议,端口查找连接, 前面的连接c只是根据双方网关地址来找的,
// 两者可能相同, 也可能不同
 struct connection *p = find_client_connection(c
     , our_net, his_net, b->my.proto, b->my.port, b->his.proto, b->his.port);
#ifdef NAT_TRAVERSAL
// 这个宏是比较paranoid了, 表示传输模式虽然有安全问题但还是要用传输模式
// 对于网关, RFC中定义只需要支持通道(tunnel)模式就行了,不必要支持传输模式
#ifdef I_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT
// 如果检测到NAT穿越
    if( (p1st->hidden_variables.st_nat_traversal & NAT_T_DETECTED)
// 是传输模式
       && !(p1st->st_policy & POLICY_TUNNEL)
// 本地是在NAT后?
       && (p1st->hidden_variables.st_nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_ME))
// 没找到客户端连接
       && (p == NULL) )
        {
// 将p赋值为c
          p = c;
          DBG(DBG_CONTROL, DBG_log("using (something) old for transport mode connection \"%s\"", p->name));
        }
#endif
#endif
 if (p == NULL)
 {
// 如果没找到合适连接, 输出相关信息, 返回错误
     /* This message occurs in very puzzling circumstances
      * so we must add as much information and beauty as we can.
      */
     struct end
  me = c->spd.this,
  he = c->spd.that;
     char buf[2*SUBNETTOT_BUF + 2*ADDRTOT_BUF + 2*IDTOA_BUF + 2*ADDRTOT_BUF + 12]; /* + 12 for separating */
     size_t l;
     me.client = *our_net;
     me.has_client = !subnetisaddr(our_net, &me.host_addr);
     me.protocol = b->my.proto;
     me.port = b->my.port;
     he.client = *his_net;
     he.has_client = !subnetisaddr(his_net, &he.host_addr);
     he.protocol = b->his.proto;
     he.port = b->his.port;
// 格式化本地地址信息参数
     l = format_end(buf, sizeof(buf), &me, NULL, TRUE, LEMPTY);
     l += snprintf(buf + l, sizeof(buf) - l, "...");
// 格式化本地地址信息参数
     (void)format_end(buf + l, sizeof(buf) - l, &he, NULL, FALSE, LEMPTY);
     openswan_log("cannot respond to IPsec SA request"
  " because no connection is known for %s"
  , buf);
     return STF_FAIL + INVALID_ID_INFORMATION;
 }
 else if (p != c)
 {
// 两个连接不同
     /* We've got a better connection: it can support the
      * specified clients.  But it may need instantiation.
      */
     if (p->kind == CK_TEMPLATE)
     {
// 如果p是模板连接(动态连接)
  /* Yup, it needs instantiation.  How much?
   * Is it a Road Warrior connection (simple)
   * or is it an Opportunistic connection (needing gw validation)?
   */
  if (p->policy & POLICY_OPPO)
  {
// 如果要使用OE处理, 进行相关处理, 分析略
      /* Opportunistic case: delegation must be verified.
       * Here be dragons.
       */
      enum verify_oppo_step next_step;
      ip_address our_client, his_client;
      passert(subnetishost(our_net) && subnetishost(his_net));
      networkof(our_net, &our_client);
      networkof(his_net, &his_client);
      next_step = quick_inI1_outR1_process_answer(b, ac, p1st);
      if (next_step == vos_fail)
   return STF_FAIL + INVALID_ID_INFORMATION;
      /* short circuit: if peer's client is self,
       * accept that we've verified delegation in Phase 1
       */
      if (next_step == vos_his_client
      && sameaddr(&c->spd.that.host_addr, &his_client))
   next_step = vos_done;
      /* the second chunk: initiate the next DNS query (if any) */
      DBG(DBG_CONTROL,
   {
       char ours[SUBNETTOT_BUF];
       char his[SUBNETTOT_BUF];
       subnettot(&c->spd.this.client, 0, ours, sizeof(ours));
       subnettot(&c->spd.that.client, 0, his, sizeof(his));
       DBG_log("responding on demand from %s to %s new state: %s"
        , ours, his, verify_step_name[next_step]);
   });
      /* start next DNS query and suspend (if necessary) */
      if (next_step != vos_done)
   return quick_inI1_outR1_start_query(b, next_step);
      /* Instantiate inbound Opportunistic connection,
       * carrying over authenticated peer ID
       * and filling in a few more details.
       * We used to include gateways_from_dns, but that
       * seems pointless at this stage of negotiation.
       * We should record DNS sec use, if any -- belongs in
       * state during perhaps.
       */
      p = oppo_instantiate(p, &c->spd.that.host_addr, &c->spd.that.id
   , NULL, &our_client, &his_client);
  }
  else
  {
// 将连接实例化, 使用阶段1连接的对方地址和ID
      /* Plain Road Warrior:
       * instantiate, carrying over authenticated peer ID
       */
      p = rw_instantiate(p, &c->spd.that.host_addr,
           his_net,
           &c->spd.that.id);
  }
     }
#ifdef DEBUG
     /* temporarily bump up cur_debugging to get "using..." message
      * printed if we'd want it with new connection.
      */
     {
  lset_t old_cur_debugging = cur_debugging;
  set_debugging(cur_debugging | p->extra_debugging);
  DBG(DBG_CONTROL, DBG_log("using connection \"%s\"", p->name));
  set_debugging(old_cur_debugging);
     }
#endif
// 将阶段1用的连接更新为现在找到的连接
     c = p;
 }
 /* fill in the client's true ip address/subnet */
// 如果连接配置中对方子网未明确定义, 将其赋为明确值
 if (p->spd.that.has_client_wildcard)
 {
     p->spd.that.client = *his_net;
     p->spd.that.has_client_wildcard = FALSE;
 }
        /* fill in the client's true port */
// 如果连接配置中对方端口未明确定义, 将其赋为明确值
        if (p->spd.that.has_port_wildcard)
        {
            int port = htons(b->his.port);
 
            setportof(port, &p->spd.that.host_addr);
            setportof(port, &p->spd.that.client.addr);
 
            p->spd.that.port = b->his.port;
            p->spd.that.has_port_wildcard = FALSE;
        }
 
#ifdef VIRTUAL_IP
 else if (is_virtual_connection(c))
// 如果是虚拟连接(通常是×××客户端连接×××网关,虽然只有一台设备,但应用数据使用虚拟的
// 内部地址进行通信, 在外面封装外部地址, 而不是直接使用外部地址,这个内部地址通常可以
// 通过网关分配),更新内部地址
 {
     c->spd.that.client = *his_net;
     c->spd.that.virt = NULL;
     if (subnetishost(his_net) && addrinsubnet(&c->spd.that.host_addr, his_net))
  c->spd.that.has_client = FALSE;
 }
#endif
    }
    passert((p1st->st_policy & POLICY_PFS)==0 || p1st->st_pfs_group != NULL );
    /* now that we are sure of our connection, create our new state, and
     * do any asynchronous cryptographic operations that we may need to
     * make it all work.
     */
   
    /* now that we are sure of our connection, create our new state */
    {
// 复制ISAKMP SA状态,准备构造出第2阶段的状态, 用于描述IPSEC SA
 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.
  */
// 如果新状态所在连接不是刚才找到的连接c,将其释放,然后更新状态的连接
 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 */
// 消息ID
 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 */
// 本地和对方的ID和端口
 st->st_peeruserprotoid = b->his.proto;
 st->st_peeruserport = b->his.port;
 st->st_myuserprotoid = b->my.proto;
 st->st_myuserport = b->my.port;
// 状态类型设置为QUICK_R0
 st->st_state = STATE_QUICK_R0;
// 插入系统状态哈希链表
 insert_state(st); /* needs cookies, connection, and msgid */
 /* 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
// 需要NAT穿越时, 查找新端口
 if (p1st->hidden_variables.st_nat_traversal & NAT_T_DETECTED) {
     st->hidden_variables.st_nat_traversal = p1st->hidden_variables.st_nat_traversal;
     nat_traversal_change_port_lookup(md, md->st);
 }
 else {
     st->hidden_variables.st_nat_traversal = 0;
 }
 if ((st->hidden_variables.st_nat_traversal & NAT_T_DETECTED) &&
     (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATOA)) {
// 读取NAT内部地址
     nat_traversal_natoa_lookup(md);
 }
#endif
 passert(st->st_connection != NULL);
 passert(st->st_connection == c);
 /* process SA in */
 {
// 数据包中的SA载荷链表
     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().
      */
// 解析SA载荷
     st->st_pfs_group = &unset_group;
     RETURN_STF_FAILURE(parse_ipsec_sa_body(&in_pbs
         , &sapd->payload.sa
         , NULL
         , FALSE, st));
 }
 /* Ni in */
// 读取NONCE载荷
 RETURN_STF_FAILURE(accept_nonce(md, &st->st_ni, "Ni"));
 /* [ KE ] in (for PFS) */
// 读取PFS KE载荷
 RETURN_STF_FAILURE(accept_PFS_KE(md, &st->st_gi
      , "Gi", "Quick Mode I1"));
 passert(st->st_pfs_group != &unset_group);
 passert(st->st_connection != NULL);
 {
// 分配快速密钥交换结构
     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->st = st;
     qke->isakmp_sa = p1st;
     qke->md = md;
// 加密继续函数, 可能需要使用OE机制来认证对方
     qke->qke_pcrc.pcrc_func = quick_inI1_outR1_cryptocontinue;
     if (st->st_pfs_group != NULL) {
// 需要使用PFS, 由于需要重新协商密钥, 重新进行密钥交换
  e = build_ke(&qke->qke_pcrc, st, st->st_pfs_group, ci);
     } else {
// 这个和build_ke的区别在于没有用到st->st_pfs_group参数,其他都一样
  e = build_nonce(&qke->qke_pcrc, st, ci);
     }
 
     passert(st->st_connection != NULL);
     return e;
 }
    }
}

// 加密继续函数
static void
quick_inI1_outR1_cryptocontinue(struct pluto_crypto_req_cont *pcrc
         , struct pluto_crypto_req *r
         , err_t ugh)
{
// 快速密钥交换结构
    struct qke_continuation *qke = (struct qke_continuation *)pcrc;
// 状态
    struct state *const st = qke->st;
    stf_status e;
    DBG(DBG_CONTROLMORE
 , DBG_log("quick inI1_outR1: calculated ke+nonce, sending R1"));
    /* XXX should check out ugh */
    passert(ugh == NULL);
    passert(cur_state == NULL);
    passert(st != NULL);
    passert(st->st_connection != NULL);
// 设置当前状态
    set_cur_state(st); /* we must reset before exit */
    st->st_calculating=FALSE;
// 加密结束处理函数
    e = quick_inI1_outR1_cryptotail(qke, r);
    if(qke->md != NULL) {
// 如果消息摘要非空, 完成状态转换操作
 complete_state_transition(&qke->md, e);
 release_md(qke->md);
    }
// 复位当前状态
    reset_cur_state();
}

// 加密结束处理函数, 构造回应数据包
static stf_status
quick_inI1_outR1_cryptotail(struct qke_continuation *qke
      , struct pluto_crypto_req *r)
{
// 消息摘要
    struct msg_digest *md = qke->md;
// 状态
    struct state *st = md->st;
// 连接
    struct connection *c = st->st_connection;
// 输入数据包的ID载荷
    struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID];
// 输入数据包的SA载荷
    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 */
// 填充哈希载荷, 下一载荷为SA载荷
    START_HASH_PAYLOAD(md->rbody, ISAKMP_NEXT_SA);
    passert(st->st_connection == c);
    passert(st->st_connection != NULL);
    /* sa header is unchanged -- except for np */
// SA载荷的下一载荷是NONCE载荷
    sa.isasa_np = ISAKMP_NEXT_NONCE;
// 填充SA载荷
    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 */
    RETURN_STF_FAILURE(parse_ipsec_sa_body(&sapd->pbs
        , &sapd->payload.sa
        , &r_sa_pbs
        , FALSE, st));
    passert(st->st_pfs_group != &unset_group);
// 检查是否设置了PFS标志但没有进行PFS处理
    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 {msgid:%08x}", st->st_msgid);
    /**** finish reply packet: Nr [, KE ] [, IDci, IDcr ] ****/
   
    {
 int np;
 if(st->st_pfs_group != NULL) {
// 如果使用PFS, NONCE载荷的下一载荷为密钥交换载荷
     np = ISAKMP_NEXT_KE;
 } else if(id_pd != NULL) {
// 不需要PFS, 有ID载荷的话NONCE载荷的下一载荷为ID载荷
     np = ISAKMP_NEXT_ID;
 } else {
// 否则NONCE载荷的下一载荷为NONE, 数据包结束
     np = ISAKMP_NEXT_NONE;
 }

 /* Nr out */
// 填充NONCE载荷
 if (!ship_nonce(&st->st_nr, r, &md->rbody
   , np, "Nr"))
     return STF_INTERNAL_ERROR;
    }
   
    /* [ KE ] out (for PFS) */
    if (st->st_pfs_group != NULL) {
 stf_status stat;
// 填充KE载荷, 下一载荷为ID载荷或空
 if (!ship_KE(st, r, &st->st_gr
       , &md->rbody
       , id_pd != NULL? ISAKMP_NEXT_ID : ISAKMP_NEXT_NONE))
     return STF_INTERNAL_ERROR;
// 进行DH交换计算 
 stat = perform_dh_secret(st, RESPONDER, st->st_pfs_group->group);
 if(stat != STF_OK) {
     return stat;
 }
    }
   
    /* [ IDci, IDcr ] out */
    if  (id_pd != NULL) {
 struct isakmp_ipsec_id *p = (void *)md->rbody.cur; /* UGH! */
// 填充ID载荷    
 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 NAT_TRAVERSAL
// NAT穿越处理
    if ((st->hidden_variables.st_nat_traversal & NAT_T_DETECTED) &&
 (st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TRANSPORT) &&
 (c->spd.that.has_client)) {
 /** Remove client **/
 addrtosubnet(&c->spd.that.host_addr, &c->spd.that.client);
 c->spd.that.has_client = FALSE;
    }
#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.
     */
// 创建向内的IPSEC SA
    if (!install_inbound_ipsec_sa(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;
}
 
15.4 发起方收到响应方第一回应包, 发送第2初始包

/* Handle (the single) message from Responder in Quick Mode.
 * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] -->
 * HDR*, HASH(3)
 * (see RFC 2409 "IKE" 5.5)
 * Installs inbound and outbound IPsec SAs, routing, etc.
 */
stf_status
quick_inR1_outI2(struct msg_digest *md)
{
// 相关状态结构
    struct state *const st = md->st;
// 连接结构
    const struct connection *c = st->st_connection;
    /* HASH(2) in */
// 检查数据包哈希值是否正确
    CHECK_QUICK_HASH(md
 , quick_mode_hash12(hash_val, hash_pbs->roof, md->message_pbs.roof
     , st, &st->st_msgid, TRUE)
 , "HASH(2)", "Quick R1");
    /* SA in */
    {
 struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_SA];
// 读取SA载荷
 RETURN_STF_FAILURE(parse_ipsec_sa_body(&sa_pd->pbs
     , &sa_pd->payload.sa, NULL, TRUE, st));
    }
    /* Nr in */
// 读取NONCE载荷
    RETURN_STF_FAILURE(accept_nonce(md, &st->st_nr, "Nr"));
    /* [ KE ] in (for PFS) */
// 读取密钥交换载荷
    RETURN_STF_FAILURE(accept_PFS_KE(md, &st->st_gr, "Gr", "Quick Mode R1"));
    if (st->st_pfs_group != NULL) {
 stf_status stat;
// 如果需要PFS支持, 进行DH交换计算
 stat = perform_dh_secret(st, INITIATOR, st->st_pfs_group->group);
 if(stat != STF_OK) {
     return stat;
 }
    }
    /* [ IDci, IDcr ] in; these must match what we sent */
    {
// ID载荷链表
 struct payload_digest *const id_pd = md->chain[ISAKMP_NEXT_ID];
 if (id_pd != NULL)
 {
// ID载荷非空
     /* ??? we are assuming IPSEC_DOI */
     /* IDci (we are initiator) */
// 检查发起方IDci载荷
     if (!check_net_id(&id_pd->payload.ipsec_id, &id_pd->pbs
     , &st->st_myuserprotoid, &st->st_myuserport
     , &st->st_connection->spd.this.client
     , "our client"))
  return STF_FAIL + INVALID_ID_INFORMATION;
     /* IDcr (responder is peer) */
// 检查响应方IDcr载荷
     if (!check_net_id(&id_pd->next->payload.ipsec_id, &id_pd->next->pbs
     , &st->st_peeruserprotoid, &st->st_peeruserport
     , &st->st_connection->spd.that.client
     , "peer client"))
  return STF_FAIL + INVALID_ID_INFORMATION;
 }
 else
 {
// ID载荷为空
     /* no IDci, IDcr: we must check that the defaults match our proposal */
// 检查地址参数是否匹配
     if (!subnetisaddr(&c->spd.this.client, &c->spd.this.host_addr)
  || !subnetisaddr(&c->spd.that.client, &c->spd.that.host_addr))
     {
  loglog(RC_LOG_SERIOUS, "IDci, IDcr payloads missing in message"
      " but default does not match proposal");
  return STF_FAIL + INVALID_ID_INFORMATION;
     }
 }
    }
#ifdef NAT_TRAVERSAL
 if ((st->hidden_variables.st_nat_traversal & NAT_T_DETECTED) &&
     (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATOA)) {
// 读取NAT内部地址
     nat_traversal_natoa_lookup(md);
 }
#endif
    /* ??? We used to copy the accepted proposal into the state, but it was
     * never used.  From sa_pd->pbs.start, length pbs_room(&sa_pd->pbs).
     */
    /**************** build reply packet HDR*, HASH(3) ****************/
// 构建回应数据包
    /* HDR* out done */
    /* HASH(3) out -- since this is the only content, no passes needed */
    {
 u_char /* set by START_HASH_PAYLOAD: */
     *r_hashval, /* where in reply to jam hash value */
     *r_hash_start; /* start of what is to be hashed */
// 构造哈希载荷, 仅此载荷, 下一载荷类型为NONE
 START_HASH_PAYLOAD(md->rbody, ISAKMP_NEXT_NONE);
// 计算哈希值
 (void)quick_mode_hash3(r_hashval, st);
    }
    /* Derive new keying material */
// 计算新的加密信息
    compute_keymats(st);
    /* Tell the kernel to establish the inbound, outbound, and routing part
     * of the new 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.
     */
// 通知内核建立IPSEC SA, 包括向内和向外两个方向的SA
    if (!install_ipsec_sa(st, TRUE))
 return STF_INTERNAL_ERROR;
    /* encrypt message, except for fixed part of header */
// 加密数据包
    if (!encrypt_message(&md->rbody, st))
    {
 delete_ipsec_sa(st, FALSE);
 return STF_INTERNAL_ERROR; /* ??? we may be partly committed */
    }
    {
// 输出IPSEC SA建立信息
      DBG(DBG_CONTROLMORE, DBG_log("inR1_outI2: instance %s[%ld], setting newest_ipsec_sa to #%ld (was #%ld) (spd.eroute=#%ld)"
          , st->st_connection->name
          , st->st_connection->instance_serial
          , st->st_serialno
          , st->st_connection->newest_ipsec_sa
          , st->st_connection->spd.eroute_owner));
    }
// 状态相关连接的最新IPSEC SA序号
    st->st_connection->newest_ipsec_sa = st->st_serialno;
    /* note (presumed) success */
// 网关最新IPSEC工作时间
    if (c->gw_info != NULL)
 c->gw_info->key->last_worked_time = now();
    /* If we have dpd delay and dpdtimeout set, then we are doing DPD
 on this conn, so initialize it */
    if (st->st_connection->dpd_delay && st->st_connection->dpd_timeout) {
// 如果需要使用DPD, 启动DPD处理
 if(dpd_init(st) != STF_OK) {
     delete_ipsec_sa(st, FALSE);
     return STF_FAIL;
 }
    }
    return STF_OK;
}
 

15.5 响应方接收到发起方的第2个数据包

/* Handle last message of Quick Mode.
 * HDR*, HASH(3) -> done
 * (see RFC 2409 "IKE" 5.5)
 * Installs outbound IPsec SAs, routing, etc.
 */
stf_status
quick_inI2(struct msg_digest *md)
{
// 相关状态结构
    struct state *const st = md->st;
    /* HASH(3) in */
// 检查数据包哈希值
    CHECK_QUICK_HASH(md, quick_mode_hash3(hash_val, st)
 , "HASH(3)", "Quick I2");
    /* Tell the kernel to establish the outbound and routing part of the new SA
     * (the previous state established inbound)
     * (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.
     */
// 通知内核建立IPSEC SA, 包括向内和向外两个方向的SA
    if (!install_ipsec_sa(st, FALSE))
 return STF_INTERNAL_ERROR;
    {
// 输出IPSEC SA建立信息
      DBG(DBG_CONTROLMORE, DBG_log("inI2: instance %s[%ld], setting newest_ipsec_sa to #%ld (was #%ld) (spd.eroute=#%ld)"
          , st->st_connection->name
          , st->st_connection->instance_serial
          , st->st_serialno
          , st->st_connection->newest_ipsec_sa
          , st->st_connection->spd.eroute_owner));
    }
   
// 状态相关连接的最新IPSEC SA序号
    st->st_connection->newest_ipsec_sa = st->st_serialno;
// 更新状态初始化向量
    update_iv(st); /* not actually used, but tidy */
    /* note (presumed) success */
    {
 struct gw_info *gw = st->st_connection->gw_info;
// 网关最新IPSEC工作时间
 if (gw != NULL)
     gw->key->last_worked_time = now();
    }
    /* If we have dpd delay and dpdtimeout set, then we are doing DPD
 on this conn, so initialize it */
    if(st->st_connection->dpd_delay && st->st_connection->dpd_timeout) {
// 如果需要使用DPD, 启动DPD处理
 if(dpd_init(st) != STF_OK) {
     delete_ipsec_sa(st, FALSE);
     return STF_FAIL;
 }
    }
    return STF_OK;
}
 
...... 待续 ......