1. IKEv2 协商总体框架

IKEv1协议建立一对IPSec SA,使用主动模式需要9个报文,使用野蛮模式需要使用6个报文方能协商成功。IKEv2对IKEv1协议进行了优化,IKEv2只需要进行两次交互,使用 4 条消息就可以完成一个 IKEv2 SA 和一对 IPsec SA 的协商建立。IKEv2 定义了三种交互:

  • 初始交换

  • 创建子 SA 交换

  • 通知交换

    下面简单介绍一下 IKEv2 协商过程中的初始交换过程:

初始化交换通过两次交换共4个报文便可以完成一对IKE SA和IPSec SA的协商。上图主要用来描述协商报文的内容和对应的处理函数入口。下图则是用来说明各接口对应的协商状态。协商过程中是根据该状态来确定当前的协商阶段。


RFC文档中的报文格式:

Initiator                         Responder
-------------------------------------------------------------------
HDR, SAi1, KEi, Ni -->
<-- HDR, SAr1, KEr, Nr, [CERTREQ] HDR, SK {IDi, [CERT,] [CERTREQ,]
[IDr,] AUTH, SAi2,
TSi, TSr} -->
<-- HDR, SK {IDr, [CERT,] AUTH,
SAr2, TSi, TSr}

其中:

报文字段 说明
HDR 报文头部
SAi1、SAr1 IKE SA建议
SAi2、SAr2 IPSEC SA建议载荷
KEi、KEr DH算法公共值
Ni、Nr Nonce随机数
CERT、CERTREQ 证书载荷、证书请求载荷
IDi、IDr ID载荷
TSi、TSr 流量选择器,使用此载荷完成保护子网的协商
AUTH 认证数据载荷

这里面需要说明的是:报文中的SK并不是一个载荷。而是:SK {…}表示里面的内容被加密和认证保护。

2. 第一包流程图

3. openswan源码学习

3.1 ikev2parent_outI1()

此函数是IKEv2协议发起协商的入口函数。主要功能为:

  • 新建一个协商状态结构:state
  • 发起端随机成功Cookie值
  • 将连接上的参数信息、配置信息初始化state结构上参数
    • 隧道本端IP和端口
    • 隧道对端IP和端口
    • 隧道的出接口
    • 配置策略
    • … …
stf_status
ikev2parent_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 UNUSED
)
{
struct state *st = new_state();
/*发起端随机生成cookie值*/
get_cookie(TRUE, st->st_icookie, COOKIE_SIZE, &c->spd.that.host_addr); /*将连接上的信息初始化到状态上*/
initialize_new_state(st, c, policy, try, whack_sock, importance); if(newstateno) *newstateno = st->st_serialno; /*
* initialize the local end point address, so that NAT calculation will
* have something to work with.
*
*在set_state_ike_endpoints中将隧道两端的地址更新到了st_localaddr,st_port上
*这里再次修改为出接口的地址和端口,暂不清楚用意
*/
st->st_localaddr = st->st_interface->ip_addr;
st->st_localport = st->st_interface->port; return
ikev2parent_outI1_withstate(st, whack_sock, c
, predecessor, policy
, try, importance
, uctx);
}

3.2 ikev2parent_outI1_withstate()

此函数的主要功能包括如下几个方面:

  • 根据配置策略(认证方式: PSK or RSA etc.)选择使用的SADB模板
  • 根据配置的算法信息(alg_info_ike)生成对应的SADB结构
  • 将SADB由IKEv1类型转换为IKEv2类型(两者的SA结构不相同)。
  • 获取SADB上的DH组(第一个即可,用作猜想的DH组号)
    • 使用猜想的DH组生成DH的公共值gi
    • … …
