上篇分析到数据包的收发,这篇开始着手分析数据包的处理问题。在openVswitch中数据包的处理是其核心技术,该技术分为三部分来实现:第一、根据skb数据包提取相关信息封装成key值;第二、根据提取到key值和skb数据包进行流表的匹配;第三、根据匹配到的流表做相应的action操作(若没匹配到则调用函数往用户空间传递数据包);其具体的代码实现在 datapath/datapath.c 中的,函数为: void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb);当接受到一个数据包后,自然而然的就应该是开始对其进行处理了。所以其实在上篇的openVswitch(OVS)源代码分析之工作流程(收发数据包)中的接受数据包函数:void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)中已有体现,该函数在最后调用了ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb);来把数据包传递到该函数中去进行处理。也由此可见所有进入到openVswitch的数据包都必须经过ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb);函数的处理。所以说ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb);是整个openVswitch的中间枢纽,是openVswitch的核心部分。

对于ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb);的重要性已经解释的非常清楚,紧接着就应该分析该函数源代码了,在分析源代码之前还是得提醒下,其中涉及到很多数据结构,如果有些陌生可以到openVswitch(OVS)源代码分析之数据结构中进行查阅,最好能先大概的看下那文章,了解下其中的数据结构,对以后分析源代码有很大的帮助。

下面来分析几个ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb);函数中涉及到但在openVswitch(OVS)源代码分析之数据结构又没有分析到的数据结构:

第一个、是数据包的统计结构体,是CPU用来对所有数据的一个统计作用:

  1. // CPU对给定的数据包处理统计
  2. struct dp_stats_percpu {
  3. u64 n_hit; // 匹配成功的数据包个数
  4. u64 n_missed; // 匹配失败的数据包个数,n_hit + n_missed就是接受到的数据包总和
  5. u64 n_lost; // 丢失的数据包个数(可能是datapath队列溢出导致)
  6. struct u64_stats_sync sync;
  7. };

第二、是数据包发送到用户空间的参数结构体,在匹配流表没有成功时,数据将发送到用户空间。而内核空间和用户空间进行数据交互是通过netLinks来实现的,所以这个函数就是为了实现netLink通信而设置的一些参数:

  1. // 把数据包传送给用户空间所需的参数结构体
  2. struct dp_upcall_info {
  3. u8 cmd; // 命令,OVS_PACKET_CMD_ *之一
  4. const struct sw_flow_key *key; // key值,不能为空
  5. const struct nlattr *userdata; // 数据的大小,若为空,OVS_PACKET_ATTR_USERDATA传送到用户空间
  6. u32 portid; // 发送数据包的Netlink的PID,其实就是netLink通信的id号
  7. };

