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. 8.8考试总结(NOIP模拟33)[Hunter·Defence·Connect]

    无法逃避的是自我,而无法挽回的是过去. 前言 还算可以,不过 T1 少 \(\bmod\) 了一下挂了 25pts,T2 没看清题面挂了 27pts. 下回注意吧.. T1 Hunter 解题思路 感 ...

  2. Linux从头学06:16张结构图,彻底理解【代码重定位】的底层原理

    作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...

  3. Android全新UI编程 - Jetpack Compose 超详细教程

    1. 简介 Jetpack Compose是在2019Google i/O大会上发布的新的库.Compose库是用响应式编程的方式对View进行构建,可以用更少更直观的代码,更强大的功能,能提高开发速 ...

  4. 为什么npm install 经常失败

    Hello 您好,我是大粽子.深耕线上商城的攻城狮(程序员)一枚. 前言 这段时间真的是忙,最近能抽时间搞搞大家在自己环境中遇到的各种问题了,我呢就是见不得我的代码在你的电脑运行不起来的.就像姜子牙睡 ...

  5. Java8 新特性 Stream Api 之集合遍历

    前言 随着java版本的不断更新迭代,java开发也可以变得甜甜的,最新版本都到java11了,但是后面版本也是不在提供商用支持,需要收费,但是java8 依然是持续免费更新使用的,后面版本也更新很快 ...

  6. thunderbird发送纯文本邮件

    向邮件列表中发邮件时,要求邮件格式必须是纯文本格式的,在thunderbird中,邮件格式(plain text或者html格式)在[工具->账户设置->[账户名称]->通讯录]下的 ...

  7. SpringMVC学习09(文件的上传和下载)

    文件上传和下载 准备工作 文件上传是项目开发中最常见的功能之一 ,springMVC 可以很好的支持文件上传,但是SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况 ...

  8. Python日志记录

    官方文档:https://docs.python.org/2/library/logging.html logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为, ...

  9. uniapp scroll-view 组件横向滑动失效(ios问题出的最多)

    注意事项(做好以下几点就很难出问题): 一.scroll-view组件必须有固定高度,不可出现高度坍塌或让高度消失等现象;(重中之重) 二.一般问题出的多的就是在nvue环境下去使用的scroll-v ...

  10. 题解 P4449 于神之怒加强版

    这道题算是我完完整整推的第一道题,写篇题解纪念一下. 题目 废话不多说,直接开始推式子(给新手准备,过程较详细,大佬可自行跳步),以下过程中均假设 \((n\le m)\),\([d=1]\) 类似于 ...