stf_status
ikev2parent_outI1_withstate(struct state *st
, int whack_sock
, struct connection *c
, struct state *predecessor
, lset_t policy
, unsigned long try /* how many attempts so far */
, enum crypto_importance importance
, struct xfrm_user_sec_ctx_ike * uctx UNUSED
)
{
struct db_sa *sadb;
int groupnum;
int need_to_add_pending = 0;
/*根据配置的策略索引找到对应的SADB模板*/
int policy_index = POLICY_ISAKMP(policy
, c->spd.this.xauth_server
, c->spd.this.xauth_client); /* set up new state */
st->st_ikev2 = TRUE;/*用来标记是否为IKEv2*/
change_state(st, STATE_PARENT_I1);/*当前状态为STATE_PARENT_I1*/
st->st_try = try; /* IKE version numbers -- used mostly in logging */
st->st_ike_maj = IKEv2_MAJOR_VERSION;
st->st_ike_min = IKEv2_MINOR_VERSION;
st->st_policy = policy & ~POLICY_IPSEC_MASK;
st->st_ikev2_orig_initiator = TRUE; ... ... /*
* now, we need to initialize st->st_oakley, specifically, the group
* number needs to be initialized.
*/
groupnum = 0; st->st_sadb = &oakley_sadb[policy_index];
/*根据配置信息alg_info_ike生成相应的sadb*/
sadb = oakley_alg_makedb(st->st_connection->alg_info_ike
, st->st_sadb, 0);
if(sadb != NULL) {
st->st_sadb = sadb;
}
/*由于ikev1和ikev2的SA载荷的结构不一致,因此需要做一个转换*/
sadb = st->st_sadb = sa_v2_convert(st->st_sadb); {
unsigned int pc_cnt; /* look at all the proposals */
if(st->st_sadb->prop_disj!=NULL) {/*获取SA载荷中采用的DH组,只获取第一个DH即可*/
for(pc_cnt=0; pc_cnt < st->st_sadb->prop_disj_cnt && groupnum==0;
pc_cnt++)
{
struct db_v2_prop *vp = &st->st_sadb->prop_disj[pc_cnt];
unsigned int pr_cnt; /* look at all the proposals */
if(vp->props!=NULL) {
for(pr_cnt=0; pr_cnt < vp->prop_cnt && groupnum==0; pr_cnt++)
{
unsigned int ts_cnt;
struct db_v2_prop_conj *vpc = &vp->props[pr_cnt]; for(ts_cnt=0; ts_cnt < vpc->trans_cnt && groupnum==0; ts_cnt++) {
struct db_v2_trans *tr = &vpc->trans[ts_cnt];
if(tr!=NULL&& tr->transform_type == IKEv2_TRANS_TYPE_DH) {
groupnum = tr->transid;
}
}
}
}
}
}
}
if(groupnum == 0) {
groupnum = OAKLEY_GROUP_MODP2048;/*如果未设置DH组,则默认使用MODP2048,这里是猜想!!!*/
}
st->st_oakley.group=lookup_group(groupnum);
st->st_oakley.groupnum=groupnum; /* now. we need to go calculate the nonce, and the KE */
{
struct ke_continuation *ke = alloc_thing(struct ke_continuation
, "ikev2_outI1 KE");
stf_status e; ke->md = alloc_md();
ke->md->from_state = STATE_IKEv2_BASE;
ke->md->svm = &ikev2_parent_firststate_microcode;
ke->md->st = st;
set_suspended(st, ke->md); if (!st->st_sec_in_use) {/* st_sec_in_use ???*/
pcrc_init(&ke->ke_pcrc);
ke->ke_pcrc.pcrc_func = ikev2_parent_outI1_continue;
/*计算KE需要确定使用的DH组,因此使用了st->st_oakley.group*/
e = build_ke(&ke->ke_pcrc, st, st->st_oakley.group, importance);
if( (e != STF_SUSPEND && e != STF_INLINE) || (e == STF_TOOMUCHCRYPTO)) {
delete_state(st);
}
} else {
/* this case is that st_sec already is initialized */
e = ikev2_parent_outI1_tail((struct pluto_crypto_req_cont *)ke
, NULL);
} reset_globals(); return e;
}
}

3.3 ikev2_parent_outI1_common()

此函数最主要的功能就是构建协商报文。具体的步骤包括如下几个方面:

  • 构建IKEv2报文头部
  • IKEv2新增的用于抗重放功能的Cookie challenge操作
    • 如果先前收到了响应端的Cookie通知报文(载荷)时,使用收到的cookie构造一个通知载荷,用于相应端的抗重放。
  • 构建SA载荷。
  • 构建KE载荷
  • 构建Nonce载荷
  • 构建NAT-D载荷
  • 保存当前报文供后续的认证等操作
  • 发送报文
