Ryu之流量监控

为了网络的安全和业务的正常运作,持续注意网络的健康状况是最基本的工作,找出网络的错误,发现真正的原因需要清楚的知道网络的状态,本文介绍如何使用OpenFlow来取得相关的统计信息。

from operator import attrgetter

from ryu.app import simple_switch_13
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER, DEAD_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.lib import hub


class SimpleMonitor(simple_switch_13.SimpleSwitch13):

    def __init__(self, *args, **kwargs):
        super(SimpleMonitor, self).__init__(*args, **kwargs)
        self.datapaths = {}
        self.monitor_thread = hub.spawn(self._monitor)#建立一个绿色线程,运行监控程序
#一般状态和连线中断状态,EventOFPStatureChange的信息类用来监测交换器的连线中断,会被触发在#Dathpath状态改变时
    @set_ev_cls(ofp_event.EventOFPStateChange,
                [MAIN_DISPATCHER, DEAD_DISPATCHER])
    def _state_change_handler(self, ev):#通过判断当前状态从监测列表添加或移除当前datapath
        datapath = ev.datapath
        if ev.state == MAIN_DISPATCHER:
            if not datapath.id in self.datapaths:
                self.logger.debug('register datapath: %016x', datapath.id)
                self.datapaths[datapath.id] = datapath
        elif ev.state == DEAD_DISPATCHER:
            if datapath.id in self.datapaths:
                self.logger.debug('unregister datapath: %016x', datapath.id)
                del self.datapaths[datapath.id]

    def _monitor(self):
        while True:
            for dp in self.datapaths.values():
                self._request_stats(dp)#每隔10s查询一次当前的监视datapath名单中的各个#datapath状况
            hub.sleep(10)

    def _request_stats(self, datapath):
        self.logger.debug('send stats request: %016x', datapath.id)
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser
#向指定的datapath发送OFPFlowStatsRequest和OFPStatsResquest消息类实体,即对相关统计信息进行
#请求
        req = parser.OFPFlowStatsRequest(datapath)
        datapath.send_msg(req)

        req = parser.OFPPortStatsRequest(datapath, 0, ofproto.OFPP_ANY)
        datapath.send_msg(req)
#对FlowStatsReply消息的回复进行事件处理
    @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)
    def _flow_stats_reply_handler(self, ev):
        body = ev.msg.body

        self.logger.info('datapath         '
                         'in-port  eth-dst           '
                         'out-port packets  bytes')
        self.logger.info('---------------- '
                         '-------- ----------------- '
                         '-------- -------- --------')
#对各个优先级非0的流表项按接收端口和目的MAC地址进行排序后遍历
        for stat in sorted([flow for flow in body if flow.priority == 1],
                           key=lambda flow: (flow.match['in_port'],
                                             flow.match['eth_dst'])):
#对交换机的datapath.id,目的MAC地址,输出端口和包以及字节流量进行打印
            self.logger.info('%016x %8x %17s %8x %8d %8d',
                             ev.msg.datapath.id,
                             stat.match['in_port'], stat.match['eth_dst'],
                             stat.instructions[0].actions[0].port,
                             stat.packet_count, stat.byte_count)
#对PortStatsReply消息的回复事件进行处理
    @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)
    def _port_stats_reply_handler(self, ev):
        body = ev.msg.body

        self.logger.info('datapath         port     '
                         'rx-pkts  rx-bytes rx-error '
                         'tx-pkts  tx-bytes tx-error')
        self.logger.info('---------------- -------- '
                         '-------- -------- -------- '
                         '-------- -------- --------')
#根据端口号进行排序并遍历
        for stat in sorted(body, key=attrgetter('port_no')):
#打印交换机id,端口号和接收及发送的包的数量字节数和错误数
            self.logger.info('%016x %8x %8d %8d %8d %8d %8d %8d', 
                             ev.msg.datapath.id, stat.port_no,
                             stat.rx_packets, stat.rx_bytes, stat.rx_errors,
                             stat.tx_packets, stat.tx_bytes, stat.tx_errors)

