这来主要看看ovs从网络接口收到packet后的一系列操作。

在内核模块启动的时候会初始化vport子系统(ovs_vport_init),各种vport类型,那么什么时候会调用相应的函数与实际网络设备建立联系?其实当我们在为网桥增设端口的时候,就会进入ovs_netdev_vport_ops中的create方法,进而 注册网络设备。

看ovs-vsctl add-port br0 eth1 实际做了什么?

struct netdev_vport {  struct rcu_head rcu;  struct net_device *dev;
};const struct vport_ops ovs_netdev_vport_ops = {  .type          = OVS_VPORT_TYPE_NETDEV,  .flags          = VPORT_F_REQUIRED,  .init          = netdev_init,   //之后的内核版本,这里直接return 0;  .exit          = netdev_exit,  .create          = netdev_create,  .destroy     = netdev_destroy,  .set_addr     = ovs_netdev_set_addr,  .get_name     = ovs_netdev_get_name,  .get_addr     = ovs_netdev_get_addr,  .get_kobj     = ovs_netdev_get_kobj,  .get_dev_flags     = ovs_netdev_get_dev_flags,  .is_running     = ovs_netdev_is_running,  .get_operstate     = ovs_netdev_get_operstate,  .get_ifindex     = ovs_netdev_get_ifindex,  .get_mtu     = ovs_netdev_get_mtu,  .send          = netdev_send,
};--datapath/vport-netdev.cstatic struct vport *netdev_create(const struct vport_parms *parms)
{  struct vport *vport;  struct netdev_vport *netdev_vport;  int err;  vport = ovs_vport_alloc(sizeof(struct netdev_vport),   &ovs_netdev_vport_ops, parms);//有ovs_netdev_vport_ops和vport parameters 来构造初始化一个vport;  netdev_vport = netdev_vport_priv(vport);  //获得vport私有区域??  netdev_vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name);[//通过interface]() name比如eth0 得到具体具体的net_device 结构体,然后下面注册 rx_handler;  if (netdev_vport->dev->flags & IFF_LOOPBACK ||  netdev_vport->dev->type != ARPHRD_ETHER ||  ovs_is_internal_dev(netdev_vport->dev)) {  err = -EINVAL;  goto error_put;  }//不是环回接口;而且底层链路层是以太网;netdev->netdev_ops == &internal_dev_netdev_ops 显然为falseerr = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, vport);  [//核心,收到packet后会调用]() netdev_frame_hook处理;  dev_set_promiscuity(netdev_vport->dev, 1);  //设置为混杂模式;  netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;  //设置netdevice私有区域的标识;  return vport;
}  --datapath/vport.h 创建vport所需要的参数结构
struct vport_parms {  const char *name;      enum ovs_vport_type type;  struct nlattr *options;   [//利于必要的时候从 netlink msg通过属性OVS_VPORT_ATTR_OPTIONS取得
]()  /* For ovs_vport_alloc(). */  struct datapath *dp;  // 这个vport所从属的datapath  u16 port_no;   //端口号  u32 upcall_portid; // 如果从这个vport收到的包 在flow table没有得到匹配就会从 netlink端口upcall_portid 发送到用户空间;
};

函数netdev_rx_handler_register(struct net_device *dev,rx_handler_func_t *rx_handler, void *rx_handler_data)定义在 linux/netdevice.h 实现在 net/core/dev.c 中,为网络设备dev注册一个receive handler,rx_handler_data指向的是这个receive handler是用的内存区域(这里存的是vport,里面有datapath的相关信息)。这个handler 以后会被 __netif_receive_skb() 呼叫,实际就是更新netdevice中的两个指针域,rcu_assign_pointer(dev->rx_handler_data, rx_handler_data), rcu_assign_pointer(dev->rx_handler, rx_handler) 。

netif_receive_skb(struct sk_buff *skb)从网络中接收数据,它是主要的接收数据处理函数,总是成功,这个buffer在拥塞处理或协议层的时候可能被丢弃。这个函数只能从软中断环境(softirq context)中调用,并且中断允许。返回值 NET_RX_SUCCESS表示没有拥塞,NET_RX_DROP包丢弃。(实现细节暂时没看)

