Skip to content

实验 5:流表模型 ​

第 1 部分里,OVS 的全部转发逻辑就一条规则:

text
priority=0 actions=NORMAL

从这一章开始,我们不再让它自己决定,而是自己写规则。这是 OVS 和普通网桥真正拉开差距的地方。

起环境 ​

bash
cd labs/learn-ovs/05-openflow
./start.sh

这次是三台主机:

text
        h1            h2            h3
   192.168.10.1  192.168.10.2  192.168.10.3
         │             │             │
       eth1          eth2          eth3
         └───────── ovs1 br0 ────────┘

三个口的 OpenFlow 端口号:

bash
docker exec clab-openflow-ovs1 ovs-ofctl dump-ports-desc br0
text
 1(eth1)
 2(eth2)
 3(eth3)
 LOCAL(br0)

一条流表项的结构 ​

bash
docker exec clab-openflow-ovs1 ovs-ofctl dump-flows br0
text
 cookie=0x0, duration=0.354s, table=0, n_packets=5, n_bytes=430,
 idle_age=0, priority=0 actions=NORMAL

拆开看:

text
 cookie=0x0        你自己打的标记,OVS 不解释它,用来批量管理规则
 duration=0.354s   这条规则存在了多久
 table=0           在第几张表
 n_packets=5       ← 命中了多少个包。排错时最先看这个
 n_bytes=430       命中了多少字节
 idle_age=0        距离上次命中过了几秒
 priority=0        优先级
 (空)             匹配条件。这里为空 = 匹配所有包
 actions=NORMAL    动作

一条流表项的本质就是三件事:在哪张表、匹配什么、做什么。加上一个优先级来解决"多条都能匹配"的冲突。

匹配:能看的字段 ​

OVS 能匹配的字段有几十个,覆盖二到四层。常用的:

二层 ​

字段含义例子
in_port从哪个 OpenFlow 端口进来in_port=1
dl_src / dl_dst源/目的 MACdl_dst=aa:bb:cc:dd:ee:ff
dl_type以太网类型0x0800=IPv4、0x0806=ARP、0x86dd=IPv6
dl_vlanVLAN IDdl_vlan=100
dl_vlan_pcpVLAN 优先级dl_vlan_pcp=5

三层 ​

字段含义例子
nw_src / nw_dst源/目的 IP,支持掩码nw_src=10.0.0.0/8
nw_proto协议号1=ICMP、6=TCP、17=UDP
nw_tos / nw_ttlTOS / TTLnw_ttl=1
arp_spa / arp_tpa / arp_opARP 的字段arp_op=1(请求)

四层 ​

字段含义例子
tp_src / tp_dst源/目的端口tp_dst=80
tcp_flagsTCP 标志位tcp_flags=+syn-ack
icmp_type / icmp_codeICMP 类型icmp_type=8

简写 ​

OVS 提供了一批简写,会自动补上前置条件:

简写等价于
ipdl_type=0x0800
ipv6dl_type=0x86dd
arpdl_type=0x0806
tcpdl_type=0x0800,nw_proto=6
udpdl_type=0x0800,nw_proto=17
icmpdl_type=0x0800,nw_proto=1

匹配四层字段必须先限定协议

tp_dst=80 单独写是无效的 —— OVS 不知道该去哪里找端口号。必须写成 tcp,tp_dst=80。

同理 nw_dst=1.2.3.4 要写成 ip,nw_dst=1.2.3.4。

这是新手最常见的报错来源。忘了写前缀,OVS 会直接拒绝或者规则永远不命中。

优先级:谁说了算 ​

同一张表里多条规则都能匹配时,优先级高的赢。范围 0–65535,默认 32768。

试一下:

bash
N="docker exec clab-openflow-ovs1"

