2.4 指令与动作:报文的处置语义


2.4 指令与动作:报文的处置语义

本节摘要:流条目命中后,交换机执行的是"指令"(Instruction),而指令里装的才是"动作"(Action)。这个两层结构是 1.1 之后引入的关键设计:指令管流程(立即应用、写集、跳表、过 Meter),动作管具体操作(输出、改写、入队)。本节拆清两层语义、动作集的结算时机,以及 Output 与_CONTROLLER 这类特殊端口的含义。

学习目标

阅读完本节,你应当能够:

  1. 区分 Instruction 与 Action 的层级与职责
  2. 解释 APPLY_ACTIONS 与 WRITE_ACTIONS 的执行时机差异
  3. 说出动作集何时结算、为什么这样设计
  4. 列举三个特殊输出端口及用途

为什么要分两层

1.0 只有动作:命中即做,简单直接。但多级流水线出现后问题来了——表 0 想给包贴个 VLAN,表 2 才决定从哪个口出去,"从哪个口出去"在表 0 时刻还不知道。于是 1.1 把体系拆成两层:

  • 指令(Instructions):改变包在流水线里的流程。四员主将:APPLY_ACTIONS(立即执行动作)、WRITE_ACTIONS / WRITE_METADATA(暂存,留给后面的表)、GOTO_TABLE(跳下一张表)、METER(过限速器)
  • 动作(Actions):对包本身或转发行为的具体操作。OUTPUT、SET_FIELD、PUSH_VLAN、POP_MPLS、GROUP、DROP(动作集为空的隐式结果)等

指令与动作的执行分层

指令与动作的执行分层

动作集的结算规则

"最后统一结算"是 1.3 的一个重要语义:表 0 写了 SET_FIELD 改 TTL,表 3 又写了 OUTPUT,包真正发出去是在离开流水线那一刻,改写顺序按类型固定(先链路层再网络层再传输层),不按写入顺序。这样设计保证了多表协作时改写结果可预测。

动作清单(1.3 常用)速查:

动作 作用 典型场景
OUTPUT:port 从指定端口发出 转发
OUTPUT:CONTROLLER 上送控制器 显式监控
GROUP:group_id 交给组表处理 组播、选路
SET_FIELD:field=value 改写包头字段 改 TTL、改 MAC
PUSH_VLAN / POP_VLAN 压入/弹出 VLAN 标签 网络虚拟化
SET_QUEUE:qid 映射到某个队列 QoS 分流
COPY_TTL_IN / OUT MPLS 与 IP 间拷 TTL 隧道封装

特殊端口:OUTPUT 的暗门

端口号不只对应物理口。1.3 保留了一组特殊值,在解剖 Flow-Mod 时经常遇到:

0xfffffffa ALL 除入口外所有端口(泛洪前的谨慎版) 0xfffffffb CONTROLLER 上送控制器 0xfffffffc LOCAL 交换机自身管理栈 0xfffffffd NORMAL 按传统 L2/L3 转发(OVS 特有逃生门) 0xfffffffe FLOOD 泛洪,遵从生成树约束

抓包看到 OUTPUT:FLOOD,八成是控制器还没有目的 MAC 信息,先泛洪再说——等 Packet-In 学到拓扑后,后续 Flow-Mod 会收敛成精确 OUTPUT。NORMAL 与 FLOOD 的区别值得记牢:前者把包交还给交换机传统的转发逻辑,等于局部退出 OpenFlow 管理。

⚠️ 常见坑:APPLY 了 OUTPUT 之后还跟 GOTO_TABLE——执行到 OUTPUT 包就发出去了,后面的跳转形同虚设,多数交换机直接报错。输出类动作只应出现在流水线终点。

💡 关键直觉:把指令当"控制流"、动作当"数据操作"来理解——这个分层与 CPU 指令和运算单元的关系神似。

对比实验:APPLY 与 WRITE 的可观测差异

两层结构的语义差异可以在解剖台上用两条规则做对照。方案甲:表 0 的规则用 APPLY_ACTIONS 立即改写 VLAN 并 GOTO 表 1;方案乙:表 0 用 WRITE_ACTIONS 把改写压进动作集,表 1 再 GOTO 表 2 让结算发生在最后:

# -> 流水线走完,出口处一次性结算:剥 VLAN、发往 2 口

同一逻辑位置,匹配视图完全不同。这带来两条工程结论:想让后续表"看到改写结果",必须用 APPLY;想让动作在所有表的裁决之后统一生效(比如最终出口才决定,避免中间表覆盖),用 WRITE 进动作集。还有一条更隐蔽的规则:动作集只能整体覆盖,不能增删单项——后续表的 WRITE_ACTIONS 是替换整个集合而非追加,这一点规范写得明确,却是很多自写控制器的 bug 所在。

概念辨析:DROP 从来不是一个动作

初学者常在动作列表里找 DROP,找不到。OpenFlow 1.3 里丢弃是隐式语义:动作集结算后为空、且没有任何 OUTPUT 类动作,包即被丢弃。因此"丢弃规则"的正确写法是匹配条件加空动作集(OVS 语法里写作 actions= 什么都不跟,或 drop)。与之相对,Meter 的 DROP band 是显式丢弃,组表的桶也可以为空但语义各异。三者出现在三个不同的层——指令层、限速层、组层,混淆它们会导致排错时找错地方:动作集为空的丢弃在流表统计里表现为命中计数增长而无输出计数,Meter 丢弃则记在 meter_stats 的 band 计数器里,看错表就永远找不到"丢在哪"的答案。

动作列表的次序约束与 metadata 传递

APPLY_ACTIONS 里的动作按书写顺序逐条执行——先 SET_FIELD 改字段再 OUTPUT,包带着改完的字段出门;次序写反则出口看到的是旧值。动作集则是另一套规则:同类型字段只保留最后一次写入,WRITE_SET_FIELD 对同一字段写两次以第二次为准,"中间表想覆盖前表"直接再写即可。跨表传情报靠 WRITE_METADATA:metadata 是随包游走的 64 位私有区域,表 0 写入的标记,表 3 的匹配条件能读到,它不上线缆、不改包头,是流水线各表共享状态的通道。调试时注意,ovs-ofctl dump-flows 看不到 metadata 的中间值,要用 dpif 层的 trace 输出逐表回放才能确认"标记到底写没写进去"。

本节要点回顾

  • 两层结构:指令管流程(APPLY/WRITE/GOTO/METER),动作管操作(OUTPUT/SET_FIELD/…)
  • 结算时机:APPLY 立即执行,WRITE 进动作集、终点统一结算
  • 改写顺序:按字段类型固定排序,不按写入先后
  • 特殊端口:CONTROLLER、FLOOD、NORMAL 等是 OUTPUT 的暗门,各有语义

版本演化如何改变了这些字段?下一节补齐版本坐标。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U