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

12.4.4 状态转换处理完成
 
/* complete job started by the state-specific state transition function */
// 完成状态转换后的收尾处理工作
void
complete_state_transition(struct msg_digest **mdp, stf_status result)
{
// 消息摘要
    struct msg_digest *md = *mdp;
// 微码结构
    const struct state_microcode *smc = md->smc;
// 起始状态
    enum state_kind from_state = md->from_state;
    struct state *st;
// 设置为当前状态
    cur_state = st = md->st; /* might have changed */
    /* If state has DPD support, import it */
// 如果支持DPD, 设置DPD参数
    if( st && md->dpd && st->hidden_variables.st_dpd != md->dpd) {
 DBG(DBG_DPD, DBG_log("peer supports dpd"));
 st->hidden_variables.st_dpd = md->dpd;
 if(st->st_connection->dpd_delay && st->st_connection->dpd_timeout) {
     /* Set local policy for DPD to be on */
     st->hidden_variables.st_dpd_local = 1;
     DBG(DBG_DPD, DBG_log("enabling sending dpd"));
 }
    }
    /* advance the state */
    DBG(DBG_CONTROL
 , DBG_log("complete state transition with %s"
    , enum_name(&stfstatus_name, result)));
    /*
     * we can only be in calculating state if state is ignore,
     * or suspended.
     */
    passert(result == STF_IGNORE || result == STF_SUSPEND || st->st_calculating==FALSE);
// result为状态转移函数的处理结果
    switch (result)
    {
// 忽略
 case STF_IGNORE:
     break;

        case STF_INLINE:         /* this is second time through complete
      * state transition, so the MD has already
      * been freed.
0      */
     *mdp = NULL;
     break;
// 未完成
 case STF_SUSPEND:
     /* update the previous packet history */
     if(md->packet_pbs.start) {
                 update_retransmit_history(st, md);
            }
     /* the stf didn't complete its job: don't relase md */
     *mdp = NULL;
     break;
// 成功
 case STF_OK:
     /* advance the state */
     openswan_log("transition from state %s to state %s"
                 , enum_name(&state_names, from_state)
                 , enum_name(&state_names, smc->next_state));
// 状态结构的状态类型转换为下一状态
     st->st_state = smc->next_state;
     /* Delete previous retransmission event.
      * New event will be scheduled below.
      */
// 删除状态相关事件
     delete_event(st);
     /* update the previous packet history */
// 更新数据包历史记录
     update_retransmit_history(st, md);
     /* free previous transmit packet */
// 释放先前发送的数据包的空间
     freeanychunk(st->st_tpacket);
     /* if requested, send the new reply packet */
// 如果属于回应包
     if (smc->flags & SMF_REPLY)
     {
  char buf[ADDRTOT_BUF];
  DBG(DBG_CONTROL
      , DBG_log("sending reply packet to %s:%u (from port=%d)"
         , (addrtot(&st->st_remoteaddr
      , 0, buf, sizeof(buf)), buf)
         , st->st_remoteport
         , st->st_interface->port));
// 完成输出的数据包的结构
  close_output_pbs(&md->reply);   /* good form, but actually a no-op */
// 将构造出的数据拷贝到数据缓冲区用于发送
  clonetochunk(st->st_tpacket, md->reply.start
      , pbs_offset(&md->reply), "reply packet");
#ifdef NAT_TRAVERSAL
// 修改NAT时的端口
  if (nat_traversal_enabled) {
      nat_traversal_change_port_lookup(md, md->st);
  }
#endif
  /* actually send the packet
   * Note: this is a great place to implement "impairments"
   * for testing purposes.  Suppress or duplicate the
   * send_packet call depending on st->st_state.
   */
// 发送回应包
  send_packet(st, enum_name(&state_names, from_state), TRUE);
     }
     /* Schedule for whatever timeout is specified */
     {
  time_t delay;
// 超时类型
  enum event_type kind = smc->timeout_event;
  bool agreed_time = FALSE;
// 状态相关连接
  struct connection *c = st->st_connection;
// 以下根据超时类型确定超时时间
  switch (kind)
  {
  case EVENT_RETRANSMIT: /* Retransmit packet */
// 重发超时(10秒),固定值
      delay = EVENT_RETRANSMIT_DELAY_0;
      break;
  case EVENT_SA_REPLACE: /* SA replacement event */
// SA替换
      if (IS_PHASE1(st->st_state))
      {
// 如果是阶段1的状态
   /* Note: we will defer to the "negotiated" (dictated)
    * lifetime if we are POLICY_DONT_REKEY.
    * This allows the other side to dictate
    * a time we would not otherwise accept
    * but it prevents us from having to initiate
    * rekeying.  The negative consequences seem
    * minor.
    */
// 超时为IKE生命期,可在配置文件中定义
   delay = c->sa_ike_life_seconds;
   if ((c->policy & POLICY_DONT_REKEY)
   || delay >= st->st_oakley.life_seconds)
   {
       agreed_time = TRUE;
       delay = st->st_oakley.life_seconds;
   }
      }
      else
      {
   /* Delay is min of up to four things:
    * each can limit the lifetime.
    */
// 否则为IPSEC生命期,可在配置文件中定义
   delay = c->sa_ipsec_life_seconds;
   if (st->st_ah.present
   && delay >= st->st_ah.attrs.life_seconds)
   {
       agreed_time = TRUE;
       delay = st->st_ah.attrs.life_seconds;
   }
   if (st->st_esp.present
   && delay >= st->st_esp.attrs.life_seconds)
   {
       agreed_time = TRUE;
       delay = st->st_esp.attrs.life_seconds;
   }
   if (st->st_ipcomp.present
   && delay >= st->st_ipcomp.attrs.life_seconds)
   {
       agreed_time = TRUE;
       delay = st->st_ipcomp.attrs.life_seconds;
   }
      }
      /* By default, we plan to rekey.
       *
       * If there isn't enough time to rekey, plan to
       * expire.
       *
       * If we are --dontrekey, a lot more rules apply.
       * If we are the Initiator, use REPLACE_IF_USED.
       * If we are the Responder, and the dictated time
       * was unacceptable (too large), plan to REPLACE
       * (the only way to ratchet down the time).
       * If we are the Responder, and the dictated time
       * is acceptable, plan to EXPIRE.
       *
       * Important policy lies buried here.
       * For example, we favour the initiator over the
       * responder by making the initiator start rekeying
       * sooner.  Also, fuzz is only added to the
       * initiator's margin.
       *
       * Note: for ISAKMP SA, we let the negotiated
       * time stand (implemented by earlier logic).
       */
      if (agreed_time
      && (c->policy & POLICY_DONT_REKEY))
      {
   kind = (smc->flags & SMF_INITIATOR)
       ? EVENT_SA_REPLACE_IF_USED
       : EVENT_SA_EXPIRE;
      }
      if (kind != EVENT_SA_EXPIRE)
      {
   unsigned long marg = c->sa_rekey_margin;
   if (smc->flags & SMF_INITIATOR)
       marg += marg
    * c->sa_rekey_fuzz / 100.E0
    * (rand() / (RAND_MAX + 1.E0));
   else
       marg /= 2;
   if ((unsigned long)delay > marg)
   {
       delay -= marg;
       st->st_margin = marg;
   }
   else
   {
       kind = EVENT_SA_EXPIRE;
   }
      }
      break;
  case EVENT_NULL: /* non-event */
  case EVENT_REINIT_SECRET: /* Refresh cookie secret */
  default:
      bad_case(kind);
  }
// 调度事件
  event_schedule(kind, delay, st);
     }
     /* tell whack and log of progress */
// 通知whack当前的处理情况
     {
// 当前状态的描述信息
  const char *story = enum_name(&state_stories, st->st_state);
  enum rc_type w = RC_NEW_STATE + st->st_state;
  char sadetails[128];
  passert(st->st_state < STATE_IKE_ROOF);
  
  sadetails[0]='\0';
  /* document IPsec SA details for admin's pleasure */
// 如果IPSEC SA已经建立(协商完成)
  if(IS_IPSEC_SA_ESTABLISHED(st->st_state))
  {
      char *b = sadetails;
      const char *ini = " {";
      const char *fin = "";
      /* -1 is to leave space for "fin" */
      if(st->st_esp.present)
      {
   const char *natinfo="";
   if((st->st_connection->spd.that.host_port != IKE_UDP_PORT
       && st->st_connection->spd.that.host_port != 0)
      || st->st_connection->forceencaps) {
       natinfo="/NAT";
   }
// 输出NAT信息, ESP的SPI信息, XFRM信息和密钥参数
   snprintf(b, sizeof(sadetails)-(b-sadetails)-1
     , "%sESP%s=>0x%08x <0x%08x xfrm=%s_%d-%s"
     , ini
     , natinfo
     , ntohl(st->st_esp.attrs.spi)
     , ntohl(st->st_esp.our_spi)
     , enum_show(&esp_transformid_names, st->st_esp.attrs.transid)+strlen("ESP_")
     , st->st_esp.attrs.key_len
     , enum_show(&auth_alg_names, st->st_esp.attrs.auth)+strlen("AUTH_ALGORITHM_"));
   ini = " ";
   fin = "}";
      }
      /* advance b to end of string */
      b = b + strlen(b);
// 输出AH协议相关信息     
      if(st->st_ah.present)
      {
   snprintf(b, sizeof(sadetails)-(b-sadetails)-1
     , "%sAH=>0x%08x <0x%08x"
     , ini
     , ntohl(st->st_ah.attrs.spi)
     , ntohl(st->st_ah.our_spi));
   ini = " ";
   fin = "}";
      }
      /* advance b to end of string */
      b = b + strlen(b);
// 输出IPCOMP相关信息     
      if(st->st_ipcomp.present)
      {
   snprintf(b, sizeof(sadetails)-(b-sadetails)-1
     , "%sIPCOMP=>0x%08x <0x%08x"
     , ini
     , ntohl(st->st_ipcomp.attrs.spi)
     , ntohl(st->st_ipcomp.our_spi));
   ini = " ";
   fin = "}";
      }
      /* advance b to end of string */
      b = b + strlen(b);
#ifdef NAT_TRAVERSAL     
      {
   char oa[ADDRTOT_BUF];
// 输出NAT穿越时的地址信息
   strcpy(oa, "none");
   if(!isanyaddr(&st->hidden_variables.st_nat_oa)) {
     addrtot(&st->hidden_variables.st_nat_oa, 0
      , oa, sizeof(oa));
   }
   snprintf(b, sizeof(sadetails)-(b-sadetails)-1
     , "%sNATOA=%s"
     , ini, oa);
   ini = " ";
   fin = "}";
      }
      {
   char oa[ADDRTOT_BUF+sizeof(":00000")];
   strcpy(oa, "none");
   if(!isanyaddr(&st->hidden_variables.st_natd)) {
       char oa2[ADDRTOT_BUF];
       addrtot(&st->hidden_variables.st_natd, 0
        , oa2, sizeof(oa2));
       snprintf(oa, sizeof(oa)
         , "%s:%d", oa2, st->st_remoteport);
   }
   snprintf(b, sizeof(sadetails)-(b-sadetails)-1
     , "%sNATD=%s"
     , ini, oa);
   ini = " ";
   fin = "}";
      }
#endif
      /* advance b to end of string */
      b = b + strlen(b);
// 输出DPD信息     
      snprintf(b, sizeof(sadetails)-(b-sadetails)-1
        , "%sDPD=%s"
        , ini
        , st->hidden_variables.st_dpd_local ?
        "enabled" : "none");
      ini = " ";
      fin = "}";
      strcat(b, fin);
// 如果ISAKMP SA建立(第一阶段协商完成)而且要求记录
  } else if(IS_ISAKMP_SA_ESTABLISHED(st->st_state)
     && !st->hidden_variables.st_logged_p1algos) {
      /* document ISAKMP SA details for admin's pleasure */
      char *b = sadetails;
      passert(st->st_oakley.encrypter != NULL);
      passert(st->st_oakley.hasher != NULL);
      passert(st->st_oakley.group != NULL);
// 输出认证,加密,伪随机算法,DH组信息
      snprintf(b, sizeof(sadetails)-(b-sadetails)-1
        , " {auth=%s cipher=%s_%d prf=%s group=modp%d}"
        , enum_show(&oakley_auth_names, st->st_oakley.auth)
        , st->st_oakley.encrypter->common.name
        , st->st_oakley.enckeylen
        , st->st_oakley.hasher->common.name
        , (int)st->st_oakley.group->bytes*8);
      st->hidden_variables.st_logged_p1algos = TRUE;
  }
// 如果阶段1或阶段2的SA成功建立
  if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
      || IS_IPSEC_SA_ESTABLISHED(st->st_state))
  {
      /* log our success */
// 发送给whack成功信息
      w = RC_SUCCESS;
  }
  /* tell whack and logs our progress */
// 发送给whack
  loglog(w
      , "%s: %s%s"
      , enum_name(&state_names, st->st_state)
      , story, sadetails);
     }
     /*
      * make sure that a DPD event gets created for a new phase 1
      * SA.
      */
// 如果ISAKMP SA已经建立, 而且配置了DPD参数
     if(IS_ISAKMP_SA_ESTABLISHED(st->st_state)) {
  if(st->st_connection->dpd_delay>0
     && st->st_connection->dpd_timeout>0) {
// 初始化DPD
      (void)dpd_init(st);
  }
     }
     
#ifdef XAUTH
     /* Special case for XAUTH server */
// 如果本地是XAUTH服务器
     if(st->st_connection->spd.this.xauth_server) {
// XAUTH还未完成, 而且ISAKMP SA已经建立(第1阶段完成)
       if((st->st_oakley.xauth != 0)
   && IS_ISAKMP_SA_ESTABLISHED(st->st_state))
  {
    openswan_log("XAUTH: Sending XAUTH Login/Password Request");
// 发送XAUTH请求
    xauth_send_request(st);
    break;
  }
     }
     /*
      * for XAUTH client, we are also done, because we need to
      * stay in this state, and let the server query us
      */
// 如果还没到快速模式,
     if(!IS_QUICK(st->st_state)
// 本地是XAUTH客户端
        && st->st_connection->spd.this.xauth_client
// 认证还未完成
        && !st->hidden_variables.st_xauth_client_done) {
// 打印未进行认证信息,中断
       DBG(DBG_CONTROL, DBG_log("XAUTH client is not yet authenticated"));
       break;
     }
#endif
#ifdef MODECFG
// 模式配置
     /*
      * when talking to some vendors, we need to initiate a mode
      * cfg request to get challenged, but there is also an
      * override in the form of a policy bit.
      */
     DBG(DBG_CONTROL
  , DBG_log("modecfg pull: %s policy:%s %s"
     , (st->quirks.modecfg_pull_mode
        ? "quirk-poll" : "noquirk")
     , (st->st_connection->policy & POLICY_MODECFG_PULL)
     ? "pull" : "push"
     , (st->st_connection->spd.this.modecfg_client
        ? "modecfg-client" :"not-client")));
// 如果本地是模式配置的客户端    
     if(st->st_connection->spd.this.modecfg_client
// ISAKMP SA已经建立
        && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
// 配置是pull模式
        && (st->quirks.modecfg_pull_mode
     || st->st_connection->policy & POLICY_MODECFG_PULL)
// modecfg操作未启动
        && !st->hidden_variables.st_modecfg_started) {
  DBG(DBG_CONTROL
      , DBG_log("modecfg client is starting due to %s"
         , st->quirks.modecfg_pull_mode ? "quirk" : "policy"));
// 发送配置请求信息
  modecfg_send_request(st);
  break;
     }
     /* Should we set the peer's IP address regardless? */
// 如果本地是modecfg服务器
     if(st->st_connection->spd.this.modecfg_server
// ISAKMP SA已经建立
        && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
// modecfg未完成
        && !st->hidden_variables.st_modecfg_vars_set
// 非PULL模式
        && !(st->st_connection->policy & POLICY_MODECFG_PULL))
     {
// 状态更新为MODE_CFG_R1
      st->st_state = STATE_MODE_CFG_R1;
      set_cur_state(st);
      openswan_log("Sending MODE CONFIG set");
// modecfg启动
      modecfg_start_set(st);
      break;
     }
     /* If we are the responder and the client is in "Contivity mode",
        we need to initiate Quick mode */
// 非SMF_INITIATOR
     if (!(smc->flags & SMF_INITIATOR)
// MODECFG完成
  && IS_MODE_CFG_ESTABLISHED(st->st_state)
// 带VID_NORTEL标志
  && (st->st_seen_vendorid & LELEM(VID_NORTEL)))
     {
// 状态改为主模式结束
  st->st_state = STATE_MAIN_R3;     /* ISAKMP is up... */
         set_cur_state(st);
// 进入快速模式
         quick_outI1(st->st_whack_sock, st, st->st_connection, st->st_connection->policy, 1, SOS_NOBODY);
  break;
     }    
     /* wait for modecfg_set */
// 如果本地是modecfg客户端
     if(st->st_connection->spd.this.modecfg_client
// ISAKMP SA已经建立
        && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
// modecfg参数还没有设置
        && !st->hidden_variables.st_modecfg_vars_set)
       {
// 等待
    DBG(DBG_CONTROL
        , DBG_log("waiting for modecfg set from server"));
    break;
       }
#endif
     DBG(DBG_CONTROL
  , DBG_log("phase 1 is done, looking for phase 1 to unpend"));
// 如果有PENDING_P2标志
     if (smc->flags & SMF_RELEASE_PENDING_P2)
     {
  /* Initiate any Quick Mode negotiations that
   * were waiting to piggyback on this Keying Channel.
   *
   * ??? there is a potential race condition
   * if we are the responder: the initial Phase 2
   * message might outrun the final Phase 1 message.
   * I think that retransmission will recover.
   *
   * The same race condition exists if we are in aggressive
   * mode as the final phase 1 message might not have
   * been received yet, and in fact, we might even have
   * a situation where the responder does not accept our
   * our identification.
   */
// 释放状态相关的pending结构
  unpend(st);
     }
// 如果建立了ISAKMP SA或IPSEC SA, 释放whack套接口
     if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
     || IS_IPSEC_SA_ESTABLISHED(st->st_state))
  release_whack(st);
     if (IS_QUICK(st->st_state))
       break;
     break;
 case STF_INTERNAL_ERROR:
