OpenvSwitch源码阅读笔记-SDNLAB

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Open vSwitch源码阅读笔记
引言
本文主要对OpenvSwitch(基于2.3.90版本)重点模块的源码实现流程做了简要的阅读记录,适合阅读OpenvSwitch源码的初级读者参考使用,任何错误和建议欢迎加作者QQ 号38293996沟通交流。

1. OVS网络架构
Openvswitch是一个虚拟交换机,支持Open Flow协议(也有一些硬件交换机支持Open Flow),他们被远端的controller通过Open Flow协议统一管理着,从而实现对接入的虚拟机(或设备)进行组网和互通,整体组网结构如下图:
2. OVS内部架构
●ovs-vswitchd 主要模块,实现vswitch的守候进程daemon;
●ovsdb-server 轻量级数据库服务器,用于ovs的配置信息;
●ovs-vsctl 通过和ovsdb-server通信,查询和更新vswitch的配置;
●ovs-dpctl 用来配置vswitch内核模块的一个工具;
●ovs-appctl 发送命令消息到ovs进程;
●ovs-ofctl 查询和控制OpenFlow虚拟交换机的流表;
●datapath 内核模块,根据流表匹配结果做相应处理;
3. OVS代码架构
●vswitchd是ovs主要的用户态程序,它从ovsdb-server读取配置并发送到ofproto
层,也从ofproto读取特定的状态和统计信息并发送到数据库;
●ofproto是openflow的接口层,负责和Openflow controller通信并通过
ofproto_class与ofproto provider底层交互;
●ofproto-dpif是ofproto接口类的具体实现;
●netdev是ovs系统的网络设备抽象(比如linux的net_device或交换机的port),
netdev_class定义了netdev-provider的具体实现需要的接口,具体的平台实现需要支持这些统一的接口,从而完成netdev设备的创建、销毁、打开、关闭等一系列操作;
3.1 datapath
由于openvswitch用户态代码相对复杂,首先从内核模块入手分析。

datapath为 ovs内核模块,负责执行数据处理,也就是把从接收端口收到的数据
包在流表中进行匹配,并执行匹配到的动作。

一个datapath可以对应多个vport,一个vport类似物理交换机的端口概念。

一个datapth关联一个flow table,一个flow table
包含多个条目,每个条目包括两个内容:一个match/key和一个action。

3.1.1 数据流向
一般的数据包在 Linux网络协议中的流向为上图中的蓝色箭头流向:网卡eth0收到数据包后判断报文走向,如果是本地报文把数据传送到用户态,如果是转发报文根据选路(二层交换或三层路由)把报文送到另一个网卡如eth1。

当有 OVS时,数据流向如红色所
示:从网卡
eth0收到报文后进入ovs的端口,根据 key值进行流表匹配,如果匹配成功执行流表对应的 action;如果失败通过upcall送入用户态处理。

3.1.2 模块初始化
内核模块采用module_init(dp_init)进行datapath的初始化,代码如下:
其中dp的genl_family注册了如下四个类型:
3.1.3 收包处理
通过vport注册的回调函数netdev_frame_hook()->netdev_frame_hook()->
netdev_port_receive()->ovs_vport_receive()处理接收报文,ovs_flow_key_extract()函数生成flow的key内容用以接下来进行流表匹配,最后调用ovs_dp_process_packet()函数进入真正的ovs数据包处理,代码流程如下:
3.1.3 流表哈希桶
流表采用hash的方式排列存放,流表的hash头结点存储数据结构如下:
该hash 桶的初始化函数alloc_buckets (),生成的数据格式可参考如下:
parts[1]
parts[n]
如果元素空间超过FLEX_ARRAY_BASE_BYTES_LEFT 大小,需要再次分配空间
3.1.4 流表创建
用户态通过netlink 进行datapath 流表更新的入口函数都定义在dp_flow_genl_ops 中,流表创建的入口函数是ovs_flow_cmd_new 函数,代码分析如下:
根据上述流程给出流表的主要数据结构如下:
struct sw_flow_mask
3.1.6 流表查询
流表查找主要是查表关键字的匹配,关键字数据结构如下,根据skb中的Ethernet帧生成key的函数为ovs_flow_key_extract():
Encapsulating
tunnel key
struct addr
struct arp
流表查询的入口函数ovs_flow_tbl_lookup_stats(),flow的匹配策略是和流表中所有mask和所有key进行匹配处理,为了加速查询效率,在调用真正的流表查询函数
flow_lookup()之前,对于mask的查询采用了缓存机制,实现原理是首先查询缓存的mask_cache_entry,这些cache是查询成功后形成的cache,并针对cache采用分段查询
的方式,代码如下:
flow_lookup()函数的处理流程如下:
masked_flow_lookup()函数处理如下:
3.1.7 action处理
ovs的action类型如下,使用nla_type()函数获取nl_type的值,入口处理函数为
do_execute_actions()。

