OVS 常见问题记录

一、基础架构类

Q1. OVS 的整体架构?内核态和用户态分别负责什么?

  • 用户态:ovs-vswitchd(核心守护进程,管 OpenFlow 流表、做慢路径决策)、ovsdb-server(配置库)。
  • 内核态:openvswitch.ko内核模块,即 datapath(数据路径),只做快速转发 + 精确匹配缓存。
  • 分工:用户态是"大脑"(完整 OpenFlow 流水线、复杂精确匹配),内核态(缓存已生成的的高效流表,快速转发)。

Q2. OpenFlow 流表和内核流表(datapath flow)是一回事吗?

  • OpenFlow 流表:在ovs-vswitchd用户态,支持多级流水线(multi-table)、复杂匹配和 action,是"规则源头"。
  • 内核 datapath 流表:内核里的缓存,是 OpenFlow 流水线计算后的结果(把多表流水线"压平"成一条 datapath 流),只做匹配→执行 action。
  • 内核流表不是手动配置的,是首包 upcall 后由用户态下发的。

二、快慢路径 / upcall 类(核心)

Q3. 描述一个报文第一次进入 OVS 的完整处理流程(首包路径)。

  1. 报文进入内核 datapath,查内核流表 → miss(首包没有缓存)。
  2. 内核通过 upcall(Netlink)把包送到用户态ovs-vswitchd
  3. 用户态跑完整 OpenFlow 流水线(多级流表匹配),算出最终 action。
  4. 用户态通过 Netlink 把结果下发一条 datapath 流到内核,并让这个包按 action 执行(packet_out)。
  5. 后续同类报文在内核直接命中缓存,快速转发,不再 upcall。

Q4. 什么是 upcall?为什么它开销大?

  • upcall = 内核 datapath 未命中缓存时,把报文/元数据经 Netlink 上送用户态决策的过程。
  • 开销大:内核↔用户态上下文切换 + 拷贝 + 完整流水线计算。所以 OVS 性能优化的核心就是尽量减少 upcall(提高内核缓存命中率)。

Q5. upcall 有哪几种类型?

  • MISS upcall:流表未命中,请求用户态决策(最常见)。
  • ACTION upcall(userspace action):action 明确要求把包送用户态(如CONTROLLER、采样、部分 learn 等)。

三、缓存机制类(重中之重)

Q6. OVS 的两级流缓存是什么?microflow cache 和 megaflow cache 的区别?

  • Microflow cache(精确匹配缓存):
    • 早期设计,key 是完整五元组等精确匹配(exact match)。
    • 一条流一个 entry,命中率对短连接/大量流很差(每条新连接都 miss)。
  • Megaflow cache(通配匹配缓存):
    • 现在的主力。带通配符(wildcard mask)的匹配,一条 megaflow 可覆盖一批报文。
    • 通过只匹配真正影响转发决策的字段(其它字段 wildcard),大幅提高命中率、减少 upcall。
  • 现代 OVS 内核 datapath 用 megaflow;用户态还有一层 microflow cache(EMC) 做二次加速(DPDK 场景更明显)。

Q7. megaflow 的 mask 是怎么来的?为什么它能减少流表条目?

  • 用户态跑流水线时,会追踪这次决策到底"看了"哪些字段(用了哪些 match 域)。
  • 没被"看"的字段就打上 wildcard。
  • 于是一条 megaflow 覆盖"所有这些字段取任意值、但关键字段相同"的报文 → 一条顶多条,条目更少、命中率更高。

Q8. EMC(Exact Match Cache)是什么?

  • 主要在 OVS-DPDK(用户态 datapath) 里:在 megaflow 之前再加一层精确匹配的小缓存,命中最热的流,避免 megaflow 的通配查找开销。
  • 内核 datapath 也有类似的"microflow→megaflow"两级思路。

四、匹配与查找类

Q9. 内核 datapath 用什么数据结构做通配符匹配?

  • TSS(Tuple Space Search,元组空间搜索):把相同 mask 的流归为一个 "mask tuple",每个 tuple 内做哈希精确查找,遍历所有 tuple 取最高优先级命中。
  • mask 种类越多,要查的 tuple 越多,查找越慢 → 所以控制 mask 数量很重要。

Q10. datapath flow 的 key 包含哪些内容?

报文的解析字段集合:in_port、以太头(src/dst mac、ethertype)、VLAN、L3(IP src/dst、proto、tos、ttl)、L4(端口、flags)、隧道元数据(tun_id/tun_src/tun_dst,如 VXLAN/Geneve)、以及 recirc_id、ct 状态等。


五、进阶特性类

Q11. recirculation(recirc)是什么?为什么需要?

  • 某些操作需要重新解析/多轮处理(如剥掉隧道/VLAN 后再按内层匹配、连接跟踪后再决策)。
  • OVS 用 recirc_id 把处理分成多个阶段:执行到某步后带着 recirc_id 重新进入 datapath 查表,实现"分段流水线"。
  • 常见于:隧道解封装、conntrack、MPLS、bond 哈希后再匹配。

