主模式第四包:main_inI2_outR2

1. 序言

main_inI2_outR2()函数是ISAKMP协商过程中第四包的核心处理函数的入口,同时在此处理流程中已经获取到足够的隧道信息,可以生成需要的密钥信息。这里我们主要说明main_inI2_outR2的函数调用关系、处理流程以及对源码的注释分析,关于main_inI2_outR2的上下文环境暂不叙述,留给后面的文章进行更新。

ISAKMP协商报文的处理流程都比较复杂,此函数在协商的报文处理函数中比较复杂的,因此个人学习期间难免有遗漏和理解错误的地方,请大家多多批评指正。

对于源码的学习,我并没有把每一行进行备注,而是将自己认为的关键点做了注释或者标注。

2.函数调用关系

暂略。(此流程调用比较多,后面会在补充上)

3. 第四个报文流程图

第四个报文处理流程大致可以划分为四类功能:

  • 解析收到的第三个报文内容
  • 生成随机数(本段的KE和Nonce值)
  • 构造应答报文
  • 使用DH算法制作秘钥(三把钥匙)

整理的处理流程如下:

个人觉得这个流程做的还是很清晰和准确的(原谅我没有上色)

4. main_inI2_outR2源码分析

stf_status
main_inI2_outR2(struct msg_digest *md)
{
struct state *const st = md->st;
pb_stream *keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; /* if we are already processing a packet on this st, we will be unable
* to start another crypto operation below */
if (is_suspended(st)) {/*为了方式该流程处理时间过长导致对端超时重发*/
openswan_log("%s: already processing a suspended cyrpto operation "
"on this SA, duplicate will be dropped.", __func__);
return STF_TOOMUCHCRYPTO;
} /* KE in *//*从报文中获取KE载荷,并填充到st->st_gi上*/
RETURN_STF_FAILURE(accept_KE(&st->st_gi, "Gi"
, st->st_oakley.group, keyex_pbs)); /* Ni in *//*从报文中获取Nonce载荷,并填充到st->st_ni上*/
RETURN_STF_FAILURE(accept_v1_nonce(md, &st->st_ni, "Ni")); /* decode certificate requests *//*解析证书载荷,以链表的方式存储在st->st_connection->requested_ca*/
ikev1_decode_cr(md, &st->st_connection->ikev1_requested_ca_names); if(st->st_connection->requested_ca != NULL)
{
st->hidden_variables.st_got_certrequest = TRUE;
} #ifdef NAT_TRAVERSAL
DBG(DBG_NATT
, DBG_log("inI2: checking NAT-T: %d and %d"
, nat_traversal_enabled
, st->hidden_variables.st_nat_traversal)); if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATD) {
DBG(DBG_NATT, DBG_log(" NAT_T_WITH_NATD detected"));
nat_traversal_natd_lookup(md);/*根据哈希值确定是否经过NAT;状态上的NAT-T标志在此处做的修改*/
}
if (st->hidden_variables.st_nat_traversal) {/*打印NAT-T、端口浮动相关信息*/
nat_traversal_show_result(st->hidden_variables.st_nat_traversal
, md->sender_port);
}
if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_KA) {
DBG(DBG_NATT, DBG_log(" NAT_T_WITH_KA detected"));
nat_traversal_new_ka_event();/*添加NAT-T的保活事件*/
}
#endif {
struct ke_continuation *ke = alloc_thing(struct ke_continuation
, "inI2_outR2 KE"); ke->md = md;
set_suspended(st, md); passert(st->st_sec_in_use == FALSE);
pcrc_init(&ke->ke_pcrc);
ke->ke_pcrc.pcrc_func = main_inI2_outR2_continue;
return build_ke(&ke->ke_pcrc, st
, st->st_oakley.group, st->st_import);
}
}

5. nat_traversal_natd_lookup源码分析

