本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn:
[email protected]
来源: 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");
}
}
...... 待续 ......