// 状态转换过程中出现了内部错误
     /* update the previous packet history */
// 更新历史记录
     update_retransmit_history(st, md);
// 向whack输出错误信息
     whack_log(RC_INTERNALERR + md->note
  , "%s: internal error"
  , enum_name(&state_names, st->st_state));
     DBG(DBG_CONTROL,
  DBG_log("state transition function for %s had internal error"
      , enum_name(&state_names, from_state)));
     break;
        case STF_TOOMUCHCRYPTO:
// 加密太多
     /* well, this should never happen during a whack, since
      * a whack will always force crypto.
      */
            st->st_suspended_md = NULL;
     openswan_log("message in state %s ignored due to cryptographic overload"
    , enum_name(&state_names, from_state));
     break;
        case STF_FATAL:
// 致命错误, 释放状态
     /* update the previous packet history */
     update_retransmit_history(st, md);
     whack_log(RC_FATAL
        , "encountered fatal error in state %s"
        , enum_name(&state_names, st->st_state));
// 删除状态相关事件
     delete_event(st);
// 释放pending
     release_pending_whacks(st, "fatal error");
// 删除状态
     delete_state(st);
     break;
 default: /* a shortcut to STF_FAIL, setting md->note */
// 缺省的都算STF_FAIL失败
     passert(result > STF_FAIL);
     md->note = result - STF_FAIL;
     result = STF_FAIL;
     /* FALL THROUGH ... */
 case STF_FAIL:
// 操作失败
     /* As it is, we act as if this message never happened:
      * whatever retrying was in place, remains in place.
      */
     whack_log(RC_NOTIFICATION + md->note
  , "%s: %s", enum_name(&state_names, st->st_state)
  , enum_name(&ipsec_notification_names, md->note));
// 发送通知
     if(md->note > 0) {
  SEND_NOTIFICATION(md->note);
     }
     DBG(DBG_CONTROL,
  DBG_log("state transition function for %s failed: %s"
   , enum_name(&state_names, from_state)
   , enum_name(&ipsec_notification_names, md->note)));
// 如果状态非空, 而且已经完成了阶段1的初始化
     if(st!=NULL && IS_PHASE1_INIT(st->st_state)) {
// 释放状态相关事件
  delete_event(st);
  release_whack(st);
            }
// 如果是快速模式的状态,删除状态
     if(st!=NULL && IS_QUICK(st->st_state)) {
  delete_state(st);
     }
            break;
    }
}
 
// 更新重发历史数据
static void update_retransmit_history(struct state *st, struct msg_digest *md)
{
 /*
  * replace previous receive packet with latest, to update
  * our notion of a retransmitted packet. This is important
  * to do, even for failing transitions, and suspended transitions
  * because the sender may well retransmit their request.
  */
// 释放数据包缓冲区空间
 pfreeany(st->st_rpacket.ptr);
 
 if (md->encrypted)
 {
// 如果是加密数据,数据缓冲指针指向原始数据
  /* if encrypted, duplication already done */
  st->st_rpacket = md->raw_packet;
  md->raw_packet.ptr = NULL;
 }
 else
 {
// 明文数据, 克隆拷贝数据
  clonetochunk(st->st_rpacket
        , md->packet_pbs.start
        , pbs_room(&md->packet_pbs), "raw packet");
 }

...... 待续 ......