下面是来分析ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb);函数的实现源代码:

  1. // 数据包的处理函数,openVswitch的核心部分
  2. void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
  3. {
  4. struct datapath *dp = p->dp; // 定义网桥变量,得到端口所在的网桥指针
  5. struct sw_flow *flow; // 流表
  6. struct dp_stats_percpu *stats; // cpu中对数据包的统计
  7. struct sw_flow_key key; // skb中提取到的key值
  8. u64 *stats_counter;
  9. int error;
  10. // 这应该是linux内核中的,openVswitch中很多是根据linux内核设计而来的
  11. // 这里应该是对网桥中的数据包统计属性进行初始化
  12. stats = this_cpu_ptr(dp->stats_percpu);
  13. // 根据端口和skb数据包的相关值进行提取,然后封装成key值
  14. error = ovs_flow_extract(skb, p->port_no, &key);
  15. if (unlikely(error)) { // 定义宏来判断key值得提取封装是否成功
  16. kfree_skb(skb);// 如果没有成功,则销毁掉skb数据包,然后直接退出
  17. return;
  18. }
  19. // 调用函数根据key值对流表中所有流表项进行匹配,把结果返回到flow中
  20. flow = ovs_flow_lookup(rcu_dereference(dp->table), &key);
  21. if (unlikely(!flow)) { // 定义宏判断是否匹配到相应的流表项,如没有,执行下面代码
  22. struct dp_upcall_info upcall; // 定义一个结构体,设置相应的值,然后把数据包发送到用户空间
  23. // 下面是根据dp_upcall_info数据结构,对其成员进行填充
  24. upcall.cmd = OVS_PACKET_CMD_MISS;// 命令
  25. upcall.key = &key;// key值
  26. upcall.userdata = NULL;// 数据长度
  27. upcall.portid = p->upcall_portid;// netLink通信时的id号
  28. ovs_dp_upcall(dp, skb, &upcall);// 把数据包发送到用户空间
  29. consume_skb(skb);// 销毁skb
  30. stats_counter = &stats->n_missed;// 用未匹配到流表项的包数,给计数器赋值
  31. goto out;// goto语句,内部跳转,跳转到out处
  32. }
  33. // 在分析下面的代码时,先看下OVS_CB()这个宏:#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
  34. // 这个宏如果知道skb数据结构的话,就好理解。大概的意思是把skb中保存的当前层协议信息的数据强转为ovs_skb_cb*数据指针
  35. OVS_CB(skb)->flow = flow;// 能够执行到这里,说明匹配到了流表。把匹配到的流表想flow赋值给结构体中成员
  36. OVS_CB(skb)->pkt_key = &key;// 同上,把相应的key值赋值到结构体变量中
  37. //  这是匹配成功的,用匹配成功的数据包数赋值于计数器变量
  38. stats_counter = &stats->n_hit;
  39. ovs_flow_used(OVS_CB(skb)->flow, skb);// 调用函数调整流表项成员变量(也许是用来流表项的更新)
  40. ovs_execute_actions(dp, skb); // 根据匹配到的流表项(已经在skb中的cb)执行相应的action操作
  41. out:
  42. // 这是流表匹配失败,数据包发到用户空间后,跳转到该处,
  43. // 对处理过的数据包数进行调整(虽然没匹配到流表,但也算是处理掉了一个数据包,所以计数器变量应该增加1)
  44. u64_stats_update_begin(&stats->sync);
  45. (*stats_counter)++;
  46. u64_stats_update_end(&stats->sync);
  47. }

上面就是openVswitch的核心部分,所有的数据包都要经过此函数进行逻辑处理。这只是一个逻辑处理的大体框架,还有一些细节(key值得提取,流表的匹配查询,数据传输到用户空间,根据流表执行相应action)将在后面分析。当把整个openVswitch的工作流程梳理清晰,会发现这其实就是openVswitch的头脑部分,所有的逻辑处理都在里实现,所以我们自己添加代码时,这里往往也是个不错的选择。

如果看了前面那篇openVswitch(OVS)源代码分析之工作流程(收发数据包),那么应该记得其中也说到了可以在收发函数中添加自己代码,因为一般来说收发函数也是数据包的必经之地(发送函数可能不是)。那么怎么区分在哪里添加自己代码合适呢?

其实在接受数据包函数中添加自己代码和在这里的逻辑处理函数中添加自己代码,没有多大区别,因为接受函数中没有做什么处理就把数据包直接发送打逻辑处理函数中去了,所以这两个地方添加自己代码其实是没什么区别的。但是从习惯和规范来说,数据包接受函数只是根据条件控制数据包的接受,并不对数据包进行逻辑上的处理,也不会对数据包进行修改等操作。而逻辑处理函数是会对数据包进行某些逻辑上的处理。(最明显的是修改数据包内的数据,一般来说接受数据包函数中是不会对数据包内容修改的,但逻辑处理函数则有可能会去修改的)。

而在数据包发送函数中添加自己代码和逻辑函数中添加自己代码也有些区别,数据包发送函数其性质和接受函数一样,一般不会去修改数据包,而仅仅是根据条件判断该数据包是否发送而已。

那下面就逻辑处理函数中添加代码来举例:

假若要把某个指定的IP主机上发来的ARP数据包进行处理,把所有的请求数据包变成应答数据包,原路返回。这里最好就是把自己的代码添加到逻辑处理函数中去(如果你要强制的添加到数据包接受函数中去也可以),因为这里要修改数据包的内容,是一个逻辑处理。具体实现:可以在key值提取前对数据包进行判断,看是否是ARP数据包,并且是否是指定IP主机发来的。若不是,交给系统去处理;若是,则对Mac地址和IP地址进行交换,并且把请求标识变成应答标识;最后调用发送函数从原来的端口直接发送出去。这只是一个简单的应用,旨在说明逻辑处理代码最好添加到逻辑处理函数中去。如果要处理复杂的操作也是可以的,比如定义自己的流表,然后然后屏蔽掉系统的流表查询,按自己的流表来操作。这就是一个对openVswitch比较大的改造了,流表、action、流表匹配等这些openVswitch主要功能和结构都要自己去定义实现完成。