接下来进入我们的钩子函数 netdev_frame_hook(datapath/vport-netdev.c)这里主要看内核版本>=2.6.39的实现。

   static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb){  struct sk_buff *skb = *pskb;  struct vport *vport;  if (unlikely(skb->pkt_type == PACKET_LOOPBACK))  return RX_HANDLER_PASS;  vport = ovs_netdev_get_vport(skb->dev);  //提携出前面存入的那个vport结构体,vport-netdev.c line 401;  netdev_port_receive(vport, skb);  return RX_HANDLER_CONSUMED;
}

函数 netdev_port_receive 首先得到一个packet的拷贝,否则会损坏先于我们而来的packet使用者 (e.g. tcpdump via AF_PACKET),我们之后没有这种情况,因为会告知handle_bridge()我们获得了那个packet 。

skb_push是将skb的数据区向后移动*_HLEN长度,为了存入帧头;而skb_put是扩展数据区后面为存数据memcpy做准备。

static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
{  skb = skb_share_check(skb, GFP_ATOMIC);  //GFP_ATOMIC用于在中断处理例程或其它运行于进程上下文之外的地方分配内存,不会休眠(LDD214)。  skb_push(skb, ETH_HLEN);  //疑问:刚接收到的packet应该是有 ether header的,为何还执行这个操作??  if (unlikely(compute_ip_summed(skb, false)))  goto error;  vlan_copy_skb_tci(skb); // <2.6.27版本的时候才需要VLAN field;  ovs_vport_receive(vport, skb);  return;  .................
}

接下来将收到的packet传给datapath处理(datapath/vport.c),参数vport是收到这个包的vport(表征物理接口和datapath),skb是收到的数据。读的时候要用rcu_read_lock,这个包不能被共享而且skb->data 应该指向以太网头域,而且调用者要确保已经执行过 compute_ip_summed() 初始化那些校验和域。

void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
{  struct vport_percpu_stats *stats;  stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id());  //每当收发数据的时候更新这个vport的状态(包数,字节数),struct vport_percpu_stats定义在vport.h中。  u64_stats_update_begin(&stats->sync);  stats->rx_packets++;  stats->rx_bytes += skb->len;  u64_stats_update_end(&stats->sync);  if (!(vport->ops->flags & VPORT_F_FLOW))  OVS_CB(skb)->flow = NULL;  [//vport->ops->flags]() (VPORT_F_*)影响的是这个通用vport层如何处理这个packet;if (!(vport->ops->flags & VPORT_F_TUN_ID))  OVS_CB(skb)->tun_key = NULL;  ovs_dp_process_received_packet(vport, skb);
}

接下来我们的datapath模块来处理传上来的packet(datapath/datapath.c),首先我们要判断如果存在skb->cb域中的OVS data sw_flow 是空的话,就要从packet中提携构造;函数 ovs_flow_extract 从以太网帧中构造 sw_flow_key,为接下来的流表查询做准备;流表结构struct flow_table定义在flow.h中,流表实在ovs_flow_init的时候初始化的?? 如果没有match成功,就会upcall递交给用户空间处理(见vswitchd模块分析),匹配成功的话执行flow action(接下来就是openflow相关)。

void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
{  struct datapath *dp = p->dp;  struct sw_flow *flow;  struct dp_stats_percpu *stats;  u64 *stats_counter;  int error;  stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());  if (!OVS_CB(skb)->flow) {  struct sw_flow_key key;  int key_len;  /* Extract flow from 'skb' into 'key'. */  error = ovs_flow_extract(skb, p->port_no, &key, &key_len);  /* Look up flow. */  flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table),  &key, key_len);  if (unlikely(!flow)) {  struct dp_upcall_info upcall;  upcall.cmd = OVS_PACKET_CMD_MISS;  upcall.key = &key;  upcall.userdata = NULL;  upcall.portid = p->upcall_portid;  ovs_dp_upcall(dp, skb, &upcall);  consume_skb(skb);  stats_counter = &stats->n_missed;  goto out;  }  OVS_CB(skb)->flow = flow;  }  stats_counter = &stats->n_hit;  ovs_flow_used(OVS_CB(skb)->flow, skb);  ovs_execute_actions(dp, skb);  out:  /* Update datapath statistics. */  u64_stats_update_begin(&stats->sync);  (*stats_counter)++;  u64_stats_update_end(&stats->sync);
}

原文链接:https://www.kancloud.cn/digest/openvswitch/117245

