主模式第二包:main_inI1_outR1()

1. 序言

main_inI1_outR1()函数是ISAKMP协商过程中第二包的核心处理函数,同时也是响应端口的首包。这里我们主要说明main_inI1_outR1的处理流程,关于main_inI1_outR1的上下文环境暂不叙述,留给后面的文章进行更新。

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

2. main_inI1_outR1()处理流程图

下面的流程图不仅包括main_inI1_outR1,还包括它其中的一个重要函数:parse_isakmp_sa_body。此函数的作用就是解析对端的SA信息,然后根据对端的建议载荷,选择本地支持的算法,并构造自己的应答SA载荷。

3. main_inI1_outR1()源码

/* State Transition Functions.
*
* The definition of state_microcode_table in demux.c is a good
* overview of these routines.
*
* - Called from process_packet; result handled by complete_v1_state_transition
* - struct state_microcode member "processor" points to these
* - these routine definitionss are in state order
* - these routines must be restartable from any point of error return:
* beware of memory allocated before any error.
* - output HDR is usually emitted by process_packet (if state_microcode
* member first_out_payload isn't ISAKMP_NEXT_NONE).
*
* The transition functions' functions include:
* - process and judge payloads
* - update st_iv (result of decryption is in st_new_iv)
* - build reply packet
*/ /* Handle a Main Mode Oakley first packet (responder side).
* HDR;SA --> HDR;SA
*/ /********************************
*main_inI1_outR1函数被process_packet函数调用
*它的返回结果由complete_v1_state_transition处理
*
*********************************/
stf_status
main_inI1_outR1(struct msg_digest *md)
{
#ifdef DMALLOC
if (_dm_initialized != 0) {
/* log unfreed pointers that have been added to the heap since mark */
dmalloc_log_changed(_dm_mark, 1, 0, 1);
dmalloc_log_stats ();
}
_dm_mark = dmalloc_mark() ;
_dm_initialized = 1;
#endif
/*接收到的数据包中的SA载荷部分*/
struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_SA];
struct state *st;
struct connection *c;
/*准备作为应答的SA载荷缓冲区*/
pb_stream r_sa_pbs;
lset_t policy_hint = 0; /* we are looking for an OpenPGP Vendor ID sent by the peer */
bool openpgp_peer = FALSE; /* Determin how many Vendor ID payloads we will be sending */
int next;
int numvidtosend = 1; /* we always send DPD VID */
/*至少会发送DPD的VID,因此初始值为1*/
#ifdef NAT_TRAVERSAL /*支持NAT-T,则增加一个nat-t探测的VID*/
if (md->quirks.nat_traversal_vid && nat_traversal_enabled) {
DBG(DBG_NATT, DBG_log("nat-t detected, sending nat-t VID"));
numvidtosend++;
}
#endif
/*如果预定义了PLUUO VID 或openpgp 对端, 增加VID 数量*/
#if SEND_PLUTO_VID || defined(openpgp_peer)
numvidtosend++;
#endif #if defined(openpgp_peer)
{
struct payload_digest *p;
/*遍历接收到的数据包中的VID 载荷链表*/
for (p = md->chain[ISAKMP_NEXT_VID]; p != NULL; p = p->next)
{
int vid_len = sizeof(pgp_vendorid) - 1 < pbs_left(&p->pbs)
? sizeof(pgp_vendorid) - 1 : pbs_left(&p->pbs); /*检查是否是pgp VID, 是则设置openpgp_peer 标志为TRUE*/
if (memcmp(pgp_vendorid, p->pbs.cur, vid_len) == 0)
{
openpgp_peer = TRUE;
DBG(DBG_PARSING,
DBG_log("we have an OpenPGP peer")
)
}
}
}
#endif /*根据接收到的数据包的目的地址,目的端口,源地址,源端口查找连接结构*/
/* random source ports are handled by find_host_connection */
c = find_host_connection(ANY_MATCH, &md->iface->ip_addr, pluto_port500
, KH_IPADDR
, &md->sender
, md->sender_port, LEMPTY, POLICY_IKEV1_DISABLE, &policy_hint); if (c == NULL)/*如果没找到*/
{
pb_stream pre_sa_pbs = sa_pd->pbs;
lset_t policy = preparse_isakmp_sa_body(&pre_sa_pbs);
/*
* If there is XAUTH VID, copy it to policies.
*/
if (md->quirks.xauth_vid == TRUE)
{
policy |= POLICY_XAUTH;
}
/* See if a wildcarded connection can be found.
* We cannot pick the right connection, so we're making a guess.
* All Road Warrior connections are fair game:
* we pick the first we come across (if any).
* If we don't find any, we pick the first opportunistic
* with the smallest subnet that includes the peer.
* There is, of course, no necessary relationship between
* an Initiator's address and that of its client,
* but Food Groups kind of assumes one.
*/
{
// 可能是那种未定义对方地址的动态连接, 将源地址条件置空,重新查找连接,
// 可能会找到多个连接结构,返回的是一个链表
struct connection *d;
d = find_host_connection(ANY_MATCH, &md->iface->ip_addr, pluto_port500
, KH_ANY
, (ip_address*)NULL
, md->sender_port, policy, POLICY_IKEV1_DISABLE, &policy_hint);
// 遍历链表
for (; d != NULL; d = d->IPhp_next)
{
// GROUP 类型连接不考虑
if (d->kind == CK_GROUP)
{
/* ignore */
}
else
{
// 如果连接类型为模板型而且没定义OE, 该连接可用于处理该数据包, 连接找到,中断循环
if (d->kind == CK_TEMPLATE && !(d->policy & POLICY_OPPO))
{
/* must be Road Warrior: we have a winner */
c = d;
break;
} /* Opportunistic or Shunt: pick tightest match */
// 比较数据包源地址是否匹配连接中对方地址的定义
if (addrinsubnet(&md->sender, &d->spd.that.client)
// 而且目前可用连接为空,或目前可用连接的地址范围比新找到的连接范围大, 更新可用连接
&& (c == NULL || !subnetinsubnet(&c->spd.that.client, &d->spd.that.client)))
c = d;
}
}
}
// 如果没找到连接, 该数据包处理不了, 返回忽略该数据包
if (c == NULL)
{
loglog(RC_LOG_SERIOUS, "initial Main Mode message received on %s:%u"
" but no connection has been authorized%s%s"
, ip_str(&md->iface->ip_addr), ntohs(portof(&md->iface->ip_addr))
, (policy != LEMPTY) ? " with policy=" : ""
, (policy != LEMPTY) ? bitnamesof(sa_policy_bit_names, policy) : ""); if(policy_hint & POLICY_IKEV1_DISABLE) {
md->note = INVALID_MAJOR_VERSION;
return STF_FAIL;
} /* XXX notification is in order! */
return STF_IGNORE;
}/* 否则如果不是模板类型连接(动态连接), 返回忽略*/
else if (c->kind != CK_TEMPLATE)
{
loglog(RC_LOG_SERIOUS, "initial Main Mode message received on %s:%u"
" but \"%s\" forbids connection"
, ip_str(&md->iface->ip_addr), pluto_port500, c->name);
/* XXX notification is in order! */
return STF_IGNORE;
}
else
{
/* Create a temporary connection that is a copy of this one.
* His ID isn't declared yet.
*/
DBG(DBG_CONTROL, DBG_log("instantiating \"%s\" for initial Main Mode message received on %s:%u"
, c->name, ip_str(&md->iface->ip_addr), pluto_port500));
// 将模板连接进行实例化, 根据模板连接新生成一个新连接结构, 填充对方地址
c = rw_instantiate(c, &md->sender
, NULL, NULL);
}
} else {
/* we found a non-wildcard conn. double check if it needs instantiation anyway (eg vnet=) */
if ((c->kind == CK_TEMPLATE) && c->spd.that.virt) {
DBG(DBG_CONTROL, DBG_log("local endpoint has virt (vnet/vhost) set without wildcards - needs instantiation"));
c = rw_instantiate(c,&md->sender,NULL,NULL);
}
} #ifdef XAUTH
if(c->spd.this.xauth_server || c->spd.this.xauth_client)
{
numvidtosend++;
}
#endif
/* Set up state */
// 新分配状态结构
md->st = st = new_state();
#ifdef XAUTH
passert(st->st_oakley.xauth == 0);
#endif
st->st_connection = c;
st->st_remoteaddr = md->sender;
st->st_remoteport = md->sender_port;
st->st_localaddr = md->iface->ip_addr;
st->st_localport = md->iface->port;
st->st_interface = md->iface; /* IKE version numbers -- used mostly in logging */
st->st_ike_maj = md->maj;
st->st_ike_min = md->min; set_cur_state(st); /* (caller will reset cur_state) */
st->st_try = 0; /* not our job to try again from start */
st->st_policy = c->policy & ~POLICY_IPSEC_MASK; /* only as accurate as connection */
// 状态类型为R0(接收到初始化包)
change_state(st, STATE_MAIN_R0); // 复制对方的cookie
memcpy(st->st_icookie, md->hdr.isa_icookie, COOKIE_SIZE); // 生成本地的cookie
get_cookie(FALSE, st->st_rcookie, COOKIE_SIZE, &md->sender); // 将新状态插入到状态哈希表
insert_state(st); /* needs cookies, connection, and msgid (0) */ st->st_doi = ISAKMP_DOI_IPSEC;
st->st_situation = SIT_IDENTITY_ONLY; /* We only support this */ /* copy the quirks we might have accumulated */
// 复制特殊标志
copy_quirks(&st->quirks,&md->quirks); if ((c->kind == CK_INSTANCE) && (c->spd.that.host_port_specific))
{
openswan_log("responding to Main Mode from unknown peer %s:%u"
, ip_str(&c->spd.that.host_addr), c->spd.that.host_port);
}
else if (c->kind == CK_INSTANCE)
{
openswan_log("responding to Main Mode from unknown peer %s"
, ip_str(&c->spd.that.host_addr));
}
else
{
openswan_log("responding to Main Mode");
} /* parse_isakmp_sa also spits out a winning SA into our reply,
* so we have to build our reply_stream and emit HDR before calling it.
*/ /* HDR out.
* We can't leave this to comm_handle() because we must
* fill in the cookie.
*/
// 以下开始填充要发送的回应包信息 zero(reply_buffer);
init_pbs(&reply_stream, reply_buffer, sizeof(reply_buffer), "reply packet");
{
// 填充ISAKMP头部
struct isakmp_hdr r_hdr = md->hdr; r_hdr.isa_flags &= ~ISAKMP_FLAG_COMMIT; /* we won't ever turn on this bit */
memcpy(r_hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE);
r_hdr.isa_np = ISAKMP_NEXT_SA;
if (!out_struct(&r_hdr, &isakmp_hdr_desc, &reply_stream, &md->rbody))
return STF_INTERNAL_ERROR;
} // 填充SA 结构信息
/* start of SA out */
{
struct isakmp_sa r_sa = sa_pd->payload.sa; /* if we to send any VID, then set the NEXT payload correctly */
r_sa.isasa_np = numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_struct(&r_sa, &isakmp_sa_desc, &md->rbody, &r_sa_pbs))
return STF_INTERNAL_ERROR;
} /* SA body in and out */
/*解析对方的SA,根据他的建议载荷来确定本地是否支持,如果支持则回复支持的算法信息,并构建应答SA*/
RETURN_STF_FAILURE(parse_isakmp_sa_body(&sa_pd->pbs, &sa_pd->payload.sa
, &r_sa_pbs, FALSE, st)); /*填充VID*/
if (SEND_PLUTO_VID || openpgp_peer)
{
char *vendorid = (openpgp_peer) ?
pgp_vendorid : pluto_vendorid; next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_generic_raw(next, &isakmp_vendor_id_desc, &md->rbody
, vendorid, strlen(vendorid), "Vendor ID"))
return STF_INTERNAL_ERROR;
} /*
* NOW SEND VENDOR ID payloads
*/
/*填充DPD*/
/* Announce our ability to do RFC 3706 Dead Peer Detection */
next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if( !out_vid(next, &md->rbody, VID_MISC_DPD))
return STF_INTERNAL_ERROR; #ifdef XAUTH
/* If XAUTH is required, insert here Vendor ID */
if(c->spd.this.xauth_server || c->spd.this.xauth_client)
{
next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
if (!out_vendorid(next, &md->rbody, VID_MISC_XAUTH))
return STF_INTERNAL_ERROR;
}
#endif
#ifdef NAT_TRAVERSAL
DBG(DBG_NATT, DBG_log("sender checking NAT-T: %d and %d"
, nat_traversal_enabled
, md->quirks.nat_traversal_vid));
/*填充NAT-T的VID*/
if (md->quirks.nat_traversal_vid && nat_traversal_enabled) { next = --numvidtosend ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE;
/* reply if NAT-Traversal draft is supported */
/*nat_traversal_vid_to_method:将VID转换为对应的标准*/
st->hidden_variables.st_nat_traversal = nat_traversal_vid_to_method(md->quirks.nat_traversal_vid);
/*填充VID载荷,这里的NAT-T VID选择的是对方发来的最大的NAT-T 的VID,也就是说选用最新的NAT-T标准*/
if ((st->hidden_variables.st_nat_traversal) && (!out_vendorid(next,
&md->rbody, md->quirks.nat_traversal_vid))) {
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_inI1_outR1 (num=%d)", numvidtosend);
}
#endif
/*应答报文封装完毕,填充长度字段*/
close_message(&md->rbody); /* save initiator SA for HASH */
clonereplacechunk(st->st_p1isa, sa_pd->pbs.start, pbs_room(&sa_pd->pbs), "sa in main_inI1_outR1()"); return STF_OK;
}