/*检查是否需要经过NAT-T*/
void nat_traversal_natd_lookup(struct msg_digest *md)
{
unsigned char hash_me[MAX_DIGEST_LEN];
unsigned char hash_him[MAX_DIGEST_LEN];
struct payload_digest *p;
struct state *st = md->st;
bool found_me = FALSE;
bool found_him= FALSE;
int i; passert(st);
passert(md->iface);
passert(st->st_oakley.prf_hasher); /** Count NAT-D **/
for (p = md->chain[ISAKMP_NEXT_NATD_RFC], i=0;
p != NULL;
p = p->next, i++);/*统计NAT-D的数量*/ /**
* We need at least 2 NAT-D (1 for us, many for peer)
*/
if (i < 2) {
loglog(RC_LOG_SERIOUS,
"NAT-Traversal: Only %d NAT-D - Aborting NAT-Traversal negotiation", i);
st->hidden_variables.st_nat_traversal = 0;
return;
} /**
* First one with my IP & port
*/
_natd_hash(st->st_oakley.prf_hasher, hash_me
, st->st_icookie, st->st_rcookie
, &(md->iface->ip_addr)
, ntohs(md->iface->port)); /**
* The others with sender IP & port
*/
_natd_hash(st->st_oakley.prf_hasher, hash_him
, st->st_icookie, st->st_rcookie
, &(md->sender), ntohs(md->sender_port)); for (p = md->chain[ISAKMP_NEXT_NATD_RFC], i=0;
p != NULL && (!found_me || !found_him);
p = p->next)
{
DBG(DBG_NATT,
DBG_log("NAT_TRAVERSAL hash=%d (me:%d) (him:%d)"
, i, found_me, found_him);
DBG_dump("expected NAT-D(me):", hash_me,
st->st_oakley.prf_hasher->hash_digest_len);
DBG_dump("expected NAT-D(him):", hash_him,
st->st_oakley.prf_hasher->hash_digest_len);
DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs));
); if ( (pbs_left(&p->pbs) == st->st_oakley.prf_hasher->hash_digest_len)
&& (memcmp(p->pbs.cur, hash_me
, st->st_oakley.prf_hasher->hash_digest_len)==0))
{
found_me = TRUE;/*本端未经过NAT*/
} if ( (pbs_left(&p->pbs) == st->st_oakley.prf_hasher->hash_digest_len)
&& (memcmp(p->pbs.cur, hash_him
, st->st_oakley.prf_hasher->hash_digest_len)==0))
{
found_him = TRUE;/*对端未经过NAT*/
} i++;
} DBG(DBG_NATT,
DBG_log("NAT_TRAVERSAL hash=%d (me:%d) (him:%d)"
, i, found_me, found_him)); if(!found_me) {
st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME);/*本端经过NAT*/
st->hidden_variables.st_natd = md->sender;
} memset(&st->hidden_variables.st_natd,0,sizeof(st->hidden_variables.st_natd));
anyaddr(AF_INET, &st->hidden_variables.st_natd); if(!found_him) {
st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER);/*对端经过NAT*/
st->hidden_variables.st_natd = md->sender;
} if(st->st_connection->forceencaps) {/*如果需要强制使用UDP封装双方都需要NAT-D*/
DBG(DBG_NATT,
DBG_log("NAT_TRAVERSAL forceencaps enabled")); st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER);
st->hidden_variables.st_nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME);
st->hidden_variables.st_natd = md->sender;
}
}

6. build_ke源码分析

stf_status build_ke(struct pluto_crypto_req_cont *cn
, struct state *st
, const struct oakley_group_desc *group
, enum crypto_importance importance)
{
struct pluto_crypto_req rd;
struct pluto_crypto_req *r = &rd;
err_t e;
bool toomuch = FALSE;
/*初始化加密请求*/
pcr_init(r, pcr_build_kenonce, importance);
r->pcr_d.kn.oakley_group = group->group; cn->pcrc_serialno = st->st_serialno;
/*申请制作加密材料请求*/
e= send_crypto_helper_request(r, cn, &toomuch); if(e != NULL) {/*加密失败*/
loglog(RC_LOG_SERIOUS, "can not start crypto helper: %s", e);
if(toomuch) {
return STF_TOOMUCHCRYPTO;
} else {
return STF_FAIL;
}
} else if(!toomuch) {/*加密任务繁忙,先挂起等待再次调度*/
st->st_calculating = TRUE;
delete_event(st);
event_schedule(EVENT_CRYPTO_FAILED, EVENT_CRYPTO_FAILED_DELAY, st);
return STF_SUSPEND;
} else {
/* we must have run the continuation directly, so
* complete_v1_state_transition already got called.
* 由于我们已经手动执行了main_inR1_outI2_continue(),该函数最终会调用到complete_v1_state_transition
* 因此在(process_v1_state_xxx流程中不必再此执行状态转换函数。因此返回STF_INLINE,当再次到complete_v1_state_transition
*判断返回值为它,则不再执行此函数。)
*/
return STF_INLINE;
}
}