OVS packet处理流程(三十二)相关推荐

  1. Android Track的play流程(三十二)

    android audio 生产者与消费者 简介 全面接触生产者/消费者问题是在操作系统原理中,并发性原理讨论的问题 生产者/消费者问题.最近的工作偏向音频,接着上一篇文章,用生产者,消费者模型来理解 ...

  2. 【正点原子Linux连载】第三十二章 U-Boot启动流程详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  3. 【正点原子FPGA连载】第三十二章RTC实时时钟LCD显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

    1)实验平台:正点原子新起点V2开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码+手册+视频下载地址:ht ...

  4. axi dma 寄存器配置_FPGA Xilinx Zynq 系列(三十二)AXI 接口

    大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分.大侠可以关注FPGA技术江湖,在"闯荡江湖"."行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢. ...

  5. ASP 三十二条精华代码

    整理收藏: ASP 三十二条精华代码 1. οncοntextmenu="window.event.returnvalue=false" 将彻底屏蔽鼠标右键 <table b ...

  6. tensorflow学习笔记(三十二):conv2d_transpose (解卷积)

    tensorflow学习笔记(三十二):conv2d_transpose ("解卷积") deconv解卷积,实际是叫做conv_transpose, conv_transpose ...

  7. OpenCV学习笔记(三十一)——让demo在他人电脑跑起来 OpenCV学习笔记(三十二)——制作静态库的demo,没有dll也能hold住 OpenCV学习笔记(三十三)——用haar特征训练自己

    OpenCV学习笔记(三十一)--让demo在他人电脑跑起来 这一节的内容感觉比较土鳖.这从来就是一个老生常谈的问题.学MFC的时候就知道这个事情了,那时候记得老师强调多次,如果写的demo想在人家那 ...

  8. 三十二、Java集合中的ArrayList

    @Author:Runsen @Date:2020/6/3 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件.导致翘课严重,专业排名 ...

  9. python建站部署_SpringBoot入门建站全系列(三十二)接入xxl-job分布式任务调度平台...

    SpringBoot入门建站全系列(三十二)接入xxl-job分布式任务调度平台 一.概述 XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源 ...

  10. Android项目实战(三十二):圆角对话框Dialog

    原文:Android项目实战(三十二):圆角对话框Dialog 前言: 项目中多处用到对话框,用系统对话框太难看,就自己写一个自定义对话框. 对话框包括:1.圆角 2.app图标 , 提示文本,关闭对 ...

最新文章

  1. Window server 补丁修复及补丁回滚
  2. Metasploit之渗透安卓实战
  3. Howto: 如何将ArcGIS Server缓存移动到新服务器
  4. 单引号内的双引号内的双引号怎么写
  5. 安装mysql5.5出现1045_安装MySQL提示error Nr.1045如何解决
  6. java 执行jar_windows下如何用java指令运行jar包?
  7. charles请求转发_Charles安装与基本操作
  8. ArcGIS没有或未启动Spatial Analyst许可
  9. 【Wonder整理】防止重复提交并弹出半透明对话框
  10. 点线面的意义_如何运用设计中的点线面?
  11. 计算机毕业设计(附源码)python兴澜幼儿园管理系统
  12. c语言pow函数原型_c语言中pow函数的用法是什么?_后端开发
  13. 在Panda3d中转换Maya模型为egg格式并使用pview查看
  14. 前端实战:教你写出简单的侧边栏功能以及返回顶部特效
  15. zabbix监控WEB页面及告警 实战
  16. 分组卷积与DW卷积、Residuals与Inverted Residuals、bottleneck与linearbottleneck
  17. wan口有流量但电脑上不了网_路由器WAN口有IP地址上不了网如何解决【详细介绍】...
  18. javaWEB如何实现一个电影票预定购票系统javaee电影选票选座平台
  19. ExifTool 抽取、修改Image tag 信息
  20. “我转行做测试开发的这一年多,月薪5K变成了24K”,中文系萌妹的自白

热门文章

  1. python学费多少-Python开发学费一般多少钱?
  2. python 数据分析学什么-利用Python做数据分析 需要学习哪些知识
  3. python教程廖雪峰云-Python3基础教程-廖雪峰[带标签完整版]
  4. python快速编程入门教程-python从入门到精通之30天快速学python视频教程
  5. 大学python实训总结-千锋Python实训总结 学好基础才能走的更远
  6. python对象编程例子-python编程进阶之类和对象用法实例分析
  7. python自动化测试视频百度云-python接口自动化测试视频教程全集
  8. c语言和python哪个自学好-自学编程应该从c语言还是python入手?
  9. java excel md5,excel表格数据md5加密-excel 怎么把文本转化成md5
  10. 优秀大数据GitHub项目一览