以上分析得就是openVswitch的核心部分,当然了只是一个大体框架而已,后续将会逐步完善。

转载请注明原文出处,原文地址:http://blog.csdn.net/yuzhihui_no1/article/details/39378195

如有不正确之处,望大家指正,谢谢!!!

openVswitch(OVS)源代码分析之工作流程(数据包处理)的更多相关文章

  1. openVswitch(OVS)源代码分析之工作流程(flow流表查询)

    原文链接: openVswitch(OVS)源代码分析之工作流程(flow流表查询)

  2. openVswitch(OVS)源码分析之工作流程(哈希桶结构体的解释)

    这篇blog是专门解决前篇openVswitch(OVS)源码分析之工作流程(哈希桶结构体的疑惑)中提到的哈希桶结构flex_array结构体成员变量含义的问题. 引用下前篇blog中分析讨论得到的f ...

  3. J2EE进阶(十八)基于留言板分析SSH工作流程

    J2EE进阶(十八)基于留言板分析SSH工作流程   留言板采用SSH(Struts1.2 + Spring3.0 + Hibernate3.0)架构.   工作流程(以用户登录为例):   首先是用 ...

  4. nodejs的Express框架源码分析、工作流程分析

    nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...

  5. Linux内核--网络栈实现分析(二)--数据包的传递过程--转

    转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的”(上 ...

  6. Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地 ...

  7. Monkey源代码分析之执行流程

    在<MonkeyRunner源代码分析之与Android设备通讯方式>中.我们谈及到MonkeyRunner控制目标android设备有多种方法.当中之中的一个就是在目标机器启动一个mon ...

  8. Spark SQL源代码分析之核心流程

    /** Spark SQL源代码分析系列文章*/ 自从去年Spark Submit 2013 Michael Armbrust分享了他的Catalyst,到至今1年多了,Spark SQL的贡献者从几 ...

  9. IM通信协议逆向分析、Wireshark自定义数据包格式解析插件编程学习

    相关学习资料 http://hi.baidu.com/hucyuansheng/item/bf2bfddefd1ee70ad68ed04d http://en.wikipedia.org/wiki/I ...

随机推荐

  1. length()

    在MATLAB中: size:获取数组的行数和列数 length:数组长度(即行数或列数中的较大值) numel:元素总数. s=size(A),当只有一个输出参数时,返回一个行向量,该行向量的第一个 ...

  2. 【c++基础】多个txt文件合并到一个文件的几种方式

    参考 1.windows命令: 2.linux-command; 完

  3. BZOJ1095: [ZJOI2007]Hide 捉迷藏【线段树维护括号序列】【思维好题】

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

  4. 2018-2019-2 《网络对抗技术》Kali安装 Week1 20165212

    2018-2019-2 <网络对抗技术>Kali安装 Week1 20165212 1.完成安装linux kali和vm tools 装的第三遍成功安装.前面两次镜像文件不行,没驱动(网 ...

  5. java1.8操作日期

    java1.8获取年份: int year = Calendar.getInstance().get(Calendar.YEAR); StringBuilder code = new StringBu ...

  6. C# winform 使用DsoFramer 创建 显示office 文档

    使用微软DsoFramer 组件创建,显示office 1. DsoFramer  组件的介绍 dsoframer是微软提供一款开源的用于在线编辑.调用Word. Excel .PowerPoint等 ...

  7. 用 c 写 CGI 程序简要指南

    文章摘要:  CGI规定了Web服务器调用其他可执行程序(CGI程 序)的接口协议标准.Web服务器通过调用CGI程序实现和Web浏览器的交互.CGI程序可以用任何程序设计语言编写,如Shell脚本语 ...

  8. Centos安装git2.2.1

    由于Centos6.5使用yum -y install git 安装的git版本是 git --versiongit version 1.7.1 想要升级到2.2.1: ># yum remov ...

  9. Linux开机自动启动ORACLE设置

    1.安装好Oracle数据库后: 执行 dbstart和dbshut会提示: [oracle@oracle11g ~]$ dbstartORACLE_HOME_LISTNER is not SET, ...

  10. win7 配置微软的深度学习caffe

    win7 配置微软的深度学习caffe   官方下载: https://github.com/Microsoft/caffe 然后 直接修改caffe目录下的windows目录下的项目的props文件 ...