7. main_inI2_outR2_continue源码分析

static void
main_inI2_outR2_continue(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r
, err_t ugh)
{
struct ke_continuation *ke = (struct ke_continuation *)pcrc;
struct msg_digest *md = ke->md;
struct state *const st = md->st;
stf_status e;
... ...
set_suspended(st, NULL); /* no longer connected or suspended */ set_cur_state(st); st->st_calculating = FALSE;
e = main_inI2_outR2_tail(pcrc, r);/*构造应答报文*/ if(ke->md != NULL) {
complete_v1_state_transition(&ke->md, e);/*发送报文并完后后续处理工作*/
if(ke->md) release_md(ke->md);
}
reset_cur_state();
}

8. main_inI2_outR2_tail源码分析

/*
* this routine gets called after any DH exponentiation that needs to be done
* has been done, and we are ready to send our g^y.
*/
stf_status
main_inI2_outR2_tail(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r)
{
struct ke_continuation *ke = (struct ke_continuation *)pcrc;
struct msg_digest *md = ke->md;
struct state *st = md->st; /* send CR if auth is RSA and no preloaded RSA public key exists*/
bool send_cr = FALSE; /**************** build output packet HDR;KE;Nr ****************/ /*如果以下四个条件同时满足,则通过需要发送证书。。*/
send_cr = !no_cr_send /*配置中允许发送证书*/
&& (st->st_oakley.auth == OAKLEY_RSA_SIG) /*使用RSA签名*/
&& !has_preloaded_public_key(st) /*未加载未共享秘钥*/
&& st->st_connection->spd.that.ca.ptr != NULL; /*对端证书非空*/ /* HDR out */
echo_hdr(md, FALSE, ISAKMP_NEXT_KE); /* KE out *//*添加KE载荷,并将其存储在st->st_gr*/
if (!ship_KE(st, r, &st->st_gr
, &md->rbody, ISAKMP_NEXT_NONCE))
{
osw_abort();
return STF_INTERNAL_ERROR;
} #ifdef DEBUG
{
/* Nr out */
int next_payload;
next_payload = ISAKMP_NEXT_NONE; if(cur_debugging & IMPAIR_BUST_MR2)
{
next_payload = ISAKMP_NEXT_VID;
}
if(send_cr)
{
next_payload = ISAKMP_NEXT_CR;
}
if (!ship_nonce(&st->st_nr, r
, &md->rbody
, next_payload
, "Nr"))
return STF_INTERNAL_ERROR; if (cur_debugging & IMPAIR_BUST_MR2)
{
/* generate a pointless large VID payload to push message over MTU */
pb_stream vid_pbs; if (!out_generic((send_cr)? ISAKMP_NEXT_CR : ISAKMP_NEXT_NONE,
&isakmp_vendor_id_desc, &md->rbody, &vid_pbs))
return STF_INTERNAL_ERROR;
if (!out_zero(1500 /*MTU?*/, &vid_pbs, "Filler VID"))
return STF_INTERNAL_ERROR;
close_output_pbs(&vid_pbs);
}
}
#else
/* Nr out *//*添加NONCE载荷,并将其存储在st->st_nr*/
if (!ship_nonce(&st->st_nr, r
, &md->rbody
, (send_cr)? ISAKMP_NEXT_CR : ISAKMP_NEXT_NONE
, "Nr"))
return STF_INTERNAL_ERROR;
#endif /* CR out *//*如果需要发送证书*/
if (send_cr)
{
if (st->st_connection->kind == CK_PERMANENT)/*双方连接固定,即两端的IP的确定的*/
{
if (!build_and_ship_CR(CERT_X509_SIGNATURE /*添加对端证书载荷*/
, st->st_connection->spd.that.ca
, &md->rbody, ISAKMP_NEXT_NONE))
return STF_INTERNAL_ERROR;
}
else
{
generalName_t *ca = NULL;
/*查询可用的证书 ???*/
if (collect_rw_ca_candidates(md, &ca))/*收集所有可用证书,并全部加载到ca链表上*/
{
generalName_t *gn; for (gn = ca; gn != NULL; gn = gn->next)
{
if (!build_and_ship_CR(CERT_X509_SIGNATURE, gn->name/*将所有的可用证书加载到链表上*/
, &md->rbody
, gn->next == NULL ? ISAKMP_NEXT_NONE : ISAKMP_NEXT_CR))
return STF_INTERNAL_ERROR;
}
free_generalNames(ca, FALSE);/*释放可用证书链表*/
}
else
{/*确实没有找到可用的证书,则填充一个空的证书载荷*/
if (!build_and_ship_CR(CERT_X509_SIGNATURE, empty_chunk
, &md->rbody, ISAKMP_NEXT_NONE))
return STF_INTERNAL_ERROR;
}
}
} #ifdef NAT_TRAVERSAL
if (st->hidden_variables.st_nat_traversal & NAT_T_WITH_NATD) {
if (!nat_traversal_add_natd(ISAKMP_NEXT_NONE, &md->rbody, md))/*添加NAT-D载荷*/
return STF_INTERNAL_ERROR;
}
#endif /* finish message */
close_message(&md->rbody);
/********************************************* 使 用 DH 算 法 开 始 制 作 密 钥 ***********************************************/
/*
* next message will be encrypted, so, we need to have
* the DH value calculated. We can do this in the background,
* sending the reply right away. We have to be careful on the next
* state, since the other end may reply faster than we can calculate
* things. If it is the case, then the packet is placed in the
* continuation, and we let the continuation process it. If there
* is a retransmit, we keep only the last packet.
*
* Also, note that this is not a suspended state, since we are
* actually just doing work in the background.
*
*/
{
/* Looks like we missed perform_dh() declared at
* programs/pluto/pluto_crypt.h as external and implemented nowhere.
* Following code regarding dh_continuation allocation seems useless
* as it's never used. At least, we should free it.
*/
struct dh_continuation *dh = alloc_thing(struct dh_continuation
, "main_inI2_outR2_tail");
stf_status e; dh->md = NULL;
dh->serialno = st->st_serialno;
pcrc_init(&dh->dh_pcrc);
dh->dh_pcrc.pcrc_func = main_inI2_outR2_calcdone;
passert(st->st_suspended_md == NULL); DBG(DBG_CONTROLMORE
, DBG_log("main inI2_outR2: starting async DH calculation (group=%d)", st->st_oakley.group->group)); e = start_dh_secretiv(&dh->dh_pcrc, st
, st->st_import
, RESPONDER
, st->st_oakley.group->group); DBG(DBG_CONTROLMORE,
DBG_log("started dh_secretiv, returned: stf=%s\n"
, stf_status_name(e))); if(e == STF_FAIL) {
loglog(RC_LOG_SERIOUS, "failed to start async DH calculation, stf=%s\n"
, stf_status_name(e));
return e;
} /* we are calculating in the background, so it doesn't count */
if(e == STF_SUSPEND) {
st->st_calculating = FALSE;
}
}
return STF_OK;
}