●OVS_ACTION_ATTR_OUTPUT:获取port号,调用do_output()发送报文到该port;
●OVS_ACTION_ATTR_USERSPACE:调用output_userspace()发送到用户态;
●OVS_ACTION_ATTR_HASH:调用execute_hash()获取skb的hash赋值到ovs_flow_hash ●OVS_ACTION_ATTR_PUSH_VLAN:调用push_vlan()增加vlan头部
●OVS_ACTION_ATTR_POP_VLAN:调用pop_vlan()移除vlan头
●OVS_ACTION_ATTR_RECIRC:在action_fifos全局数组中添加一个deferred_action;
●OVS_ACTION_ATTR_SET:调用execute_set_action()设置相关参数;
●OVS_ACTION_ATTR_SAMPLE:概率性的发送报文到用户态(详见sflow章节)。

3.1.8 upcall处理
当没有找到匹配的流表时,内核通过netlink发送报文到用户层处理,入口函数
ovs_dp_upcall(),该函数调用queue_userspace_packet()构造发往用户层的skb,通过netlink通信机制发送到用户层,其中形成的主要数据格式如下:
3.2 ovs-vswitchd
vswitchd作为守护进程和ovsdb通信以及和controller进行openflow通信,并完成和底层内核的交互。

代码在vswitchd/目录下面,可以从main函数入口分析,整体处理流程如下:
3.3 ofproto
ofproto层通过ofproto_class类(实现是ofproto_dpif_class)实现了openflow的接口,它主要包括如下几个接口类对象:
●ofproto 代表了一个openflow switch的具体实现,是ofproto层的整体结构体;
●ofport代表了一个openflow switch的端口,关联一个netdev设备;
●ofrule代表了一条openflow规则,rule里面包含一组actions;
●ofgroup代表了一个openflow的行为组合,openflow 1.1+以上版本支持;
3.3.1 ofproto数据结构
struct dpif_backer
3.3.2 ofproto创建流程
其中rule和group的创建流程不在本节列出
3.3.3 udpif
udpif接口层采用多个线程处理内核发往用户层的upcall请求,入口函数为
udpif_set_threads(),主要处理流程如下:
3.4 openflow
OpenFlow 是用于管理交换机流表的协议,ovs-ofctl 则是 OVS 提供的命令行工具。

在没有配置 OpenFlow controller的模式下,用户可以使用 ovs-ofctl 命令通过OpenFlow 协议去连接 OVS,创建、修改或删除 OVS中的流表项,并对 OVS 的运行状况进行动态监控。

3.4.1 openflow连接建立
在bridge_reconfigure()函数中调用bridge_configure_remotes进行openflow连接的相关处理,主要创建两个对象:ofconn作为客户端负责和远端conntroller主动建立连接;ofservice作为服务器提供被动式的监听服务,主要数据结构及流程如下图:
3.4.2 ofconn报文处理
入口函数ofproto_run(),主要调用流程如下:
3.4.3 ofservice报文处理
3.4.4 flow_mod消息格式
openflow协议消息处理入口函数是handle_openflow(),其中最重要的是flow_mod流表项的处理,flow_mod流表的报文格式主要有四部分组成:openflow头部、flow_mod固定字段、match字段和instruction字段。

match分为OFPMT_STANDARD和OFPMT_OXM两种类型,可以包含多个oxm,instruction 可以包含多个action,也可以没有。

抓包示例可参考如下:
3.4.5 match字段处理
match字段的解析处理入口函数为ofputil_pull_ofp11_match(),其中的核心处理函数为nx_pull_raw(),主要流程是解析出flow_mod的match字段,和flow中的match相关参数做一些合法性检测,最后使用解析出的value更新flow中的match。

目前match匹配域用的较多的是OXM即TLV格式,字段解析结构示意图如下:
核心流程处理如下:
3.4.6 instruction字段处理
instruction字段的解析处理入口函数ofpacts_pull_openflow_instructions(),主要流程是解析出flow_mod的instruction字段,根据不同的instruction做不同的处理,其中函数decode_openflow11_instructions()解析出所有instruction并按照不同类型放入ofp11_instruction *insts[N_OVS_INSTRUCTIONS]数组中,N_OVS_INSTRUCTIONS根据OVS_INSTRUCTIONS定义推导出值为6(即instruction支持的所有类型)。

其中最重要的宏OVS_INSTRUCTIONS完成了主要的数据生成和转换,根据它的定义可推导出instruction的类型和后续主要的处理函数对应关系:
ofpacts_decode()函数完成actions的解析,字段解析结构示意图如下:

ofpacts 最后调用ofpacts_check_consistency()进行参数的有效性检查。

