POX 控制器之 ofp_stats_reply 消息结构
收到 ofp_stats_reply 数据包,但 POX 框架相关教程很少,没有解析方法教学。其实可以直接用 print 方法打印出来,POX 在这方面的支持很友好,格式缩进什么的也很体贴。
如下是 MAC 地址为 00-00-00-00-00-04,DPID 为 9 的交换机上报的 ofp_stats_reply 消息:
[00-00-00-00-00-04 9] ofp_stats_reply
[00-00-00-00-00-04 9] header:
[00-00-00-00-00-04 9] version: 1
[00-00-00-00-00-04 9] type: 17 (OFPT_STATS_REPLY)
[00-00-00-00-00-04 9] length: 396
[00-00-00-00-00-04 9] xid: 330
[00-00-00-00-00-04 9] type: 1
[00-00-00-00-00-04 9] flags: 0
[00-00-00-00-00-04 9] body:
[00-00-00-00-00-04 9] length: 96
[00-00-00-00-00-04 9] table_id: 0
[00-00-00-00-00-04 9] match:
[00-00-00-00-00-04 9] wildcards: nw_tos|tp_dst|dl_dst|dl_src|dl_vlan_pcp|dl_vlan|tp_src (1100000000000011001110 = 3000ce)
[00-00-00-00-00-04 9] in_port: 2
[00-00-00-00-00-04 9] dl_type: 0x800
[00-00-00-00-00-04 9] nw_proto: 17
[00-00-00-00-00-04 9] nw_src: 10.0.0.11
[00-00-00-00-00-04 9] nw_dst: 10.0.0.8
[00-00-00-00-00-04 9] duration_sec: 58
[00-00-00-00-00-04 9] duration_nsec: 696000000
[00-00-00-00-00-04 9] priority: 3
[00-00-00-00-00-04 9] idle_timeout: 0
[00-00-00-00-00-04 9] hard_timeout: 0
[00-00-00-00-00-04 9] cookie: 0
[00-00-00-00-00-04 9] packet_count: 0
[00-00-00-00-00-04 9] byte_count: 0
[00-00-00-00-00-04 9] actions:
[00-00-00-00-00-04 9] type: 0
[00-00-00-00-00-04 9] len: 8
[00-00-00-00-00-04 9] port: 4
[00-00-00-00-00-04 9] max_len: 0
[00-00-00-00-00-04 9]
[00-00-00-00-00-04 9] length: 96
[00-00-00-00-00-04 9] table_id: 0
[00-00-00-00-00-04 9] match:
[00-00-00-00-00-04 9] wildcards: nw_tos|tp_dst|dl_dst|dl_src|dl_vlan_pcp|dl_vlan|tp_src (1100000000000011001110 = 3000ce)
[00-00-00-00-00-04 9] in_port: 4
[00-00-00-00-00-04 9] dl_type: 0x800
[00-00-00-00-00-04 9] nw_proto: 17
[00-00-00-00-00-04 9] nw_src: 10.0.0.8
[00-00-00-00-00-04 9] nw_dst: 10.0.0.11
[00-00-00-00-00-04 9] duration_sec: 58
[00-00-00-00-00-04 9] duration_nsec: 656000000
[00-00-00-00-00-04 9] priority: 3
[00-00-00-00-00-04 9] idle_timeout: 0
[00-00-00-00-00-04 9] hard_timeout: 0
[00-00-00-00-00-04 9] cookie: 0
[00-00-00-00-00-04 9] packet_count: 0
[00-00-00-00-00-04 9] byte_count: 0
[00-00-00-00-00-04 9] actions:
[00-00-00-00-00-04 9] type: 0
[00-00-00-00-00-04 9] len: 8
[00-00-00-00-00-04 9] port: 2
[00-00-00-00-00-04 9] max_len: 0
[00-00-00-00-00-04 9]
[00-00-00-00-00-04 9] length: 96
[00-00-00-00-00-04 9] table_id: 0
[00-00-00-00-00-04 9] match:
[00-00-00-00-00-04 9] wildcards: nw_tos|tp_dst|dl_dst|dl_src|dl_vlan_pcp|dl_vlan|tp_src (1100000000000011001110 = 3000ce)
[00-00-00-00-00-04 9] in_port: 4
[00-00-00-00-00-04 9] dl_type: 0x800
[00-00-00-00-00-04 9] nw_proto: 17
[00-00-00-00-00-04 9] nw_src: 10.0.0.19
[00-00-00-00-00-04 9] nw_dst: 10.0.0.12
[00-00-00-00-00-04 9] duration_sec: 58
[00-00-00-00-00-04 9] duration_nsec: 621000000
[00-00-00-00-00-04 9] priority: 3
[00-00-00-00-00-04 9] idle_timeout: 0
[00-00-00-00-00-04 9] hard_timeout: 0
[00-00-00-00-00-04 9] cookie: 0
[00-00-00-00-00-04 9] packet_count: 0
[00-00-00-00-00-04 9] byte_count: 0
[00-00-00-00-00-04 9] actions:
[00-00-00-00-00-04 9] type: 0
[00-00-00-00-00-04 9] len: 8
[00-00-00-00-00-04 9] port: 3
[00-00-00-00-00-04 9] max_len: 0
[00-00-00-00-00-04 9]
[00-00-00-00-00-04 9] length: 96
[00-00-00-00-00-04 9] table_id: 0
[00-00-00-00-00-04 9] match:
[00-00-00-00-00-04 9] wildcards: nw_tos|tp_dst|dl_dst|dl_src|dl_vlan_pcp|dl_vlan|tp_src (1100000000000011001110 = 3000ce)
[00-00-00-00-00-04 9] in_port: 3
[00-00-00-00-00-04 9] dl_type: 0x800
[00-00-00-00-00-04 9] nw_proto: 17
[00-00-00-00-00-04 9] nw_src: 10.0.0.12
[00-00-00-00-00-04 9] nw_dst: 10.0.0.19
[00-00-00-00-00-04 9] duration_sec: 58
[00-00-00-00-00-04 9] duration_nsec: 621000000
[00-00-00-00-00-04 9] priority: 3
[00-00-00-00-00-04 9] idle_timeout: 0
[00-00-00-00-00-04 9] hard_timeout: 0
[00-00-00-00-00-04 9] cookie: 0
[00-00-00-00-00-04 9] packet_count: 0
[00-00-00-00-00-04 9] byte_count: 0
[00-00-00-00-00-04 9] actions:
[00-00-00-00-00-04 9] type: 0
[00-00-00-00-00-04 9] len: 8
[00-00-00-00-00-04 9] port: 4
[00-00-00-00-00-04 9] max_len: 0
数据包主要分为四部分:
- header
- type
- flags
- body:是一个 list,有多少条流表就包含多少个元素,应使用 for 循环依次解析。
接下来依次分析这四个部分。
header
header 内包含四个字段:
- version:OpenFlow 版本号,上例中为 1(0x01) 即为 OpenFlow 1.0 版本,后续版本 1.1、1.2、1.3 的 version 值递增,即为 2、3、4。
- type:OpenFlow 消息类型,定义在枚举类 ofp_type 中,主要包括:
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一致。如 ofp_stats_request 和对应的 ofp_stats_reply 就使用同一个事件 ID。
type
type 表示这个 ofp_stats_request/ofp_stats_reply 消息请求/回应的统计类型,决定如何解析 body 字段。类型如表所示:
取值 | 消息类型 | 说明 | 请求 body | 回复 body |
---|---|---|---|---|
0 | OFPMP_DESC | 整体统计信息 | 空 | ofp_desc_stats(制造商、软硬件描述、序列号、可读的数据通道描述) |
1 | OFPMP_FLOW | 单流请求信息 | ofp_aggregate_stats_request(要读的 TABLE_ID 或 OFPPT_ALL 代表读所有表) | ofp_aggregate_stats_reply(这个项的长度、TABLE ID、流已生成的时间、超过已生成时间的时间、优先级、硬超时时间、未命中时间、包数目、字节数) |
2 | OFPMP_AGGREGATE | 多流请求信息 | ofp_aggregate_stats_request(要读的 TABLE_ID) | ofp_aggregate_stats_replay(包数目、字节数、流数目) |
3 | OFPMP_TABLE | 流表请求信息 | 空 | ofp_table_stats(表的标识符、通配符、最大支持的流表项、活跃流表项的数量、被查找表的数量、匹配表的数量) |
4 | FPMP_PORT_STATS | 端口信息请求 | fp_port_stats_request(端口号或 OFPP_ANY 代表所有端口) | ofp_port_stats(收到包的数量、已传送包的数量、收到的字节数、传送的字节数、被 RX 丢弃的包数、被 RX 丢弃的包数、接收到错误的包数、帧调整的错误数量、在 RX 溢出的数据包数、CRC 错误数、冲突数量) |
5 | OFPMP_QUEUE | 队列请求信息 | - | - |
6 | - | vendor 请求信息 | - | - |
本例中的类型号为 1,代表是单流请求信息。
flags
未定义。
body
已在 type 部分介绍,需按照类型解析。
编程实现 POX 控制器发送 ofp_stats_request 和接收 ofp_stats_reply 并解析
只粘贴相关部分。
def req_for_flow_stats():
tik = 30
while True:
time.sleep(1)
tik = (tik - 1 + 30) % 30
if tik == 0:
allCon = core.openflow.connections
for con in allCon:
con.send(of.ofp_stats_request(body=of.ofp_flow_stats_request()))
class DefaultOpenFlowHandlers (OpenFlowHandlers):
"""
Basic OpenFlow message handling functionality
There is generally a single instance of this class which is shared by all
Connections.
"""
@staticmethod
def handle_STATS_REPLY (con, msg):
e = con.ofnexus.raiseEventNoErrors(RawStatsReply, con, msg)
if e is None or e.halt != True:
con.raiseEventNoErrors(RawStatsReply, con, msg)
con._incoming_stats_reply(msg)
dpid = con.dpid
if msg.type == of.OFPST_FLOW:
for f in msg.body:
print(f)
pkts = f.packet_count
byts = f.byte_count
if input_pkts != 0:
print("[byte, packet]: [", f.byte_count, f.packet_count, "]")
print(dpid, ", Path Loss Rate =", (input_pkts - output_pkts) * 1.0 / input_pkts * 100, "%")
完整实现见LXiao2015's GitHub