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发送状态机分析的更多相关文章

  1. osip状态机分析

    转载于:http://blog.csdn.net/lbc2100/article/details/48342889 OSIP的核心是系统状态机,在不同情况下,系统处于不同的状态,在某一状态下当系统发生 ...

  2. twitter storm源码走读之2 -- tuple消息发送场景分析

    欢迎转载,转载请注明出处源自徽沪一郎.本文尝试分析tuple发送时的具体细节,本博的另一篇文章<bolt消息传递路径之源码解读>主要从消息接收方面来阐述问题,两篇文章互为补充. worke ...

  3. Memcached 状态机分析

    worker线程拿到了这个连接之后,就应该是分配给这个连接一个结构体,包括这个连接所有的状态,都写buf等,这个结构体就是conn,然后这个worker线程会在它自己的event_base加入对这个新 ...

  4. Thrift线程和状态机分析

    目录 目录 1 1. 工作线程和IO线程 1 2. TNonblockingServer::TConnection::transition() 2 3. RPC函数被调用过程 3 4. 管道和任务队列 ...

  5. Springboot邮件发送思路分析

    毕业设计里需要邮件发送,所以学习,总的来讲,我考虑以下几点, 代码量少,代码简单.配置少,一看就懂,使用 JavaMail 太麻烦了. 异步执行,添加员工之后会发送入职邮件, 多线程处理,设计里有一个 ...

  6. ActiveMQ(2)---ActiveMQ原理分析之消息发送

    持久化消息和非持久化消息的发送策略 消息同步发送和异步发送 ActiveMQ支持同步.异步两种发送模式将消息发送到broker上.同步发送过程中,发送者发送一条消息会阻塞直到broker反馈一个确认消 ...

  7. capwap协议重点分析

    一.     CAPWAP概述 CAPWAP由两个部分组成:CAPWAP协议和无线BINDING协议. (1)CAPWAP协议是一个通用的隧道协议,完成AP发现AC等基本协议功能,和具体的无线接入技术 ...

  8. freemodbus-v1.5.0 源码分析

    注:转载请注明出处   http://www.cnblogs.com/wujing-hubei/p/5935142.html FreeModbus协议栈作为从机,等待主机传送的数据,当从机接收到一帧完 ...

  9. [RM 状态机详解4] RMNode状态机详解

    摘要 RMNode状态机是ResourceManager的四个状态机(RMApp,RMAppAttempt,RMContainer,RMNode)中最简单的一个,状态机如图1所示.RMNode是Res ...

随机推荐

  1. Jupyter Kernel Error 解决办法

    首先在pycharm的terminal中输入 python -m ipykernel install --user 之后会如下图显示: 这就是kernel的安装路径. 然后输入: jupyter ke ...

  2. MySQL 执行优化查询

    查询执行的基础 当希望MySQL能够以更高的性能运行查询时,最好的办法就是弄清楚MySQL是如何优化和执行查询的.当向MySQL发送一个请求的时候,MySQL执行过程如图1-1所示: 图1-1 查询执 ...

  3. Spring源码阅读-BeanFactory体系结构分析

    BeanFactory是Spring中非常重要的一个类,搞懂了它,你就知道了bean的初始化和摧毁过程,对于深入理解IOC有很大的帮助. BeanFactory体系结构 首先看一下使用IDEA生成的继 ...

  4. Java-Stream流方法学习及总结

    1 前言 Stream是一个来自数据源的元素队列并支持聚合操作,其中具有以下特性: Stream只负责计算,不存储任何元素,元素是特定类型的对象,形成一个队列 数据源可以实集合.数组.I/O chan ...

  5. 跟我一起写 Makefile(三)

    Makefile 总述 ------- 一.Makefile里有什么? Makefile里主要包含了五个东西:显式规则.隐晦规则.变量定义.文件指示和注释. 1.显式规则.显式规则说明了,如何生成一个 ...

  6. murmur3 hash(hash算法)

    HashUtil.java package com.example.test.util; import com.google.common.base.Charsets; import com.goog ...

  7. WPF在圆上画出刻度线

    思路 我们可以使用Ellipse先画出一个圆当背景,然后用Canvas再叠加画上刻度线,就能得到如下的效果 我们先用Ellipse画一个橙色的圆,然后将Canvas的宽度和高度绑定到Ellipse的宽 ...

  8. Windows内核基础知识-1-段寄存器

    Windows内核基础知识-1-段寄存器 学过汇编的应该都知道段寄存器,在Windows里段寄存器有很多,之前可能只接触了ds数据段,cs 代码段这种,今天这个博客就介绍Windows一些比较常用的段 ...

  9. NOIP 模拟 $16\; \rm Lost My Music$

    题解 \(by\;zj\varphi\) 一道凸包的题 设 \(\rm dep_u\) 表示节点 \(u\) 的深度,那么原式就可化为 \(-\frac{c_v-c_u}{dep_v-dep_u}\) ...

  10. NOIP 模拟 $14\; \text{影魔}$

    题解 \(by\;\;zj\varphi\) 不是原题 一道(对我来说)很需要技巧的题 对于颜色数如何处理 离线,将子树转化为 \(dfs\) 序,但这种做法无法处理深度 我们按照深度加点(可以通过 ...