4. parse_isakmp_sa_body()源码

暂略。

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

  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_inI3_outR3()

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

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

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

  5. openswan协商流程之(四):main_inI2_outR2()

    主模式第四包:main_inI2_outR2 1. 序言 main_inI2_outR2()函数是ISAKMP协商过程中第四包的核心处理函数的入口,同时在此处理流程中已经获取到足够的隧道信息,可以生成 ...

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

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

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

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

  8. Java中的流程控制(二)

    关于Java程序的流程控制(二) 关于Java程序的流程控制(二) 3.switch选择语句 switch语句用于将一个表达式的值同许多其他值比较,并按比较结果选择下面该执行哪些语句. switch( ...

  9. Android Framework层Power键关机流程(二,关机流程)

    二,关机流程 从前一篇博文我们知道,当用户长按Power键时会弹出(关机.重启,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框.那么从选项对话框到关机确认对话框又是一个什么流程呢.下面我 ...

随机推荐

  1. SQL语句(四)联表查询

    目录 一.关联查询的分类 按年代分 按功能分 二.sql92语法的连接 语法 1. 简单应用 2. 为表起别名 3. 加入筛选 4. 加入分组 5. 三表连接 6. 非等值连接 7. 自连接 三.sq ...

  2. Mybatis学习笔记-第一个Mybatis程序

    思路 搭建环境 搭建数据库(略) CREATE DDATABASE CREATE TABLE INSERT VALUES 新建项目 普通Maven项目 删除src文件夹 --> 建立父工程 导入 ...

  3. C++ 2(将类分文件) //点和圆的关系 //设计一个圆形类 和一个点类 计算点和圆的关系 //点到圆心的距离 == 半径 点在圆上 //点到圆心的距离 > 半径 点在圆外 //点到圆心的距离 < 半径 点在圆内 //点到圆心的距离 获取 ....... (x1 -x2)^2 + (y1-y2)^2 开根号 和半径对比 // 计算 可以 两边同时 平方

    1 源文件 main.cpp 2 //点和圆的关系 3 //设计一个圆形类 和一个点类 计算点和圆的关系 4 //点到圆心的距离 == 半径 点在圆上 5 //点到圆心的距离 > 半径 点在圆外 ...

  4. 【剑指offer】59 - I. 滑动窗口的最大值

    剑指 Offer 59 - I. 滑动窗口的最大值 知识点:队列:滑动窗口:单调 题目描述 给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值. 示例 输入: nums = [ ...

  5. curl的基本使用

    基本使用 1. 初始化 初始化非常简单,只需要调用curl_init()函数即可,他会返回一个curl句柄,后边几乎其他关于curl的设置,关闭等函数都需要使用这个句柄 $curl = curl_in ...

  6. 将vim打造成Java IDE

    需要的插件列表: Taglist Conque Shell FuzzyFinder NERDTree javaComplete 其他选项: JavaKit exVim winmanager (Depr ...

  7. 毕业设计java实验室预约管理系统SSH机房预约系统javaweb机房实验室排课系统mysql机房管理系统 实验室管理系统 课程设计 代码讲解 调试运行

    毕业设计java实验室预约管理系统SSH机房预约系统javaweb机房实验室排课系统mysql机房管理系统 实验室管理系统 课程设计 代码讲解 调试运行 注意:该项目只展示部分功能,如需了解,评论区咨 ...

  8. 如何用Git上传项目到GitHub

    1.登录gitHub,进入主页面,点击"+"号,建立新仓库. 2. 输入自己的仓库名,和简单的描述,根据自己设置为公开的或私有的. 我输入的是仓库名为ESMS. 勾选此选项,rea ...

  9. Guava入门第一章(Joiner)

    Guava是什么? Guava是一种基于开源的Java库,Google Guava源于2007年的"Google Collections Library".这个库是为了方便编码,并 ...

  10. 【笔记】主成分分析法PCA的原理及计算

    主成分分析法PCA的原理及计算 主成分分析法 主成分分析法(Principal Component Analysis),简称PCA,其是一种统计方法,是数据降维,简化数据集的一种常用的方法 它本身是一 ...