openvswitch虚拟交换机架构

标签:
sdnovs杂谈 |
分类: programming |
vswitchd Overview
ovs-vswitchd是OVS的关键组件,和
OpenFlow控制器,OVSDB,内核模块交互。
通过OpenFlow和外界通讯
通过OVSDB协议和
ovsdb-server通讯
通过netlink和内核通讯
通过
netdev
抽象接口和系统通讯
ovs-ofctl
, ovs-appctl
下列示意图解释了更多细节:
因此,
vswitch模块再分为下列子模块/库
ovs-vswitchd,
vswitchd
后台进程ofproto,
ovs桥的抽象库ofproto-provider,用于控制特定类型的
OpenFlow交换机的接口netdev,抽象网络设备的库
netdev-provider,硬件和OS中的到网络设备的特定接口
2. Key Data Structures
一个OVS网桥管理2类资源:
- 它所控制的转发平面(
datapath
)- 连接到它的网络设备(物理的或虚拟的)(
netdev
)
-
OVS网桥实现
ofproto,
ofproto-provider.h
datapath
管理
dpif,
dpif-provider.h
- 网络设备管理
netdev,
netdev-provider.h
2.1 ofproto
struct
ofproto抽象了
OpenFlow交换机,一个ofproto实例就是一个OpenFlow交换机(网桥),相关的数据结构(
ofproto/ofproto-provider.h
):struct ofproto
: 表示一个OpenFlow交换机(OVS网桥),所有的流/端口操作都针对ofprotostruct ofport
:表示ofproto内的一个端口 struct rule
: 表示ofproto内的一个OpenFlow流struct ofgroup
:表示ofproto内的一个OpenFlow 1.1+组
=======================================
* An OpenFlow switch.
*
* With few exceptions, ofproto implementations
may look at these fields but
* should not modify them. */
struct ofproto {
struct
hmap_node hmap_node;
const struct
ofproto_class *ofproto_class;
char
*type;
char
*name;
uint64_t
fallback_dpid;
uint64_t
datapath_id;
bool
forward_bpdu;
char
*mfr_desc;
char
*hw_desc;
char
*sw_desc;
char
*serial_desc;
char
*dp_desc;
enum
ofputil_frag_handling frag_handling;
struct hmap
ports;
struct shash
port_by_name;
struct simap
ofp_requests;
uint16_t
alloc_port_no;
uint16_t
max_ports;
struct hmap
ofport_usage;
uint64_t
change_seq;
struct ofproto {
}
struct ofport {
};
===================================
2.2 ofproto-provider
ofproto_class,定义在
ofproto/ofproto-provider.h,定义了接口来为新的硬件和软件实现一个
ofproto
providerOpenvSwitch定义了内嵌的ofproto
provider即ofproto-dpif,
这是基于库dpif以控制datapaths,一个datapath是个简单的流表,此流表只需支持精确匹配流表,即没有通配符的流表,当报文到达网络设备后,数据通路(datapath)在表中查找,如果匹配,则执行相关动作,如果失配,数据通路传递报文给
ofproto-dpif,
ofproto-dpif维护所有的
OpenFlow流表,如果报文在这匹配了流表,ofproto-dpif执行相应的动作,并把匹配的流表项插入到dpif流表,
(否则ofproto-dpif吧报文上交给ofproto并发送到OpenFlow控制器)。dpif库继而授权它的大部分功能给一个dpif provider,上图表示了dpif providers如何适配进Open
vSwitch架构
2.3 netdev
Open
vSwitch库,定义在
lib/netdev-provider.h,实现在
lib/netdev.c,此库抽象和网络设备交互接口,即以太网接口。交换机上的每一个端口都有一个对应的netdev并支持最小的操作,比如读取netdev的MTU值,得到RX/TX的队列大小。netdev库是
在netdev
provider之上的一个薄层,解析如下:2.4 netdev-provider

struct
netdev_class,
定义在lib/netdev-provider.h,定义了实现netdev的接口,结构中包含了很多函数指针。在
netdev provider实现了以后,可以选择移植策略是在用户空间实现“userspace
switch”,只要所实现的netdev
provider可以收发报文,因为每个报文经过ovs-vswitchd进程,所以性能很差。如果需要高性能,在必须选择实现一个“ofproto provider”或者“dpif
provider”,这依赖于几个因素:
-
只有 ofproto provider可以充分利用硬件,支持通配符(ACL 表或者TCAM) - 一个dpif provider可以利用Open vSwitch内建bonding实现,LACP, 802.1ag, 802.1Q VLANs,以及其他功能,ofproto provider必须提供它自身的实现,如果硬件支持
- dpif provider通常容易实现,但最适合软件switching,它转换通配规则为精确匹配规则,(带有可选通配掩码),这允许软件快速hash查找,但在TCAM硬件支持通配操作TCAM使用效率低下。
netdev
provider实现了到网络设备的OS和硬件相关的接口,例如到以太网设备,Open
vSwitch必须能够打开交换机上的每一个端口作为netdev,因此必须为软件或者硬件交换机实现一个“netdev
provider”,上图描绘了OVS中的
netdev和
netdev
providers,
netdev的详细信息如下:
所有的
netdev类型包括:
- inux netdev(
lib/netdev-linux.c,Linux平台
) -
system
-netdev_linux_class
tap
-netdev_tap_class
internal
-netdev_internal_class
- bsd netdev (
lib/netdev-bsd.c
, bsd平台) -
system
-netdev_bsd_class
tap
-netdev_tap_class
- windows netdev(windows 平台)
-
system
-netdev_windows_class
internal
-netdev_internal_class
- dummy netdev(
lib/netdev-dummy.c
) -
dummy
-dummy_class
dummy-internal
-dummy_internal_class
dummy-pmd
-dummy_pmd_class
- vport netdev(
lib/netdev-vport.c, vport
保有到datapath的端口的引用,可以通过netdev_open()打开
) -
- tunnel class:
-
geneve
gre
vxlan
stt
- patch - patch_class
- dpdk netdev
-
- dpdk_class
- dpdk_ring_class
- dpdk_vhost_class
- dpdk_vhost_client_class
===========================
void
netdev_dpdk_register(void)
{
netdev_register_provider(&dpdk_class);
netdev_register_provider(&dpdk_ring_class);
netdev_register_provider(&dpdk_vhost_class);
netdev_register_provider(&dpdk_vhost_client_class);
}
netdev_dpdk_register(void)
{
}
dpif Provider
Open vSwitch有个内建ofproto
provider,称为“ofproto-dpif”,构建与lib库之上,用于操作datapath,称为"dpif",一个“datapath”就是一个简单的flow表,只需要支持精确匹配流,即没有通配符流,当报文到达网络设备,datapath在这个表查找,如果有流匹配流,执行关联的action,如果没有匹配,datapath传递报文到ofproto-dpif,这里会维护完整的OpenFlow流表,如果报文在这个流表匹配,ofproto-dpif执行动作插入新表项到dpif流表,(否则ofproto-dpif
传递报文到ofproto并发送报文给控制器)。
在计算dpif流,ofproto-dpif产生精确匹配流来描述失配报文,它基于交换机的配置和OpenFlow
流表,尽力提取可以进行通配的字段,dpif可以忽略推荐的通配符并仅仅支持精确匹配,然而,如果dpif支持通配符,则它可以使用掩码匹配多个流,基于更少的表项,并潜在的极大的减少了基于ofproto-dpif失配产生的ofproto-dpif流的数量。
“dpif”库授权它的大部分功能给“dpif provider”,下图显示了dpif providers如何适配到Open
vSwitch架构

struct
dpif_class,定义在
lib/dpif-provider.h,定义了接口来为新的硬件或软件平台实现新的
dpif
provider,这个结构含有很多函数指针,当前的两个实现可以作为参考例子:- lib/dpif-netlink.c,是Linux相关的dpif实现,和 Open vSwitch关联内核模块工作,Open vSwitch内核模块执行所有的交换工作(代码在 “datapath”目录),传递未匹配报文到用户空间,这个dpif实现是所有到内核模块的调用的封装。
- lib/dpif-netdev.c,是个dpif的通用实现,在内部执行所有的交换功能,这实现了所有用户空间的交换实现。
===========================
3. Call Flows
vswitchd
模块的进入点在vswitchd/ovs-vswitchd.c,它的逻辑功能框图如上所示;在起始阶段,初始化网桥模块,定义在
vswitchd/bridge.c,网桥模块从ovsdb获取配置参数;
之后
ovs-vswitchd进入主循环,在循环中的第一件事是初始化一些库,包括DPDK,以及最重要的,
ofproto
库。
然后每个datapath调用
ofproto_type_run()来完成开始工作,此函数再调用每个
datapath类型对应的type_run实现。
随后每个bridge调用
ofproto_run()开始工作,再调用每个
ofproto
class的run()实现.
然后
ovs-vswitchd处理从
ovs-appctl和
ovsdb-server来的JSON-RPC消息.
netdev_run()用来处理所有的
netdevs这些工作之后,网桥,unixctl server,netdev模块进入阻塞状态,等待新的信号触发。
伪代码如下:
==========================
int main()
{
bridge_init();
while
(!exiting) {
bridge_run()
|
|--dpdk_init()
|--bridge_init_ofproto() // init bridges, only once
|--bridge_run__()
|
|--for(datapath types):
ofproto_type_run(type)
|--for(all_bridges):
ofproto_run(bridge) // handle messages from OpenFlow
Controller
unixctl_server_run(unixctl);
netdev_run();
bridge_wait();
unixctl_server_wait(unixctl);
netdev_wait();
poll_block();
}
}
{
}
==========================
4. Procedures and Submodules
先看看bridge_init()具体内容
=================================
void
bridge_init(const char *remote)
{
idl =
ovsdb_idl_create(remote, &ovsrec_idl_class, true, true);
unixctl_command_register("qos/show-types", "interface", 1, 1)
...
unixctl_command_register("bridge/dump-flows", "bridge", 1,
1,)
lacp_init();
// register command lacp/show
bond_init();
// register bond commands
cfm_init();
bfd_init();
ovs_numa_init();
stp_init();
lldp_init();
rstp_init();
ifnotifier =
if_notifier_create(if_change_cb, NULL);
}
==============================
unixctl_command_register()用来注册一个unixctl 命令,这样可以通过CLI控制ovs-vswitchd,每一个组模块调用此函数来注册自命令并对外输出此命令,如开始提到的,和vswitchd 交互的命令行工具是ovs-appctl,可以验证这些注册的命令。
================================
================================
在bridge_init()末尾,一些vswitchd 子模块被初始化,包括LACP, BOND, CFM, BDF, NUMA, STP, LLDP, RSTP, and inotifiers.
ovs-vswitchd的内部结构示例:

4.2. ofproto library init
ofproto维护了一个注册的ofproto
class数组,
ofproto_classes,定义在
ofproto/ofproto.c
:==========================
static const struct ofproto_class **ofproto_classes;
static size_t n_ofproto_classes;
static size_t allocated_ofproto_classes;
============================
在ofproto_init()中,内嵌
ofproto
class类ofproto_dpif_class
将被注册,这定义在ofproto/ofproto-dpif.c
:=========================
const struct ofproto_class ofproto_dpif_class = {
init,
...
port_alloc,
port_construct,
port_destruct,
port_dealloc,
port_modified,
port_query_by_name,
port_add,
port_del,
...
ct_flush,
};
};
===========================
ofproto_dpif_class的
init()函数将注册它自己的
unixctl
命令
--------------------------
static void init(const struct shash *iface_hints)
{
struct
shash_node *node;
SHASH_FOR_EACH(node, iface_hints) {
const struct iface_hint *orig_hint = node->data;
struct iface_hint *new_hint = xmalloc(sizeof *new_hint);
new_hint->br_name = xstrdup(orig_hint->br_name);
new_hint->br_type = xstrdup(orig_hint->br_type);
new_hint->ofp_port = orig_hint->ofp_port;
shash_add(&init_ofp_ports, node->name, new_hint);
}
ofproto_unixctl_init();
ofproto_dpif_trace_init();
udpif_init();
}
-----------------------------
前一篇:信息摘要-散列算法
后一篇:ovsdb管理协议概述