OpenFlow协议抓包实验
1 操作步骤
本实验采用opendaylight控制器和mininet,使用mininet连接opendaylight。连接后pingall测试连接是否成功,测试完连通性后进行抓包实验。
打开wireshark抓包:
sudo wireshark
2 OpenFlow1.0协议解析
控制器通过Openflow管理、控制交换机,分析wireshark抓到的OpenFlow包就可以大体推测控制器与交换机通信的流程。虽然没有进行任何操作,但是一旦交换机连接控制器,wireshark就能捕捉到很多OpenFlow包。依次是hello消息、feature消息,stats消息,以及packet_in和packet_out。
2.1 Hello
控制器与交换机建立连接时由其中某一方发起Hello消息,双方协调协议版本号。Hello消息只有of包头,没有主体部分。而of头部结果如下:
/* 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);
Hello消息如下:
- Version:OpenFlow版本,低位为版本号,如上所示。
- Type:OpenFlow消息类型,主要包括:
注:
enum ofp_type {
/* Immutable messages. */
OFPT_HELLO, /* Symmetric message */
OFPT_ERROR, /* Symmetric message */
OFPT_ECHO_REQUEST, /* Symmetric message */
OFPT_ECHO_REPLY, /* Symmetric message */
OFPT_VENDOR, /* Symmetric message */
/* Switch configuration messages. */
OFPT_FEATURES_REQUEST, /* Controller/switch message */
OFPT_FEATURES_REPLY, /* Controller/switch message */
OFPT_GET_CONFIG_REQUEST, /* Controller/switch message */
OFPT_GET_CONFIG_REPLY, /* Controller/switch message */
OFPT_SET_CONFIG, /* Controller/switch message */
/* Asynchronous messages. */
OFPT_PACKET_IN, /* Async message */
OFPT_FLOW_REMOVED, /* Async message */
OFPT_PORT_STATUS, /* Async message */
/* Controller command messages. */
OFPT_PACKET_OUT, /* Controller/switch message */
OFPT_FLOW_MOD, /* Controller/switch message */
OFPT_PORT_MOD, /* Controller/switch message */
/* Statistics messages. */
OFPT_STATS_REQUEST, /* Controller/switch message */
OFPT_STATS_REPLY, /* Controller/switch message */
/* Barrier messages. */
OFPT_BARRIER_REQUEST, /* Controller/switch message */
OFPT_BARRIER_REPLY, /* Controller/switch message */
/* Queue Configuration messages. */
OFPT_QUEUE_GET_CONFIG_REQUEST, /* Controller/switch message */
OFPT_QUEUE_GET_CONFIG_REPLY /* Controller/switch message */
};
- Length:消息总长度,包含头部。
- Xid:事件ID,同一件事件的ID号一致。如feature_request和对应的feature_reply就使用同一个Transaction id,但是两个hello消息的Transaction id并不相同,不过据实验结果看两个id一般是两个相邻的数字。并且packet_in的transaction id都为0。
2.2 Feature消息
TLS会话一建立,控制器就会向交换机发送一个ofpt_feature_request消息,该消息只有of包头,如下所示。交换机会回复一条ofpt_feature_reply消息。
ofpt_feature_request如图:(有时候会发现一个数据包中有多个request,并且后面会有一一对应的reply包。)
ofpt_feature_reply如图:
ofpt_feature_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 pad[3]; /* Align to 64-bits. */
/* Features. */
uint32_t capabilities; /* Bitmap of support "ofp_capabilities". */
uint32_t actions; /* Bitmap of supported "ofp_action_type"s. */
/* Port info.*/
struct ofp_phy_port ports[0]; /* Port definitions. The number of ports is inferred from the length field in the header. */
};
OFP_ASSERT(sizeof(struct ofp_switch_features) == 32);
- datapath_id:数据通道独一无二的标识符,低48位是一个MAC地址,而高16位是自定义的。例如,用高16位代表VLAN ID区别一个物理交换机中的多个虚拟交换机。
- n_buffers:一次最多缓存的数据包数量。
- n_tables:表示交换机支持的流表数量。而每个流表可以设置不同的通配符和不同数量的流表项。控制器和交换机第一次通信的时候,控制器会从feature_reply消息中找出交换机支持多少流表,如果控制器还想了解大小、类型和流表查询的顺序,就发送一个ofpst_table stats请求,交换机必须按照数据包遍历流表的顺序把这些流表回复给控制器,并且精确匹配流表排在通配流表前。
- capabilities:所支持的功能,该字段使用以下flags:
注:
/* 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_STP = 1 << 3, /* 802.1d spanning tree. */
OFPC_RESERVED = 1 << 4, /* Reserved, must be zero. */
OFPC_IP_REASM = 1 << 5, /* Can reassemble IP fragments. */
OFPC_QUEUE_STATS = 1 << 6, /* Queue statistics. */
OFPC_ARP_MATCH_IP = 1 << 7 /* Match IP addresses in ARP pkts. */
};
- actions:该bitmask表示交换机所支持的actions,“required”action必须支持,vendor action不应该通过该bitmask显示,actions根据ofp_action_type的值决定需要左移几位。
- ports[0]:以数组的形式罗列出该系统中支持OpenFlow的物理端口。数据长度可以根据OpenFlow头部中的length推测出来。
2.3 Packet_in消息
当交换机碰到新数据包不知道如何处理,或者action要求发送给控制器,那么交换机就会用packet_in消息发送给控制器。一般将数据包缓存在交换机中,将有效的数据包信息(默认的128字节,如果原因是 “send to controller” action,那么长度由action_out的max_len决定;如果是原因table miss,那么长度由set_config消息中的miss_send_len决定。)和缓存id发送给控制器,不过,如果交换机不支持缓存或者内存用光了,那么就把整个数据包放在数据部分发给控制器,并且缓存id为-1。
Packet_in消息如下:
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. */
uint16_t in_port; /* Port on which frame was received. */
uint8_t reason; /* Reason packet is being sent (one of OFPR_*) */
uint8_t pad;
uint8_t data[0]; /* Ethernet frame, halfway through 32-bit word,
so the IP header is 32-bit aligned. The amount of data is inferred from the length field in the header. Because of padding, offsetof(struct ofp_packet_in, data) == sizeof(struct ofp_packet_in) - 2. */
};
OFP_ASSERT(sizeof(struct ofp_packet_in) == 20);
- buffer_id:数据通道分配的缓存id,标志数据包存在交换机中的位置,如果没有缓存在交换机中则buffer_id则为-1。
- total_len:整个数据帧的长度。
- in_port:接收数据帧的端口。
- reason:将数据包发送给控制器的原因,一般有俩原因,一是没有匹配到流表项,二是动作要求发给控制器。
2.4 Packet_out消息
当控制器希望交换机发送某个数据包,就使用packet_out消息。Packet_out消息如下所示:
Packet_out消息结构如下:
/* Send packet (controller -> datapath). */
struct ofp_packet_out {
struct ofp_header header;
uint32_t buffer_id; /* ID assigned by datapath (-1 if none). */
uint16_t in_port; /* Packet's input port (OFPP_NONE if none). */
uint16_t actions_len; /* Size of action array in bytes. */
struct ofp_action_header actions[0]; /* Actions. */
/* uint8_t data[0]; */ /* Packet data. The length is inferred
from the length field in the header.
(Only meaningful if buffer_id == -1.) */
};
OFP_ASSERT(sizeof(struct ofp_packet_out) == 16);
buffer_id:与packet_in中给的一样,如果为-1就表明数据包附在数据数组中。
2.5 Configuration消息
控制器可以发送OFPT_SET_CONFIG / OFPT_GET_CONFIG_REQUEST消息设置/查询交换机配置参数,交换机只需响应OFPT_GET_CONFIG_REQUEST消息即可。
OFPT_SET_CONFIG消息如下所示:
OFPT_GET_CONFIG_REQUEST消息没有消息主体,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; /* Max bytes of new flow that datapath should
send to the controller. */
};
OFP_ASSERT(sizeof(struct ofp_switch_config) == 12);
- Flags:配置类型flags,主要包括:
注:
enum ofp_config_flags {
/* Handling of IP fragments. */
OFPC_FRAG_NORMAL = 0, /* No special handling for fragments. */
OFPC_FRAG_DROP = 1, /* Drop fragments. */
OFPC_FRAG_REASM = 2, /* Reassemble (only if OFPC_IP_REASM set). */
OFPC_FRAG_MASK = 3
};
- miss_send_len:决定发送给控制器的数据包长度,如果该字段为0,则交换机发送一个大小为0的packet_in消息。
2.6 Stats消息
交换机和控制器连接后,控制器会不断发送stats消息询问交换机的状态,就好像两个打电话的人,一方不断询问“你在吗?”另一方不断回答“在呀!”抓到的stats消息如下所示:
ofp_stats_request和ofp_stats_reply消息结构相同,都使用以下结构:
注:
struct ofp_stats_request {
struct ofp_header header;
uint16_t type; /* One of the OFPST_* constants. */
uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */
uint8_t body[0]; /* Body of the request. */
};
OFP_ASSERT(sizeof(struct ofp_stats_request) == 12);
- Type:该字段决定传递的信息的类型并且怎样解释body部分。主要包括:
注:
enum ofp_stats_types {
/* Description of this OpenFlow switch.
* The request body is empty.
* The reply body is struct ofp_desc_stats. */
OFPST_DESC,
/* Individual flow statistics.
* The request body is struct ofp_flow_stats_request.
* The reply body is an array of struct ofp_flow_stats. */
OFPST_FLOW,
/* Aggregate flow statistics.
* The request body is struct ofp_aggregate_stats_request.
* The reply body is struct ofp_aggregate_stats_reply. */
OFPST_AGGREGATE,
/* Flow table statistics.
* The request body is empty.
* The reply body is an array of struct ofp_table_stats. */
OFPST_TABLE,
/* Physical port statistics.
* The request body is struct ofp_port_stats_request.
* The reply body is an array of struct ofp_port_stats. */
OFPST_PORT,
/* Queue statistics for a port
* The request body defines the port
* The reply body is an array of struct ofp_queue_stats */
OFPST_QUEUE,
/* Vendor extension.
* The request and reply bodies begin with a 32-bit vendor ID, which takes
* the same form as in "struct ofp_vendor_header". The request and reply
* bodies are otherwise vendor-defined. */
OFPST_VENDOR = 0xffff
};
- Flags:ofp_stats_request的flag字段目前还没有定义,而reply消息的flag字段只有一个值0x0001,该值表示后面是否还有更多的reply。
2.7 Flow_mod
控制器与交换机建立连接后,控制器会自发给交换机发送一组flow_mod消息,抓到的消息如下:
flow_mod消息结构:
/* Flow setup and teardown (controller -> datapath). */
struct ofp_flow_mod {
struct ofp_header header;
struct ofp_match match; /* Fields to match */
uint64_t cookie; /* Opaque controller-issued identifier. */
/* Flow actions. */
uint16_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 -1).
Not meaningful for OFPFC_DELETE*. */
uint16_t out_port; /* For OFPFC_DELETE* commands, require matching entries to include this as an output port. A value of OFPP_NONE indicates no restriction. */
uint16_t flags; /* One of OFPFF_*. */
struct ofp_action_header actions[0]; /* The action length is inferred
from the length field in the
header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 72);
- Cookie: 控制器设置的不透明数据。当command为OFPFC_MODIFY或OFPFC_MODIFY_STRICT时,匹配了的流表项需要适当的更新cookie字段。
- Command可能会是
注:
enum ofp_flow_mod_command {
OFPFC_ADD, /* New flow. */
OFPFC_MODIFY, /* Modify all matching flows. */
OFPFC_MODIFY_STRICT, /* Modify entry strictly matching wildcards */
OFPFC_DELETE, /* Delete all matching flows. */
OFPFC_DELETE_STRICT /* Strictly match wildcards and priority. */
};
- idle_timeout和hard_timeout控制流表项的生命周期。当idle_timeout为x(x不为0)并且hard_timeout为0时,流表项在x秒收不到数据包后就到期。当idle_timeout为0并且hard_timeout为x时,x秒后无论收不收得到数据包流表项都到期。当idle_timeout为x(x不为0)并且hard_timeout为y(y不为0)时,x秒收不到数据包或者y秒后,哪个期限先到就按哪个算。当idle_timeout和hard_timeout都为0时,表明这个流表项是永恒的,除非用OFPFC_DELETE删除,否则不会到期。
- priority:priority只和含通配字段的流表项有关。Priority值越大,表明流表优先级越高。交换机必须将优先级最高的通配流表项放在编号最低的通配流表中。无论流表是什么配置,交换机需要确保精确流表项排在通配流表项之前。
- buffer_id:标志由packet_in消息发送的数据包存储的地方。
- out_port:delete和delete_strict消息利用该字段确定作用域,而add、modify、modify_strict消息会自动忽略该字段。如果out_port为ofpp_none则表明禁用output端口过滤功能,否则就相当于添加了一个额外的匹配限制条件。连0也是有效端口id,表示约束条件是必须有关于该端口的output动作。而ofp_match和优先级等条件依旧生效,该字段只是添加了一个额外的约束。
- flags:flags主要包括:
注:
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_EMERG = 1 << 2 /* Remark this is for emergency. */
};
(1)、设置OFPFF_SEND_FLOW_REM标识表示当流表项到期时交换机发送一条flow removed消息,一般默认情况下交换机是不会为新添加的流表项发送flow removed消息的。
(2)、OFPFF_CHECK_OVERLAP生效表明交换机必须检查是否有优先级冲突的流表项,如果冲突,则flow mod无效并且回复错误提醒。
(3、)OFPFF_EMERG为1则代表该流表项是紧急流表项,当交换机与控制器断开连接时就利用该表项转发数据包。