OVS原理
OVS架构
ovs
的架构如上图所示,主要由内核datapath
和用户空间的vswitchd
、ovsdb
组成。
主要模块职责
datapath
是负责数据交换的内核模块,其从网口读取数据,并快速匹配Flowtable中的流表项,成功的直接转发,失败的上交vswitchd
处理。它在初始化和port binding
的时候注册钩子函数,把端口的报文处理接管到内核模块。vswitchd
是一个守护进程,是ovs的管理和控制服务,通过unix socket将配置信息保存到ovsdb
,并通过netlink
和内核模块交互ovsdb
则是ovs
的数据库,保存了ovs
配置信息
主要数据结构
(图片来自csdn)
主要流程
注:部分转载自OVS 源码分析整理
添加网桥
- 键入命令
ovs-vsctl add-br testBR
- 内核中的
openvswitch.ko
收到一个添加网桥的命令时候——即收到OVS_DATAPATH_FAMILY
通道的OVS_DP_CMD_NEW
命令。该命令绑定的回调函数为ovs_dp_cmd_new
ovs_dp_cmd_new
函数除了初始化dp
结构外,调用new_vport
函数来生成新的vport
new_vport
函数调用ovs_vport_add()
来尝试生成一个新的vport
ovs_vport_add()
函数会检查vport
类型(通过vport_ops_list[]
数组),并调用相关的 create()函数来生成vport
结构- 当dp是网络设备时(
vport_netdev.c
),最终由ovs_vport_add()
函数调用的是netdev_create()
【在vport_ops_list
的ovs_netdev_ops
中】 netdev_create()
函数最关键的一步是注册了收到网包时的回调函数err=netdev_rx_handler_register(netdev_vport->dev,netdev_frame_hook,vport);
- 操作是将
netdev_vport->dev
收到网包时的相关数据由netdev_frame_hook()
函数来处理,都是些辅助处理,依次调用各处理函数,在netdev_port_receive()
【这里会进行数据包的拷贝,避免损坏】进入 ovs_vport_receive()回到vport.c
,从ovs_dp_process_receive_packet()
回到datapath.c
,进行统一处理 - 流程:
netdev_frame_hook()->netdev_port_receive->ovs_vport_receive->ovs_dp_process_received_packet()
net_port_receive()
首先检测是否 skb 被共享,若是则得到packet
的拷贝。net_port_receive()
其调用ovs_vport_receive()
,检查包的校验和,然后交付给我们的vport
通用层来处理。
(图片来自简书)
流表匹配
flow_lookup()
查找对应的流表项for
循环调用rcu_dereference_ovs
对流表结构体中的mask_list
成员遍历,找到对应的的 成员flow=masked_flow_lookup()
遍历进行下一级hmap
查找,找到为止- 进入 包含函数
ovs_flow_mask_key(&masked_key,unmasked,mask)
,将最开始提取的Key
值和mask
的key
值进行“与”操作,结果存放在masked_key
中,用来得到后面的Hash
值 hash=flow_hash(&masked_key,key_start,key_end)key
值的匹配字段只有部分ovs_vport_add()
函数会检查vport
类型(通过vport_ops_list[]
数组),并调用相关的create()
函数来生成vport
结构- 可见,当
dp
时网络设备时(vport_netdev.c)
,最终由ovs_vport_add()
函数调用的是netdev_create()
【在vport_ops_list
的ovs_netdev_ops
中】 netdev_vport->dev
收到网包时的相关数据由netdev_frame_hook()
函数来处理,都是些辅助处理,依次调用各处理函数,在netdev_port_receive()
【这里会进行数据包的拷贝,避免损坏】进入ovs_vport_receive()
回到vport.c
,从ovs_dp_process_receive_packet()
回到datapath.c
,进行统一处理
收包处理
ovs_vport_receive_packets()
调用ovs_flow_extract
基于skb
生成key
值,并检查是否有错,然后调用ovs_dp_process_packet
。交付给datapath
处理ovs_flow_tbl_lookup_stats
。基于前面生成的key
值进行流表查找,返回匹配的流表项,结构为sw_flow
。- 若不存在匹配,则调用
ovs_dp_upcall
上传至userspace
进行匹配。 (包括包和key
都要上传) - 若存在匹配,则直接调用
ovs_execute_actions
执行对应的action
,比如添加vlan
头,转发到某个port等。
upcall 消息处理
ovs_dp_upcall()
首先调用err=queue_userspace_packet()
将信息排队发到用户空间去dp_ifindex=get_dpifindex(dp)
获取网卡设备索引号- 调整
VLAN
的MAC
地址头指针 - 网络链路属性,如果不需要填充则调用此函数
len=upcall_msg_size()
,获得upcall
发送消息的大小user_skb=genlmsg_new_unicast
,创建一个新的netlink
消息upcall=genlmsg_put()
增加一个新的netlink
消息到skb
err=genlmsg_unicast()
,发送消息到用户空间去处理