openswan协商流程之(四):main_inI2_outR2()
主模式第四包: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()的更多相关文章
- openswan协商流程之(七):main_inR3
		
主模式第六包(收包):main_inR3 1. 序言 main_inR3()函数是ISAKMP协商过程中第一阶段的最后一个报文的接收处理函数,它的作用同main_inI3_outR3()部分功能相同: ...
 - openswan协商流程之(一):main_outI1()
		
主模式第一包:main_outI1() 1. 序言 main_outI1()作为主模式主动发起连接请求的核心处理函数,我们可以通过学习该函数的处理流程来探究openswan中报文封装的基本思想.如果之 ...
 - openswan协商流程之(五):main_inR2_outI3()
		
主模式第五包:main_inR2_outI3 文章目录 主模式第五包:main_inR2_outI3 1. 序言 2.函数调用关系 3. 第五个报文流程图 4. main_inR2_outI3()源码 ...
 - openswan协商流程之(三):main_inR1_outI2
		
主模式第三包:main_inR1_outI2 1. 序言 main_inR1_outI2()函数是ISAKMP协商过程中第三包的核心处理函数的入口.这里我们主要说明main_inR1_outI2的函数 ...
 - openswan协商流程之(六):main_inI3_outR3()
		
主模式第六包:main_inI3_outR3 1. 序言 main_inI3_outR3()函数是ISAKMP协商过程中第六包的核心处理函数的入口,第五六包主要用来验证对方的身份信息,同时此报文也是加 ...
 - openswan协商流程之(二):main_inI1_outR1()
		
主模式第二包:main_inI1_outR1() 文章目录 主模式第二包:main_inI1_outR1() 1. 序言 2. `main_inI1_outR1()`处理流程图 3. `main_in ...
 - IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包
		
IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 文章目录 IKEv2协议协商流程: (IKE-SA-INIT 交换)第二包 1. IKEv2 协商总体框架 2. 第二包流程图 3. ...
 - 一个简单的Eclipse调试Debug流程(四)
		
本文链接:https://blog.csdn.net/u011781521/article/details/55000066 http://blog.csdn.net/u010075335/ar ...
 - 以太网驱动的流程浅析(四)-以太网驱动probe流程【原创】
		
以太网驱动的流程浅析(四)-以太网驱动probe流程 Author:张昺华 Email:920052390@qq.com Time:2019年3月23日星期六 此文也在我的个人公众号以及<Lin ...
 
随机推荐
- Mariadb常用管理操作
			
一 Mariadb常用管理操作 纯干货,没有一点废话,全是使用频率最高和常用的操作,运维必不可少的基础资料. 1.1 创建数据库 >create database <db_name> ...
 - WordPress如何配置邮件发送?
			
WordPress配置了邮件发送最直接的用处就是可以通过邮件找回密码,当然还有其他的用处,比如Wordpress有新用户注册,订单状态.评论等发生变化时给管理员发送邮件提醒等. 经过大量用户实践反馈, ...
 - rancher清理主机脚本
			
#!/bin/bash #From:rancher #date:2019-10-18 #admin:jarno # 停止服务 systemctl disable kubelet.service sys ...
 - 图 邻接表 邻接矩阵 BFS生成树 DFS生成树
 - 让Dev支持C++11特性
			
如果你使用的是 Dev-C++ ,忘记了在设置中包含 -std=c++11是无法让Dev支持C++11特性的,导致一些C++11 中的好用的函数可能会产⽣生编译⽆无法通过的情况. 这时候你有两个方法 ...
 - S3C2440—3.用点亮LED来熟悉裸机开发的详细流程
			
文章目录 一.硬件知识 1.LED原理图 2.芯片手册 Ⅰ.找LED原理图 Ⅱ.找对应引脚 Ⅲ.在芯片手册中查找引脚信息 Ⅳ.查看寄存器说明 Ⅴ.配置寄存器 二.S3C2440框架与启动过程 三.要用 ...
 - C# 通过反射实现对象映射:将2个属性相近的对象相互转换
			
前言 我们在编程过程中,经常需要将一个对象转成另一个对象(一般称为对象映射). 比如我们有2个类: //第1个类 CLS1 class CLS1 { public int i {get; set;} ...
 - 如果被问到 HTTP 协议,你真的能讲清楚吗?
			
前段时间,在和许久未见的老同学聊天时,突然被问到 http 协议到底是什么?脑海里面第一时间想起来的就是 request 请求.response 响应之类的词汇,但是这样讲他真的能知道是什么吗?我反问 ...
 - ManagementEventWatcher throws ManagementException with call to Stop()
			
参考网址:https://stackoverflow.com/questions/46100105/managementeventwatcher-throws-managementexception- ...
 - linux下C编程初篇
			
对于程序设计员来说,makefile是我们绕不过去的一个坎.可能对于习惯Visual C++的用户来说,是否会编写makefile无所谓.毕竟工具本身已经帮我们做好了全部的编译流程.但是在Linux上 ...