static stf_status
ikev2_parent_outI1_common(struct msg_digest *md
, struct state *st)
{
/* struct connection *c = st->st_connection; */
int numvidtosend = 0;
#ifdef PLUTO_SENDS_VENDORID
numvidtosend++; /* if we need to send Openswan VID */
#endif /* set up reply */
/*
* reply_buffer:真正开辟的内存空间,用来存储报文
* reply_stream:可以简单的认为是个指针,记录已经使用的位置
*
**/
init_pbs(&reply_stream, reply_buffer, sizeof(reply_buffer), "reply packet"); /*1. 构造HDR头部*/
{
struct isakmp_hdr hdr; zero(&hdr); /* default to 0 */
/* testing fake major new IKE version, should fail */ if(DBGP(IMPAIR_MAJOR_VERSION_BUMP))
hdr.isa_version = IKEv2_MAJOR_BUMP << ISA_MAJ_SHIFT | IKEv2_MINOR_VERSION; /* testing fake minor new IKE version, should success */
else if(DBGP(IMPAIR_MINOR_VERSION_BUMP))
hdr.isa_version = IKEv2_MAJOR_VERSION << ISA_MAJ_SHIFT | IKEv2_MINOR_BUMP;
else { /* normal production case with real version */
hdr.isa_version = IKEv2_MAJOR_VERSION << ISA_MAJ_SHIFT | IKEv2_MINOR_VERSION;
} hdr.isa_xchg = ISAKMP_v2_SA_INIT;
hdr.isa_flags = IKEv2_ORIG_INITIATOR_FLAG(st);
memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);
/* R-cookie, msgid are left zero */ if (!out_struct(&hdr, &isakmp_hdr_desc, &reply_stream, &md->rbody))
{
reset_cur_state();
return STF_INTERNAL_ERROR;
}
}
/* send an anti DOS cookie, 4306 2.6, if we have received one from the
* responder
*/ if(st->st_dcookie.ptr) {
chunk_t child_spi;
memset(&child_spi, 0, sizeof(child_spi));
/*
*anti Dos时通过在报文中添加一个通知载荷,里面包含对端的cookie信息(st->st_dcookie)
*
* HDR头部中的np字段填充的是设么????? --->ship_v2N -->pbs_set_np -->out_struct
*/
ship_v2N(ISAKMP_NEXT_NONE, ISAKMP_PAYLOAD_NONCRITICAL, PROTO_ISAKMP,
&child_spi, v2N_COOKIE, &st->st_dcookie, &md->rbody);
} /* SA out */
{
u_char *sa_start = md->rbody.cur; /* if we have an OpenPGP certificate we assume an
* OpenPGP peer and have to send the Vendor ID
*/
/*
*
* 如何判断一个SA是ikev1 or ikev2的结构???
*/
if(st->st_sadb->prop_disj_cnt == 0 || st->st_sadb->prop_disj) {/*SA结构未转换???*/
st->st_sadb = sa_v2_convert(st->st_sadb);
} if (!ikev2_out_sa(&md->rbody
, PROTO_ISAKMP
, st->st_sadb
, st, TRUE /* parentSA */
, ISAKMP_NEXT_v2KE))
{
openswan_log("outsa fail");
reset_cur_state();
return STF_INTERNAL_ERROR;
}
/* save initiator SA for later HASH */
if(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");
}
} /* send KE */
if(!justship_v2KE(st, &st->st_gi, st->st_oakley.groupnum, &md->rbody, 0))
return STF_INTERNAL_ERROR; /* send NONCE */
if(!justship_v2Nonce(st, &md->rbody, &st->st_ni, 0)) {
return STF_INTERNAL_ERROR;
} if(!justship_v2nat(st, &md->rbody)) {/*填充NAT-D载荷*/
return STF_INTERNAL_ERROR;
} /* Send Vendor VID if needed */
{/*省略了NAT-T的VID???*/
/*pluto_vendorid在init_pluto_vendorid()完成了初始化*/
pbs_set_np(&md->rbody, ISAKMP_NEXT_v2V);
if (!out_generic_raw(0, &isakmp_vendor_id_desc, &md->rbody
, pluto_vendorid, strlen(pluto_vendorid), "Vendor ID"))/*pluto_vendorid内容并不是真正的VID*/
return STF_INTERNAL_ERROR;
} close_message(&md->rbody);
close_output_pbs(&reply_stream); /* let TCL hack it before we mark the length and copy it */
TCLCALLOUT("v2_avoidEmitting", st, st->st_connection, md); freeanychunk(st->st_tpacket);
clonetochunk(st->st_tpacket, reply_stream.start, pbs_offset(&reply_stream)
, "reply packet for ikev2_parent_outI1_tail"); /* save packet for later signing *//*将第一个报文保存起来*/
freeanychunk(st->st_firstpacket_me);
clonetochunk(st->st_firstpacket_me, reply_stream.start
, pbs_offset(&reply_stream), "saved first packet"); /* Transmit */
send_packet(st, __FUNCTION__, TRUE); delete_event(st);
event_schedule(EVENT_v2_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); reset_cur_state();
return STF_OK;
}