3.4.7 flow_mod处理流程
以增加流表OFPTYPE_FLOW_MOD为例整理函数处理流程如下:
3.5 sflow
采样流sFlow(Sampled Flow)是一种基于报文采样的网络流量监控技术,主要用于对网络流量进行统计分析。

sFlow系统包含一个嵌入在设备中的sFlow Agent和远端的sFlow Collector。

其中,sFlow Agent通过sFlow采样获取本设备上的接口统计信息和数据信息,将信息封装成sFlow报文,当sFlow报文缓冲区满或是在sFlow报文缓存时间超时后,sFlow Agent会将sFlow报文发送到指定的sFlow Collector。

sFlow Collector对sFlow报文进行分析,并显示分析结果,组网图如下:
3.5.1 sflow初始化
SFlow的配置入口函数是bridge_configure_sflow(),该函数创建SFlow相关的数据区并初始化相应配置,形成的主要数据结构如下:
3.5.2 sflow流表生成
sflow的实现是在用户态生成sflow的流表并配置到内核datapath,内核完成数据采样,发送到用户态,然后有上层sflow agent发送到collector。

sflow在用户态生成sflow的流表。

流表生成的代码调用流程add_sflow_action()->compose_sflow_cookie ()->compose_sample_action(),生成OVS_ACTION_ATTR_SAMPLE类型的action,主要代码如下:
3.5.3 sflow内核处理
sflow在内核执行action的时候处理,入口函数do_execute_actions()->sample(),代码如下,如果是最后一个action,调用output_userspace()发送数据到用户空间;否则把该action加入到队列中等待执行,代码如下:
3.5.4 sflow消息处理
内核datapath采样的数据通过netlink发送到用户空间的vswithd进程,接收函数为recv_upcalls(),调用process_upcall()函数进入核心处理流程,其中对SFLOW_UPCALL分支的处理就是sflow的入口,处理函数dpif_sflow_received(),核心流程如下:
3.6 ovs-vsctl
ovs-vsctl根据用户的命令和ovsdb-server通信,用于查询和更新数据库配置。

而vswithcd会在需要重新更新配置的时候和ovsdb交互,然后和内核dp模块通过netlink 消息执行真正的操作。

本节以添加网桥、端口、vxlan端口为例分析主要实现流程,其中ovsctl进程的主要处理流程如下:
3.6.1 添加网桥
用户态shell键入命令ovs-vsctl add-br br0,启动vsctl进程用户完成数据库配置,流程如上面所述,最后调用add-br对应的run函数cmd_add_br(),流程如下:
vswithcd检测到数据库的设置后完成业务配置,流程如下:
内核datapath通过OVS_DATAPATH_FAMILY通道收到一个 OVS_DP_CMD_NEW类型的添加
网桥的命令,该命令绑定的回调函数为ovs_dp_cmd_new(),处理流程如下:
3.6.2 添加端口
shell键入命令ovs-vsctl add-port br0 eth0(这里分析netdev类型的vport端口,vxlan在下一章节单独分析),vsctl调用add-port对应的函数cmd_add_port()配置数据库,流程如下:
vswitchd调用dpif_netlink_port_add()通过netlink发送对应消息到内核,流程和
上一节所述的添加网桥类似,如下所示:
内核datapath通过OVS_VPORT_FAMILY通道收到一个类型为OVS_VPORT_CMD_NEW的添加端口的命令,该命令绑定的回调函数为ovs_vport_cmd_new (),处理流程如下:
3.7 vxlan
vxlan端口是ovs的OVS_VPORT_TYPE_VXLAN类型的隧道端口,用户态netdev库通过netdev_vport_tunnel_register()注册vport_class结构,它包含如vxlan、gre等各种类型隧道的相关处理函数。

3.7.1添加vxlan端口
添加命令为ovs-vsctl add-port br0 vxlan -- set interface vxlan type=vxlan,用户态处理流程和上节的添加端口相同,不同时的是对vxlan端口的参数配置,发往内核dp的消息类型为OVS_VPORT_TYPE_VXLAN,流程如下:
3.7.2内核vxlan创建
内核ovs_vport_cmd_new ()函数中ovs_vport_add()调用ovs_vxlan_vport_ops对应的操作函数,其中创建函数vxlan_tnl_create()流程如下:
3.7.3 vport数据结构
根据上述流程分析,vxlan类型的vport数据结构如下:
3.7.4 vxlan报文格式
3.7.5 vxlan收包处理
vxlan隧道报文的处理的入口函数是udp sock监听函数vxlan_udp_encap_recv(),处理流程如下:
3.7.6 vxlan发包处理
vlan在执行action时,判断需要发送数据的时候调用发送函数,函数调用
do_execute_actions()->do_output()->ovs_vport_send()->vxlan_tnl_send(),这里vxlan_tnl_send()函数即为创建vport端口是注册ovs_vxlan_vport_ops的send函数,主要处理流程如下:。

相关文档
最新文档