$N ovs-ofctl add-flow br0 "priority=100,ip,nw_dst=192.168.10.2,actions=output:2"
$N ovs-ofctl add-flow br0 "priority=200,ip,nw_dst=192.168.10.2,tcp,tp_dst=22,actions=drop"
$N ovs-ofctl dump-flows br0
text
 priority=200,tcp,nw_dst=192.168.10.2,tp_dst=22 actions=drop
 priority=100,ip,nw_dst=192.168.10.2 actions=output:2
 priority=0 actions=NORMAL

一个发往 192.168.10.2:22 的 TCP 包,三条规则都能匹配,但只有 priority=200 那条生效,包被丢掉。发往 192.168.10.2:80 的包不匹配第一条,落到 priority=100,被转发。

优先级的常见约定

写复杂流表时给优先级留出档位,方便以后插规则:

text
  0        兜底(drop 或 NORMAL)
  1-99     宽泛的默认策略
  100-199  正常业务转发
  200-299  例外和特殊处理
  300+     安全策略、紧急封禁

优先级相同又都能匹配时,OVS 选哪条是未定义的。 不要依赖这种情况。

动作:能做什么 ​

输出 ​

动作含义
output:N从 N 号端口发出
output:NXM_...从寄存器里存的端口号发出
FLOOD发给除入口外所有端口(不含被 STP 阻塞的)
ALL发给除入口外所有端口(不管 STP)
LOCAL发给网桥自己的 internal 口
IN_PORT从进来的那个口原路发回
CONTROLLER送给控制器
drop丢弃。其实是"没有任何动作"的写法
NORMAL交给传统二层转发逻辑

改写报文 ​

动作含义
mod_dl_src / mod_dl_dst改 MAC
mod_nw_src / mod_nw_dst改 IP
mod_tp_src / mod_tp_dst改端口号
mod_vlan_vid改 VLAN ID
push_vlan / pop_vlan加/去 VLAN 标签(实验 10)
dec_ttlTTL 减一

流程控制 ​

动作含义详见
resubmit(port,table)跳到另一张表处理,会返回实验 7
goto_table:N跳到第 N 张表,不返回实验 7
learn(...)动态生成新的流表项实验 8
ct(...)送连接跟踪实验 16

多个动作用逗号分隔,按顺序执行:

text
actions=mod_dl_dst:02:00:00:00:00:01,output:2

drop 的真相

actions=drop 不是一个真的动作,它是"动作列表为空"的语法糖。这两条等价:

text
actions=drop
actions=

所以永远不要写 actions=drop,output:2 —— OVS 会报错。

超时:让规则自己消失 ​

bash
$N ovs-ofctl add-flow br0 \
  "priority=300,idle_timeout=10,hard_timeout=30,udp,tp_dst=53,actions=drop"
$N ovs-ofctl dump-flows br0 | grep 300
text
 idle_timeout=10, hard_timeout=30, priority=300,udp,tp_dst=53 actions=drop
参数含义
idle_timeout=N连续 N 秒没有包命中就删除。0 表示不过期
hard_timeout=N加进来 N 秒后无条件删除,不管有没有流量

控制器下发的规则常带 idle_timeout,让不活跃的流自动清理,避免流表无限膨胀。

bash
$N ovs-ofctl add-flow br0 "cookie=0x1234,priority=50,arp,actions=NORMAL"
$N ovs-ofctl dump-flows br0 | grep 0x1234
text
 cookie=0x1234, priority=50,arp actions=NORMAL

OVS 完全不解释 cookie 的含义,它就是个 64 位标记。价值在于批量管理:

bash
$N ovs-ofctl del-flows br0 "cookie=0x1234/-1"

/-1 是掩码,表示全部位都要匹配。一条命令删掉所有打了这个标记的规则。

控制器通常用 cookie 标识"这批规则是哪个业务下发的",重连后可以精确清理自己的旧规则而不动别人的。

增删改查 ​

bash
# 加
ovs-ofctl add-flow br0 "priority=100,ip,actions=output:2"

# 从文件批量加(每行一条,适合初始化)
ovs-ofctl add-flows br0 flows.txt

