ovs删除dp流表后的流量不中断

前段时间无意触发了一个删除dp流表的操作,然后出现流量依然正常的奇怪现象,遂对其进行了分析,走查了一下代码,本文记录一下。

现象描述

现象:ping一个虚机的fip的时候的同时,使用ovs-dpctl del-flow ufid:xxxx删除dp里的流表,发现流量依然正常没有中断,并且通过ovs-dpctl dump-flows却没有发现重新创建flow。
社区类似现象

问题分析

分析本问题,首先需要摸清ovs接收到普通报文后,如何进行流表处理的:
1.当ovs的datapath接收到报文后,首先会提取报文的端口信息,源目的ip/mac等信息
2.查看内核里的flow table,如果命中了就直接执行action
3.如果没有命中,则通过netlink upcall上送给用户态的ovs-vswitchd处理
4.ovs-vswitchd 查询用户态精确流表和模糊流表
5.如果模糊命中, ovs-vswitchd 会同时刷新用户态精确流表和内核态精确流表;如果精确命中,则只更新内核态流表。
6.刷新后,重新把该数据包注入给内核态 datapath 模块处理。
7.datapath 重新发起选路,查询内核流表,匹配;报文转发,结束

好了,在我们的认知里,上述流程主要针对流量的首包。
那么本问题的现象里,却是另一种情形,ovs datapath已经生成了flow,但是把它给删了,会造成什么后果呢?

进一步地,我们查看了后续dp的flow匹配统计:
dp
在这里我们发现dp flow的统计里,报文不再hit命中,而是一直在missed,也就是一直上送vswitchd处理了。
在此,我们就基本确认了,删除dp flow后为什么流量依然正常:原因就是后续报文一直被上送给了vswitchd处理。

那接下来,我们分析为什么没有生成新的flow
这一部分的分析,需要理解upcall中如何通知datapath创建flow的处理,细节在函数handle_upcalls

static void
handle_upcalls(struct udpif *udpif, struct upcall *upcalls,
size_t n_upcalls)
{
struct dpif_op *opsp[UPCALL_MAX_BATCH * 2];
struct ukey_op ops[UPCALL_MAX_BATCH * 2];
size_t n_ops, n_opsp, i;

/* Handle the packets individually in order of arrival.
*
* - For SLOW_CFM, SLOW_LACP, SLOW_STP, SLOW_BFD, and SLOW_LLDP,
* translation is what processes received packets for these
* protocols.
*
* - For SLOW_ACTION, translation executes the actions directly.
*
* The loop fills 'ops' with an array of operations to execute in the
* datapath. */
n_ops = 0;
for (i = 0; i < n_upcalls; i++) {
struct upcall *upcall = &upcalls[i];
const struct dp_packet *packet = upcall->packet;
struct ukey_op *op;

if (should_install_flow(udpif, upcall)) {
struct udpif_key *ukey = upcall->ukey;

if (ukey_install(udpif, ukey)) {
upcall->ukey_persists = true;
put_op_init(&ops[n_ops++], ukey, DPIF_FP_CREATE);
}
}

vswitchd通知datapath创建flow:通过netlink下发DPIF_FP_CREATE指令给datapath。
那么我们可以把问题范围缩小到if (should_install_flow(udpif, upcall))包含的这一块代码:

should_install_flow判断为false了吗?
1.查看本函数的实现细节,只要是miss upcall的报文,该判断一般都是true的,暂时排除;
2.那么问题就应该是ukey_install判断为false了,于是在该函数的内部,我们加了打印后,基本确认了问题所在。
原来在首包进行ukey_install后,vswitchd就缓存了一份包含该flow的ukey,而ukey的索引是通过flow的ufid来唯一确认的。如果在进行ukey_install的时候,查询ukey存在的话,就会返回false。

那么:
1.通过dpctl del-flow是无法触发vswitchd删除该ukey,所以该ukey会在vswitchd里缓存一段时间。
2.只要vswitchd没有重启的话,flow对应的ufid也是不会变化的。

再基于以上这两个前提,我们不难分析出:当删除了dp的flow后,后续报文upcall给vswitchd,而vswitchd是能查到已有的ukey->ufid的,所以也就不会再创建新的flow。

问题总结

到这里,问题基本就确认了,将dp flow删除后,报文会一直upcall;而由于vswitchd里缓存了对应的ukey,所以upcall的报文也就不会下发新的flow了。
当然,ovs-doc里也是不建议通过dpctl操作流表:

DATAPATH FLOW TABLE DEBUGGING COMMANDS
The following commands are primarily useful for debugging Open vSwitch.
The flow table entries (both matches and actions) that they work with
are not OpenFlow flow entries. Instead, they are different and consid‐
erably simpler flows maintained by the Open vSwitch kernel module. Do
not use commands to add or remove or modify datapath flows if
ovs-vswitchd is running because it interferes with ovs-vswitchd's own
datapath flow management. Use ovs-ofctl(8), instead, to work with
OpenFlow flow entries.

The dp argument to each of these commands is optional when exactly one
datapath exists, in which case that datapath is the default. When mul‐
tiple datapaths exist, then a datapath name is required.

所以大家在操作的时候一定注意,尽量避免通过dpctl去操作流表,当然查询流表是允许的。

个人分析,欢迎指正,若转载请注明出处!