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 ...
随机推荐
- Hadoop 3.1.1 - Yarn 服务 - 快速开始
快速开始 本文描述了如何用 Yarn 服务框架在 Yarn 上部署服务. 配置和启动 HDFS 和 Yarn 组件 首先启动 HDFS 和 Yarn 的各个组件.为启用 Yarn 服务框架,添加以下参 ...
- YOLO-V4 实现口罩识别(附加数据、数据批量处理程序)
一.YOLO-v4概念 如果想要了解和认识yolo-v4的基本概念,首先要提的就是它的基础版本yolo-v1,对于yolo来说,最经典的算是yolo-v3.如果想要了解它的由来和历史的话,可以自行搜索 ...
- 学习笔记:数学-GCD与LCM-素数筛法
筛法 埃筛 埃拉托斯特尼筛法的缩写,EraSieve (这个英文其实是为了方便做函数名不要再写shake了) 它的核心思想其实是当确认了一个数是质数以后,把它的所有倍数打上标记说这玩意不是质数.那现在 ...
- AJAX的学习与使用>前端技术系列
目录 AJAX的学习与使用 什么是AJAX 为什么要使用AJAX AJAX接收服务器响应数据的3种格式 文本格式(重要) JSON格式(重要) 服务器端响应实体类JSON格式的3种方式 修改实体类的t ...
- python之(mysql数据库操作)
前言:关心3步骤(此文章只针对python自动化根基展开描述) 什么是mysql数据库操作? 答:利用python对mysql数据库进行增, 删, 改, 查 操作 为什么要用python对mysql ...
- 40k*16 薪,五年Android开发4轮面试拿下腾讯 Offer !(附真题)
概述 感觉毕业后时间过得真快啊,从16年6月本科毕业入职了一家不大的公司,到现在快五年了,前段时间金三银四想着找一个新的工作,前前后后花了一个多月的时间复习以及面试,前几天拿到了腾讯的offer,想把 ...
- Typora加七牛云实现实时图片自动上传
Typora加七牛云实现实时图片自动上传 前言: Typora是一款轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别.重点是免费! 其风格简约 ...
- 【加解密】使用CFSSL生成证书并使用gRPC验证证书
写在前面的话 CFSSL是CloudFlare旗下的PKI/TLS工具.可以用于数字签名,签名验证和TLS证书捆绑的命令行工具和HTTP API服务器. 是使用golang语言开发的证书工具. 官方地 ...
- Windows莫名内存到百分之百,需要修改虚拟内存
借鉴别人的操作: https://blog.csdn.net/xjpdf10/article/details/82849112
- final、finally与finalize的区别?
一.final.finally与finalize的区别 final:final是一个修饰符,可以修饰类,方法和变量.final修饰类表示类不能被其它类继承,并且该类中的所有方法都会隐式的被final修 ...