# 改:匹配条件相同的规则,动作被替换
ovs-ofctl mod-flows br0 "priority=100,ip,actions=drop"

# 删:按匹配条件
ovs-ofctl del-flows br0 "ip,nw_dst=192.168.10.2"

# 删:清空整张表(连默认的 NORMAL 一起)
ovs-ofctl del-flows br0

# 查:全部
ovs-ofctl dump-flows br0

# 查:只看匹配某个条件的
ovs-ofctl dump-flows br0 "ip,nw_dst=192.168.10.2"

# 查:只看某张表
ovs-ofctl dump-flows br0 table=1

筛选查询很实用:

bash
$N ovs-ofctl dump-flows br0 "ip,nw_dst=192.168.10.2"
text
 priority=200,tcp,nw_dst=192.168.10.2,tp_dst=22 actions=drop
 priority=100,ip,nw_dst=192.168.10.2 actions=output:2

只列出可能匹配这个条件的规则,流表大了以后离不开它。

del-flows 不加参数会删掉一切

包括默认的 priority=0 actions=NORMAL。执行完网桥就完全不转发了 —— 这正是下一章要做的事,但在生产环境上这么干等于断网。

OpenFlow 版本 ​

bash
$N ovs-vsctl get Bridge br0 protocols
text
[]

空表示只启用 OpenFlow 1.0。1.0 没有多级流表、没有 goto_table、匹配字段也少。要用新特性得显式打开:

bash
ovs-vsctl set Bridge br0 protocols=OpenFlow10,OpenFlow13

然后命令行也要指定版本:

bash
ovs-ofctl -O OpenFlow13 dump-flows br0

为什么 1.0 还能用 resubmit

resubmit 是 Nicira 的私有扩展,不属于标准 OpenFlow,所以在 1.0 下也能用。这是 OVS 特有的能力,实验 7 会详细对比它和 goto_table。

自测清单 ​

  1. 一条流表项由哪几部分组成?排错时最该先看哪个字段?
  2. tp_dst=80 为什么不能单独写?
  3. 两条规则都能匹配同一个包时,怎么决定用哪条?优先级也相同呢?
  4. idle_timeout 和 hard_timeout 有什么区别?
  5. actions=drop 和 actions= 有区别吗?
  6. cookie 是给谁用的?
  7. ovs-vsctl get Bridge br0 protocols 返回空表示什么?

常见问题 ​

规则加进去了但 n_packets 一直是 0 ​

三种可能,按顺序查:

  1. 匹配条件写错,包根本不满足 —— 用 ofproto/trace 验证
  2. 被更高优先级的规则截胡 —— dump-flows 看有没有优先级更高又能匹配的
  3. 流量根本没到这个网桥 —— 检查端口是否 up、接线是否正确

报错 unknown field 或 invalid field ​

多半是没写协议前缀(tp_dst 前面要有 tcp),或者该字段需要更高的 OpenFlow 版本。

加了规则以后全网不通 ​

检查是不是不小心执行了 del-flows(不带参数),把默认的 NORMAL 删掉了。补回来:

bash
ovs-ofctl add-flow br0 "priority=0,actions=NORMAL"

小结 ​

  • 流表项 = 表号 + 优先级 + 匹配条件 + 动作,外加 cookie 和超时
  • n_packets 是排错第一现场 —— 规则有没有被命中一看便知
  • 匹配四层字段必须先限定协议(tcp,tp_dst=80)
  • 优先级高的赢;优先级相同又都能匹配时行为未定义,不要依赖
  • actions=drop 就是空动作列表,不能和其它动作并列
  • idle_timeout 看流量,hard_timeout 看时间
  • cookie 用于批量管理,del-flows "cookie=0x1234/-1" 精确清理
  • 默认只开 OpenFlow 1.0,多级流表需要手动启用 1.3

下一章:实验 6:用流表替掉 NORMAL,把这些拼起来实现一个完整的二层交换机。