2. OpenFlow协议规范精读

OpenFlow 是 SDN 控制平面与数据平面之间的南向接口协议,其规范由 ONF(Open Networking Foundation)维护。要真正读懂 OpenFlow,不能只记住“控制器下发流表”这句话,而必须理解三个层次:协议消息如何封装与交互、流表如何组织匹配与处理规则、端口/组表/计量表如何扩展转发能力。本节以 OpenFlow 1.0、1.3、1.5 三个代表性版本为主线,逐层拆解这些核心概念。

一、版本演进与消息类型

OpenFlow 1.0 定义了最基础的消息集合,1.3 是部署最广泛的长期支持版本,1.5 则进一步补充了出口表、计量表增强等能力。三个版本的消息都可分为三大类:

  • Controller-to-Switch(控制器到交换机):由控制器主动发起,如 OFPT_FEATURES_REQUEST 查询交换机能力、OFPT_FLOW_MOD 增删改流表、OFPT_PACKET_OUT 让交换机直接发出指定报文、OFPT_STATS_REQUEST 读取统计信息。
  • Asynchronous(异步):交换机主动上报,如 OFPT_PACKET_IN 把无法匹配的报文上送控制器、OFPT_FLOW_REMOVED 通知流表被删除、OFPT_PORT_STATUS 报告端口状态变化。
  • Symmetric(对称):双方均可发起,如 OFPT_HELLO 用于版本协商、OFPT_ECHO_REQUEST/REPLY 用于保活、OFPT_ERROR 用于错误通告。

版本协商是建立连接的第一步:双方各自发送携带最高支持版本的 HELLO,取两者交集的最小版本作为会话版本。例如控制器支持 1.5、交换机支持 1.3,则最终使用 1.3。若没有交集,交换机会回复 OFPT_ERROR 并终止连接。

二、流表结构:匹配域、优先级与指令集

流表是 OpenFlow 的核心数据结构。1.0 只有单级流表,1.3 起引入多级流表(Table 0 到 Table N),报文按 table_id 顺序逐级匹配。每条流表项(flow entry)由以下字段构成:

  • 匹配域(Match Fields):1.0 使用固定结构,支持入端口、以太网源/目的、VLAN、IP 源/目的、TCP/UDP 端口等;1.3 起改为 TLV 形式的 OXM(OpenFlow Extensible Match),可匹配更多字段,如 IPv6、MPLS、隧道 ID、元数据 metadata。
  • 优先级(Priority):数值越大越优先,同优先级下由交换机决定匹配顺序。
  • 计数器(Counters):统计匹配报文数和字节数。
  • 指令集(Instructions):1.0 中称为动作(Actions),1.3 起统一为指令,包括 Goto-Table、Apply-Actions、Write-Actions、Clear-Actions、Write-Metadata、Meter。
  • 超时(Timeouts):idle_timeout 与 hard_timeout,到期后流表项自动删除并触发 FLOW_REMOVED。
  • Cookie:控制器自定义的标识,便于批量删除或统计。

需要特别区分 Apply-Actions 与 Write-Actions:前者立即执行动作,后者把动作写入动作集,等报文离开流水线时统一执行。若同时存在 Goto-Table,则先跳转,动作集在后续表中继续累积。

三、指令与动作

动作决定报文如何被处理,常见动作包括:OUTPUT(从指定端口发出)、DROP(丢弃)、SET_FIELD(修改字段,如改 VLAN、改目的 MAC)、PUSH_VLAN/POP_VLAN(压入/弹出 VLAN 标签)、GROUP(交给组表处理)、METER(交给计量表限速)。1.0 的动作直接挂在流表项上,1.3 起动作被封装进指令,结构更清晰。

四、端口、组表与计量表

端口(Port)是报文进出的逻辑或物理接口。除物理端口外,OpenFlow 还定义了特殊端口:ALL(除入端口外所有端口)、CONTROLLER(上送控制器)、LOCAL(交换机本地协议栈)、IN_PORT(从入端口原路返回)、FLOOD(泛洪)。

组表(Group Table)用于实现组播、负载均衡和快速故障切换。组类型包括:ALL(复制到所有桶)、SELECT(按哈希选一个桶)、INDIRECT(固定单桶)、FF(快速故障切换,第一个存活桶)。每个桶包含动作集和权重。

计量表(Meter Table)用于限速与统计,1.3 引入。计量表由 meter_id 标识,包含多个 band,每个 band 定义速率阈值 rate、突发 burst_size 和动作(如 DROP 或 DSCP_REMARK)。流表项通过 Meter 指令引用计量表,实现按流限速。

OpenFlow 1.3 流水线核心结构 报文进入 → 多级流表匹配 → 指令/动作集 → 组表/计量表 → 端口输出 控制器 FLOW_MOD / PACKET_OUT PACKET_IN / FLOW_REMOVED OpenFlow 交换机 Table 0 匹配 + 优先级 Goto-Table 1 Table 1 匹配 + 优先级 Write-Actions Table N 匹配 + 优先级 Apply-Actions 流表项(Flow Entry)字段 • Match Fields(OXM:in_port, eth_dst, ipv4_src, tcp_dst…) • Priority(数值越大越优先) • Counters(packets / bytes) • Instructions(Goto / Apply / Write / Meter) • Timeouts • Cookie 组表 Group Table ALL / SELECT / INDIRECT / FF Bucket:动作集 + 权重 用于组播、负载均衡、故障切换 计量表 Meter Table meter_id + band rate / burst_size 动作:DROP / DSCP_REMARK 端口 Port 物理 / 逻辑 ALL / CONTROLLER LOCAL / IN_PORT / FLOOD 下发流表 上送报文 入口报文 出口报文

五、版本差异速查

1.0 与 1.3 的关键差异在于:1.0 只有单级流表、动作直接挂在流表项、匹配字段固定;1.3 引入多级流表、OXM 可扩展匹配、指令与动作分离、组表与计量表、以及 metadata 在表间传递。1.5 进一步增加了出口表(Egress Table)、OFPT_CONTROLLER_STATUS 等消息,并增强了计量表与组表的灵活性。实际开发中,若控制器与交换机都支持 1.3,优先使用 1.3,因为其生态最成熟、文档最完整。

学习建议:阅读规范时,先抓住 OFPT_HELLO → OFPT_FEATURES_REQUEST/REPLY → OFPT_FLOW_MOD → OFPT_PACKET_IN 这条主线,再逐步展开流表字段与指令细节。用 Wireshark 抓取 OpenFlow 报文,对照规范逐字段查看,比单纯阅读文本效率高得多。

六、一个可观察的示例

假设控制器向交换机下发一条流表项:匹配 in_port=1, eth_type=0x0800, ipv4_dst=10.0.0.2,优先级 100,指令为 Apply-Actions: SET_FIELD(eth_dst=00:11:22:33:44:55), OUTPUT:2,idle_timeout=30。当端口 1 收到目的 IP 为 10.0.0.2 的报文时,交换机会修改目的 MAC 并从端口 2 发出;若 30 秒内无匹配报文,该流表项被删除并上报 FLOW_REMOVED。通过 OFPT_STATS_REQUEST 查询该流表项的 packet_count 与 byte_count,即可验证转发是否生效。