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 的完整处理流程(首包路径)。
- 报文进入内核 datapath,查内核流表 → miss(首包没有缓存)。
- 内核通过 upcall(Netlink)把包送到用户态
ovs-vswitchd。 - 用户态跑完整 OpenFlow 流水线(多级流表匹配),算出最终 action。
- 用户态通过 Netlink 把结果下发一条 datapath 流到内核,并让这个包按 action 执行(
packet_out)。 - 后续同类报文在内核直接命中缓存,快速转发,不再 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。
- 之前你遇到的 mlx5
Invalid 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(...)—— 隧道元数据(说明是隧道收上来的包)
| 子字段 | 含义 |
|---|---|
| 隧道 ID = 31(十进制)。VXLAN 即 VNI=31 |
| 外层隧道源 IP(对端 VTEP) |
| 外层隧道目的 IP(本机 VTEP) |
| 隧道标志: |
有
tunnel(...)段 = 这条流匹配的是从 VXLAN/Geneve 隧道解封装进来的内层报文。
in_port(1)
入端口 datapath port号 = 1(通常是隧道口 vxlan_sys / 上行口)。
eth(src=...,dst=...)
内层以太头:
src=e8:eb:d3:43:40:11dst=f2:01:a9:fe:00:c8
eth_type(0x0800)
以太类型 = IPv4。
ipv4(dst=10.52.251.9,proto=6,ttl=58,frag=no)
| 子字段 | 含义 |
|---|---|
| 目的 IP(注意这里没匹配 src,说明 src 被 wildcard 了) |
| L4 协议 = TCP |
| 匹配 TTL=58(进来时的值) |
| 非分片包 |
注意:只列出的字段才是被匹配的;没列出的字段(如 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= SYNF= FINP= PSHR= 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 发出
逐条:
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 换下一跳。
- 新
set(ipv4(ttl=57)):TTL 减 1(58→57),符合经过一次路由跳的语义。35:output 到 datapath port 35(目的虚机/容器对应的 vport 或下一段口)。
四、整体语义(一句话读懂)
VNI=31 的 VXLAN 隧道(对端 VTEP
10.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 show或ovs-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)。
七、高频"陷阱"小题
| 问题 | 答案要点 |
|---|---|
| 前者=用户态 OpenFlow 规则;后者=内核 datapath 缓存(流水线压平后的结果) |
内核流表条目是配置的还是学习的? | 不是配置,是首包 upcall 后用户态下发的缓存 |
megaflow 为什么比 microflow 好? | 带 wildcard,一条覆盖一批,减少条目与 upcall |
一条 OpenFlow 多表流水线在内核里是几条流? | 压平成一条 datapath 流(可能因 recirc 分成几段) |
为什么隧道/conntrack 常伴随 recirc? | 需要解封装/打状态后重新匹配 |