9. main_inI2_outR2_calcdone源码分析

static void
main_inI2_outR2_calcdone(struct pluto_crypto_req_cont *pcrc
, struct pluto_crypto_req *r
, err_t ugh)
{
struct dh_continuation *dh = (struct dh_continuation *)pcrc;
struct state *st; DBG(DBG_CONTROLMORE
, DBG_log("main inI2_outR2: calculated DH finished")); st = state_with_serialno(dh->serialno);
if(st == NULL) {
openswan_log("state %ld disappeared during crypto\n", dh->serialno);
return;
} set_cur_state(st);
if(ugh) {
loglog(RC_LOG_SERIOUS, "DH crypto failed: %s\n", ugh);
return;
}
/*将生成的三把秘钥、DH-IV等信息存储在状态上*/
finish_dh_secretiv(st, r);
if(!r->pcr_success) {
loglog(RC_LOG_SERIOUS, "DH crypto failed, invalid keys");
return;
} ikev2_validate_key_lengths(st); st->hidden_variables.st_skeyid_calculated = TRUE;
update_iv(st);/*更新IV值*/
/* XXX: Do we need to free dh here? If so, how about the other exits?
* pfree(dh); dh = NULL;
*/ /*
* if there was a packet received while we were calculating, then
* process it now.
*/
/*如果在计算秘钥的过程中收到新的报文则现在再处理该报文*/
if(st->st_suspended_md != NULL) {
struct msg_digest *md = st->st_suspended_md; set_suspended(st, NULL);
process_packet_tail(&md);
if(md != NULL) {
release_md(md);
}
}
reset_cur_state();
return;
}

