0x01 概述


这一章节介绍协议的消息格式。OpenFlow消息是一个二进制的消息格式,在前面介绍的OpenFlow Channel中传输。需要解决以下几个问题。

  • 兼容性问题。
    • 通信两端的异构问题。
      • 协议规定报文内容采用大端传输,小端架构的需要做相应的转换
      • 结构体内字段按照8字节对齐,对于不够8字节的内容由padding字段补齐。
    • 版本迭代的兼容性
      • 向前兼容。
      • 向后兼容。对于本版本未使用的预留的字段,应该忽略其作用。
      • 通信两端的兼容性,通信两端通过协商版本号来通信,控制器通过特性查询以获取交换机的能力。
  • 扩展性
    • TLV,协议结构的设计上多处使用了TLV结构保证了可扩展性。
    • Reserve,协议在可能存在扩展的位置预留了多个比特位供后续使用。
  • 网络传输问题
    • 粘包问题
      • OpenFlow Channel的传输层可能为字节流协议,因此协议消息提供了消息头部来描述消息的格式,其中就包括长度,来描述本次消息的大小,以此来定界。这里面应该加个Magic来保证消息的正确性。
    • 异步传输
      • 协议的头部里面包括了一个Xid来识别消息,从而保证异步消息得以实现。

0x02 通用数据结构


消息头部

OpenFlow定义的头部如下,其中OFP_ASSERT用来保证异构设备的兼容性。

  1. /* Header on all OpenFlow packets. */
  2. struct ofp_header {
  3. uint8_t version; /* OFP_VERSION. */
  4. uint8_t type; /* One of the OFPT_ constants. */
  5. uint16_t length; /* Length including this ofp_header. */
  6. uint32_t xid; /* Transaction id associated with this packet. Replies use the same id as was in the request to facilitate pairing. */
  7. };
  8. 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 /

};

  1. <a name="OQryi"></a>
  2. ####
  3. <a name="c76cfefe"></a>
  4. ## 端口
  5. `OpenFlow`的端口号是一个`uint32_t`的整数,从`1`开始,用于控制器和交换机通信的时候标识一个`Datapath`的端口。存在一些预定义的特殊端口号,这部分不能被其他地方使用,其含义在前面的章节已经描述过。定义如下:
  6. ```c
  7. /* Port numbering. Ports are numbered starting from 1. */
  8. enum ofp_port_no {
  9. /* Maximum number of physical and logical switch ports. */
  10. OFPP_MAX = 0xffffff00,
  11. /* Reserved OpenFlow Port (fake output "ports"). */
  12. OFPP_UNSET = 0xfffffff7, /* Output port not set in action-set.
  13. used only in OXM_OF_ACTSET_OUTPUT. */
  14. OFPP_IN_PORT = 0xfffffff8, /* Send the packet out the input port. This
  15. reserved port must be explicitly used
  16. in order to send back out of the input
  17. port. */
  18. OFPP_TABLE = 0xfffffff9, /* Submit the packet to the first flow table
  19. NB: This destination port can only be
  20. used in packet-out messages. */
  21. OFPP_NORMAL = 0xfffffffa, /* Forward using non-OpenFlow pipeline. */
  22. OFPP_FLOOD = 0xfffffffb, /* Flood using non-OpenFlow pipeline. */
  23. OFPP_ALL = 0xfffffffc, /* All standard ports except input port. */
  24. OFPP_CONTROLLER = 0xfffffffd, /* Send to controller. */
  25. OFPP_LOCAL = 0xfffffffe, /* Local openflow "port". */
  26. OFPP_ANY = 0xffffffff /* Special value used in some requests when
  27. no port is specified (i.e. wildcarded). */
  28. };

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_MODOFPT_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_EVENTSVANCANCY即交换机流表的剩余空间,如果上升/下降超过阈值则上报事件。
  • 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_MODPACKET-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);