Q12. conntrack 在 datapath 里如何体现?

  • OVS 通过ct()action 调用内核 nf_conntrack,给报文打上连接状态(new/est/rel/inv)。
  • 通常配合 recirc:先ct(),recirc 回来后再按 ct_state/ct_zone/ct_mark/ct_label 匹配 → 实现有状态防火墙/NAT。

Q13. datapath 流表条目会一直存在吗?如何老化?

  • 不会。内核流有空闲超时老化:ovs-vswitchd周期性 dump datapath 流的统计(packet/byte/used 时间),长时间无命中的流会被回收删除。
  • 这也是为什么ovs-dpctl dump-flows看到的流是动态变化的。

Q14. 硬件卸载(hw-offload)时内核流表怎么变?

  • 开启hw-offload后,OVS 把 datapath 流通过 TC flower 下发到网卡硬件。
  • 命中的流在网卡里直接转发,不进内核软件 datapath,进一步降 CPU。
  • 之前你遇到的 mlx5Invalid action sequence就是某些 action 组合硬件不支持、卸载失败的表现。

六、实操 / 排查类

Q15. 常用查看内核流表的命令?

ovs-dpctl dump-flows # 直接看内核 datapath 流(含 mask、stats)

recirc_id(0),in_port(18),eth(src=fa:16:84:5b:69:d4,dst=f2:01:0a:34:fb:c1),eth_type(0x0806),arp(sip=10.52.251.197,tip=10.52.251.193,op=1/0xff,sha=fa:16:84:5b:69:d4,tha=00:00:00:00:00:00), packets:20234, bytes:849828, used:21.378s, actions:userspace(pid=4294967295,slow_path(action))
recirc_id(0),in_port(33),eth(dst=f2:01:c0:be:00:01),eth_type(0x0800),ipv4(dst=172.0.0.0/252.0.0.0,proto=6,ttl=64,frag=no), packets:41463, bytes:2736558, used:61.193s, flags:S, actions:drop
recirc_id(0),tunnel(tun_id=0x1f,src=10.52.254.1,dst=10.52.136.82,flags(-df+csum+key)),in_port(1),eth(src=10:70:fd:49:e8:cb,dst=f2:01:a9:fe:00:c8),eth_type(0x0800),ipv4(dst=10.52.251.9,proto=6,ttl=59,frag=no), packets:7839772, bytes:425244996, used:0.704s, flags:SPR., actions:set(eth(src=f2:01:0a:34:fb:01,dst=fa:16:ce:6a:dd:a8)),set(ipv4(ttl=58)),35
recirc_id(0),tunnel(tun_id=0x1f,src=10.52.254.1,dst=10.52.136.82,flags(-df+csum+key)),in_port(1),eth(src=e8:eb:d3:43:40:11,dst=f2:01:a9:fe:00:c8),eth_type(0x0800),ipv4(dst=10.52.251.9,proto=6,ttl=58,frag=no), packets:2317085, bytes:171060244, used:1.458s, flags:SFPR., actions:set(eth(src=f2:01:0a:34:fb:01,dst=fa:16:ce:6a:dd:a8)),set(ipv4(ttl=57)),35

说明含义:

从 VXLAN 隧道进来、发往某内网 IP 的报文,改写 MAC/TTL 后从 port 35 发出"。逐段拆解:


一、匹配字段(Match,报文要满足这些才命中)

recirc_id(0)

重入 ID = 0,表示这是第一轮处理(未经过 recirculation)。若是隧道解封装/conntrack 后再匹配,会是非 0 值。这里 0 = 报文刚进 datapath 的原始阶段。

tunnel(...)—— 隧道元数据(说明是隧道收上来的包)

子字段含义

tun_id=0x1f

隧道 ID = 31(十进制)。VXLAN 即 VNI=31

src=10.52.254.1

外层隧道源 IP(对端 VTEP)

dst=10.52.136.82

外层隧道目的 IP(本机 VTEP)

flags(-df+csum+key)

隧道标志:-df=不设 DF(允许分片);+csum=带校验和;+key=携带 tunnel key(VNI)

tunnel(...)段 = 这条流匹配的是从 VXLAN/Geneve 隧道解封装进来的内层报文。

in_port(1)

入端口 datapath port号 = 1(通常是隧道口 vxlan_sys / 上行口)。

eth(src=...,dst=...)

内层以太头:

  • src=e8:eb:d3:43:40:11
  • dst=f2:01:a9:fe:00:c8

eth_type(0x0800)

以太类型 = IPv4。

ipv4(dst=10.52.251.9,proto=6,ttl=58,frag=no)

子字段含义

dst=10.52.251.9

目的 IP(注意这里没匹配 src,说明 src 被 wildcard 了)

proto=6

L4 协议 = TCP

ttl=58

匹配 TTL=58(进来时的值)