openswan协商流程之(四):main_inI2_outR2()的更多相关文章

  1. openswan协商流程之(七):main_inR3

    主模式第六包(收包):main_inR3 1. 序言 main_inR3()函数是ISAKMP协商过程中第一阶段的最后一个报文的接收处理函数,它的作用同main_inI3_outR3()部分功能相同: ...

  2. openswan协商流程之(一):main_outI1()

    主模式第一包:main_outI1() 1. 序言 main_outI1()作为主模式主动发起连接请求的核心处理函数,我们可以通过学习该函数的处理流程来探究openswan中报文封装的基本思想.如果之 ...

  3. openswan协商流程之(五):main_inR2_outI3()

    主模式第五包:main_inR2_outI3 文章目录 主模式第五包:main_inR2_outI3 1. 序言 2.函数调用关系 3. 第五个报文流程图 4. main_inR2_outI3()源码 ...

  4. openswan协商流程之(三):main_inR1_outI2

    主模式第三包:main_inR1_outI2 1. 序言 main_inR1_outI2()函数是ISAKMP协商过程中第三包的核心处理函数的入口.这里我们主要说明main_inR1_outI2的函数 ...

  5. openswan协商流程之(六):main_inI3_outR3()

    主模式第六包:main_inI3_outR3 1. 序言 main_inI3_outR3()函数是ISAKMP协商过程中第六包的核心处理函数的入口,第五六包主要用来验证对方的身份信息,同时此报文也是加 ...

  6. openswan协商流程之(二):main_inI1_outR1()

    主模式第二包:main_inI1_outR1() 文章目录 主模式第二包:main_inI1_outR1() 1. 序言 2. `main_inI1_outR1()`处理流程图 3. `main_in ...

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

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

  8. 一个简单的Eclipse调试Debug流程(四)

    本文链接:https://blog.csdn.net/u011781521/article/details/55000066    http://blog.csdn.net/u010075335/ar ...

  9. 以太网驱动的流程浅析(四)-以太网驱动probe流程【原创】

    以太网驱动的流程浅析(四)-以太网驱动probe流程 Author:张昺华 Email:920052390@qq.com Time:2019年3月23日星期六 此文也在我的个人公众号以及<Lin ...

随机推荐

  1. 进入mysql的学习>从零开始学JAVA系列

    目录 MySQL的学习 什么是MYSQL 安装MYSQL Window安装MYSQL(压缩包版) 什么是MYSQL 安装MYSQL Window安装MYSQL(压缩包版) MYSQL基本指令 DDL ...

  2. 从零开始学习JAVA(入门基础)

    目录 博主从零开始学习JAVA(入门基础) 1.搭建JAVA开发环境 卸载JDK(未安装的请忽略) 安装JDK 2.编程语言中,何为编译型与解释型 编译型 解释型 3.第一个JAVA应用程序 4.JA ...

  3. 大厂需要什么样的 Android 开发?

    前言 昨天和一个百度的朋友闲聊,他说根据最近招聘 Android工程师的经验来看,大部分候选人在工作 3 年的时候基本都会遇上一道难过的坎. 为啥这么说呢? 因为工作一段时间之后,大部分工程师都已经完 ...

  4. JavaGUI画笔工具的使用

    JavaGUI画笔工具的使用 package GUI; import java.awt.*; public class TestPaint { public static void main(Stri ...

  5. 用VirtualBox搭建虚拟局域网

    用 Oracle VM VirtualBox 安装虚拟机,我在Windows 7上安装了ubuntu 11.10和xubuntu12.04两个虚拟机: 将这两个虚拟机的"网络"属性 ...

  6. 5、Python断言及常用断言函数总结

    Python断言 Python assert 语句,又称断言语句,可以看做是功能缩小版的 if 语句,它用于判断某个表达式的值,如果值为真,则程序可以继续往下执行:反之,Python 解释器会报 As ...

  7. SSM自学笔记(七)

    14.MyBatis的多表操作 1.MyBatis的多表操作 1.1 一对一查询 一对一查询的模型 用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户 一对一查询的需求:查询一个订单 ...

  8. 字符串匹配--Regex

    利用Regix实现字符串匹配 Eg:匹配嵌入到[]中的字符 string pattern = Regex.Escape("[") + "(.*?)]"; str ...

  9. 【maven】入门教程

    一:Maven简介 1.Maven是什么? 是一个跨平台的项目管理工具.使用java语言开发(Maven 3.3+ require JDK 1.7 or above to execute)2. 为什么 ...

  10. 【java虚拟机】类加载机制

    作者:平凡希 原文地址:https://www.cnblogs.com/xiaoxi/p/6959615.html 一.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中, ...