4. 注意事项

4.1 关于此报文中涉及的对IKEv2引入的“新特性”说明

  • IKEv2 支持DH猜想

    在 IKE_SA_INIT 交换阶段,发起方采用“猜”的办法,猜一个响应方最可能使用的 DH 组携带在第一条消息中发送。响应方根据发起方“猜”的 DH 组来响应发起方。如果发起方猜测成功,则这样通过两条消息就可以完成 IKE_SA_INIT 交换。如果发起方猜测错误,则响应方会回应一个INVALID_KE_PAYLOAD 消息,并在该消息中指明将要使用的 DH 组。之后,发起方采用响应方指定的 DH 组重新发起协商。这种 DH 猜想机制,使得发起方的 DH 组配置更为灵活,可适应不同的响应方。

  • IKEv2 支持cookie-challenge机制

    在 IKE_SA_INIT 交换中消息是明文传输的,响应方接收到第一个消息后无法确认该消息是否来自一个仿冒的地址。如果此时一个网络攻击者伪造大量地址向响应方发送 IKE_SA_INIT 请求,根据IKEv1 协议,响应方需要维护这些半开的 IKE 会话信息,从而耗费大量响应方的系统资源,造成对响应方的 DoS 攻击。IKEv2 使用 cookie-challenge 机制来解决这类 DoS 攻击问题。当响应方发现存在的半开 IKE SA 超过指定的数目时,就启用 cookie-challenge 机制。响应方收到 IKE_SA_INIT 请求后,构造一个 Cookie通知载荷并响应发起方,若发起方能够正确携带收到的 Cookie 通知载荷向响应方重新发起IKE_SA_INIT 请求,则可以继续后续的协商过程。

4.2 在IKEv1与IKEv2在SA载荷结构上的不同之处:

  • IKEv2中的SA载荷部分内容

  • IKEv1中的SA载荷部分内容

IKEv1与IKEv2除了载荷类型不同,报文的结构也发生了变化,IKEv2中没有了属性载荷,相反IKEv1中的每一个属性载荷对应IKEv2中的一个变化载荷。因此IKEv2在oakley_alg_makedb()生成SA后需要sa_v2_convert()进行依次格式转换,转换为IKEv2的SA格式。


未经本人同意,不得转载!!!


