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. 无法解析插件 org.apache.maven.plugins:maven-clean-plugin:2.5

    在Idea创建项目中,出现7出错误,告诉我 无法解析插件 org.apache.maven.plugins:maven-clean-plugin:2.5 但是在maven设置中都一致 后来加了几个镜像 ...

  2. Android 键盘属性

    键盘定义:通过Keyboard说明是一个软键盘定义文件,Row元素说明这是一行按键的定义,Key元素说明这是一个按键的定义.Key元素通过一些属性来定义每个按键,下面是一些常用的属性介绍: Codes ...

  3. Kubernetes安装报错总结

    1.kubeadm  init初使化报错 [root@k8s01 ~]# kubeadm  init --kubernetes-version=v1.13.3 --pod-network-cidr=1 ...

  4. MySQL 创建高性能索引

    索引是存储引擎用于快速找到记录的一种数据结构.除了加速查找,索引在其他方面也有一些有用的属性.索引对于良好的性能非常关键.尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要.在数据量较小且负载较 ...

  5. NTP\rsync+inotify

    NTP网络时间协议 NTP(Network Time Protocol)网络时间协议基于UDP,用于网络时间同步的协议,使网络中的计算机时钟同步到UTC(世界统一时间),再配合各个时区的偏移调整就能实 ...

  6. UVa120 煎饼(选择排序思想)

    题目背景 给你一迭薄煎饼,请你写一个程式来指出要如何安排才能使这些薄煎饼由上到下依薄煎饼的半径由小到大排好.所有的薄煎饼半径均不相同. 要把薄煎饼排好序需要对这些薄煎饼做翻面(flip)的动作.方法是 ...

  7. Clipboard Manager on Xfce

    Clipman-plugin sudo apt-get install xfce4-clipman-plugin No config function. No hotkey. Very basic f ...

  8. Axure RP 9 安装

    安装好的样子 官方安装包下载地址(速度较慢) 下载Axure RP 9 MAC版:https://axure.cachefly.net/AxureRP-Setup.dmg 下载Axure RP 9 P ...

  9. 用python 30行代码,搞定一个简单截图调取的百度识字功能

    在做一个数据标注过程中人工需要识别文字. 想了想写了一个小脚本, 大致过程这样的. 截图功能写了好久也没写明白,索性直接调用第三方的截图工具了,在采用qq或者微信截图时,截图完成后保存大致保存在剪切板 ...

  10. STP工作流程

    第一步:选择一个根网桥: 第二步:在每个非根网乔上选举一个根端口: 第三步:在每个网段上选举一个指定端口: 第四步:阻塞非根,非指定端口: