0x01 概述
这一章节介绍协议的消息格式。OpenFlow消息是一个二进制的消息格式,在前面介绍的OpenFlow Channel
中传输。需要解决以下几个问题。
- 兼容性问题。
- 通信两端的异构问题。
- 协议规定报文内容采用
大端
传输,小端架构的需要做相应的转换 - 结构体内字段按照
8字节
对齐,对于不够8字节的内容由padding
字段补齐。
- 协议规定报文内容采用
- 版本迭代的兼容性
- 向前兼容。
- 向后兼容。对于本版本未使用的预留的字段,应该忽略其作用。
- 通信两端的兼容性,通信两端通过协商版本号来通信,控制器通过特性查询以获取交换机的能力。
- 通信两端的异构问题。
- 扩展性
TLV
,协议结构的设计上多处使用了TLV
结构保证了可扩展性。Reserve
,协议在可能存在扩展的位置预留了多个比特位供后续使用。
- 网络传输问题
- 粘包问题
OpenFlow Channel
的传输层可能为字节流协议,因此协议消息提供了消息头部来描述消息的格式,其中就包括长度,来描述本次消息的大小,以此来定界。这里面应该加个Magic
来保证消息的正确性。
- 异步传输
- 协议的头部里面包括了一个
Xid
来识别消息,从而保证异步消息得以实现。
- 协议的头部里面包括了一个
- 粘包问题
0x02 通用数据结构
消息头部
OpenFlow
定义的头部如下,其中OFP_ASSERT
用来保证异构设备的兼容性。
/* Header on all OpenFlow packets. */
struct ofp_header {
uint8_t version; /* OFP_VERSION. */
uint8_t type; /* One of the OFPT_ constants. */
uint16_t length; /* Length including this ofp_header. */
uint32_t xid; /* Transaction id associated with this packet. Replies use the same id as was in the request to facilitate pairing. */
};
OFP_ASSERT(sizeof(struct ofp_header) == 8);
- version:
version[7]
为预留字段,值为0,Version[6,0]
为版本号,当前的版本号1.5.1为Ox6
。 type为消息类型,当前版本可用的消息类型如下,这里面的注释比较详细,这里就不再解释了。 ```c enum ofp_type { / Immutable messages. / OFPT_HELLO = 0, / Symmetric message / OFPT_ERROR = 1, / Symmetric message / OFPT_ECHO_REQUEST = 2, / Symmetric message / OFPT_ECHO_REPLY = 3, / Symmetric message / OFPT_EXPERIMENTER = 4, / Symmetric message /
/ Switch configuration messages. / OFPT_FEATURES_REQUEST = 5, / Controller/switch message / OFPT_FEATURES_REPLY = 6, / Controller/switch message / OFPT_GET_CONFIG_REQUEST = 7, / Controller/switch message / OFPT_GET_CONFIG_REPLY = 8, / Controller/switch message / OFPT_SET_CONFIG = 9, / Controller/switch message /
/ Asynchronous messages. / OFPT_PACKET_IN = 10, / Async message / OFPT_FLOW_REMOVED = 11, / Async message / OFPT_PORT_STATUS = 12, / Async message /
/ Controller command messages. / OFPT_PACKET_OUT = 13, / Controller/switch message / OFPT_FLOW_MOD = 14, / Controller/switch message / OFPT_GROUP_MOD = 15, / Controller/switch message / OFPT_PORT_MOD = 16, / Controller/switch message / OFPT_TABLE_MOD = 17, / Controller/switch message /
/ Multipart messages. / OFPT_MULTIPART_REQUEST = 18, / Controller/switch message / OFPT_MULTIPART_REPLY = 19, / Controller/switch message /
/ Barrier messages. / OFPT_BARRIER_REQUEST = 20, / Controller/switch message / OFPT_BARRIER_REPLY = 21, / Controller/switch message /
/ Controller role change request messages. / OFPT_ROLE_REQUEST = 24, / Controller/switch message / OFPT_ROLE_REPLY = 25, / Controller/switch message /
/ Asynchronous message configuration. / OFPT_GET_ASYNC_REQUEST = 26, / Controller/switch message / OFPT_GET_ASYNC_REPLY = 27, / Controller/switch message / OFPT_SET_ASYNC = 28, / Controller/switch message /
/ Meters and rate limiters configuration messages. / OFPT_METER_MOD = 29, / Controller/switch message /
/ Controller role change event messages. / OFPT_ROLE_STATUS = 30, / Async message /
/ Asynchronous messages. / OFPT_TABLE_STATUS = 31, / Async message /
/ Request forwarding by the switch. / OFPT_REQUESTFORWARD = 32, / Async message /
/ Bundle operations (multiple messages as a single operation). / OFPT_BUNDLE_CONTROL = 33, / Controller/switch message / OFPT_BUNDLE_ADD_MESSAGE = 34, / Controller/switch message /
/ Controller Status async message. / OFPT_CONTROLLER_STATUS = 35, / Async message /
};
<a name="OQryi"></a>
####
<a name="c76cfefe"></a>
## 端口
`OpenFlow`的端口号是一个`uint32_t`的整数,从`1`开始,用于控制器和交换机通信的时候标识一个`Datapath`的端口。存在一些预定义的特殊端口号,这部分不能被其他地方使用,其含义在前面的章节已经描述过。定义如下:
```c
/* Port numbering. Ports are numbered starting from 1. */
enum ofp_port_no {
/* Maximum number of physical and logical switch ports. */
OFPP_MAX = 0xffffff00,
/* Reserved OpenFlow Port (fake output "ports"). */
OFPP_UNSET = 0xfffffff7, /* Output port not set in action-set.
used only in OXM_OF_ACTSET_OUTPUT. */
OFPP_IN_PORT = 0xfffffff8, /* Send the packet out the input port. This
reserved port must be explicitly used
in order to send back out of the input
port. */
OFPP_TABLE = 0xfffffff9, /* Submit the packet to the first flow table
NB: This destination port can only be
used in packet-out messages. */
OFPP_NORMAL = 0xfffffffa, /* Forward using non-OpenFlow pipeline. */
OFPP_FLOOD = 0xfffffffb, /* Flood using non-OpenFlow pipeline. */
OFPP_ALL = 0xfffffffc, /* All standard ports except input port. */
OFPP_CONTROLLER = 0xfffffffd, /* Send to controller. */
OFPP_LOCAL = 0xfffffffe, /* Local openflow "port". */
OFPP_ANY = 0xffffffff /* Special value used in some requests when
no port is specified (i.e. wildcarded). */
};
0x3 消息格式
以下介绍各种消息类型的结构,由于消息和结构分开后显得有点难以理解,因此这里调整一下白皮书里面的顺序。具体结构将在消息里面介绍。这里只介绍一些重要的消息,部分消息可能没有涉及。待补充
Controlller-to-Switch
握手
在连接建立之后(hello报文协商后),控制器发送握手消息用于获取交换机的特性,能力。其消息类型为。
- 控制器:消息类型
OFPT_FEATURES_REQUEST
不带有消息体,即只有头部。 交换机:
OFPT_FEATURES_REPLY
,消息格式如下:/* Switch features. */ struct ofp_switch_features { struct ofp_header header; uint64_t datapath_id; /* Datapath unique ID. The lower 48-bits are for a MAC address, while the upper 16-bits are implementer-defined. */ uint32_t n_buffers; /* Max packets buffered at once. */ uint8_t n_tables; /* Number of tables supported by datapath. */ uint8_t auxiliary_id; /* Identify auxiliary connections */ uint8_t pad[2]; /* Align to 64-bits. */ /* Features. */ uint32_t capabilities; /* Bitmap of support "ofp_capabilities". */ uint32_t reserved; }; OFP_ASSERT(sizeof(struct ofp_switch_features) == 32);
n_buffers
:交换机发送Packet-in
所能缓存的数据包个数。n_tables
:交换机所能支持的流表个数。capabilites
:定义如下,其中OFPC_PORT_BLOCKED
表示交换机支持防环功能,比如STP。/* Capabilities supported by the datapath. */ enum ofp_capabilities { OFPC_FLOW_STATS = 1 << 0, /* Flow statistics. */ OFPC_TABLE_STATS = 1 << 1, /* Table statistics. */ OFPC_PORT_STATS = 1 << 2, /* Port statistics. */ OFPC_GROUP_STATS = 1 << 3, /* Group statistics. */ OFPC_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */ OFPC_QUEUE_STATS = 1 << 6, /* Queue statistics. */ OFPC_PORT_BLOCKED = 1 << 8, /* Switch will block looping ports. */ OFPC_BUNDLES = 1 << 9, /* Switch supports bundles. */ OFPC_FLOW_MONITORING = 1 << 10, /* Switch supports flow monitoring. */ };
交换机配置
查询配置
- 控制器:
OFPT_GET_CONFIG_REQUEST
,不带消息体。 - 交换机:
OFPT_GET_CONFIG_REPLY
,消息结构为struct ofp_switch_config
。
- 控制器:
下发配置
- 控制器:
OFPT_SET_CONFIG
,消息结构同OFPT_GET_CONFIG_REPLY
- 交换机:无需回复。
/* Switch configuration. */ struct ofp_switch_config { struct ofp_header header; uint16_t flags; /* Bitmap of OFPC_* flags. */ uint16_t miss_send_len; /* Max bytes of packet that datapath should send to the controller. See ofp_controller_max_len for valid values. */ }; OFP_ASSERT(sizeof(struct ofp_switch_config) == 12);
- 控制器:
flags
,定义如下所示。enum ofp_config_flags { /* Handling of IP fragments. */ OFPC_FRAG_NORMAL = 0, /* No special handling for fragments. */ OFPC_FRAG_DROP = 1 << 0, /* Drop fragments. */ OFPC_FRAG_REASM = 1 << 1, /* Reassemble (only if OFPC_IP_REASM set). */ OFPC_FRAG_MASK = 3, /* Bitmask of flags dealing with frag. */ };
miss_send_len
:pipeline不是通过output action to controller
发送的报文大小。enum ofp_controller_max_len { OFPCML_MAX = 0xffe5, /* maximum max_len value which can be used to request a specific byte length. */ OFPCML_NO_BUFFER = 0xffff /* indicates that no buffering should be applied and the whole packet is to be sent to the controller. */ };
流表配置
流表通过id
来标识,大小为uint8
,从0
开始。流表的配置消息包括OFPT_TABLE_MOD
,OFPT_FLOW_MOD
。
/* Table numbering. Tables can use any number up to OFPT_MAX. */
enum ofp_table {
/* Last usable table number. */
OFPTT_MAX = 0xfe,
/* Fake tables. */
OFPTT_ALL = 0xff /* Wildcard table used for table config,
flow stats and flow deletes. */
};
OFPT_TABLE_MOD
/* Configure/Modify behavior of a flow table */
struct ofp_table_mod {
struct ofp_header header;
uint8_t table_id; /* ID of the table, OFPTT_ALL indicates all tables */
uint8_t pad[3]; /* Pad to 32 bits */
uint32_t config; /* Bitmap of OFPTC_* flags */
/* Table Mod Property list */
struct ofp_table_mod_prop_header properties[0];
};
OFP_ASSERT(sizeof(struct ofp_table_mod) == 16);
/* Flags to configure the table. */
enum ofp_table_config {
OFPTC_DEPRECATED_MASK = 3, /* Deprecated bits */
OFPTC_EVICTION = 1 << 2, /* Authorise table to evict flows. */
OFPTC_VACANCY_EVENTS = 1 << 3, /* Enable vacancy events. */
};
/* Common header for all Table Mod Properties */
struct ofp_table_mod_prop_header {
uint16_t type; /* One of OFPTMPT_*. */
uint16_t length; /* Length in bytes of this property. */
};
OFP_ASSERT(sizeof(struct ofp_table_mod_prop_header) == 4);
/* Table Mod property types.
*/
enum ofp_table_mod_prop_type {
OFPTMPT_EVICTION = 0x2, /* Eviction property. */
OFPTMPT_VACANCY = 0x3, /* Vacancy property. */
OFPTMPT_EXPERIMENTER = 0xFFFF, /* Experimenter property. */
};
/* Eviction table mod Property. Mostly used in OFPMP_TABLE_DESC replies. */
struct ofp_table_mod_prop_eviction {
uint16_t type; /* OFPTMPT_EVICTION. */
uint16_t length; /* Length in bytes of this property. */
uint32_t flags; /* Bitmap of OFPTMPEF_* flags */
};
OFP_ASSERT(sizeof(struct ofp_table_mod_prop_eviction) == 8);
/* Eviction flags. */
enum ofp_table_mod_prop_eviction_flag {
OFPTMPEF_OTHER = 1 << 0, /* Using other factors. */
OFPTMPEF_IMPORTANCE = 1 << 1, /* Using flow entry importance. */
OFPTMPEF_LIFETIME = 1 << 2, /* Using flow entry lifetime. */
};
/* Vacancy table mod property */
struct ofp_table_mod_prop_vacancy {
uint16_t type; /* OFPTMPT_VACANCY. */
uint16_t length; /* Length in bytes of this property. */
uint8_t vacancy_down; /* Vacancy threshold when space decreases (%). */
uint8_t vacancy_up; /* Vacancy threshold when space increases (%). */
uint8_t vacancy; /* Current vacancy (%) - only in ofp_table_desc. */
uint8_t pad[1]; /* Align to 64 bits. */
};
OFP_ASSERT(sizeof(struct ofp_table_mod_prop_vacancy) == 8);
config
字段的定义如下:OFPTC_EVICTON
:代表交换机可以删除流表项,比如资源不够的情况下。OFPTC_VACANCY_EVENTS
:VANCANCY
即交换机流表的剩余空间,如果上升/下降超过阈值则上报事件。
struct ofp_table_mod_prop_header
:保存property的内容,对应于config。比如config配置了OFPTC_EVICTION
,那么该EVICTION
的具体内容将保存在该property里面。
OFPT_FLOW_MOD
/* Flow setup and teardown (controller -> datapath). */
struct ofp_flow_mod {
struct ofp_header header;
uint64_t cookie; /* Opaque controller-issued identifier. */
uint64_t cookie_mask; /* Mask used to restrict the cookie bits
that must match when the command is
OFPFC_MODIFY* or OFPFC_DELETE*. A value
of 0 indicates no restriction. */
uint8_t table_id; /* ID of the table to put the flow in.
For OFPFC_DELETE_* commands, OFPTT_ALL
can also be used to delete matching
flows from all tables. */
uint8_t command; /* One of OFPFC_*. */
uint16_t idle_timeout; /* Idle time before discarding (seconds). */
uint16_t hard_timeout; /* Max time before discarding (seconds). */
uint16_t priority; /* Priority level of flow entry. */
uint32_t buffer_id; /* Buffered packet to apply to, or
OFP_NO_BUFFER.
Not meaningful for OFPFC_DELETE*. */
uint32_t out_port; /* For OFPFC_DELETE* commands, require
matching entries to include this as an
output port. A value of OFPP_ANY
indicates no restriction. */
uint32_t out_group; /* For OFPFC_DELETE* commands, require
matching entries to include this as an
output group. A value of OFPG_ANY
indicates no restriction. */
uint16_t flags; /* Bitmap of OFPFF_* flags. */
uint16_t importance; /* Eviction precedence (optional). */
struct ofp_match match; /* Fields to match. Variable size. */
/* The variable size and padded match is always followed by instructions. */
//struct ofp_instruction_header instructions[0];
/* Instruction set - 0 or more. The length
of the instruction set is inferred from
the length field in the header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 56);
enum ofp_flow_mod_command {
OFPFC_ADD = 0, /* New flow. */
OFPFC_MODIFY = 1, /* Modify all matching flows. */
OFPFC_MODIFY_STRICT = 2, /* Modify entry strictly matching wildcards and
priority. */
OFPFC_DELETE = 3, /* Delete all matching flows. */
OFPFC_DELETE_STRICT = 4, /* Delete entry strictly matching wildcards and
priority. */
};
enum ofp_flow_mod_flags {
OFPFF_SEND_FLOW_REM = 1 << 0, /* Send flow removed message when flow
* expires or is deleted. */
OFPFF_CHECK_OVERLAP = 1 << 1, /* Check for overlapping entries first. */
OFPFF_RESET_COUNTS = 1 << 2, /* Reset flow packet and byte counts. */
OFPFF_NO_PKT_COUNTS = 1 << 3, /* Don't keep track of packet count. */
OFPFF_NO_BYT_COUNTS = 1 << 4, /* Don't keep track of byte count. */
};
- cookie:由控制器设置,pipeline不处理,可以用于过滤流表。
- buffer_id:当buffer_id存在的时候,交换机应该取出该buffer_id的报文,然后在处理完流表修改之后,从头开始处理这个报文。即代表了
FLOW_MOD
和PACKET-OUT
两个消息。
组表配置
组表配置消息类型为OFPT_GROUP_MOD
。
/* Group setup and teardown (controller -> datapath). */
struct ofp_group_mod {
struct ofp_header header;
uint16_t command; /* One of OFPGC_*. */
uint8_t type; /* One of OFPGT_*. */
uint8_t pad; /* Pad to 64 bits. */
uint32_t group_id; /* Group identifier. */
uint16_t bucket_array_len; /* Length of action buckets data. */
uint8_t pad2[2]; /* Pad to 64 bits. */
uint32_t command_bucket_id; /* Bucket Id used as part of
OFPGC_INSERT_BUCKET and OFPGC_REMOVE_BUCKET
commands execution.*/
/* Followed by:
* - Exactly 'bucket_array_len' bytes containing an array of
* struct ofp_bucket.
* - Zero or more bytes of group properties to fill out the overall
* length in header.length. */
struct ofp_bucket buckets[0]; /* The length of the bucket array is
bucket_array_len bytes. */
//struct ofp_group_prop_header properties[0];
};
OFP_ASSERT (sizeof(struct ofp_group_mod) == 24);
/* Group commands */
enum ofp_group_mod_command {
OFPGC_ADD = 0, /* New group. */
OFPGC_MODIFY = 1, /* Modify all matching groups. */
OFPGC_DELETE = 2, /* Delete all matching groups. */
OFPGC_INSERT_BUCKET = 3,/* Insert action buckets to the already available
list of action buckets in a matching group */
/* OFPGC_??? = 4, */ /* Reserved for future use. */
OFPGC_REMOVE_BUCKET = 5,/* Remove all action buckets or any specific action
bucket from matching group */
};
Meter表配置
meter表配置消息类型OFPT_METER_MOD
。
/* Meter configuration. OFPT_METER_MOD. */
struct ofp_meter_mod {
struct ofp_header header;
uint16_t command; /* One of OFPMC_*. */
uint16_t flags; /* Bitmap of OFPMF_* flags. */
uint32_t meter_id; /* Meter instance. */
struct ofp_meter_band_header bands[0]; /* The band list length is
inferred from the length field
in the header. */
};
端口配置
端口配置主要配置端口的行为,比如协商速率,协商方式,pause帧,禁用启用等。
/* Modify behavior of the physical port */
struct ofp_port_mod {
struct ofp_header header;
uint32_t port_no;
uint8_t pad[4];
uint8_t hw_addr[OFP_ETH_ALEN]; /* The hardware address is not
configurable. This is used to
sanity-check the request, so it must
be the same as returned in an
ofp_port struct. */
uint8_t pad2[2]; /* Pad to 64 bits. */
uint32_t config; /* Bitmap of OFPPC_* flags. */
uint32_t mask; /* Bitmap of OFPPC_* flags to be changed. */
/* Port mod property list - 0 or more properties */
struct ofp_port_mod_prop_header properties[0];
};
OFP_ASSERT(sizeof(struct ofp_port_mod) == 32);
Asynchronous Message
Packet-In
消息类型:
OFPT_PACKET_IN
/* Packet received on port (datapath -> controller). */ struct ofp_packet_in { struct ofp_header header; uint32_t buffer_id; /* ID assigned by datapath. */ uint16_t total_len; /* Full length of frame. */ uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */ uint8_t table_id; /* ID of the table that was looked up */ uint64_t cookie; /* Cookie of the flow entry that was looked up. */ struct ofp_match match; /* Packet metadata. Variable size. */ /* The variable size and padded match is always followed by: * - Exactly 2 all-zero padding bytes, then * - An Ethernet frame whose length is inferred from header.length. * The padding bytes preceding the Ethernet frame ensure that the IP * header (if any) following the Ethernet header is 32-bit aligned. */ //uint8_t pad[2]; /* Align to 64 bit + 16 bit */ //uint8_t data[0]; /* Ethernet frame */ }; /* Why is this packet being sent to the controller? */ enum ofp_packet_in_reason { OFPR_TABLE_MISS = 0, /* No matching flow (table-miss flow entry). */ OFPR_APPLY_ACTION = 1, /* Output to controller in apply-actions. */ OFPR_INVALID_TTL = 2, /* Packet has invalid TTL */ OFPR_ACTION_SET = 3, /* Output to controller in action set. */ OFPR_GROUP = 4, /* Output to controller in group bucket. */ OFPR_PACKET_OUT = 5, /* Output to controller in packet-out. */ };
Symmetric Message
Hello
消息类型:
OFPT_HELLO
版本号协商扩展性
使用TLV,每一位代表一个支持的版本号,这样就可以无限扩展。 ```c struct ofp_hello { struct ofp_header header;
/ Hello element list / struct ofp_hello_elem_header elements[0]; / List of elements - 0 or more / }; OFP_ASSERT(sizeof(struct ofp_hello) == 8);
/ Common header for all Hello Elements / struct ofphello_elem_header { uint16_t type; /* One of OFPHET. / uint16_t length; / Length in bytes of the element, including this header, excluding padding. / }; / Hello elements types. / enum ofp_hello_elem_type { OFPHET_VERSIONBITMAP = 1, / Bitmap of version supported. / }; / Version bitmap Hello Element / struct ofp_hello_elem_versionbitmap { uint16_t type; / OFPHET_VERSIONBITMAP. / uint16_t length; / Length in bytes of this element, including this header, excluding padding. / /* Followed by:
* - Exactly (length - 4) bytes containing the bitmaps, then
* - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
* bytes of all-zero bytes */
uint32_t bitmaps[0]; /* List of bitmaps - supported versions */
};
<a name="wksv3"></a>
### Echo Request
消息包括头部和消息体,消息体没有定义。实现该消息体主要用于检查网络的延时等,可根据需要随意添加。
<a name="JjbeC"></a>
### Echo Reply
消息包括头部和`Echo Request`带来的消息体。
<a name="b756889c"></a>
### Error Message
```c
/* OFPT_ERROR: Error message. */
struct ofp_error_msg {
struct ofp_header header;
uint16_t type;
uint16_t code;
uint8_t data[0]; /* Variable-length data. Interpreted based on the type and code. No padding. */
};
OFP_ASSERT(sizeof(struct ofp_error_msg) == 12);