原版OpenFlow Switch Specification Version 1.3.0 (Wire Protocol 0x04) June 25, 2012 包含两部分,前一部分是交换机规范,共6个章节,可以简称为“规范”;后一部分是将OpenFlow协议本身作为附件,可以认为是整体第7章,可以简称为“协议”。
本系列作为中文版,基本忠实原文语句和术语,大部分关键词都是中英文对照。将规范和协议再切分为2部分,共4篇完整呈现。
OpenFlow 交换机规范 1.3.0 第一部分 1-5章
https://www.jianshu.com/p/acfeae1771b3
OpenFlow 交换机规范 1.3.0 第二部分 6章
https://www.jianshu.com/p/82e238eb8d14
OpenFlow协议第一部分
https://www.jianshu.com/p/7eb86d164d26
OpenFlow协议第二部分
https://www.jianshu.com/p/9cc08c698106
Appendix A The OpenFlow Protocol
附件A openflow 协议
Openflow 交换机规范的核心就是建立 Openflow 协议消息的结构。
下面描述的结构,定义和枚举都是源于文件 include/openflow/openflow.h,此文件是 openflow 标准规范的一部分。所有结构都是用填充和 8 字节来对齐(8-byte aligned),并且由断言语句检查。所有OpenFlow消息都是以 big-endian 格式发送。
A.1 openflow 头部
每个 openflow 消息都是以 head 开头
/* 所有 OpenFlow 的头部 /
struct ofp_header {
uint8_t version; / OFP_VERSION. 协议版本 /
uint8_t type; / 一个 OFPT_常数 /
uint16_t length; / 包含头部的长度 /
uint32_t xid; / 与包有关的事件 ID,回复配对请求时使用相同的 ID */
};
OFP_ASSERT(sizeof(struct ofp_header) == 8);
openflow 版本指正在使用的协议版本,早期阶段的 openflow 草稿中,用最高位表示实验版本,低位表明协议版本号。现在介绍的协议 1.3.2,版本是 0x04。
长度字段表明了消息的总长度,因此并没有用额外的帧标识去区分每个帧。
类型可以有以下值:
enum ofp_type {
/* 不可改变的消息 */
OFPT_HELLO = 0, /* 对称消息 */
OFPT_ERROR= 1, /* 对称消息 */
OFPT_ECHO_REQUEST= 2, /* 对称消息 */
OFPT_ECHO_REPLY= 3, /* 对称消息 */
OFPT_EXPERIMENTER= 4, /* 对称消息 */
/* 交换机配置消息 */
OFPT_FEATURES_REQUEST = 5, /* 控制器 / 交换机消息 */
OFPT_FEATURES_REPLY= 6, /* 控制器 / 交换机消息 */
OFPT_GET_CONFIG_REQUEST = 7, /* 控制器 / 交换机消息 */
OFPT_GET_CONFIG_REPLY= 8, /* 控制器 / 交换机消息 */
OFPT_SET_CONFIG= 9, /* 控制器 / 交换机消息 */
/* 异步消息 */
OFPT_PACKET_IN = 10, /* 异步消息 */
OFPT_FLOW_REMOVED= 11, /* 异步消息 */
OFPT_PORT_STATUS = 12, /* 异步消息 */
/* 控制器命令消息 */
OFPT_PACKET_OUT= 13, 控制器 / 交换机消息 */
OFPT_FLOW_MOD = 14, 控制器 / 交换机消息 */
OFPT_GROUP_MOD= 15, 控制器 / 交换机消息 */
OFPT_PORT_MOD = 16, 控制器 / 交换机消息 */
OFPT_TABLE_MOD= 17, 控制器 / 交换机消息 */
/* 复合消息 */
OFPT_MULTIPART_REQUEST= 18, 控制器 / 交换机消息 */
OFPT_MULTIPART_REPLY= 19, 控制器 / 交换机消息 */
/* 屏障消息( 见 6.1.1 ) */
OFPT_BARRIER_REQUEST = 20, 控制器 / 交换机消息 */
OFPT_BARRIER_REPLY= 21, 控制器 / 交换机消息 */
/* 队列配置消息. */
OFPT_QUEUE_GET_CONFIG_REQUEST = 22,/* 控制器 / 交换机消息 */
OFPT_QUEUE_GET_CONFIG_REPLY = 23, /* 控制器 / 交换机消息 */
/* 控制器角色改变请求消息 */
OFPT_ROLE_REQUEST = 24, 控制器 / 交换机消息 */
OFPT_ROLE_REPLY= 25, 控制器 / 交换机消息 */
/* 异步消息配置 */
OFPT_GET_ASYNC_REQUEST= 26, 控制器 / 交换机消息 */
OFPT_GET_ASYNC_REPLY = 27, 控制器 / 交换机消息 */
OFPT_SET_ASYNC= 28, 控制器 / 交换机消息 */
/* 测量器和速率限制器配置消息 */
OFPT_METER_MOD= 29, 控制器 / 交换机消息 */
};
A.1.1 填充
大多数 openflow 消息都包含填充字段, 各种类型消息和各种共有的结构中都有。 这些填充字段实际上它们的名字以 pad 开始,填充字段的目的就是使多字节实体和实际处理器边界对齐。
所有消息中共有结构都是 64 比特对齐。其它类型则根据需要对齐,例如 32 位整数在32 位边界中对齐。一个填充规则例外就是 OXM匹配字段,它从不进行填充 (A.3.2)。通常openflow 消息除非明确说明, 否则并不填充; 另一方面, 大多数共有的结构都在结尾填充。
填充字段应设置为零。 一个 openflow 实现必须能接受填充字段中设置的任何值, 而且必须忽略填充域中的内容。
A.2 公共结构
本节介绍各种消息类型使用的结构。
A.2.1 端口结构
Openflow 流水线通过端口收发数据包。交换机可以定义物理端口和逻辑端口, openflow规范定义一些保留端口( 4.1 )。
物理端口,交换机定义的逻辑端口, OFPP_LOCAL 保留端口结构描述如下:
/* Description of a port */
struct ofp_port {
uint32_t port_no;
uint8_t pad[4];
uint8_t hw_addr[OFP_ETH_ALEN];
uint8_t pad2[2]; /* Align to 64 bits. */
char name[OFP_MAX_PORT_NAME_LEN]; /* Null-terminated */
uint32_t config; /* Bitmap of OFPPC_* flags. */
uint32_t state; /* Bitmap of OFPPS_* flags. */
/* Bitmaps of OFPPF_* that describe features. All bits zeroed if
* unsupported or unavailable. */
uint32_t curr; /* Current features. */
uint32_t advertised; /* Features being advertised by the port. */
uint32_t supported; /* Features supported by the port. */
uint32_t peer; /* Features advertised by peer. */
uint32_t curr_speed; /* Current port bitrate in kbps. */
uint32_t max_speed; /* Max port bitrate in kbps */
};
OFP_ASSERT(sizeof(struct ofp_port) == 64);
Port-no 唯一标识交换机内的端口。 hw_addr 通常是端口 MAC地址; OFP_ETH_ALEN为 6.
名字字段是一个空终止字符串,包含一个可读接口名称。 OFP_MAC_PORT_NAME_LEN值为 16.
配置字段描述端口的管理设置,有以下结构:
/* 物理端口的行为标志。 在 ofp_port 用这个标志描述当前端口配置。用来设定端口行为。
*/
enum ofp_port_config {
OFPPC_PORT_DOWN= 1 << 0, /* 端口在管理下关闭 */
OFPPC_NO_RECV= 1 << 2, /* 丢弃端口接收的所有包 */
OFPPC_NO_FWD= 1 << 5, /* 丢弃所有转发给该端口的包 */
OFPPC_NO_PACKET_IN= 1 << 6 /* 不向端口发送 packet-in 消息*/
};
这个OFPPC_PORT_DOWN位表明端口在管理下断开,OpenFlow不应该使用。 这 个OFPPC_NO_RECV位表明从哪个端口收到的包应该忽略。 这个 OFPPC_NO_FWD位表明不应该发送包给这个端口。这个 OFPPFL_NO_PACKET_IN位表明这个端口的包发生漏表行为,不会触发packet-in 消息给控制器。
一般来说,端口配置位由控制器设置并且不被交换机改变。 这些位在交换机实现协议 ( 如:STP或者 BFD)时非常有用。如果端口配置位被交换机通过其他管理接口改变了,交换机发送一个 OFPT_PORT_STATUS消息通知控制器发生了改变。 (STP:生成树协议 BFD:快速故障
检测标准协议)
状态字段描述了端口的内部状态,有以下结构:
/* 物理端口的当前状态,并不是由控制器配置的 */
enum ofp_port_state {
OFPPS_LINK_DOWN= 1 << 0, /* 目前没有物理连接 */
OFPPS_BLOCKED= 1 << 1, /* 端口堵塞 */
OFPPS_LIVE= 1 << 2, /* 有效的快速故障备份组 */
};
端口状态位代表物理连接或 openflower 以外的交换机协议。 OFPPS_LINK_DOWN位表明协议连接断开。 OFPPS_BLOCKED位表明 openflowe 以外的交换,例如 802.1D 生成树协议,防止OFPP_FLOOD端口的使用。
所有端口状态位都是只读的,不能被控制器改变。当端口标志改变时,交换机发送OFPT_PORT_STATUS消息通知控制器。
端口号使用以下规则:
/* 端口编号。端口从 1 开始编号。 */
enum ofp_port_no {
OFPP_MAX= 0xffffff00, /* 物理端口和逻辑交换端口的最大号码 */
/* 保留 OpenFlow 端口 ( 假装输出 "ports"). */
OFPP_IN_PORT= 0xfffffff8, /* 将包从输入端口发出。为了将包发回给输入端口,这个保留端口必须要明确使用。 */
OFPP_TABLE= 0xfffffff9, /* 将包提交给第一个表。 NB: 这个目的端口只能在 packet-out消息中使用 */
OFPP_NORMAL= 0xfffffffa, /* 处理常规的 L2/L3 交换 */
OFPP_FLOOD= 0xfffffffb, /*VLAN 中所有物理端口, 除了输入端口和阻塞的或者链路断开的端口。 */
OFPP_ALL= 0xfffffffc, /* 除了输入端口外的所有物理端口 */
OFPP_CONTROLLER= 0xfffffffd, /* 发给控制器 */
OFPP_LOCAL= 0xfffffffe, /* 本地 openflow "port". */
OFPP_ANY= 0xffffffff /* 通配端口只能在流修改(删除)、流统计请求中使用。筛选所
有流,无论是什么输出端口(包括没有输出端口的流) */
};
Curr,advertised,supported,peer 字段表示链路模式( speed and duplexity ;10M 到
10G,全双工、半双工),链路类型(铜 / 光纤),链路特征(自动协商和暂停)。 Curr 是当前连接方式、类型、特征; advertised 是广播给对方的连接方式、类型、特征; supported是支持的链路方式、类型、特征。 Peer 是对方的连接方式、类型、特征。
端口特征由如下结构表示:
/* 数据通道中有效端口的特征. */
enum ofp_port_features {
OFPPF_10MB_HD= 1 << 0, /* 支持 10M半双工速率 */
OFPPF_10MB_FD= 1 << 1, /* 支持 10M全双工速率 */
OFPPF_100MB_HD= 1 << 2, /* 支持 100M半双工速率 */
OFPPF_100MB_FD= 1 << 3, /* 支持 100M全双工速率 */
OFPPF_1GB_HD= 1 << 4, /* 支持 1 Gb半双工速率 */
OFPPF_1GB_FD= 1 << 5, /* 支持 1 Gb全双工速率 */
OFPPF_10GB_FD= 1 << 6, /* 支持 10Gb全双工速率 */
OFPPF_40GB_FD= 1 << 7, /* 支持 40 Gb全双工速率 */
OFPPF_100GB_FD= 1 << 8, /* 支持 100 Gb全双工速率 */
OFPPF_1TB_FD= 1 << 9, /* 支持 1Tb 全双工速率 */
OFPPF_OTHER= 1 << 10, /* 其他速率,不在列表中 */
OFPPF_COPPER= 1 << 11, /* 铜线媒质. */
OFPPF_FIBER= 1 << 12, /* 光纤媒质 */
OFPPF_AUTONEG= 1 << 13, /* 自动协商 */
OFPPF_PAUSE= 1 << 14, /* 暂停 */
OFPPF_PAUSE_ASYM= 1 << 15 /* Asymmetric pause. */
};
多个标志可同时进行设置。如果没有设置端口速度标志,就会使用 max_speed 或者curr_speed。
curr_speed 表明链路当前传输速率, max_speed是最大传输速率, 以 kbps 为单位。 将数字四舍五入适应通常的用法。例如,一个光学的 10Gb以太网端口应该设置字段为 10000000而不是 10312500,一个 OC_192端口应该将字段设置为 100000000 而不是 9953280
Max_speed表明了链路的最大速率,而 curr_speed 表明了当前速率。一个有三条链路的 LAG端口最大速率为 1Gb/s, 其中一个端口关闭, 一个端口自动协商速率为 1Gb/s, 一个端口自动协商速率 100Mb/s,最大速率为3Gb/s, 当前速率为 1.1Gb/s。
A.2.2 队列结构
一个 openflow 交换机通过简单的排队机制提供有限的 QoS服务。 一个(或多个) 队列可以连接到端口,用来与流表项映射。流表项映射的某个队列,就根据这个队列的配置处理。
(注:将队列绑定在某个端口,实现有限的流量控制操作)
队列由一个 ofp_packet_queue 结构来描述:
/* 队列的完整描述 */
struct ofp_packet_queue {
uint32_t queue_id; /* 指定队列的 ID */
uint32_t port; /* 队列所属的端口 */
uint16_t len; /* 队列的字节长度 */
uint8_t pad[6];/* 64-bit 校正*/ (与上面的凑成 64 倍数)
struct ofp_queue_prop_header properties[0]; /* 特性列表 */
};
OFP_ASSERT(sizeof(struct ofp_packet_queue) == 16);
一组属性,类型和配置来进一步描述每个队列。
enum ofp_queue_properties {
OFPQT_MIN_RATE= 1, /* 最低速率保证 */
OFPQT_MAX_RATE= 2, /* 最大速率 */
OFPQT_EXPERIMENTER= 0xffff /* 实验定义属性 */
};
每个队列属性描述以共同的头部开始:
/* 队列的一般描述。 */
struct ofp_queue_prop_header {
uint16_t property; /* OFPQT_.中的某一个 */
uint16_t len; /* 属性的长度,包括头部的长度 */
uint8_t pad[4]; /* 64-bit alignemnt. */
};
OFP_ASSERT(sizeof(struct ofp_queue_prop_header) == 8);
一个最小速率的队列特性使用下列结构和字段:
/* Min-Rate 队列特征描述 */
struct ofp_queue_prop_min_rate {
struct ofp_queue_prop_header prop_header; /* prop: OFPQT_MIN, len: 16.*/
uint16_t rate; /* 以 0.1%为单位; 禁止大于 1000 */
uint8_t pad[6]; /* 64-bit 对齐 */
};
OFP_ASSERT(sizeof(struct ofp_queue_prop_min_rate) == 16);
如果没有设定速率,则速率设置为 OFPQ_MIN_RATE_UNCFG,即 0xfff。
一个最大速率队列用的结构和字段如下:
/* Max-Rate 队列特征描述 */
struct ofp_queue_prop_max_rate {
struct ofp_queue_prop_header prop_header; /* prop: OFPQT_MAX,len: 16. */
uint16_t rate; /* 以 0.1%为单位; 禁止大于 1000 */
uint8_t pad[6]; /* 64-bit 对齐 */
};
OFP_ASSERT(sizeof(struct ofp_queue_prop_max_rate) == 16);
如果没有设定速率,则速率为 OFPQ_MAX_RATE_UNCFG,即 0fff。
一个实验队列属性用的结构和字段如下:
/* Experimenter 队列特征描述 */
struct ofp_queue_prop_experimenter {
struct ofp_queue_prop_header prop_header; /* prop: OFPQT_EXPERIMENTER,len:
16. */
uint32_t experimenter; /* Experimenter ID 与 ofp_experimenter_header 结构形式
相同*/
uint8_t pad[4]; /* 64-bit 对齐 */
uint8_t data[0]; /* 实验者定义的数据 */
};
OFP_ASSERT(sizeof(struct ofp_queue_prop_experimenter) == 16);
标准 Openflow 处理并没有解释其余的实验队列属性,而是由实验者自己定义。
A.2.3 流匹配结构
一个 openflow 匹配表是由流匹配头部和一序列 0 或者一些流匹配字段组成。
A.2.3.1 流匹配头部
用 ofp_match 结构描述匹配头部:
/* 流的匹配字段 */
struct ofp_match {
uint16_t type; /* One of OFPMT_**/
uint16_t length; /* ofp_match 长度( 包括填充字段) */
/* Followed by:
* - Exactly (length - 4) (possibly 0) bytes containing OXMTLVs, then
* - Exactly ((length + 7)/8*8 - length) (between 0 and 7) bytes of
* all-zero bytes
*总之, 需要时就在 ofp_match 中进行填充, 使其为 8 的倍数,结构长度对齐。
*/
uint8_t oxm_fields[0]; /* 0 or more OXMmatch fields */
uint8_t pad[4]; /* Zero bytes - see above for sizing */
};
OFP_ASSERT(sizeof(struct ofp_match) == 8);
type 字段设置为 OFPMT_OXM,长度字段为包括所有匹配字段在内的 ofp_match 结构的实际长度。 OpenFlow 有效载荷是一组 OXM流匹配字段。
/* 匹配类型表明正在使用的匹配结构 (构成匹配的字段组)。所有匹配结构中匹配类型在类型字段的开始。 "OpenFlow Extensible Match" 类型 与下面介绍的 OXMTLV 格式相符,并且必须所有交换机都支持。 Extensions that define other match types maybe published on the ONF wiki. 扩展项支持是可选的。
*/
enum ofp_match_type {
OFPMT_STANDARD= 0, /* 不赞成 */
OFPMT_OXM= 1, /* OpenFlow Extensible Match */
};
此 规 范 中 唯 一 有 效 的 匹 配 类 型 是 OFPMT_OXM,不 支 持 OpenFlow 1.1 匹 配 类 型OFPMT_STANDARD。如果使用另一种匹配类型,匹配字段和有效载荷可能变得会不一样,但是这超出了本规范的范围。
A.2.3.2 Flow Match Field Structures
流 匹 配 字 段 用 OpenFlow 可 扩 展 的 匹 配 (OXM)格 式 来 描 述, 即一种简化的形式type-length-value (TLV)。每个 OXMTLV长度从 5 到 259 ( 包括全部) 字节。 OXMTLVs 没有相同的长度, 也没有采取填充来对齐。 一个 OXMTLV 的前四个字节是头部,下面接的是 body部分。
一个 OXMTLV的头部可看成一个网络字节顺序的 32-bit 字 ( 见图 4)。
OXMTLV的头部定义如 Table 9
oxm_class 是一个 OXM匹配类,包含相关的匹配类型,在 A.2.3.3 节有相关介绍。
oxm_field 是一个 class-specfic 值, 在匹配类里区分某一个匹配类型。 oxm_class 和oxm_field 两者(the most-signicant 23 bits of the header) 都是 oxm_type。oxm_type 通常表示一个协议的头部字段,例如以太网类型,也可以适用于元数据,例如数据包到达的那个交换机端口。
oxm_hasmask 定义如果 OXMTLV 包含位掩码,在 A.2.3.5 部分有详细介绍。
oxm_length 是以字节为单位的正整数,表示 OXMTLV负荷长度。 OXMTLV的长度,包括头部,是精确地 4+oxm_length 个字节。
对于一个指定的 oxm_class, oxm_field, and oxm_hasmask 值, oxm_length 是一个常量。包括只允许软件最低限度的解析 OXMTLV的未知类型。(类似的,若指定 oxm_class,oxm_field, and oxm_length, 则 oxm_hasmask 是一个常量。)
A.2.3.3 OXMclasses
匹配类型使用 OXM匹配类进行了结构化, openflow 规范可以区分 OXM的两种匹配类, ONF成员类和 ONF保留类, 使用高位比特区分。 高位比特为 1 是 ONF保留类, 供 openflow 规范本身使用。高位比特为 0 的是ONF成员类,必要时由 ONF分配,标识一个 ONF成员,并且可任意使用。对 ONF成员类的支持是可选的。
OXM类定义如下:
/* OXMClass IDs.
* 高位比特区分保留类和成员类
* 0x0000 到 0x7FFF 的是成员类,由 ONF分配
* 0x8000 到 0xFFFE 的是保留类, 用于标准化。
*/
enum ofp_oxm_class {
OFPXMC_NXM_0= 0x0000, /* 与 NXM后向兼容 */
OFPXMC_NXM_1= 0x0001, /* 与 NXM后向兼容 */
OFPXMC_OPENFLOW_BASIC = 0x8000, /* OpenFlow 基本类 */
OFPXMC_EXPERIMENTER= 0xFFFF, /* 实验者类 */
};
OPPXMC_OPENFLOW_BASIC 类包含 openflow 匹配域的基本设置(见 A.2.3.7 )。可选类OFPXMC_EXPERIMENTER 用于实验者匹配(见 A.2.3.8 )。其他 ONF保留类用于将来使用,如用于规范的 模块化。 ONF成员类的前两个OFPXMC_NXM_0和 OFPXMC_NXM_1用 于 与 Nicira Extensible Match(NXM)规范的后向兼容。
A.2.3.4 Flow Match
一个 zero —length 的 openflow 匹配( 即没有 OXMTLVs)可以匹配每个包。应被通配的匹配字段都从 openflow 匹配中忽略。
OXMTLV 对由 openflow 匹配进行匹配的包设置限制:
如果 oxm_hasmask为 0,OXM TLV的 body 包含该字段的值,称为 oxm_value。OXM TLV匹配仅与相应字段等于 oxm_value 的包相匹配。
如果 oxm_hasmask为 1,oxm_entry 的 body 包含该字段的值 (oxm_value),紧接着是一个相同长度的位掩码,叫为 oxm_mask。oxm_mask 里的每 1_bit 限制 OXMTLV 只能匹配字段与 oxm_value 相应位相等的包。 oxm_mask 的 0_bit 没有这样的限制。
当使用掩码时, oxm_mask中的 0_bit 与 oxm_value 中的 1_bit 一致时产生错误。交换机必须用OFPET_BAD_MATCH类型的消息和 OFPBMC_BAD_WILDCARDS 事件报告错误。
下表总结了相对应的 oxm_mask与 oxm_value 位之间在使用掩码时的约束关系。忽略oxm_mask等同于提供一个全是 1-bit 的 oxm_mask。
当有多个 OXMTLV 时,要满足所有的约束关系:包字段必须匹配 openflow 匹配中所有OXMTLV的部分。若没有 OXMTLV的字段,则通配成 ANY, 忽略的 OXMTLV全部掩码为零。
A.2.3.5 流匹配字段掩码
当 oxm_hasmask为 1,OXMTLV包含一位掩码并且长度有效地增加一倍, 所以 oxm_length总是 奇数, 位掩码在字段值之后,使用相同的方式编码。掩码定义某个指定的位为 0,表明与其相应字段相同位的匹配是一种“ don’t care”匹配,而 1 意味着要精确匹配。
一个全 0比特 oxm_mask等同于 彻底忽略 OXM TLV,一个全1 比特oxm_mask等同于 指定oxm_hasmask为0,并且忽略 oxm_mask。
一些oxm_type不支持掩码的通配符,也就是说,当这些字段被指定时 oxm_hasmask必须为0。例如,标识数据包接收的输入端口(接受包的端口)字段可能未被掩码。
一些oxm_type 不支持掩码通配符,可能只支持特定 oxm_mask模式。例如,一些有 IPv4地址的字段可能被限制为 CIDR掩码(子网掩码)。
个别字段的限定会在规范中详细说明。交换机可能接受规范不允许的 oxm_hasmask oxm_mask值,只要交换机能正确执行对 oxm_hasmask 或oxm_mask值的支持,交换机必须拒绝尝试创建包含它不支持的 oxm_hasmask或oxm_mask值的流表项(见 6.4 )。
A.2.3.6 流匹配字段基础
给定oxm_type的OXMTLV的存在是受到其他 OXM TLV的存在或值的限制,一般来说,只有当openflow 匹配明确地匹配了相应的协议,才能匹配协议的头部字段。
例如:
只 有 在 另 一 个 表 项 中 oxm_type=OXM_OF_ETH_TYPE,oxm_hasmask=0,并且oxm_value=0x0800的前提下,才允许 OXM TLV 中xom_type=OXM_OF_IPV4_SRC。也就是说,只有以太网的类型明确设置为 IPV4时,IPV4源地址的匹配才被允许。
只有在一个表项中 oxm_type=OXM_OF_ETH_TYPE,oxm_hasmask=0, oxm_value=0x0800或0x6dd, 并且另一个表项中 oxm_type=OXM_OF_IP_PROTO,oxm_mask=0,oxm_value=6 的前提下,O才允许 OXM TLV中 xom_type=OXM_OF_TCP_SRC。也就是说,只有当以太网的类型为 IP,IP 协议为TCP时,在 TCP源端口的匹配才允许。
只有在一个表项中 oxm_type=OXM_OF_ETH_TYPE,oxm_hasmask=0,oxm_value=0x8847或0x8848时,才允许 OXM TLV中oxm_type=OXM_OF_MPLS_LABEL。
只有在一个表项中 oxm_type=OXM_OF_VLAN_VID,oxm_value!=OFPVID=NONE 时, 才允许OXM TLV中oxm_type=OXM_OF_VLAN_PCP。
这些对个别字段的限制在规范中进行了说明 (见7.2.3.7 )。交换机实现时可放宽这些限制。例如,交换机可能在没有任何先决条件的情况下接受。交换机必须拒绝建立试图违反限
制的流表项(见 6.4 ),并且必须处理那些由于缺乏先决条件而不一致的匹配 (如, 既匹配 TCP源端口又匹配 UDP目的端口)。
由成员(成员类或作为实验者字段)定义的新的匹配字段可为已有匹配字段提供了备用条件。例如,通过替换 ETH_TYPE,在可选链路技术(如 PPP)上重复使用用已有的 IP匹配字段(对于 PPP,那可能是一个假设的 PPP_PROTOCOL 字段)。
有条件约束的 OXM TLV必须出现在那些作为条件的 OXM TLV后面,那些在 openflow 匹配里的OXMTLV的顺序不受约束。
任一指定的 oxm_type最多在 openflow 匹配中出现一次,否则,交换机必须产生一个错误(见6.4 )。交换机实现时可放松这个规则, 允许在某些情况下多次出现 oxm_type, 但是, 这种匹配行为由具体实现来决定。
如果一个流表实现指定的 OXM TLV,这个流表必须接受包含其先决条件的有效匹配,即使这个流表并不支持先决条件指定的匹配域的所有可能值。例如,一个流表匹配 IPv4源地址,这个流表必须接受以太网类型精确匹配 IPV4,但是不需要支持以太网类型匹配其他任何值。
A.2.3.A.流匹配域
本规范为 oxm_class=OFPAMC_OPENFLOW_BASIC 的匹配字段定义了一个默认的集合, 其值如下:
/* OXMFlow match field types for OpenFlow basic class. */
enum oxm_ofb_match_fields {
OFPXMT_OFB_IN_PORT= 0, /* Switch input port. */
OFPXMT_OFB_IN_PHY_PORT= 1, /* Switch physical input port. */
OFPXMT_OFB_METADATA = 2, /* Metadata passed between tables. */
OFPXMT_OFB_ETH_DST= 3, /* Ethernet destination address. */
OFPXMT_OFB_ETH_SRC= 4, /* Ethernet source address. */
OFPXMT_OFB_ETH_TYPE= 5, /* Ethernet frame type. */
OFPXMT_OFB_VLAN_VID= 6, /* VLAN id. */
OFPXMT_OFB_VLAN_PCP= 7, /* VLAN priority. */
OFPXMT_OFB_IP_DSCP= 8, /* IP DSCP(6 bits in ToS field). */
OFPXMT_OFB_IP_ECN= 9, /* IP ECN (2 bits in ToS field). */
OFPXMT_OFB_IP_PROTO= 10, /* IP protocol. */
OFPXMT_OFB_IPV4_SRC= 11, /* IPv4 source address. */
OFPXMT_OFB_IPV4_DST= 12, /* IPv4 destination address. */
OFPXMT_OFB_TCP_SRC= 13, /* TCP source port. */
OFPXMT_OFB_TCP_DST= 14, /* TCP destination port. */
OFPXMT_OFB_UDP_SRC= 15, /* UDPsource port. */
OFPXMT_OFB_UDP_DST= 16, /* UDPdestination port. */
OFPXMT_OFB_SCTP_SRC= 17, /* SCTP source port. */
OFPXMT_OFB_SCTP_DST= 18, /* SCTP destination port. */
OFPXMT_OFB_ICMPV4_TYPE= 19, /* ICMP type. */
OFPXMT_OFB_ICMPV4_CODE = 20, /* ICMP code. */
OFPXMT_OFB_ARP_OP= 21, /* ARPopcode. */
OFPXMT_OFB_ARP_SPA= 22, /* ARP source IPv4 address. */
OFPXMT_OFB_ARP_TPA= 23, /* ARP target IPv4 address. */
OFPXMT_OFB_ARP_SHA= 24, /* ARP source hardware address. */
OFPXMT_OFB_ARP_THA= 25, /* ARP target hardware address. */
OFPXMT_OFB_IPV6_SRC= 26, /* IPv6 source address. */
OFPXMT_OFB_IPV6_DST= 27, /* IPv6 destination address. */
OFPXMT_OFB_IPV6_FLABEL= 28, /* IPv6 Flow Label */
OFPXMT_OFB_ICMPV6_TYPE= 29, /* ICMPv6 type. */
OFPXMT_OFB_ICMPV6_CODE = 30, /* ICMPv6 code. */
OFPXMT_OFB_IPV6_ND_TARGET = 31, /* Target address for ND. */
OFPXMT_OFB_IPV6_ND_SLL= 32, /* Source link-layer for ND. */
OFPXMT_OFB_IPV6_ND_TLL= 33, /* Target link-layer for ND. */
OFPXMT_OFB_MPLS_LABEL = 34, /* MPLSlabel. */
OFPXMT_OFB_MPLS_TC= 35, /* MPLSTC. */
OFPXMT_OFP_MPLS_BOS = 36, /* MPLSBoS bit. */
OFPXMT_OFB_PBB_ISID= 37, /* PBB I-SID. */
OFPXMT_OFB_TUNNEL_ID= 38, /* Logical Port Metadata. */
OFPXMT_OFB_IPV6_EXTHDR= 39, /* IPv6 Extension Header pseudo-field */
};
交换机在它的流水线中必须支持表 11中列出的 required 匹配域。每个 required 匹配域在交换机中必须至少有一个流表支持:流表必须保证匹配那个字段,匹配域的先决条件在表中必须已列出(见 A.2.3.6 )。required 域并不需要在所有流表中实现,也不需要在相同流表中实现。流表可以支持 no-requried 和实验者匹配域。 控制器可以查询交换机中每个流表支持哪些匹配域。
每个匹配域有不同的大小,先决条件,和掩码能力,如表 12所示。如果没有明确说明,每个字段类型指的就是数据包头域最外层事件。
输入端口 OXM_OF_IN_PORT是一个有效的标准 openflow 端口,可以是物理端口,逻辑端口,OFPP_LOCAL保留端口或是 OFPP_CONTROLLER保留端口。物理端口 OXM_OF_IN_PHY_PORT在包输入消息中用来标识逻辑端口下层的物理端口(见 A.4.1 )。
在多个表间查找时,元数据域 OXM_OF_METADATA 用来传递信息。这个值可被随意覆盖。
隧道 ID 字段 OXM_OF_TUNNEL_ID携带与逻辑端口有关的可选元数据。元数据的映射由逻辑端口实现定义。如果逻辑端口不支持这样的数据或者数据包从物理端口接收,则值为 0。
例如,一个通过 GRE隧道接收的数据包包括一个( 32 位)密钥,密钥存储在低 32 位,高 32位为 0。对 MPLS逻辑端口,低 20 位表示 MPLS标记。对 VxLAN逻辑端口,低 24 位表示 VNI。
忽略 OFPXMT_OFB_LAN_VID字段表明流表项应该匹配数据包, 无论数据包是否包含相应的标签。对于 VLAN标签定义了下列特殊值,允许其匹配任何标签的数据包,与标签的值无关,并且支持匹配没有 VLAN标签的数据包。为 OFPXMT_OFB_VLAN_VID定义的特殊值如下:
/* VLAN id 是 12-bits, 所以可以用总共 16 位来表示特殊情况
*
*/
enum ofp_vlan_id {
OFPVID_PRESENT= 0x1000, /* 表示 VLAN id 的 bit 位*/
OFPVID_NONE= 0x0000, /* 没有设置 VLAN id */
};
当 OFPXMT_OFB_VLAN_VID字段为 通配 时(不是当前)或者 OFPXMT_OFB_VLAN_VID设置为OFPVID_NONE时必须拒绝 OFPXMT_OFB_VLAN_PCP 字段。表 13 总结了通配位的组合,以及特殊的 VLAN标签匹配的域值。
OXM_OF_IPV6_EXTHDR是一个伪字段,表明数据包头部中不同的 IPV6 扩展头部。 IPV6 扩展头部比特组合在 OXM_OF_EXTHDR字段中,这些比特位有下列值:
/* IPv6扩展报头定义位为 pseudo-field */
enum ofp_ipv6exthdr_flags {
OFPIEH_NONEXT= 1 << 0, /* "No next header" encountered. */
OFPIEH_ESP= 1 << 1, /* Encrypted Sec Payload header present. */
OFPIEH_AUTH= 1 << 2, /* Authentication header present. */
OFPIEH_DEST= 1 << 3, /* 1 or 2 dest headers present. */
OFPIEH_FRAG= 1 << 4, /* Fragment header present. */
OFPIEH_ROUTER= 1 << 5, /* Router header present. */
OFPIEH_HOP= 1 << 6, /* Hop-by-hop header present. */
OFPIEH_UNREP= 1 << 7, /* Unexpected repeats encountered. */
OFPIEH_UNSEQ= 1 << 8, /* Unexpected sequencing encountered. */
};
如果IPv6逐跳选项扩展报头作为数据包中第一个扩展报头则 OFPIEH_HOP设为1。
如果目前是一个 IPv6路由扩展报头则设置 OFPIEH_ROUTER为1。
如果目前是 IPv6分片扩展报头则设 OFIEH_FRAG为1。
如果目前是一个或多个 IPv6目的地选项扩展报头则设 OFPIEH_DEST为1。IPv6数据包中有一个或两个很正常(见 RFC2460)。
如果目前是 IPv6身份验证扩展报头则设置 OFPIEH_AUTH为1。
如果目前是 IPv6封装安全净荷扩展报头则设置 OFPIEH_ESP为1。
如果目前 IPv6没有下一个头扩展报头怎设 OFIEH_NONNEXT 设为1。
如果IPv6扩展报头不是 RFC的优选项(不要求的),则设 OFPIEH_UNSEQ为1。
如果多个 IPv6扩展报头不期而遇, 则设OFPIEH_UNREP为1。(若是两个目的地的可选报头,将不需要设置该位)。
A.2.3.8 实验者流匹配字段
对 experimenter-specific 流匹配字段的支持是可选的。 Experimenter-specific 可使用oxm_class=OFPXMC_EXPERIMENTER来定义流匹配字段。 OXM TLV body 前 4 个字节包含实验
者身份,结构同 ofp_experimenter (见 A.5.4 )。Oxm_field 和 OXMTLV 其余部分都是
experimenter-defined 并且不需要填充或对齐。
/* Header for OXMexperimenter match fields. */
struct ofp_oxm_experimenter_header {
uint32_t oxm_header; /* oxm_class = OFPXMC_EXPERIMENTER*/
uint32_t experimenter; /* Experimenter ID which takes the same
form as in struct ofp_experimenter_header. */
};
OFP_ASSERT(sizeof(struct ofp_oxm_experimenter_header) == 8);
A.2.4 流指令结构
当流匹配了流表项,就执行与这个流表项相关的流指令。定义的指令列表如下:
enum ofp_instruction_type {
OFPIT_GOTO_TABLE= 1, /* Setup the next table in the lookuppipeline */
OFPIT_WRITE_METADATA= 2, /* Setup the metadata field for use later inpipeline */
OFPIT_WRITE_ACTIONS= 3, /* Write the action(s) onto the datapath actionset */
OFPIT_APPLY_ACTIONS= 4, /* Applies the action(s) immediately */
OFPIT_CLEAR_ACTIONS= 5, /* Clears all actions from the datapathaction set */
OFPIT_METER= 6, /* Apply meter (rate limiter) */
OFPIT_EXPERIMENTER= 0xFFFF /* Experimenter instruction */
};
5.9 节中介绍的指令集,流表可能支持指令类型的子集。指令定义包含指令类型,长度
及相关数据。
/* 所有指令通用头部。长度包括头部和为了实现 64-bit 对齐的填充部分。
* NB: 指令长度必须为 8 的倍数 */
struct ofp_instruction {
uint16_t type; /* Instruction type */
uint16_t len; /* Length of this struct in bytes. */
};
OFP_ASSERT(sizeof(struct ofp_instruction) == 4);
OFPIT_GOTO_TABLE指令用以下结构和字段:
/* OFPIT_GOTO_TABLE的指令结构 */
struct ofp_instruction_goto_table {
uint16_t type; /* OFPIT_GOTO_TABLE*/
uint16_t len; /* Length of this struct in bytes. */
uint8_t table_id; /* Set next table in the lookup pipeline */
uint8_t pad[3]; /* Pad to 64 bits. */
};
OFP_ASSERT(sizeof(struct ofp_instruction_goto_table) == 8);
table_id 表示数据包处理流水线中的下一个表。
OFPIT_WRITE_METADATA指令用以下结构和字段:
/* OFPIT_WRITE_METADATA的指令结构 */
struct ofp_instruction_write_metadata {
uint16_t type; /* OFPIT_WRITE_METADATA*/
uint16_t len; /* Length of this struct in bytes. */
uint8_t pad[4]; /* Align to 64-bits */
uint64_t metadata; /* Metadata value to write */
uint64_t metadata_mask; /* Metadata write bitmask */
};
OFP_ASSERT(sizeof(struct ofp_instruction_write_metadata) == 24);
下一个表要查找元数据, 可以用 metadata 和 metadata_mask 写入匹配字段的指定比特。
如果这个指令没有规定,则元数据发送时不需改变。
OFPIT_WRITE_ACTIONS,OFPIT_APPLY_ACTIONS, 和 OFPIT_CLEAR_ACTIONS指令用以下
结构和字段:
/*OFPIT_WRITE/APPLY/CLEAR_ACTIONS的结构指令 */
struct ofp_instruction_actions {
uint16_t type; /* OFPIT_*_ACTIONS的一种 */
uint16_t len; /* 此结构的比特长度 */
uint8_t pad[4]; /* 64 位对齐 */
struct ofp_action_header actions[0];
/* 0 个或多个与 OFPIT_WRITE_ACTIONS和 OFPIT_APPLY_ACTIONS相关的行动 */
};
OFP_ASSERT(sizeof(struct ofp_instruction_actions) == 8);
对于 Apply-Actions 指令,行动字段被当做一个列表,此行动应用于有序包。对于
Write-Actions 指令,行动字段作为一个集合,此行动并入当前行动集。
对于 Clear-Actions 指令,此结构不包含任何行动。
指令 OFPIT_METER使用以下的结构和字段。
/*OFPIT_METER的结构指令 */
struct ofp_instruction_meter {
uint16_t type; /* OFPIT_METER的类型 */
uint16_t len; /* Length is 8. */
uint32_t meter_id; /* Meter 实例的 ID. */
};
OFP_ASSERT(sizeof(struct ofp_instruction_meter) == 8);
meter_id 显示被应用在包上的测量。
指令 OFPIT_EXPERIMENTER使用以下的结构和字段:
/*OFPIT_EXPERIMENTER的结构指令 */
struct ofp_instruction_experimenter {
uint16_t type; /* OFPIT_EXPERIMENTER*/
uint16_t len; /* 结构的比特长度 */
uint32_t experimenter;
/*Experimenter ID,其格式同 ofp_experimenter_header */
/* 实验者自定义的其它数据. */
};
OFP_ASSERT(sizeof(struct ofp_instruction_experimenter) == 8);
A.2.5 行动的结构
许多行动是和流表项、组或数据相关的,目前定义的行动类型如下:
Enum ofp_action_type {
OFPAT_OUTPUT =0 /* 输出到交换机端口 */
OFPAT_COPY_TTL_OUT=11 /* 申请复制 TTL outwards 去打包,从相邻外层到最外层 */
OFPAT_COPY_TTL_IN=12/* 申请复制 TTL inwards 打包,从最外层到相邻外层 */
OFPAT_SET_MPLS_TTL= 15, /* MPLSTTL*/
OFPAT_DEC_MPLS_TTL= 16,/* 减少 MPLSTTL*/
OFPAT_PUSH_VLAN= 17, /* 压入一个新的 VLAN标签
OFPAT_POP_VLAN= 18, /* 弹出最外面的 VLAN 标签*/
OFPAT_PUSH_MPLS= 19, /* 压入一个新的 MPLS标签*/
OFPAT_POP_MPLS= 20, /* 弹出最外面的 MPLS标签 */
OFPAT_SET_QUEUE= 21, /* 设置输出端口一个队列的 ID*/
OFPAT_GROUP= 22, /* 申请组 */
OFPAT_SET_NW_TTL= 23, /* IP TTL. */
OFPAT_DEC_NW_TTL= 24, /* 减少 IP TTL. */
OFPAT_SET_FIELD= 25, /* 用 OXMTLV 格式设置一个头部字段 */
OFPAT_PUSH_PBB= 26, /* 压入一个新的 PBB 服务标签 (I-TAG) */
OFPAT_POP_PBB= 27, /* 弹出外面的 PBB 服务标签 (I-TAG) */
OFPAT_EXPERIMENTER= 0xffff
};
输出、组、建立队列的行动在 5.12 节进行了描述,压入 / 弹出的行动在表格 6 进行了介
绍,表 12 用 OXM类型对 Set-Field 的行动进行了描述,一个行动的定义包含了操作的类型、
长度和任何关联的数据。
/* 行动的头部是所有行动共有的,长度包括它的头部和填充,使行动达到 64-bit。
NB:操作的长度 必须 是 8 的倍数, /
struct ofp_action_header {
uint16_t type; / OFPAT_ 之中的一个 /
uint16_t len; / 行动的长度, 包括它的头部和填充,使行动达到 64-bit */
uint8_t pad[4];
};
OFP_ASSERT(sizeof(struct ofp_action_header) == 8);
一个输出行动是使用下面的结构和字段:
/*OFPAT_OUTPUT是发送数据包到端口的行动结构。
当‘port ’是 OFPP_CONTROLLER时, 'max_len' 表示发送的最大字节数。一个 0 的
'max_len' 表示没有字节需要被发送。 一个 OFPCML_NO_BUFFER的 'max_len' 表示数据包没有
缓冲并且整个数据包将会被发送到控制器。
struct ofp_action_output {
uint16_t type; /* OFPAT_OUTPUT.*/
uint16_t len; /* 长度是 16. */
uint32_t port; /* 输出端口. */
uint16_t max_len; /* 发送给控制器的最大长度. */
uint8_t pad[6]; /* 填充到 64 bits. */
};
OFP_ASSERT(sizeof(struct ofp_action_output) == 16);
端口指的是数据包将发送的端口。 max_len 表示从 OFPP_CONTROLLER端口发送出去的数据包中数据的最大值。如果 max_len 是 0,这个交换机必须发送 0 字节。 OFPCML_NO_BUFFER中的 max_len 表示整个数据包将发送,并且它不能缓冲。
enum ofp_controller_max_len {
OFPCML_MAX= 0xffe5, /* 用来请求一个指定长度的 max_len 最大值 */
OFPCML_NO_BUFFER= 0xffff /* 表明没有缓冲,整个数据包将会发送到控制器 */
};
Group 行动使用下面的结构和字段:
/* Action structure for OFPAT_GROUP.*/
struct ofp_action_group {
uint16_t type; /* OFPAT_GROUP.*/
uint16_t len; /* 长度是 8. */
uint32_t group_id; /* 数组标识符 */
};
OFP_ASSERT(sizeof(struct ofp_action_group) == 8);
group_id 标识处理数据包的组,存储段集的使用依赖于组的类型。
Set-Queue 的行动设置队列 id,用来映射一个流表项到已配置的端口队列,不考虑 ToS和 VLANPCP,这个数据包不会由于 Set-Queue 行动而发生改变。如果交换机内部处理需要设置 ToS/PCP比特, 那么在发送数据包之前应该恢复其原来的值。
一个交换机可能只支持一个特定的 PCP/ToS队列。在那种情况下, 我们不能随意映射一个流表到那个特定的队列,因此 Set-Queue 行动不被支持。用户仍然可以使用这些队列, 并通过设置有关字段 (ToS, VLAN PCP)等来映射流表项到队列。
Set-Queue 行动使用下面的结构和字段:
/* OFPAT_SET_QUEUEaction struct: 发送数据包到端口的给定队列。 */
struct ofp_action_set_queue {
uint16_t type; /* OFPAT_SET_QUEUE.*/
uint16_t len; /* Len is 8. */
uint32_t queue_id; /* 数据包的队列 id. */
};
OFP_ASSERT(sizeof(struct ofp_action_set_queue) == 8);
Set MPLSTTL 行动使用下面的结构和字段:
/* OFPAT_SET_MPLS_TTL的行动结构。 */
struct ofp_action_mpls_ttl {
uint16_t type; /* OFPAT_SET_MPLS_TTL.*/
uint16_t len; /* 长度是 8. */
uint8_t mpls_ttl; /* MPLSTTL */
uint8_t pad[3];
};
OFP_ASSERT(sizeof(struct ofp_action_mpls_ttl) == 8);
mpls_ttl 字段由 MPLSTTL来设置。
Decrement MPLSTTL 行动无需协商而且只包含一个 ofp_action_header,行动使 MPLSTTL值减少。
Set_IPv4 TTL 行动使用下列结构和字段:
/* Action structure for OFPAT_SET_NW_TTL.*/
struct ofp_action_nw_ttl {
uint16_t type; /* OFPAT_SET_NW_TTL.*/
uint16_t len; /* Length is 8. */
uint8_t nw_ttl; /* IP TTL */
uint8_t pad[3];
};
OFP_ASSERT(sizeof(struct ofp_action_nw_ttl) == 8);
nw_ttl 字段是 IP 头部的 TTL地址。
Decrement IPv4 TTL 行动无需协商而且只包含一个 ofp_action_header,行动使 IP 头部的 TTL 值减少。
Copy TTL outwards 行动无需协商而且只包含一个 ofp_action_header,行动从紧邻最外层的头部复制 TTL 值到最外层头部的 TTL。
Copy TTL inwards 行动无需协商而且只包含一个 ofp_action_header,行动从最外层的头部复制 TTL值到紧邻最外层头部的 TTL。
Push VLAN header 行动, Push MPLSheader 和 Push PBB header 行动使用下列结构和字段:
/* Action structure for OFPAT_PUSH_VLAN/MPLS/PBB.*/
struct ofp_action_push {
uint16_t type; /* OFPAT_PUSH_VLAN/MPLS/PBB. */
uint16_t len; /* Length is 8. */
uint16_t ethertype; /* Ethertype */
uint8_t pad[2];
};
OFP_ASSERT(sizeof(struct ofp_action_push) == 8);
ethertype 表示 Ethertype 的新标签。当压入一个新的 VLAN标签、新的 MPLS头部或者PBB业务头部时会使用.
Push PBBheader 行动逻辑上会压入一个新的 PBB 业务实例头部到数据包 (I-TAG TCI),并复制原始的数据包 ethertype 地址到标签的客户地址 (C-DA and C-SA)。 I-TAG 的客户地址就封装在原始的 Ethertype 地址的位置上,因此这个操作可看作把骨干 MAC-in-MAC 头部和 I-SID 字段加到数据包前面。这个 Push PBB header 行动不会把骨干 VANL头部( B-TAG)加给数据包, 它可在压入 PBB头部操作之后再通过 Push VlAN header 的操作添加。这个操作之后,常规的 set-field 行动可用来修改外部 Ethertype 地址( B-DA 和 B-SA)。
Pop VLAN header 行动不需要参数,仅由通用 ofp_action_header 组成,这个行动会弹出数据包最外面的 VLAN标签。
Pop PBBheader 行动不需要参数,仅由通用 ofp_action_header 组成。这个行动逻辑上会弹出数据包最外面的 PBB业务实例头部,并且会复制数据包 Ethernet 地址里的客户地址。
这个操作表示从数据包前面移走骨干 MAC-in-MAC 头部和 I-SID 字段。 Pop PBBheader 行动不会移走数据包的骨干 VLAN头部(B-TAG), 它应在此操作之前通过 PopVLANheader 行动移走。
Pop MPLSheader 的行动使用以下结构和字段:
/* 行动结构 for OFPAT_POP_MPLS.*/
struct ofp_action_pop_mpls {
uint16_t type; /* OFPAT_POP_MPLS.*/
uint16_t len; /* 长度是 8. */
uint16_t ethertype; /* ethertype*/
uint8_t pad[2];
};
OFP_ASSERT(sizeof(struct ofp_action_pop_mpls) == 8);
ethertype 表示 MPLS负荷的 Ethertype。不管“bottom of stack (BoS)" 比特是否在 MPLS垫片上进行了设置, ethertype 用来作为即将形成的数据包的 Ethertype。 推荐流表项使用这个行动来匹配 MPLS标签和 MPLSBoS字段来避免给 MPLS负荷错误的 ethertype。
当 BoS不等于 1 时,MPLS 规范不允许对 MPLS负荷设置任意的 ethertype。控制器负责遵守这个要求,并且只能设置 0x8847 或 0x8848 作为那些 MPLS负荷的 ethertype。交换机可以 随意的 执行 MPLS需求: 此情况下,交换机应该拒绝任何与通配 BoS相匹配的流表项, 和在 Pop MPLSheader 行动中,利用错误的 ethertype 匹配 BoS为 0 的流表项。这两种情况都应该返回一个 ofp_error_msg, 带有 OFPET_BAD_ACTION类型和OFPBAC_MATCH_INCONSISTENT
代码.
Set Field 行动使用以下结构和行动:
/* Action structure for OFPAT_SET_FIELD.*/
struct ofp_action_set_field {
uint16_t type; /* OFPAT_SET_FIELD.*/
uint16_t len; /* 长度会被填补到 64 字节. */
/* 接着:
* - 精确的 oxm_len bytes 包括一个单一 OXMTLV, then
* - Exactly ((oxm_len + 4) + 7)/8*8 - (oxm_len + 4) (between 0 and 7)
* bytes of all-zero bytes
*/
uint8_t field[4]; /* OXMTLV - Make compiler happy */
};
OFP_ASSERT(sizeof(struct ofp_action_set_field) == 8);
Field 包含使用单一 OXMTLV 结构的头部字段。 Set-Field 行动由 oxm_type、 OXM TLV类型、修改相应的数据包头部字段的值 oxm_value 以及 OXM的负荷定义的。 oxm_hasmask值必须是零而且不包括 oxm_mask。流表项匹配必须包含设置的 OXM有关字段 ( 见 A.2.3.6),否则将会生成一个错误(见 6.4 )。
set-field 行动的类型可以是任何有效的 OXM头类型,A.2.3.7 节和表 12 描述了可能的OXM 类 型。 因 为 不 是 头 部 字 段, 不 支 持 Set_Field 行 动 的 OXM 类 型OFPXMT_OFB_IN_PORT,OXM_OF_IN_PHY_PORT 和 OFPXMT_OFB_METADATA。Set-Field 行动覆盖指定的 OXM类型头部字段, 并执行所需头部 CRC计算。 OXM字段是指头部最外层, 除非字段类型明确指定其它字段, 因此一般 set-field 行动适用于 outermost-possible 头( 例如“ Set VLAN ID“set-field 行动总是设置最外层 VLAN标签的 ID)。
一个实验者行动使用以下结构和字段:
/* Action header for OFPAT_EXPERIMENTER.
* The rest of the body is experimenter-defined. */
struct ofp_action_experimenter_header {
uint16_t type; /* OFPAT_EXPERIMENTER. */
uint16_t len; /* 长度是 8 的倍数. */
uint32_t experimenter; /* 实验者 ID 与 ofp_experimenter_header 的结构相同 */
};
OFP_ASSERT(sizeof(struct ofp_action_experimenter_header) == 8);
Experimneter 字段表示实验者 ID,需要使用与 ofp_experimenter 相同的形式。
A.3 Controller-to-Switch Messages
A.3.1 握手
控制器使用 OFPT_FEATURES_REQUEST 消息识别交换机和读取它的基本功能。在会话建立( 见 6.3.1) 基础上,控制器应该发送一个 OFPT_FEATURES_REQUEST 消息。这个消息只包含OpenFlow 头部。交换机必须发送一个 OFPT_FEATURES_REPLY响应消息:
/* 交换机功能 */
struct ofp_switch_features {
struct ofp_header header;
uint64_t datapath_id; /* 数据通路唯一的 ID。低 48-bits 是 MAC地址, 高 16 位是开发者定义。 */
uint32_t n_buffers; /* 一次缓冲最大的数据包数。 */
uint8_t n_tables; /* 数据通路支持的表数量。 */
uint8_t auxiliary_id; /* 标识辅助连接/
uint8_t pad[2]; /* 64 位对齐. */
/* 功能 */
uint32_t capabilities; /* 位图的支持 "ofp_capabilities". */
uint32_t reserved;
};
OFP_ASSERT(sizeof(struct ofp_switch_features) == 32);
datapath_id 唯一标识数据通路。 低 48 位用于交换机 MAC地址, 高 16 位表示由开发者使用。一个例子就是使用高 16 位作为 VLAN ID 在一个物理交换机里区分多个虚拟交换机实例。
这些字段应该被控制器视为一个不透明的位字符串。
n_buffers 字段表示交换机使用 packet-in 消息向控制器发送数据包时指定的最大缓冲数据包数量。
n_tables 字段描述交换机的表支持的数量, 每一种表都可以对支持的匹配字段, 行动和表项数量有不同的设置。 当控制器和交换机第一次通信, 控制器从特性回复中会发现有多少表交 换 机 支 持。 如 果 它 希 望 知 道 查 询 的 表 大 小 、 类 型 和 顺 序, 控 制 器 发 送 一 个OFPMP_TABLE_FEATURES 复合请求 (见7.3.5.5)。一个交换机必须按照数据包经过表的次序返回表。
auxiliary_id 字段表示交换机到控制器连接的类型, 主连接这个字段的设置为零, 辅助连接这个字段设置为非零值(见 6.3.5 )。
Capabilities 字段使用以下的组合标记:
/* Capabilities supported by the datapath. */
enum ofp_capabilities {
OFPC_FLOW_STATS= 1 << 0, /* 流量统计. */
OFPC_TABLE_STATS= 1 << 1, /* 表统计. */
OFPC_PORT_STATS= 1 << 2, /* 端口统计. */
OFPC_GROUP_STATS= 1 << 3, /* 组统计. */
OFPC_IP_REASM= 1 << 5, /* 可以重新组装 IP 碎片. */
OFPC_QUEUE_STATS= 1 << 6, /* 队列统计. */
OFPC_PORT_BLOCKED= 1 << 8 /* 交换机将阻塞循环端口. */
};
OFPC_PORT_BLOCKED位表示交换机协议,这在 OpenFlow 以外, 如 802.1 D生成树, 将检测拓扑环路并阻塞端口防止数据包循环。如果没有设置, 在大多数情况下, 控制器应该实现一个机制来防止数据包循环。
A.3.2 交换机配置
控制器分别使用 OFPT_SET_CONFIG和 OFPT_GET_CONFIG_REQUEST 消息可以设置和查询交换机配置参数。 交换机对配置的要求做出回应 OFPT_GET_CONFIG_REPLY消息; 它没有对设置配置请求进行回复。
OFPT_GET_CONFIG_REQUEST只有OpenFlow头部。OFPT_SET_CONFIG和OFPT_GET_CONFIG_REPLY使用以下结构:
/* Switch configuration. */
struct ofp_switch_config {
struct ofp_header header;
uint16_t flags; /* OFPC_* flags 位图. */
uint16_t miss_send_len; /* 数据通路应该发送给控制器的数据包最大字节数。见ofp_controller_max_len 有效值 */
};
OFP_ASSERT(sizeof(struct ofp_switch_config) == 12);
配置标志包括以下:
enum ofp_config_flags {
/* Handling of IP fragments. */
OFPC_FRAG_NORMAL = 0, /* 分组没有特殊处理. */
OFPC_FRAG_DROP= 1 << 0, /* 丢弃分组. */
OFPC_FRAG_REASM= 1 << 1, /* 重新组装 ( 只有 OFPC_IP_REASM设置). */
OFPC_FRAG_MASK= 3,
};
OFPC_FRAG_标志表示 IP 分组是否应该正常处理、丢弃, 或重组。 “normal”处理分组意味着让分组穿过 OpenFlow 表。如果任何字段 不存在 ( 如 TCP / UDP端口不合适 ), 这个数据包应不匹配任何表项里已设置的字段。
当不使用输出行动对 OFPP_CONTROLLER逻辑端口时, miss_send_len 字段定义了 OpenFlow流水线发送到控制器的数据包字节数, 例如, 如果此消息被使能, 则发送携带无效 TTL 的数据包。如果这个字段等于 0, 交换机必须发送 ofp_packet_in 消息中零字节长的数据包。如果该值设置为 OFPCML_NO_BUFFER,消息中必须包含完整的包, 而不会被缓冲。
A.3.3 Flow Table Configuration
流表编号从 0 和任意可取值直到 OFPTT_MAX。OFPTT_ALL是一个保留值。
/ 表编号。表可以使用任何数值直至 OFPT_MAX./
enum ofp_table {
/ 最后一个可用的表数. /
OFPTT_MAX= 0xfe,
/ 假表. /
OFPTT_ALL= 0xff / 通配符表用于表配置、流统计和删除. /
};
控制器可以分别使用 OFP_TABLE_MOD和 OFPMP_TABLE请求对交换机配置和查询表状态。
交换机使用 OFPT_MULTIPART_REPLY消息响应表的复合请求。 OFP_TABLE_MOD使用以下结构和
字段:
/ 流表的配置 / 修改行为 /
struct ofp_table_mod {
struct ofp_header header;
uint8_t table_id; / 表的 ID,OFPTT_ALL 表示所有表 /
uint8_t pad[3]; / 填补到 32 字节 /
uint32_t config; / 位图的 OFPTC_* flags /
};
OFP_ASSERT(sizeof(struct ofp_table_mod) == 16);
table_id 表示配置更改时应选择的表。如果 table_id 是 OFPTT_ALL,则配置应用于交换
机中的所有表。
Config 字段是一个位图, 早期版本的规范提供向后兼容性, 保留以供将来使用。 目前该定义的字段没有标记。该字段的值定义如下:
/ 配置表的标记,保留供将来使用. /
enum ofp_table_config {
OFPTC_DEPRECATED_MASK = 3, / 废弃的比特 /
};
A.3.4 Modify State Messages
A.3.4.1 Modify Flow Entry Message
利用 OFPT_FLOW_MOD消息控制器完成一个流表修改:
/ 流建立和拆除 (controller -> datapath). /
struct ofp_flow_mod {
struct ofp_header header;
uint64_t cookie; / 不透明 controller-issued 标识符. /
uint64_t cookie_mask; / 限制 cookie 位的掩码,当使用 OFPFC_MODIFY或 OFPFC_DELETE命令时必须匹配 cookie。值为 0 表示没有限制 /
/ 流行动 /
uint8_t table_id; / 放入流的表 ID。对 OFPFC_DELETE_* 命令,OFPTT_ALL 也可以用来删除所有表里匹配的流。 /
uint8_t command; / OFPFC_之一. /
uint16_t idle_timeout; / 丢弃之前的空闲时间 (seconds). /
uint16_t hard_timeout; / 丢弃之前的最大时间 (seconds). /
uint16_t priority; / 流表项优先级. /
uint32_t buffer_id; / 缓冲的包使用。或 OFP_NO_BUFFER。对 OFPFC_DELETE则无意义 /
uint32_t out_port; / 对于 OFPFC_DELETE命令,要求匹配的表项包含这个作为输出端口。OFPP_ANY表示没有限制 /
uint32_t out_group; / 对于 OFPFC_DELETE命令, 要求匹配的 表项 包括这个作为输出组。值 OFPG_ANY表示没有限制。/
uint16_t flags; / OFPFF_的标记位图. /
uint8_t pad[2];
struct ofp_match match; / 字段相匹配,变量的大小. /
/ 变量的大小和填充匹配总是被指令跟着 /
//struct ofp_instruction instructions[0]; / . /
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 56);
cookie 字段是控制器选择的一个不透明的数据值。这个值出现在流删除消息和流统计,也可以用于过滤流统计, 流修改和删除 ( 见 6.4)。数据包处理流水线不使用它, 因此不需要驻留在硬件里。 值-1(0xffffffffffffffff) 保留, 不得使用。 当一个表项通过 OFPFC_ADD消息插入 流 表 中, 其 cookie 字 段 设 置 为 提 供 的 值。 当 一 个 流 表 项 修 改 (OFPFC_MODIFY或OFPFC_MODIFY_STRICT消息), 其 cookie 字段不变。
如果 cookie_mask 字段不为零, 当修改或删除流表项时,和 cookie 字段一起限制流匹配。OFPFC_ADD消息忽略这个字段。 cookie_mask 字段的行动在 6.4 节说明。
table_id 字段表示哪个表中的流表项应插入、修改或删除。 Table 0 表示流水线上第一个表。 OFPTT_ALL只对删除请求有效。
Command字段必须是下列之一:
enum ofp_flow_mod_command {
OFPFC_ADD= 0, / 新流表. /
OFPFC_MODIFY= 1, / 修改所有匹配的流表. /
OFPFC_MODIFY_STRICT= 2, / 修改严格匹配通配符和优先级的表项. /
OFPFC_DELETE= 3, / 删除所有匹配的流动表 /
OFPFC_DELETE_STRICT= 4, / 删除严格匹配通配符和优先级的表项. /
}
OFPFC_MODIFY和 OFPFC_MODIFY_STRICT之间的区别在 6.4 节和解释,OFPFC_DELETE和
OFPFC_DELETE_STRICT之间的区别在 6.4 节进行阐述。
Idle_timeout 和 hard_timeout 字段控制流表项过期的速度 ( 见 5.5)。当一个流表项插入表 中, 其 idle_timeout 和 hard_timeout 字 段 设 置 为 消 息 中 的 值。 当 一 个 流 表 项 修 改(OFPFC_MODIFY或 OFPFC_MODIFY_STRICT消息),idle_timeout 和 hard_timeout 字段被忽略。
如果设置了 idle_timeout 而 hard_timeout 为零, 表项在 idle_timeout 秒后没有收到信息后必须过期。如果 idle_timeout 为零而设置了 hard_timeout,无论是否有数据包正碰撞表项,表项到 hard_timeout 秒必须过期。如果 idle_timeout 和 hard_timeout 都设置了,流表项将在没有信息后 idle_timeout 秒,或 hard_timeout 秒后超时, 以先到期者为准。如果idle_timeout 和 hard_timeout 都是零, 表项认为是永久的, 不会超时。 它可以被 OFPFC_DELETE类型的 flow_mod 消息移除。
Priority 表示指定的流表中表的优先级。较高的数据表示较高的优先级。这一字段仅用于 OFPFC_ADD消息匹配和添加流表项时, 和当 OFPFC_MODIFY_STRICT或 OFPFC_DELETE_STRICT消息匹配流条目时。
buffer_id 指向交换机缓冲的数据包并用 packet-in 消息发送给控制器。如果没有缓冲数据包与 flow mod关联, 则必须设置为 OFP_NO_BUFFER。在流插入后, 包括一个有效 buffer_id的 flow mod移走缓冲区里对应数据包,并从第一个流表开始,穿过整个 OpenFlow 流水线进行处理。这实际上相当于发送了 flow mod的一个双消息和 packet-out 转发到 OFPP_TABLE逻辑端口 ( 见 A.3.7), 同时要求交换机必须在数据包输出前完全处理 flow mod。无论 flow mod指 向 哪 个 表, 或 是 flow mod 中 包 含 的 指 令 都 适 用 这 些 语 法。 OFPFC_DELETE和
OFPFC_DELETE_STRICT消息忽略这一字段。
输 出 端 口 和 组 利 用 out_port 和 out_group字段选择性的过滤OFPFC_DELETE和OFPFC_DELETE_STRICT消息。如果 out_port 或 out_group 包含一个分别大于 OFPP_ANY或OFPG_ANY的值,就在匹配时引入了一个约束。这个约束就是对端口或组来说,流表项必须包含一个输出行动。其它约束如 ofp_match 结构和优先仍会使用;这纯粹是一个额外的约束。
注意若禁用输出过滤,out_port 和 out_group 必须分别设置为 OFPP_ANY和 OFPG_ANY。这些字段由 OFPFC_ADD、OFPFC_MODIFY或 OFPFC_MODIFY_STRICT消息忽略。
Flags 字段可能包含的下列标记:
enum ofp_flow_mod_flags {
OFPFF_SEND_FLOW_REM= 1 << 0, / 发送删除消息当流过期或被删除. /
OFPFF_CHECK_OVERLAP= 1 << 1, / 首先要看的是重叠的表项. /
OFPFF_RESET_COUNTS= 1 << 2, / 重置流数据包和字节计数. /
OFPFF_NO_PKT_COUNTS= 1 << 3, / 不要监测的数据包计数. /
OFPFF_NO_BYT_COUNTS= 1 << 4, / 不要监测的字节计数. /
};
OFPFF_SEND_FLOW_REM标志设置时, 当流表项过期或删除时交换机必须发送一个流删除消息。
OFPFF_CHECK_OVERLAP标志设置时, 交换机在向流表中插入前, 必须检查没有相同优先级的表项冲突。如果有的话,flow mod 失败并返回一个错误信息(见 6.4 )。
OFPFF_NO_PKT_COUNTS标 志 被 设 置 时, 交 换 机 不 需 要 监 测 流 的 数 据 包 计 数。
OFPFF_NO_BYT_COUNTS 标记被设置时, 交换机不需要监测流的字节计数。 设置这些标记可能减少一些OpenFlow 交换机的处理负荷, 尽管这些计数器在流表项删除消息和流统计时可能不可用。交换机不需要注意这些标记,可跟踪流计数并返回它不管相关标记的设置。如果一个交换机不跟踪流计数, 相应的计数器不可用, 它必须设置为字段最大值。
当流表项插入到表中, 它的 flags 字段设置为消息中的值。当一个流表项匹配和修改(OFPFC_MODIFY或 OFPFC_MODIFY_STRICT消息), 则 flags 字段被忽略。
Instructions 字段包含了流表项添加或修改表项的指令集。 如果指令集是无效或不支持,交换机必须生成一个错误信息(见 6.4 )。
A.3.4.2 修改组表项消息
控制器利用 OFPT_GROUP_MOD 消息对组表进行修改:
/ 组建立和拆除 (controller -> datapath). /
struct ofp_group_mod {
struct ofp_header header;
uint16_t command; / OFPGC_之一. /
uint8_t type; /OFPGT_*之一. /
uint8_t pad; / 填补到 64 位. /
uint32_t group_id; / 组标识 /
struct ofp_bucket buckets[0]; / 存储段数组的 长度,从头部的长度字段计算 /
};
OFP_ASSERT(sizeof(struct ofp_group_mod) == 16);
语义类型和组字段解释 6.5 节。
Command字段必须是下列之一:
/ Group commands /
enum ofp_group_mod_command {
OFPGC_ADD= 0, / 新建群组. /
OFPGC_MODIFY= 1, / 修改所有匹配的组. /
OFPGC_DELETE= 2, / 删除所有匹配的组。 /
};
Type 字段必须是下列之一:
/ 组类型。值 [128,255] 保留给实验者使用. /
enum ofp_group_type {
OFPGT_ALL= 0, / 所有的 ( 多播/ 广播) 组. /
OFPGT_SELECT= 1, / 所选定组. /
OFPGT_INDIRECT= 2, / 间接组 /
OFPGT_FF= 3, / 快速故障转移组. /
};
group_id 字段唯一地标识在交换机中的组。指定的组标识符定义如下:
/ 组编号。组可以使用任何不超过 OFPG_MAX的编号。 /
enum ofp_group {
/ 最后一个可用的组号. /
OFPG_MAX= 0xffffff00,
/ 假的组. /
OFPG_ALL= 0xfffffffc, / 表示所有组的组删除命令. /
OFPG_ANY= 0xffffffff / 通配符组仅用于流统计请求。选择所有流 ( 包括没有组的流)
/
};
Buckets 字段是一个存储段的数组。对间接组,数组必须包含一个存储段 ( 见 5.6.1), 其它组类型在数组里可能有多个存储段。对快速故障转移组, 存储段次序就定义了其优先级 ( 见5.6.1), 通 过 修 改 组 可 以 改 变 存 储 段 的 排 序 ( 例 如 使 用 带 OFPGC_MODIFY命 令 的OFPT_GROUP_MOD 消息)。
数组中的 存储段 使用以下结构:
/ 用于组的存储段 /
struct ofp_bucket {
uint16_t len; / 存储段字节长度, 包括头部和任何对齐 64 位的填充. /
uint16_t weight; / 存储段的有关重量,为选择的组定义. /
uint32_t watch_port;/ 状态会影响存储段是否活跃的端口。只需要快速转移故障的组。 /
uint32_t watch_group; / 状态会影响存储段是否活跃的组。只需要快速转移故障的组。 /
uint8_t pad[4];
struct ofp_action_header actions[0]; / 与存储段关联的 0 或多个行为,行动列表长度依据存储段长度计算 */
};
OFP_ASSERT(sizeof(struct ofp_bucket) == 16);
Weight 字段只是为所选组定义的,它是否支持是可选的。 被组处理的存储段共享信息由组中存储段重量之和平均后的单个存储段重量来定义。 当一个端口拆除, 信息量分布的改变没有定义, 交换机的数据包分布精确度应与存储段重量匹配,但没有定义。
watch_port 和 watch_group 字段只有快速故障转移组需要, 也可能选来实现用在其它组类型上。这些字段表示端口或组, 其活跃控制这个存储段是否为转发的候选者。对快速转移故障组, 定义的第一个存储段是最高优先级,而且只有最高优先级活跃存储段被使用(见5.6.1)。
actions 字段是与存储段相关的行动集。 当存储段被数据包选中时, 其行动集就应用到数据包( 见 5.10)。
A.3.4.3 Port Modification Message
控制器使用 OFPT_PORT_MOD消息修改端口的行为:
/* 物理端口的修改行为 /
struct ofp_port_mod {
struct ofp_header header;
uint32_t port_no;
uint8_t pad[4];
uint8_t hw_addr[OFP_ETH_ALEN]; / 这是不可配置的硬件地址。这是用来对请求进行正常检查, 因此它必须返回相同 ofp_port 结构. /
uint8_t pad2[2]; / 填充到 64 字节. /
uint32_t config; / OFPPC_位图标记. /
uint32_t mask; / 位图 OFPPC_标记的改变. /
uint32_t advertise; / 位图的 OFPPF_*。所有位为零以防止任何行动发生. /
uint8_t pad3[4]; / 填充到 64 字节. */
};
OFP_ASSERT(sizeof(struct ofp_port_mod) == 40);
Mask 字段用来在 config 字段中选择比特去改变。 Advertise 字段没有掩码 ; 所有端口特性一起改变。
A.3.4.4 计量器修改消息
利用 OFPT_METER_MOD消息完成控制器的计量器修改:
/* 计量器配置. OFPT_METER_MOD. /
struct ofp_meter_mod {
struct ofp_header header;
uint16_t command; / OFPMC_之一. /
uint16_t flags; / OFPMF_位图的标记. /
uint32_t meter_id; / 计量器实例. /
struct ofp_meter_band_header bands[0]; / 带宽列表长度,从头部长度字段计算. /
};
OFP_ASSERT(sizeof(struct ofp_meter_mod) == 16);
meter_id 字段在交换机里唯一地标识计量器。计量器从 meter_id = 1 开始定义,直至交换机可以支持的最大数。 OpenFlow 协议还定义了一些额外的不与流表相关的虚拟计量器:
/ 计量器编号。 OFPM_MAX流表计可以使用任何数直至 OFPM_MAX/
enum ofp_meter {
/ 最后一个可用的计量器. /
OFPM_MAX= 0xffff0000,
/ 虚拟仪表. /
OFPM_SLOWPATH= 0xfffffffd, / 用于慢速数据通道的计量器. /
OFPM_CONTROLLER= 0xfffffffe, / 用于控制器连接的计量器。 /
OFPM_ALL= 0xffffffff, / 表示用于统计请求命令的所有计量器 /
};
OpenFlow 现有实现提供支持的虚拟计量器, 在新实现中鼓励使用常规流计量器或优先级队列。
OFPM_CONTROLLER: 虚拟计量器使用 CONTROLLER保留端口或其他处理, 控制所有数据包通过 Packet-in mes-sages 发送到控制器, ( 见 6.1.2)。可用来限制发送到控制器的流量。
OFPM_SLOWPATH: 虚拟计量器控制所有数据包被缓慢数据通路的交换机处理。许多交换机实现有一条快和慢数据通路, 例如硬件交换机可能有一条慢速软数据通路, 或软交换机可能有一条慢速的用户空间数据通道。
Command字段的命令必须是下列之一:
/ Meter commands /
enum ofp_meter_mod_command {
OFPMC_ADD, / 新的计量器. /
OFPMC_MODIFY, / 修改指定的计量器. /
OFPMC_DELETE, / 删除指定的计量器. */
};
Flags 字段可能包含以下标记的组合:
/* 计量器配置标记 */
enum ofp_meter_flags {
OFPMF_KBPS= 1 << 0, /* 速率值在 kb / s(kilo-bit 每秒. */
OFPMF_PKTPS= 1 << 1, /* 速率值用包 / 秒记. */
OFPMF_BURST= 1 << 2, /* 突发的尺寸. */
OFPMF_STATS= 1 << 3, /* 收集统计. */
};
Bands 字段是一个速率带宽列表。 它可以包含任意数量的频带, 当可以理解的话, 每个频带类型是可以重复的。一次只能使用一个频带, 如果当前数据包速度超过多个频带速率, 频带则用最高的速率配置。
所有的频带都使用相同的通用头部来定义:
/* 所有计量器共同的头部 */
struct ofp_meter_band_header {
uint16_t type; /* OFPMBT_*.之一 */
uint16_t len; /* 此频带的字节长度. */
uint32_t rate; /* 此频带的速率. */
uint32_t burst_size; /* 突发的的大小 */
};
OFP_ASSERT(sizeof(struct ofp_meter_band_header) == 12);
Rate 字段表示速率值,在此频带基础上可以传送数据包 ( 见 5.7.1)。速率值单位是千比特/ 秒, 除非 flags 字段包括 OFPMF_PKTPS, 此时速率是包 / 秒。
burst_size 字段只有在 flags 字段包含 OFPMF_BURST时使用。 对所有长度超过突发值的数据包和突发字节, 它规定了计量器的粒度, 计量器的速率将严格限制。 突发值单位是千比特,除非 flags 字段包括OFPMF_PKTPS, 在这种情况下, 突发值单位是包。
Type 字段必须是下列之一:
/* 计量器频带类型 */
enum ofp_meter_band_type {
OFPMBT_DROP= 1, /* 丢弃包. */
OFPMBT_DSCP_REMARK = 2, /* IP 头部的 DSCP. */
OFPMBT_EXPERIMENTER= 0xFFFF /* 实验者计量器频带. */
};
OpenFlow 交换机可能不支持所有频带类型, 并可能不允许在所有计量器上使用它所支持
的频带, 即一些计量器可能是专用的。
频带 OFPMBT_DROP定义了简单的速率限制器,超过频带速率值的话数据包将丢弃,并使
用以下结构:
/* OFPMBT_DROPband - drop packets */
struct ofp_meter_band_drop {
uint16_t type; /* OFPMBT_DROP.*/
uint16_t len; /* 这个频带的字节长度. */
uint32_t rate; /* 开始丢弃包的速率 */
uint32_t burst_size; /* 突发大小 */
uint8_t pad[4];
};
OFP_ASSERT(sizeof(struct ofp_meter_band_drop) == 16);
频带 OFPMBT_DSCP_REMARK 定义了一个简单的 DiffServ 策略,对超过频带速率的数据包的 IP 头部 DSCP字段,检测其丢弃的优先级,并使用以下结构:
/* OFPMBT_DSCP_REMARK 频带- IP 头部 DSCP*/
struct ofp_meter_band_dscp_remark {
uint16_t type; /* OFPMBT_DSCP_REMARK. */
uint16_t len; /* 频带的字节长度. */
uint32_t rate; /* 开始 检测 数据包的速率. */
uint32_t burst_size; /* 突发大小 */
uint8_t prec_level; /* 添加的丢弃优先级数。 */
uint8_t pad[3];
};
OFP_ASSERT(sizeof(struct ofp_meter_band_dscp_remark) == 16);
prec_level 字段表示如果超过频带速率超过某个数值, 数据包的丢弃优先级应该增加。
/* OFPMBT_EXPERIMENTER 频带- 行动集中的写行动 */
struct ofp_meter_band_experimenter {
uint16_t type; /* OFPMBT_*之一. */
uint16_t len; /* 这个频带的字节长度. */
uint32_t rate; /* 这个频带的速率 */
uint32_t burst_size; /* 突发大小. */
uint32_t experimenter; /* 实验者 ID 与 ofp_experimenter_header 结构相同 */
};
OFP_ASSERT(sizeof(struct ofp_meter_band_experimenter) == 16);