frag=no

非分片包

注意:只列出的字段才是被匹配的;没列出的字段(如 ip src、L4 端口)被 wildcard(megaflow 的通配),这也是为什么这一条能覆盖大量报文。


二、统计与状态

packets:2317085, bytes:171060244

该流累计命中 231 万包 / 约 171 MB——命中量很大,是条热流。

used:1.458s

距上次命中过去 1.458 秒。这个值决定老化:越小越活跃;长时间不更新会被 revalidator 回收。

flags:SFPR.

TCP 连接中观察到的 TCP flag 汇总(datapath 记录这条流见过哪些标志位),常见字母:

  • S= SYN
  • F= FIN
  • P= PSH
  • R= RST
  • 末尾.= 占位/其它

表示这条流的报文里出现过 SYN/FIN/PSH/RST,是一条正常有数据收发、且已看到连接建立与关闭迹象的 TCP 流。


三、动作(Actions,命中后依次执行)

actions:

set(eth(src=f2:01:0a:34:fb:01, dst=fa:16:ce:6a:dd:a8)), // 改写内层源/目的 MAC

set(ipv4(ttl=57)), // TTL 58 → 57(路由跳数减1)

35 // 从 datapath port 35 发出

逐条:

  1. set(eth(...)):把内层以太头改成新的 src/dst MAC。

    • dst=fa:16:ce:6a:dd:a8是 OpenStack/虚拟化常见的fa:16:...系列 MAC(下一跳/目的虚机的 MAC)。
    • src=f2:01:0a:34:fb:01是本网关口的 MAC。
    • 这就是三层转发/路由的典型行为:改 MAC 换下一跳。
  2. set(ipv4(ttl=57)):TTL 减 1(58→57),符合经过一次路由跳的语义。

  3. 35:output 到 datapath port 35(目的虚机/容器对应的 vport 或下一段口)。


四、整体语义(一句话读懂)

VNI=31 的 VXLAN 隧道(对端 VTEP10.52.254.1→ 本机10.52.136.82)解封装后,得到一个发往10.52.251.9的 TCP 报文;OVS 对它做三层转发:改写源/目的 MAC 为网关口和目的虚机 MAC、TTL 减 1,然后从 port 35 送到目的虚机。

这是典型的 "隧道入 → L3 路由 → 送本地虚机" 转发路径(overlay 网络里跨节点东西向流量在目的节点的处理)。


五、几个可延伸的排查点

  • 确认 port 35 是谁:ovs-dpctl showovs-vsctl -- --columns=name,ofport list interface看 port 35 对应哪个 vport(目的虚机 tap/vif)。
  • 为何 TTL=58 被精确匹配:说明流水线里"看了"TTL(可能有 TTL 相关判断),所以它没被 wildcard;这类精确字段会增加 megaflow mask 种类,量大时影响查找性能。
  • 对照 OpenFlow 规则:ovs-appctl ofproto/trace br-int <flow>可看这条 datapath 流是由哪些 OpenFlow 多表规则"压平"来的。
  • src 被通配:这条流不区分源 IP/端口,说明转发决策只依赖目的 IP + 隧道 + TTL,命中率高。

ovs-appctl dpctl/dump-flows # 经 vswitchd,信息更全

ovs-appctl dpif/dump-flows br-int

ovs-ofctl dump-flows br-int # 看的是 OpenFlow 流表(用户态规则),不是内核流

ovs-appctl upcall/show # 看 upcall/handler 统计

ovs-appctl coverage/show # 看 upcall 次数等计数

Q16. 怎么判断/追踪一条报文会怎么走?

ovs-appctl ofproto/trace br-int <flow描述> # 模拟报文走完整流水线,看命中哪些表、最终 action

(你们flowtraceCLI 的 arp/icmp/vxlan 就是封装了ofproto/trace。)

Q17. upcall 太多、CPU 高怎么排查优化?

  • ovs-appctl coverage/show看 upcall 计数是否飙升。
  • 原因常见:mask 太多导致 megaflow 命中率低、大量短连接、频繁 recirc、conntrack 表压力。
  • 优化:减少 wildcard 破坏(避免不必要的精确匹配字段)、开 hw-offload、调 handler/revalidator 线程数(n-handler-threads/n-revalidator-threads)。

七、高频"陷阱"小题

问题答案要点

ovs-ofctl dump-flowsovs-dpctl dump-flows区别?

前者=用户态 OpenFlow 规则;后者=内核 datapath 缓存(流水线压平后的结果)

内核流表条目是配置的还是学习的?

不是配置,是首包 upcall 后用户态下发的缓存

megaflow 为什么比 microflow 好?

带 wildcard,一条覆盖一批,减少条目与 upcall

一条 OpenFlow 多表流水线在内核里是几条流?

压平成一条 datapath 流(可能因 recirc 分成几段)

为什么隧道/conntrack 常伴随 recirc?

需要解封装/打状态后重新匹配