openswan发送状态机分析
openswan发送状态机分析
1. 函数调用关系
2. 函数说明
如果按用户空间、内核空间划分的话,此部分代码更多是运行在内核空间的。
2.1 ipsec_tunnel_init_devices()
该函数主要用来初始化网络设备信息。
int
ipsec_tunnel_init_devices(void)
{
int i;
int error;
/*打印调试信息*/
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
"klips_debug:ipsec_tunnel_init_devices: "
"creating and registering IPSEC_NUM_IF=%u devices, allocating %lu per device, IFNAMSIZ=%u.\n",
IPSEC_NUM_IF,
(unsigned long) (sizeof(struct net_device) + IFNAMSIZ),
IFNAMSIZ);
/*依次创建接口信息*/
for(i = 0; i < IPSEC_NUM_IF; i++) {
error = ipsec_tunnel_createnum(i);
if(error) break;
}
return 0;
}
2.2 ipsec_tunnel_createnum()
该函数主要的功能为:实现一个网络设备。 主要包含如下流程:
- 申请网络设备资源
- 绑定网络设备的操作方法集(驱动层的常见用法)
- 注册网络设备到内核
/*实现一个网络设备:申请资源、绑定操作方法集、注册网络设备*/
int
ipsec_tunnel_createnum(int ifnum)
{
char name[IFNAMSIZ];
struct net_device *dev_ipsec;
int vifentry;
/*不得超过最大接口数*/
if(ifnum >= IPSEC_NUM_IFMAX) {
return -ENOENT;
}
/*网络设备是否已经存在,如果存在则错误退出*/
if(ipsecdevices[ifnum]!=NULL) {
return -EEXIST;
}
/* no identical device *//*记录当前设备接口数*/
if(ifnum > ipsecdevices_max) {
ipsecdevices_max=ifnum;
}
vifentry = ifnum;
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
"klips_debug:ipsec_tunnel_init_devices: "
"creating and registering IPSEC_NUM_IF=%u device\n",
ifnum);
sprintf(name, IPSEC_DEV_FORMAT, ifnum);
/*根据内核版本来获取网络设备结构体*/
#ifdef ALLOC_NETDEV4
dev_ipsec = alloc_netdev(sizeof(struct ipsecpriv),name,
NET_NAME_UNKNOWN,ipsec_tunnel_netdev_setup);
#elif defined(alloc_netdev)
dev_ipsec = alloc_netdev(sizeof(struct ipsecpriv), name, ipsec_tunnel_netdev_setup);
#else/*最低档次的版本,使用kmalloc分配空间*/
dev_ipsec = (struct net_device*)kmalloc(sizeof(struct net_device), GFP_KERNEL);
#endif
if (dev_ipsec == NULL) {
printk(KERN_ERR "klips_debug:ipsec_tunnel_init_devices: "
"failed to allocate memory for device %s, quitting device init.\n",
name);
return -ENOMEM;
}
/*如果内核版本太低,则需要手动将申请的内存清空*/
#ifndef alloc_netdev
memset((caddr_t)dev_ipsec, 0, sizeof(struct net_device));
strncpy(dev_ipsec->name, name, sizeof(dev_ipsec->name));
#ifdef PAUL_FIXME
dev_ipsec->next = NULL;
#endif
#endif /* alloc_netdev */
/*由于我选用的内核是4.19.y, 因此USE_NETDEV_OPS是已经定义了,我们走else流程*/
/*用来绑定操作方法集*/
#ifndef USE_NETDEV_OPS
dev_ipsec->init = &ipsec_tunnel_probe;
#else
dev_ipsec->netdev_ops = &klips_device_ops;/*网络设备操作方法集*/
#endif
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
"klips_debug:ipsec_tunnel_init_devices: "
"registering device %s\n",
dev_ipsec->name);
/* reference and hold the device reference */
ipsec_dev_hold(dev_ipsec);
ipsecdevices[vifentry]=dev_ipsec;
/*注册网络设备*/
if (register_netdev(dev_ipsec) != 0) {
KLIPS_PRINT(1 || debug_tunnel & DB_TN_INIT,
"klips_debug:ipsec_tunnel_init_devices: "
"registering device %s failed, quitting device init.\n",
dev_ipsec->name);
return -EIO;
} else {
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
"klips_debug:ipsec_tunnel_init_devices: "
"registering device %s succeeded, continuing...\n",
dev_ipsec->name);
}
return 0;
}
2.3 ipsec_tunnel_probe()
这个函数,如果再较高的内核版中(2.6.30以后)是在驱动安装加载过程中最先执行的(作为net_device_ops的回调函数执行的)。
主要功能:
完成网络设备的初始化。 实际上网络设备的初始化最主要的工作就是:实现若干个函数指针。这也是其他驱动设备、网络设备的都需要的操作。
int
ipsec_tunnel_probe(struct net_device *dev)
{
ipsec_tunnel_init(dev);//隧道初始化
return 0;
}
int
ipsec_tunnel_init(struct net_device *dev)
{
int i;
struct ipsecpriv *iprv;
... ...
#ifdef alloc_netdev
dev->destructor = free_netdev;
#endif
... ...
#ifdef HAVE_NETDEV_HEADER_OPS
dev->header_ops = NULL;
#else
dev->hard_header = NULL;
dev->rebuild_header = NULL;
dev->header_cache_update= NULL;
#endif
#ifdef HAVE_NET_DEVICE_OPS
dev->netdev_ops = &klips_device_ops; /**/
#else
dev->open = ipsec_tunnel_open;
dev->stop = ipsec_tunnel_close;
dev->hard_start_xmit = ipsec_tunnel_start_xmit;/*发送数据时的函数接口*/
dev->get_stats = ipsec_tunnel_get_stats;
#ifdef HAVE_SET_MAC_ADDR
dev->set_mac_address = NULL;
#endif
dev->do_ioctl = ipsec_tunnel_ioctl;
dev->neigh_setup = ipsec_tunnel_neigh_setup_dev;
#endif
dev->hard_header_len = 0;
dev->mtu = 0;
dev->addr_len = 0;
dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ /* ARPHRD_ETHER; */
dev->tx_queue_len = 10; /* Small queue */
#ifdef IFF_XMIT_DST_RELEASE
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
#endif
memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */
/* New-style flags. */
dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */;
/* We're done. Have I forgotten anything? */
return 0;
}
这里可能有疑问:为什么只实现了发送函数指针,而没有接口函数指针。这是因为数据的接收采用的硬件中断的方式(最原始的时候)进行统一的接收处理,而不再单独实现。
以上为IPSec模块在驱动加载过程中的部分处理流程。它实现了一个操作方法集,其中就包括网络设备的方法函数hard_start_xmit
, 接下来主要说明发送数据时的处理过程。
3.0 网络设备发送报文处理流程
3.1 ipsec_tunnel_start_xmit()
在2.3节已经知道,在申请网络设备时,会注册实现多个函数指针,其中一个便是dev->hard_start_xmit
,这个函数指针是注册到驱动中的,经由该网口转发的报文都会调用到该函数指针。IPSec数据报文便是在这个函数中进行的封装。
/*
* This function assumes it is being called from dev_queue_xmit()
* and that skb is filled properly by that function.
*/
int
ipsec_tunnel_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ipsec_xmit_state *ixs = NULL;
enum ipsec_xmit_value stat;
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
"\n\nipsec_tunnel_start_xmit: STARTING");
stat = IPSEC_XMIT_ERRMEMALLOC;
/*创建一个新的发送状态对象*/
ixs = ipsec_xmit_state_new(dev);
if(ixs == NULL)
return NETDEV_TX_BUSY;
ixs->dev = dev;
ixs->skb = skb;
/*网络设备正确性检查*/
stat = ipsec_xmit_sanity_check_ipsec_dev(ixs);
if(stat != IPSEC_XMIT_OK) {
goto cleanup;
}
/*网络数据包(skb)的正确性检查:1.是否支持V6, 是否需要支持IP选项字段*/
stat = ipsec_xmit_sanity_check_skb(ixs);
if(stat != IPSEC_XMIT_OK) {
goto cleanup;
}
/*检测并标识报文中是否包含二层头部*/
stat = ipsec_tunnel_strip_hard_header(ixs);
if(stat != IPSEC_XMIT_OK) {
goto cleanup;
}
/*查询IPSec SA*/
stat = ipsec_tunnel_SAlookup(ixs);
if(stat != IPSEC_XMIT_OK) {
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
"klips_debug:ipsec_tunnel_start_xmit: SAlookup failed: %d\n",
stat);
goto cleanup;
}
/*报文封装完毕后(ipsec_xsm状态机),将报文发送出去*/
ixs->xsm_complete = ipsec_tunnel_xsm_complete;
ipsec_xsm(ixs);/*发送状态机*/
return 0;
cleanup:
ipsec_xmit_cleanup(ixs);
ipsec_xmit_state_delete(ixs);
return 0;
}
3.2 ipsec_tunnel_xsm_complete()
void
ipsec_tunnel_xsm_complete(
struct ipsec_xmit_state *ixs,
enum ipsec_xmit_value stat)
{
unsigned char nexthdr;
int nexthdroff;
if(stat != IPSEC_XMIT_OK) {
if(stat == IPSEC_XMIT_PASS) {
goto bypass;
}
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
"klips_debug:ipsec_tunnel_start_xmit: encap_bundle failed: %d\n",
stat);
goto cleanup;
}
... ...
{
nexthdr = osw_ip4_hdr(ixs)->protocol;
nexthdroff = 0;
if ((ntohs(osw_ip4_hdr(ixs)->frag_off) & IP_OFFSET) == 0)
nexthdroff = (ixs->iph + (osw_ip4_hdr(ixs)->ihl<<2)) -
(void *)ixs->skb->data;
ixs->matcher.sen_type = SENT_IP4;
ixs->matcher.sen_ip_src.s_addr = osw_ip4_hdr(ixs)->saddr;
ixs->matcher.sen_ip_dst.s_addr = osw_ip4_hdr(ixs)->daddr;
ixs->matcher.sen_proto = nexthdr;
}
/*提取封装后的端口信息*/
ipsec_extract_ports(ixs->skb, nexthdr, nexthdroff, &ixs->matcher);
spin_lock_bh(&eroute_lock);
/*重新查找eroute路由表*/
ixs->eroute = ipsec_findroute(&ixs->matcher);
if(ixs->eroute) {
ixs->outgoing_said = ixs->eroute->er_said;
ixs->eroute_pid = ixs->eroute->er_pid;
ixs->eroute->er_count++;
ixs->eroute->er_lasttime = jiffies/HZ;
}
spin_unlock_bh(&eroute_lock);
/*ipsec_xmit_init1中实现ixs->orgeds的初始化*/
if (/*((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc))*/
ip_address_cmp(&ixs->orgedst, &ixs->outgoing_said.dst) != 0 &&
!ip_address_isany(&ixs->outgoing_said.dst) &&
ixs->eroute) {
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
"klips_debug:ipsec_tunnel_start_xmit: "
"We are recursing here.\n");
ipsec_xsm(ixs);/*如果需要(再次匹配到其他的eroute),再次进入状态机进行封装*/
return;
}
#ifdef NAT_TRAVERSAL
stat = ipsec_nat_encap(ixs);/*如果中间经过了NAT,需要NAT-T封装*/
if(stat != IPSEC_XMIT_OK) {
goto cleanup;
}
#endif
stat = ipsec_tunnel_restore_hard_header(ixs);/*添加二层头*/
if(stat != IPSEC_XMIT_OK) {
goto cleanup;
}
bypass:
stat = ipsec_tunnel_send(ixs);/*发送报文*/
cleanup:
ipsec_xmit_cleanup(ixs);
ipsec_xmit_state_delete(ixs);
}
openswan发送状态机分析的更多相关文章
- osip状态机分析
转载于:http://blog.csdn.net/lbc2100/article/details/48342889 OSIP的核心是系统状态机,在不同情况下,系统处于不同的状态,在某一状态下当系统发生 ...
- twitter storm源码走读之2 -- tuple消息发送场景分析
欢迎转载,转载请注明出处源自徽沪一郎.本文尝试分析tuple发送时的具体细节,本博的另一篇文章<bolt消息传递路径之源码解读>主要从消息接收方面来阐述问题,两篇文章互为补充. worke ...
- Memcached 状态机分析
worker线程拿到了这个连接之后,就应该是分配给这个连接一个结构体,包括这个连接所有的状态,都写buf等,这个结构体就是conn,然后这个worker线程会在它自己的event_base加入对这个新 ...
- Thrift线程和状态机分析
目录 目录 1 1. 工作线程和IO线程 1 2. TNonblockingServer::TConnection::transition() 2 3. RPC函数被调用过程 3 4. 管道和任务队列 ...
- Springboot邮件发送思路分析
毕业设计里需要邮件发送,所以学习,总的来讲,我考虑以下几点, 代码量少,代码简单.配置少,一看就懂,使用 JavaMail 太麻烦了. 异步执行,添加员工之后会发送入职邮件, 多线程处理,设计里有一个 ...
- ActiveMQ(2)---ActiveMQ原理分析之消息发送
持久化消息和非持久化消息的发送策略 消息同步发送和异步发送 ActiveMQ支持同步.异步两种发送模式将消息发送到broker上.同步发送过程中,发送者发送一条消息会阻塞直到broker反馈一个确认消 ...
- capwap协议重点分析
一. CAPWAP概述 CAPWAP由两个部分组成:CAPWAP协议和无线BINDING协议. (1)CAPWAP协议是一个通用的隧道协议,完成AP发现AC等基本协议功能,和具体的无线接入技术 ...
- freemodbus-v1.5.0 源码分析
注:转载请注明出处 http://www.cnblogs.com/wujing-hubei/p/5935142.html FreeModbus协议栈作为从机,等待主机传送的数据,当从机接收到一帧完 ...
- [RM 状态机详解4] RMNode状态机详解
摘要 RMNode状态机是ResourceManager的四个状态机(RMApp,RMAppAttempt,RMContainer,RMNode)中最简单的一个,状态机如图1所示.RMNode是Res ...
随机推荐
- 8.8考试总结(NOIP模拟33)[Hunter·Defence·Connect]
无法逃避的是自我,而无法挽回的是过去. 前言 还算可以,不过 T1 少 \(\bmod\) 了一下挂了 25pts,T2 没看清题面挂了 27pts. 下回注意吧.. T1 Hunter 解题思路 感 ...
- Linux从头学06:16张结构图,彻底理解【代码重定位】的底层原理
作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...
- Android全新UI编程 - Jetpack Compose 超详细教程
1. 简介 Jetpack Compose是在2019Google i/O大会上发布的新的库.Compose库是用响应式编程的方式对View进行构建,可以用更少更直观的代码,更强大的功能,能提高开发速 ...
- 为什么npm install 经常失败
Hello 您好,我是大粽子.深耕线上商城的攻城狮(程序员)一枚. 前言 这段时间真的是忙,最近能抽时间搞搞大家在自己环境中遇到的各种问题了,我呢就是见不得我的代码在你的电脑运行不起来的.就像姜子牙睡 ...
- Java8 新特性 Stream Api 之集合遍历
前言 随着java版本的不断更新迭代,java开发也可以变得甜甜的,最新版本都到java11了,但是后面版本也是不在提供商用支持,需要收费,但是java8 依然是持续免费更新使用的,后面版本也更新很快 ...
- thunderbird发送纯文本邮件
向邮件列表中发邮件时,要求邮件格式必须是纯文本格式的,在thunderbird中,邮件格式(plain text或者html格式)在[工具->账户设置->[账户名称]->通讯录]下的 ...
- SpringMVC学习09(文件的上传和下载)
文件上传和下载 准备工作 文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况 ...
- Python日志记录
官方文档:https://docs.python.org/2/library/logging.html logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为, ...
- uniapp scroll-view 组件横向滑动失效(ios问题出的最多)
注意事项(做好以下几点就很难出问题): 一.scroll-view组件必须有固定高度,不可出现高度坍塌或让高度消失等现象;(重中之重) 二.一般问题出的多的就是在nvue环境下去使用的scroll-v ...
- 题解 P4449 于神之怒加强版
这道题算是我完完整整推的第一道题,写篇题解纪念一下. 题目 废话不多说,直接开始推式子(给新手准备,过程较详细,大佬可自行跳步),以下过程中均假设 \((n\le m)\),\([d=1]\) 类似于 ...