IKEv2协议协商流程: (IKE-SA-INIT 交换)第一包的更多相关文章

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

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

  2. IKEv2协议关键知识点总结整理

    文章目录 @[toc] 1. IKEv2基本原理 2. IKEv2协议重点注意事项 2.1 情景一:==IKEv2协商密钥逻辑== ①密钥协商流程 ②函数调用关系 ③流程简述 2.2 情景二:==使用 ...

  3. [ipsec][crypto] IKEv2的协商交互分析

    一: 无论协商了什么样的加密算法.DH都交换一块长度为32byte的内存,作为key. IKE和esp的key,分别基于这块内存生成. 二: 当esp的算法协商没有指定dh group时,rekey将 ...

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

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

  5. PPTP协议握手流程分析

    一  PPTP概述 PPTP(Point to Point Tunneling Protocol),即点对点隧道协议.该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网,可 ...

  6. PPTP协议握手流程分析--转载

    一  PPTP概述   PPTP(Point to Point Tunneling Protocol),即点对点隧道协议.该协议是在PPP协议的基础上开发的一种新的增强型安全协议,支持多协议虚拟专用网 ...

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

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

  8. 数据包接收系列 — IP协议处理流程(二)

    本文主要内容:在接收数据包时,IP协议的处理流程. 内核版本:2.6.37 Author:zhangskd @ csdn blog 我们接着来看数据包如何发往本地的四层协议. ip_local_del ...

  9. http协议请求流程分析

    http协议请求流程分析 用户输入URL(地址链接)(http://www.baidu.com:80/tools.html)客户端获取到端口及主机名后,客户端利用DNS解析域名,首先客户端的浏览器会先 ...

随机推荐

  1. 大数据学习(12)—— Hive Server2服务

    什么是Hive Server2 上一篇我们启动了hive --service metastore服务,可以通过命令行来访问hive服务,但是它不支持多客户端同时访问,参见官网说明:HiveServer ...

  2. 并发编程——Java线程的6种状态及切换

    前言 本次主要分享一下Java线程的六种状态及其转换. 如果对于线程的创建方式不太了解,推荐观看并发编程--认识java里的线程 线程的状态及其转换 操作系统线程的五种状态 新建(NEW) 就绪(RU ...

  3. Node.js躬行记(8)——通用接口

    一.GraphQL 最近服务端的同事分享了GraphQL,他分享的目的就是要把我们与他们的数据库隔离,这么做我们也求之不得. 我们组目前维护着一个后台管理系统,会直接读取数据库中的表,如果能隔离的话, ...

  4. 指向结构的指针 struct结构名称 *结构指针变量名

    //指向结构的指针 struct结构名称 *结构指针变量名 //(*结构指针变量名).成员变量名//结构指针变量->成员变量名 1 #include<stdio.h> 2 #incl ...

  5. 【Android面试揭秘】面试官说“回去等通知”,我到底会不会等来通知?

    前言 大部分情况下,面试结束后,面试官都会跟你说:我们会在1-2个工作日内通知你面试结果. 许多人认为:所谓「等通知」其实是面试官委婉地给你「发拒信」.但是,这不是「等通知」的全部真相. 这篇文章,我 ...

  6. 关于数字化工厂&智能工厂建设 IT 经验总结

    最近疫情闹得胆战心惊,前不久客户给我开了一个玩笑,当天我们同桌会议了一天,晚上客户回家之后就被隔离了,当他给我发这个消息的时候背都凉了一截,害怕之余在机场呆了一个晚上,捅乐鼻孔插了嗓子之后确认无事,后 ...

  7. awk-06-常用内置函数

    常用内置函数 示例 1.int 2.sqrt 3.rand rand()并不是每次运行都会产生一个随机数,会一直保持不变 所以需要srand() 函数一起 但是还是有很大的几率会生成一样 4.asor ...

  8. OSPF和ACL综合实验

    一.实验拓扑: 二.实验要求: 1.企业内网运行OSPF路由协议,区域规划如图所示:2.财务和研发所在的区域不受其他区域链路不稳定性影响:3.R1.R2.R3只允许被IT登录管理:4.YF和CW之间不 ...

  9. 013 PCIe体系结构的组成部件

    一.PCIe体系结构的组成部件 PCIe总线作为处理器系统的局部总线,其作用与PCI总线类似,主要目的是为了连接处理器系统中的外部设备,当然PCIe总线也可以连接其他处理器系统.在不同的处理器系统中, ...

  10. 一招解决微信小程序中的H5缓存问题

    一招解决微信小程序中的H5缓存问题1.问题描述开发过程中,为了更新代码方便,往往会在小程序中嵌入H5页面.但问题来了,小程序原生代码更新版本后,简单的从微信中删除或者代码强刷就可以解决缓存问题,但小程 ...