一、数据包转发流程
Open vSwitch 数据包转发流程如下图所示,其中红色数字序号表示数据包转发的步骤顺序。 以下步骤为一个数据包通过 OVS 时的首次处理流程:(步骤序号和图中序号一一对应)
- OVS 从设备接口中获取数据包并交给 Datapath 内核模块进行流表匹配
- Datapath 检查数据包的头部信息以进行流表匹配,如果匹配成功,则根据转发规则进行转发。但是由于此处是首次处理,所以匹配不到相应的流表,那么就需要通过 upcall 调用让 vswitchd 守护进程生成新的流表(数据包通过 Netlink 传输至 vswitchd 守护进程)
- vswitchd 与控制器(一般是 SDN 控制器)通信获取流表(通过 OpenFlow 协议)
- vswitchd 将获取到的流表缓存到内核态的 Flow Table 中
- vswitchd 通过 reinject 将数据包送回 Datapath 中
- Flow Table 将内容同步至 Datapath 模块
- Datapath 根据生成的流表进行匹配,并根据相应规则进行数据包的处理,然后将处理后的数据包送至设备接口
所以说,Open vSwitch 在进行数据包转发时,如果已经存在可以匹配的流表,就会直接转发;否则会通过 upcall 调用 vswitchd 守护进程和相关组件进行流表的生成,然后再进行转发。
我们在之后的内容中着重考虑数据包的首次处理流程(1→2→3→4→5→6→7),因为数据包的非首次处理流程(1→7)已经包含在首次处理流程中了,一般就不再进行分类讨论。
二、ovs-vswitchd 转发流程实现
交换机的核心功能之一就是数据包转发,所以 Open vSwitch 的数据包转发流程也是在 ovs-vswitchd 守护进程中实现的(主要是 upcall 调用这一系列的步骤)。
ovs-vswitchd 的逻辑架构如上图所示,其中可以将 upcall 调用这一系列的步骤,视为自底向上的调用后再自顶向下下发的过程,数据包流向如下图所示,其中直线为直接转发路径(1→7),曲线为首次处理(upcall 调用)路径(1→2→3→4→5→6→7)。
可以发现,当流表可以直接匹配时,数据包不需要通过用户态,完全由内核态的 Datapath 模块实现转发,这也就是在 OVS 的总体架构 中关于 OVS 功能特点介绍时的 使用 Linux 内核模块的高性能转发 对应内容的一部分。
三、ovs-vswitchd 虚拟交换机功能
根据前一节的分析,ovs-vswitchd 主要通过主循环处 bridge 相关的内容(第二部分)实现交换机功能,下面分别对相关函数进行解读:(此处为了节约空间,对代码做了省略和细微调整)
(1)交换机初始化模块 bridge_init(remote)
void bridge_init(const char *remote) {
/* Create connection to database. */
idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true, true);
idl_seqno = ovsdb_idl_get_seqno(idl);
ovsdb_idl_set_lock(idl, "ovs_vswitchd");
ovsdb_idl_verify_write_only(idl);
......
/* Register unixctl commands. */
unixctl_command_register("qos/show-types", "interface", 1, 1,
qos_unixctl_show_types, NULL);
unixctl_command_register("qos/show", "interface", 1, 1,
qos_unixctl_show, NULL);
unixctl_command_register("bridge/dump-flows", "[--offload-stats] bridge",
1, 2, bridge_unixctl_dump_flows, NULL);
unixctl_command_register("bridge/reconnect", "[bridge]", 0, 1,
bridge_unixctl_reconnect, NULL);
lacp_init();
bond_init();
cfm_init();
bfd_init();
ovs_numa_init();
stp_init();
lldp_init();
rstp_init();
odp_execute_init();
ifaces_changed = seq_create();
last_ifaces_changed = seq_read(ifaces_changed);
ifnotifier = if_notifier_create(if_change_cb, NULL);
if_notifier_manual_set_cb(if_change_cb);
}
上述部分代码主要功能如下:
- 创建与远程数据库的连接
- 注册相关的 unixctl 命令
- 初始化各种子系统,如:链路聚合控制协议 LACP,网卡绑定 bond,连续故障监测 CFM,双向转发检测 BFD,非一致内存访问 NUMA,生成树协议 STP,链路层发现协议 LLDP,快速生成树协议 RSTP,OpenFlow 数据平面 ODP
- 创建并初始化接口变更监控
(2)交换机运行模块 bridge_run();
void bridge_run(void) {
......
cfg = ovsrec_open_vswitch_first(idl);
......
/* Initialize the ofproto library. This only needs to run once, but
* it must be done after the configuration is set. If the
* initialization has already occurred, bridge_init_ofproto()
* returns immediately. */
bridge_init_ofproto(cfg);
......
bridge_run__();
......
run_stats_update();
run_status_update();
run_system_stats();
}
上述部分代码主要功能如下:
- 打开 Open vSwitch 配置数据库(ovsdb 模块)并获取配置信息(cfg)
- 初始化 ofproto 库(Open vSwitch 用于管理和配置 OpenFlow 交换机的核心组件)
- 调用 bridge_run__() 函数执行 Open vSwitch 交换机的主要逻辑,如端口管理、MAC 学习、数据包转发等。
- 更新相关信息,如:交换机统计信息、交换机状态和系统统计信息
上述相关函数都在 ovs-main/vswitchd/bridge.c 文件中。
四、ovs-vswitchd 源码架构
ovs-vswitchd 守护进程代码主要存放在 ovs-main/vswitchd/ 目录下,其中 ovs-vswitchd.c 文件是守护进程的主入口,bridge.c 文件实现了交换机相关的核心功能。
在本文中,主要探讨了 ovs-vswitchd 守护进程的代码实现,而 OVS 虚拟交换机的核心功能实现并未得到充分讨论。下图为 ovs-vswitchd 的逻辑架构图,其中各个分层模块内容将在后续文章中分别介绍。
结语:
由于本人水平有限,以上内容如有不足之处欢迎大家指正(评论区/私信均可)。
参考资料:
Open vSwitch 官网
Open vSwitch 源代码 GitHub
2015 FOSDEM - OVS Stateful Services
OVS - 数据包处理流程-CSDN博客
Open vSwitch 源码阅读笔记(1)OVS 的总体架构-CSDN博客
Open vSwitch v3.3.0 源代码阅读
Open vSwitch 2.3.90 源码阅读笔记(上) | SDNLAB
然而现在 Open vSwitch 对 DPDK 也提供了很好的支持,下图为使用 DPDK 时的 Open vSwitch 的总体架构。相对于上一个架构,使用 DPDK 的方案相当于将内核态的 Datapath 放到用户态去实现,进而提升网络性能(具体原因参见:)