OFPFlowStatsRequest主要对交换机的流表的统计信息,可以使用table ID,output port,cookie值和match条件来缩限查找范围,本例中是取得所有的流表项。

OFPPortStats是用来取得关于交换器连接端口的统计信息。可以指定端口号这里使用OFPP_ANY目的就是取得连接端口的统计资料。

若要对统计信息进行分析,需要将msg信息转换成JSON格式进行输出,可通过以下代码进行生成

import json

# ...

self.logger.info('%s', json.dumps(ev.msg.to_jsondict(), ensure_ascii=True,
                                  indent=3, sort_keys=True))

将打印出以下结果

{
   "OFPFlowStatsReply": {
      "body": [
         {
            "OFPFlowStats": {
               "byte_count": 0,
               "cookie": 0,
               "duration_nsec": 680000000,
               "duration_sec": 4,
               "flags": 0,
               "hard_timeout": 0,
               "idle_timeout": 0,
               "instructions": [
                  {
                     "OFPInstructionActions": {
                        "actions": [
                           {
                              "OFPActionOutput": {
                                 "len": 16,
                                 "max_len": 65535,
                                 "port": 4294967293,
                                 "type": 0
                              }
                           }
                        ],
                        "len": 24,
                        "type": 4
                     }
                  }
               ],
               "length": 80,
               "match": {
                  "OFPMatch": {
                     "length": 4,
                     "oxm_fields": [],
                     "type": 1
                  }
               },
               "packet_count": 0,
               "priority": 0,
               "table_id": 0
            }
         },
         {
            "OFPFlowStats": {
               "byte_count": 42,
               "cookie": 0,
               "duration_nsec": 72000000,
               "duration_sec": 57,
               "flags": 0,
               "hard_timeout": 0,
               "idle_timeout": 0,
               "instructions": [
                  {
                     "OFPInstructionActions": {
                        "actions": [
                           {
                              "OFPActionOutput": {
                                 "len": 16,
                                 "max_len": 65509,
                                 "port": 1,
                                 "type": 0
                              }
                           }
                        ],
                        "len": 24,
                        "type": 4
                     }
                  }
               ],
               "length": 96,
               "match": {
                  "OFPMatch": {
                     "length": 22,
                     "oxm_fields": [
                        {
                           "OXMTlv": {
                              "field": "in_port",
                              "mask": null,
                              "value": 2
                           }
                        },
                        {
                           "OXMTlv": {
                              "field": "eth_dst",
                              "mask": null,
                              "value": "00:00:00:00:00:01"
                           }
                        }
                     ],
                     "type": 1
                  }
               },
               "packet_count": 1,
               "priority": 1,
               "table_id": 0
            }
         }
      ],
      "flags": 0,
      "type": 1
   }
}

执行流量监控程序:

同样需要先执行miniet构建拓扑并打开5个xtem终端,也要设定OpenFlow版本为OpenFlow13.各个主机终端不必执行dump的端口监视命令。然后在控制器终端执行simple_montor_13.py的应用程序

ryu-manager --verbose simple_monitor_13.py

然后在host1终端向host2主机发送数据

ping -c1 10.0.0.2

封包的转发,Flow Entry的注册情况和统计资料开始发送变化

《Ryu之流量监控》

《Ryu之流量监控》

在Flow entry的统计信息中,只有进入接收端口的封包被match成功后才能计数,由两次握手可知两个流表项分别match了1,2次,故有两个封包。

Flow entry的统计信息和连接端口的统计信息不可混为一谈,Flow Entry的统计信息是记录match的entry所转发的统计信息,也就是被table-miss flow entry所触发的Packet-in消息以及Packet-out消息都没有被记录。

    原文作者:菜地里翻滚的猪
    原文地址: https://blog.csdn.net/qq_34099967/article/details/89075472
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