--------------------------------------------------------------------------------------------------
Openvswitch是一个优秀的开源软件交换机,支持主流的交换机功能,比如二层交换、网络隔离、QoS、流量监控等,而其最大的特点就是支持openflow,openflow定义了灵活的数据包处理规范。其所支持的所有协议如STP/LACP/BOND等最后都是落脚到流表。 同时OvS 提供ofproto dpif 编程接口,支持硬件交换机(后面有介绍)。
OvS丰富的功能和稳定性使得其被部署在各种生产环境中,加上云计算的快速发展,OvS成为了云网络里的关键组件。
一、OvS 在SDN网络中的位置
OvS 通过openflow流表可以实现各种网络功能,并且通过openflow protocol可以方便的实现控制+转发分离的SDN方案。
OvS 在云环境中概图(图片来源互联网)如下
二、OvS 各组件关系
ovs-vswitchd:主要模块,实现内核datapath upcall 处理以及ofproto 查表,同时是dpdk datapath处理程序。
ovsdb-server:数据库服务程序, 使用目前普遍认可的ovsdb 协议。
ovs-vsctl:网桥、接口等的创建、删除、设置、查询等。
ovs-dpctl:配置vswitch内核模块
ovs-appctl:发送命令消息到ovs-vswithchd, 查看不同模块状态
ovs-ofctl:下发流表信息。该命令可以配置其他openflow 交换机(采用openflow 协议)
三、OvS datapath以及查找算法
OvS 支持内核datapath以及dpdk datapath。在两种datapath模式中,内核模块和ovs-vswithd是主要的处理程序。同时两种datapath 前两级的查表实现不同,但是第三级都使用相同的ofproto 查询(这里我们只做简单介绍)。
一直以来,流Cache是提高查表性能的有效手段,已经被广泛应用于报文查表加速。它将数据平面的转发路径分为快速路径(即流Cache)和慢速路径,利用流量局部特性,使得大部分报文命中快速路径中的表项,从而提高转发性能。OVS也采用了流Cache设计思路。
OVS当前版本kernel datapath采用Megaflow Cache+Microflow Cache的流Cache组织形式,仍保留了Microflow Cache作为一级Cache,即报文进入后首先查这一级Cache。只不过这个Microflow Cache含义与原来的Microflow Cache不同。原来的Microflow Cache是一个实际存在的精确Hash表,但是最新版本中的Microflow Cache不是一个表,而是一个索引值,指向的是最近一次查Megaflow Cache表项。那么报文的首次查表就不需要进行线性地链式搜索,可直接对应到其中一张Megaflow的元组表。SDNLAB已经有文章对此进行了详细分析,见https://www.sdnlab.com/15713.html
OVS + DPDK 查找算法和kernel datapath 查找有所不同,OVS DPDK查找详见 https://software.intel.com/en-us/articles/ovs-dpdk-datapath-classifier
四、OvS 代码架构
ovsdb-server 接收配置信息,同步到ovs-vswithd ,同时获取ovs-vswithd 状态信息。
ovs-vswitchd ovsdb-server读取数据库信息,并将信息下发到ofproto,同时ovs-vswithd也会将ofproto中的status and statistical信息通过ovsdb-server写入到数据库中。
ofproto通过网络和OpenFlow 控制器通信。通过"ofproto provider”和软件交换机以及硬件交换机通信。目前openvswitch只支持ofproto-dpif,但是用户可以很容易的实现其他ofproto provider
ofproto provider 支持两种dpif,ofproto-dpif 实现自己的数据结构,但是对上层呈现为ofproto结构体,其具体实现细节对上不可见。
netdev 对网络设备(如Ethernet)的抽象,该层基于netdev provider实现。如果linux平台的system,tap,internal,以及dpdk的dpdk,dpdkr,dpdkvhostuser,dpdkvhostuserclient等。
创建ovs bridge流程分析:
1.通过ovs-vsctl 创建网桥,将创建参数发送给ovsdb-server,ovsdb-server将数据写入数据库。
2.ovs-vswitchd从ovsdb-server中读取创建网桥的信息,在ovs-vswithd层创建一个bridge结构体信息
3.然后将brdige信息应用到ofproto层,在ofproto层通过ofproto_create创建网桥,ofproto_create通过用户指定的网桥类型查找包含该类型的ofproto provider(目前只支持一个ofproto provider)。
查找后创建ofproto结构体(该结构体也表示一个bridge),并通过ofproto provider 构造函数创建ofproto provider的私有信息。
4.ofproto-dpif 层,构造函数完成如下:ofproto-dpif会为相同类型的ofproto创建一个backer结构体,所有类型的ofproto的backer使用全局列表表示。ofproto-dpif通过backer关联dpif。同时backer关联upcall处理线程
netdev没有实现upcall注册函数,所以对应的backer线程实际上不做任何处理,但依然会有该处理线程。netlink 通过backer启动的线程实现处理upcall数据包的处理。
vswitchd是ovs中最核心的组件,openflow的相关逻辑都在vswitchd里实现,一般来说ovs分为datapath, vswitchd以及ovsdb三个部分,datapath一般是和具体是数据面平台相关的,比如白盒交换机,或者linux内核等。ovsdb用于存储vswitch本身的配置信息,比如端口,拓扑,规则等。vswitchd本身是分层的结构,最上层daemon主要用于和ovsdb通信,做配置的下发和更新等,中间层ofproto,用于和openflow控制器通信,以及通过ofproto_class暴露了ofproto provider接口,不同平台上openflow的具体实现就通过ofproto_class统一。
Open vSwitch实现了两种dpif。lib/dpif-netlink.c 特定Linux实现的dpif,该dpif与Open vSwith实现的内核模块通信。内核模块执行所有的交换工作,将内核态不匹配的数据包发送到用户态。dpif封装调用内核接口。lib/dpif-netdev.c 是一种通用的 dpif 实现。该dpif就是Open vSwith在用户态的实现。数据包的交换不会进入内核。struct dpif_class是datapath interface实现的工厂接口类,用于和实际的datapath, e.g. openvswitch.ko, 或者userspace datapath交互。目前已有的两个dpif的实现是dpif-netlink和dpif-netdev,前者是基于内核datapath的dpif实现,后者基于用户态datapath。代码可以在lib/dpif-netlink.c以及lib/dpif-netdev.c里找到。
五、OvS 开发实践贡献代码之前
开发OvS 代码之前,我们需要了解如下信息
1.邮件列表,所有的邮件列表:我们一般使用discuss作为平时问题的讨论,dev作为提patch。https://mail.openvswitch.org/mailman/listinfo
2.编码风格,如果你为OvS用户态程序提供的patch,那么你需要遵循用户态编码风格。如果你是为内核datapath 编写的patch 你需要遵循内核编码风格。这两种是不同的风格。http://docs.openvswitch.org/en/latest/internals/contributing/coding-style/
3.Commit Message,在提交代码时,尽量把你遇到的问题,复现方法以及你的patch如何解决该问题的等, 描写的详尽。这不仅能够让其他人更好的理解,同时缩短接收patch时间,国内外有时差。
4.patchwork 在这里,你能够看到其他人提交的patch,同时每个patch 处于什么状态。http://patchwork.ozlabs.org/project/openvswitch/list/
六、OvS 开发实践之二层网络功能
OvS 支持各种二层网络功能,比如LACP,STP,RSTP,MAC 学习,MCAST等,如果这些协议出现问题,可以通过如下的开发过程,定位,开发。ovs-vswithd 结构分析,开发OvS 最重要的就是分析ovs-vswithd
OvS 模块的初始化
主要是模块使用的数据结构体的初始化非协议,如lacp、bond、cfm、stp、rstp都在bridge_init调用初始化
ovs-vswithd 循环处理架构
这里只是列出的基本重要的架构,其他部分读者可以进一步分析
bridge_run__ 循环处理一些必要的操作,如stp、rstp、mcast处理等,同时ovs-ofctl 下发openflow,也是在这里处理。bridge_reconfigure 主要完成根据数据以及当前进程信息,创建、更新、删除必要的网桥、接口、端口以及其他协议的配置等。最终这些操作为应用到ofproto 层、ofproto dpif 层、run_stats_update、run_status_update、run_system_stats 更新openvswitch数据库状态信息netdev_run netdev_linux_run监控网卡状态并更新。
以STP 某个功能为例,例如之前的一个bug,OvS 不会检查stp port 所处的状态。如果已经加入的port 开启的stp功能,用户使用ifconfig 禁止网卡状态,那么理论上OvS 应该停止通过该端口发送数据包括BPDU。但是OvS 并没有做这方便的检查。同时如果ifconfig up了该网卡,那么重新进入stp 状态,以及stp 各个状态的改变。
社区最后接收了该bug的修复,patch 如下:https://github.com/openvswitch/ovs/commit/52182c5f50198d0f985b10677e47a9ac49ee709b
添加链路状态监测函数:
在必要的地方调用
该补丁,在bridge_run__ stp_run中调用状态检查,并根据port 状态对stp进行响应的处理。(网卡状态的更新检查统一在netdev_run中尽心,这里我们使用最后的检查的结果做判断)同时该补丁也bridge_reconfigure bridge_configure_spanning_tree设置port时调用该函数。如果是对其他协议的bug,修复过程其实类似。
七、OvS 开发实践之unixctl
ovs-vswithd 支持ovs-appctl 很多命令,通过该方式,我们可以开发很多有用的功能,如查看ovs-vswithd 运行状态,数据包处理状态,内存使用情况,PMD线程情况。
OvS unixctl的初始化
在ovs-vswithd 初始化的时候通过unixctl_server_create函数创建unixctl 服务。命令ovs-appctl和注册的服务通信。
OvS unixctl 命令注册
我们可以通过unixctl_command_register 注册unixctl 提供个命令服务,如ovs-vswithd 退出函数 unixctl_command_register("exit", "", 0, 0, ovs_vswitchd_exit, &exiting)
OvS unixctl 命令运行
ovs-vswithd 在循环中通过调用unixctl_server_run 执行用户下发的命令。
前段时间开发的rstp/show 命令,该命令能够查看rstp 协议在各个交换机中的状态,显示格式和思科交换机类似。在开发该功能时考虑如下 :
1.使用重陷入锁还是使用内部函数。在实现该命令时需要调用其他函数,但是之前使用的锁不允许重入,但是如果更改rstp 使用锁的方式,需要更改大量的代码(声明,函数修饰)。
因此最后改成添加内部帮助函数。
2.rstp port name。之前rstp port不支持name 显示,因此为了更清晰的显示信息。我们需要在结构体中添加name 成员。
在整个rstp port 整个声明周期(如rstp port的创建,删除,使用,修改等),我们需要处理好name。独立的patch 如下 https://github.com/openvswitch/ovs/commit/8d2f8375c7f7ee3e36877ba20f4e4cc5b47841d3
添加必要属性
设置port name函数rstp_port_set_port_name__
最后需要在rstp_add_port 初始化port name, 在rstp_port_unref 删除port name,同时在rstp 模块添加必要的内部函数,实现如下(具体很简单,大家可以review)https://github.com/openvswitch/ovs/commit/066f0ab4a07d2edc07dedcf006224df7fa00a9b1
最后实现rstp/show命令https://github.com/openvswitch/ovs/commit/cc3a32f3b6891168cee98812e8f5e3d8a5a52c98
最终命令效果
Q&A
Q:我有ovs通过vmware的wmnet桥接的,但是把桥接网卡设置成trunk后就不通了,能帮忙分析下么,ovs是安装在centos7上的,cent是安装在vmware上的
A:这个好细节,你可以通过tcpdump 检查,每个节点是否 都有数据包,看看哪里丢包。ovs 你可以看看ovs flow表 看看哪个flow drop的该数据包。
Q:之前提到的ovs-dpdk的datapath,请问能讲解一下ofproto是如何把流表分发给多个PMD的呢?
A:ovs 不管哪个datapath 都是三级cache,最后一个是ofproto,dpcls miss之后才去ofproto 所以对应的dpcls 才会有,之后emc 查找去对应的dpcls 去找,一个pmd 线程可能多个dpcls
Q:ovs流表匹配采用什么算法?对精确匹配字段,前缀匹配字段,和范围匹配字段的匹配有区别对待?
A:ovs kernel datapath 使用Megaflow Cache + Microflow Cache,dpdk 和kernel 第一层cache 都是精准匹配,后面是通配的。如果是有区别,就是查找前后的区别。大家可以review dpdk 和kernel 的实现算法。
Q:能介绍一下ovs -dpdk在openstack里的应用吗?
A:ovs-dpdk 在openstack 并没有应用到实际的生产环境,如果有,也是小部分现在。不过这个是趋势。
Q:相同优先级 match条件都一样的两条流表 怎么决定匹配到哪一条
A:这个会对 表的优先级排序的,找到的肯定是优先级高的。
Q:流表中的in port 和虚机端口有什么关系吗?
A:没有