openswan协商流程之(一):main_outI1()
主模式第一包:main_outI1()
1. 序言
main_outI1()
作为主模式主动发起连接请求的核心处理函数,我们可以通过学习该函数的处理流程来探究openswan中报文封装的基本思想。如果之前没有学习基本的函数接口(如in_struct, out_struct, out_sa等),那么直接学习main_outI1()
流程是比较困难的。如果想快速学习这几个函数接口,可以查看我先前的文章,我把需要的基本知识、思想等做了基本介绍,看完那几个接口再来学习此后的ISAKMP协商流程会容易很多,起到事半功倍的效果。
ISAKMP协商报文的处理流程都比较复杂,一个函数有几百行都是很常见的,因此个人学习期间难免有遗漏和理解错误的地方,请大家多多批评指正。
2. main_outI1()流程图
下面两个流程图中主要描述了三个函数的处理流程,后面我会分别附上这三个函数的源码信息。
3. main_outI1()源码注释
stf_status
main_outI1(int whack_sock
, struct connection *c
, struct state *predecessor
, so_serial_t *newstateno
, lset_t policy
, unsigned long try
, enum crypto_importance importance
, struct xfrm_user_sec_ctx_ike * uctx
)
{
struct state *st = new_state();/*创建一个新的状态*/
struct msg_digest md; /* use reply/rbody found inside */
int numvidtosend = 1; /* we always send DPD VID */
#ifdef NAT_TRAVERSAL
if (nat_traversal_enabled) {
numvidtosend++;
}
#endif
#if SEND_PLUTO_VID || defined(openpgp_peer)
numvidtosend++;
#endif
#ifdef XAUTH
if(c->spd.this.xauth_client || c->spd.this.xauth_server) {
numvidtosend++;
}
#endif
/*统计VID个数*/
/* set up new state *//*根据对端IP地址信息生成一个新的cookie*/
get_cookie(TRUE, st->st_icookie, COOKIE_SIZE, &c->spd.that.host_addr);
/*初始化新的state结构*/
initialize_new_state(st, c, policy, try, whack_sock, importance);
if(newstateno) *newstateno = st->st_serialno;
/* IKE version numbers -- used mostly in logging */
st->st_ike_maj = IKEv1_MAJOR_VERSION;
st->st_ike_min = IKEv1_MINOR_VERSION;
change_state(st, STATE_MAIN_I1);/*设置当前的状态为STATE_MAIN_I1*/
if (HAS_IPSEC_POLICY(policy))
add_pending(dup_any(whack_sock), st, c, policy, 1
, predecessor == NULL? SOS_NOBODY : predecessor->st_serialno
, uctx
);
#ifdef HAVE_LABELED_IPSEC
/*For main modes states, sec ctx is always null*/
st->sec_ctx = NULL;
#endif
if (predecessor == NULL)
openswan_log("initiating Main Mode");
else
openswan_log("initiating Main Mode to replace #%lu", predecessor->st_serialno);
/* set up reply */
zero(reply_buffer);/*初始化应答报文(发送的报文)的结构*/
init_pbs(&reply_stream, reply_buffer, sizeof(reply_buffer), "reply packet");
/* HDR out */
{/*添加isakmp头部信息*/
struct isakmp_hdr hdr;
zero(&hdr); /* default to 0 */
hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
hdr.isa_np = ISAKMP_NEXT_SA;
hdr.isa_xchg = ISAKMP_XCHG_IDPROT;
memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);
/* R-cookie, flags and MessageID are left zero */
/*长度字段最后设置: close_output_pbs(&reply_stream);*/
if (!out_struct(&hdr, &isakmp_hdr_desc, &reply_stream, &md.rbody))
{
reset_cur_state();
return STF_INTERNAL_ERROR;
}
}
/* SA out */
{/************封装SA载荷**************/
u_char *sa_start = md.rbody.cur;
int policy_index = POLICY_ISAKMP(policy
, c->spd.this.xauth_server
, c->spd.this.xauth_client);
/* if we have an OpenPGP certificate we assume an
* OpenPGP peer and have to send the Vendor ID
*/
/*如果存在VID,则需要设置下一载荷的值*/
int np = numvidtosend > 0 ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_sa(&md.rbody
, &oakley_sadb[policy_index], st, TRUE, FALSE, np))
{
openswan_log("outsa fail");
reset_cur_state();
return STF_INTERNAL_ERROR;
}
/* save initiator SA for later HASH */
passert(st->st_p1isa.ptr == NULL); /* no leak! (MUST be first time) */
clonetochunk(st->st_p1isa, sa_start, md.rbody.cur - sa_start
, "sa in main_outI1");
}
if (SEND_PLUTO_VID || c->spd.this.cert.type == CERT_PGP)
{
char *vendorid = (c->spd.this.cert.type == CERT_PGP) ?
pgp_vendorid : pluto_vendorid;
int np = --numvidtosend > 0 ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_generic_raw(np, &isakmp_vendor_id_desc, &md.rbody
, vendorid, strlen(vendorid), "Vendor ID"))
return STF_INTERNAL_ERROR;
}
/* Send DPD VID */
{
int np = --numvidtosend > 0 ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if(!out_vid(np, &md.rbody, VID_MISC_DPD)) {
reset_cur_state();
return STF_INTERNAL_ERROR;
}
}
#ifdef NAT_TRAVERSAL
DBG(DBG_NATT, DBG_log("nat traversal enabled: %d"
, nat_traversal_enabled));
if (nat_traversal_enabled) {
int np = --numvidtosend > 0 ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
/* Add supported NAT-Traversal VID */
if (!nat_traversal_insert_vid(np, &md.rbody, st)) {
reset_cur_state();
return STF_INTERNAL_ERROR;
}
}
#endif
#ifdef XAUTH
if(c->spd.this.xauth_client || c->spd.this.xauth_server) {
int np = --numvidtosend > 0 ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if(!out_vid(np, &md.rbody, VID_MISC_XAUTH)) {
reset_cur_state();
return STF_INTERNAL_ERROR;
}
}
#endif
#ifdef DEBUG
/* if we are not 0 then something went very wrong above */
if(numvidtosend != 0) {
openswan_log("payload alignment problem please check the code in main_inR1_outR2 (num=%d)", numvidtosend);
}
#endif
close_message(&md.rbody);
close_output_pbs(&reply_stream);
/* let TCL hack it before we mark the length and copy it */
TCLCALLOUT("avoidEmitting", st, st->st_connection, &md);
clonetochunk(st->st_tpacket, reply_stream.start, pbs_offset(&reply_stream)
, "reply packet for main_outI1");
/* Transmit */
send_packet(st, "main_outI1", TRUE);
/* Set up a retransmission event, half a minute henceforth */
TCLCALLOUT("adjustTimers", st, st->st_connection, &md);
#ifdef TPM
tpm_stolen:
tpm_ignore:
#endif
delete_event(st);
event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st);
if (predecessor != NULL)
{
update_pending(predecessor, st);
whack_log(RC_NEW_STATE + STATE_MAIN_I1
, "%s: initiate, replacing #%lu"
, enum_name(&state_names, st->st_state)
, predecessor->st_serialno);
}
else
{
whack_log(RC_NEW_STATE + STATE_MAIN_I1
, "%s: initiate", enum_name(&state_names, st->st_state));
}
reset_cur_state();
return STF_OK;
}
4. out_sa()源码注释
略。此函数400多行,由于流程图上比较详细就不再列出。
5. oakley_alg_makedb()源码注释
/*
* Create an OAKLEY proposal based on alg_info and policy
*
* Note: maxtrans is an enum, not a count
* Should probably be declared an enum at some point.
* -1 - ???
* 0 - No limit
* 1 - One proposal - period
* 2 - One DH group, take first DH group and ignore any that don't match
*根据配置的秘钥算法信息重新生成一个sadb信息
*传入的sadb应该为固定的秘钥算法信息,因此需要根据策略来重新生成一个新的sadb
*/
struct db_sa *
oakley_alg_makedb(struct alg_info_ike *ai
, struct db_sa *base
, int maxtrans)
{
/* struct db_context inprog UNUSED; */
struct db_sa *gsp = NULL;
struct db_sa *emp_sp = NULL;
struct ike_info *ike_info;
unsigned ealg, halg, modp, eklen=0;
/* Next two are for multiple proposals in agressive mode... */
unsigned last_modp=0, wrong_modp=0;
struct encrypt_desc *enc_desc;
int transcnt = 0;
int i;
/*
* start by copying the proposal that would have been picked by
* standard defaults.
*/
if (!ai) {
DBG(DBG_CRYPT,DBG_log("no IKE algorithms for this connection "));
return NULL;
}
gsp = NULL;
/*
* for each group, we will create a new proposal item, and then
* append it to the list of transforms in the conjoint point.
*
* when creating each item, we will use the first transform
* from the base item as the template.
*/
ALG_INFO_IKE_FOREACH(ai, ike_info, i) {//遍历策略中的算法信息
if(ike_info->ike_default == FALSE) {
struct db_attr *enc, *hash, *auth, *grp, *enc_keylen, *new_auth;
struct db_trans *trans;
struct db_prop *prop;
struct db_prop_conj *cprop;
/*获取到加密算法、哈希算法、认证算法、加密秘钥长度等信息*/
ealg = ike_info->ike_ealg;
halg = ike_info->ike_halg;
modp = ike_info->ike_modp;
eklen= ike_info->ike_eklen;
#if 1 /*判断这几个算法是否合法、是否存在等*/
if (!ike_alg_enc_present(ealg)) {
DBG_log("oakley_alg_makedb() "
"ike enc ealg=%d not present",
ealg);
continue;
}
if (!ike_alg_hash_present(halg)) {
DBG_log("oakley_alg_makedb() "
"ike hash halg=%d not present",
halg);
continue;
}
enc_desc = ike_alg_get_encrypter(ealg);
passert(enc_desc != NULL);
if (eklen /*秘钥长度是否符合要求*/
&& (eklen < enc_desc->keyminlen
|| eklen > enc_desc->keymaxlen))
{
DBG_log("ike_alg_db_new() "
"ealg=%d (specified) keylen:%d, "
"not valid "
"min=%d, max=%d"
, ealg
, eklen
, enc_desc->keyminlen
, enc_desc->keymaxlen
);
continue;
}
#endif
/* okay copy the basic item, and modify it. */
if(eklen > 0)
{
emp_sp = sa_copy_sa(&oakley_empty, 0);/*重新分配一个新的描述信息*/
cprop = &base->prop_conjs[0];/*从定义的描述信息中获取参数*/
prop = &cprop->props[0];/*建议载荷*/
trans = &prop->trans[0];/*变换载荷*/
new_auth = &trans->attrs[2];/*属性载荷*/
cprop = &emp_sp->prop_conjs[0];
prop = &cprop->props[0];
trans = &prop->trans[0];
auth = &trans->attrs[2];
*auth = *new_auth; /*给新的描述结构中设置认证算法*/
}
else
emp_sp = sa_copy_sa_first(base);
passert(emp_sp->prop_conj_cnt == 1);
cprop = &emp_sp->prop_conjs[0];
passert(cprop->prop_cnt == 1);
prop = &cprop->props[0];
passert(prop->trans_cnt == 1);
trans = &prop->trans[0];
passert(trans->attr_cnt == 4 || trans->attr_cnt == 5);
enc = &trans->attrs[0]; /*加密*/
hash = &trans->attrs[1];/*哈希*/
auth = &trans->attrs[2];/*认证*/
grp = &trans->attrs[3];/*DH组?*/
if(eklen > 0) {
enc_keylen = &trans->attrs[4];
enc_keylen->val = eklen;/*设置加密算法长度*/
} else
trans->attr_cnt = 4;
passert(enc->type.oakley == OAKLEY_ENCRYPTION_ALGORITHM);
if(ealg > 0) {
enc->val = ealg;/*设置加密算法*/
}
modp = ike_info->ike_modp;
eklen= ike_info->ike_eklen;
passert(hash->type.oakley == OAKLEY_HASH_ALGORITHM);
if(halg > 0) {
hash->val = halg;/*设置哈希算法*/
}
passert(auth->type.oakley == OAKLEY_AUTHENTICATION_METHOD);
/* no setting for auth type for IKE */
passert(grp->type.oakley == OAKLEY_GROUP_DESCRIPTION);
if(modp > 0) {
grp->val = modp; /*设置认证算法*/
}
} else {
emp_sp = sa_copy_sa(base, 0);
}
if(maxtrans == 1) {/*最大变换载荷数*/
/*
* We're going to leave maxtrans == 1 alone in case there
* really really is a case where we only want 1.
*/
if(transcnt == 0) {
DBG(DBG_CONTROL, DBG_log("using transform (%d,%d,%d,%ld)"
, ike_info->ike_ealg
, ike_info->ike_halg
, ike_info->ike_modp
, (long)ike_info->ike_eklen));
if(gsp) {
free_sa(gsp);
}
gsp = emp_sp;
} else {
free_sa(emp_sp);
}
if(transcnt > 0) {
if(transcnt == 1) {
loglog(RC_LOG_SERIOUS
, "multiple transforms were set in aggressive mode. Only first one used.");
}
loglog(RC_LOG_SERIOUS
, "transform (%d,%d,%d,%ld) ignored."
, ike_info->ike_ealg
, ike_info->ike_halg
, ike_info->ike_modp
, (long)ike_info->ike_eklen);
}
} else {
/*
* Now... We're allowing multiple proposals... Are we allowing
* multiple DH groups?
*/
struct db_sa *new;
if(maxtrans == 2 && transcnt > 0 && ike_info->ike_modp != last_modp ) {
/* Not good.
* Already got a DH group and this one doesn't match */
if(wrong_modp == 0) {
loglog(RC_LOG_SERIOUS
, "multiple DH groups were set in aggressive mode. Only first one used.");
}
loglog(RC_LOG_SERIOUS
, "transform (%d,%d,%d,%ld) ignored."
, ike_info->ike_ealg
, ike_info->ike_halg
, ike_info->ike_modp
, (long)ike_info->ike_eklen);
wrong_modp++;
free_sa(emp_sp);
} else if(gsp) {
/* now merge emp_sa and gsp */
new = sa_merge_proposals(gsp, emp_sp);/*变换载荷合并*/
free_sa(gsp);
free_sa(emp_sp);
emp_sp = NULL;
gsp = new;
} else {
gsp = emp_sp;
}
last_modp = ike_info->ike_modp;
}
transcnt++;
}
gsp->parentSA = TRUE;
return gsp;
}
openswan协商流程之(一):main_outI1()的更多相关文章
- openswan协商流程之(七):main_inR3
主模式第六包(收包):main_inR3 1. 序言 main_inR3()函数是ISAKMP协商过程中第一阶段的最后一个报文的接收处理函数,它的作用同main_inI3_outR3()部分功能相同: ...
- openswan协商流程之(六):main_inI3_outR3()
主模式第六包:main_inI3_outR3 1. 序言 main_inI3_outR3()函数是ISAKMP协商过程中第六包的核心处理函数的入口,第五六包主要用来验证对方的身份信息,同时此报文也是加 ...
- openswan协商流程之(五):main_inR2_outI3()
主模式第五包:main_inR2_outI3 文章目录 主模式第五包:main_inR2_outI3 1. 序言 2.函数调用关系 3. 第五个报文流程图 4. main_inR2_outI3()源码 ...
- openswan协商流程之(四):main_inI2_outR2()
主模式第四包:main_inI2_outR2 1. 序言 main_inI2_outR2()函数是ISAKMP协商过程中第四包的核心处理函数的入口,同时在此处理流程中已经获取到足够的隧道信息,可以生成 ...
- openswan协商流程之(三):main_inR1_outI2
主模式第三包:main_inR1_outI2 1. 序言 main_inR1_outI2()函数是ISAKMP协商过程中第三包的核心处理函数的入口.这里我们主要说明main_inR1_outI2的函数 ...
- 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. ...
- IKEv2协议协商流程: (IKE-SA-INIT 交换)第一包
文章目录 1. IKEv2 协商总体框架 2. 第一包流程图 3. openswan源码学习 3.1 ikev2parent_outI1() 3.2 ikev2parent_outI1_withsta ...
- openswan IPSec专栏目录锦集
为了方便查阅现有的文章,特准备一个目录页供后续查询使用 专栏序言 1. 基础知识 openswan任务调度基础知识之信号 2. openswan环境搭建 openswan框架和编译时说明 opensw ...
随机推荐
- C语言复习(二)
引言: 不会将每一个部分都详述,只关注于一些自己认为重要的或常错的,若有不足,还望指出 switch()细节:括号内必须是整型或枚举类型:遇到break才会跳出:case包含的必须是常量 contin ...
- 开源ERP和其它ERP软件比较
现在有许多企业将ERP项目,在企业中没有实施好,都归咎于软件产品不好.其实,这只是你们的借口.若想要将ERP软件真正与企业融合一体,首先得考虑企业的自身情况,再去选择适合的 ERP软件. 如果你的企业 ...
- Java 使用 Apache commons-math3 线性拟合、非线性拟合实例(带效果图)
Java 使用 CommonsMath3 的线性和非线性拟合实例,带效果图 例子查看 GitHub Gitee 运行src/main/java/org/wfw/chart/Main.java 即可查看 ...
- centos7 下安装docker报错:You could try using...
搞了台VPS,想要装docker,发现死活装不上,各种报错.之前系统是centos6,发现官方现在已经不支持centos6了,遂升级到centos7,然后还是出现下面这个错误. Error: Pack ...
- Vue+SpringBoot前后端分离中的跨域问题
在前后端分离开发中,需要前端调用后端api并进行内容显示,如果前后端开发都在一台主机上,则会由于浏览器的同源策略限制,出现跨域问题(协议.域名.端口号不同等),导致不能正常调用api接口,给开发带来不 ...
- CSS Grid 布局(Grid Layout)完全指南 #flight.Archives003
Title/ CSS Grid 布局(Grid Layout)完全指南 #flight.Archives003 序 : 写完这篇文章后,我准备一直做下去了,包括flight的各个分区,也看到前方的路. ...
- 图 邻接表 邻接矩阵 BFS生成树 DFS生成树
- netty系列之:netty中的懒人编码解码器
目录 简介 netty中的内置编码器 使用codec要注意的问题 netty内置的基本codec base64 bytes compression json marshalling protobuf ...
- LDAP未授权访问学习
LDAP未授权访问学习 一.LDAP 介绍 LDAP的全称为Lightweight Directory Access Protocol(轻量级目录访问协议), 基于X.500标准, 支持 TCP/IP ...
- innodb是如何存数据的?yyds
前言 如果你使用过mysql数据库,对它的存储引擎:innodb,一定不会感到陌生. 众所周知,在mysql8以前,默认的存储引擎是:myslam.但mysql8之后,默认的存储引擎已经变成了:inn ...