ryu实例---Hub

本文章基于https://ryu.readthedocs.io/en/latest/writing_ryu_app.html里面的第一个应用,即hub的简单实现。但是,按照这里的原程序,出现了一些问题,这篇文章里我给出了一些我自己的解决方案。

接下来,我就按照官网的示例进行讲解。

第一部分:代码

新建一个类L2Switch,内容如下:

from ryu.base import app_manager
from ryu.ofproto import ofproto_v1_3


class L2Switch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

从ryu.base import app_manager,在开发APP的时候只需要继承这个基类,就获得你想要的一个APP的一切了。from ryu.ofproto import ofproto_v1_3意思是导入openflow1.3协议的数据,本次开发就是使用openflow1.3协议。此时,这个程序就是一个完整的程序了,运行并没有错误,但是由于尚未添加如何处理代码,所以这段程序说明也做不了。

接下来,接需要继续向类中添加代码以完成hub功能的开发。

from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3


class L2Switch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super(L2Switch, self).__init__(*args, **kwargs)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        msg = ev.msg
        dp = msg.datapath
        ofp = dp.ofproto
        ofp_parser = dp.ofproto_parser

        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]

        data = None
        if msg.buffer_id == ofp.OFP_NO_BUFFER:
             data = msg.data

        out = ofp_parser.OFPPacketOut(
            datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port,
            actions=actions, data = data)
        dp.send_msg(out)

将方法'packet_in_handler'添加到L2Switch类中。当ryu控制器收到OpenFlow packet_in消息时,将调用此方法。

其中“ set_ev_cls”装饰器。该装饰器告诉Ryu何时应调用装饰的函数。装饰器的第一个参数表示事件发生时应调用此函数。即,每次Ryu收到packet_in消息时,都会调用此函数。第二个参数表示交换机的状态。ryu与交换机之间的握手完成之前忽略packet_in消息,使用“ MAIN_DISPATCHER”作为第二个参数表示仅在握手完成后才调用此函数。

接下来,介绍“ packet_in_handler”函数的前半部分。

  • ev.msg是表示packet_in数据结构的对象。
  • msg.dp是代表已经格式化的msg其实就是一个packet_in报文,msg.datapath直接可以获得packet_in报文的datapath结构。datapath用于描述一个交换网桥,也是和控制器通信的实体单元。datapath.send_msg()函数用于发送数据到指定datapath,通过datapath.id可获得dpid数据。
  • dp.ofproto和dp.ofproto_parser是代表Ryu和交换机握手的OpenFlow协议的对象。

接下来时函数的下半部分。

  • OFPActionOutput类与packet_out消息一起使用,以指定要从中发送数据包的交换机端口。该应用程序使用OFPP_FLOOD标志来指示应在所有端口上发送数据包。
  • OFPPacketOut类用于构建packet_out消息。
  • 如果使用OpenFlow消息类对象调用Datapath类的send_msg方法,则Ryu会生成联机数据格式并将其发送到交换机。

至此,一个完整的HUB功能就可以实现了。(当然,这只是官网这样说的,在我实际操作中发现有错误,接下来,我就介绍一下我遇到的问题和解决方案)

第二部分:实验

首先,利用mininet构建网络拓扑,如下所示。

ryu实例---Hub_第1张图片

在Ubuntu终端命令行中运行HUB程序,命令如下。

root@yang-VirtualBox:/home/yang/ryu/ryu/app# ryu-manager hub1_yjl.py

接下来,设置c0控制器,h1、h2、h3的ip地址分别为10.0.0.1、10.0.0.2、10.0.0.3。全部设置完成后,运行拓扑。

运行拓扑后,在Ubuntu命令行查看交换机s1的流表,可以发现s1并未有任何流表项存在,所以此时需要手动添加默认流表项,如下所示。

在mininet命令行中输入命令 h1 ping h3,此时观察控制器输出的日志信息,如下所示。

ryu实例---Hub_第2张图片

可以看出,报错AttributeError: 'OFPPacketIn' object has no attribute 'in_port'。也就是说,程序msg.in_port报错,并没有in_port。至于为什么没有,主要是官网示例给的是openflow1.0版本的,而这里我用的是openflow1.3版本的,所以收到的msg的格式有所差别。通过将msg打印到控制台上,打印结果如下。

可以看出,msg中包含的还是会有in_port的信息的,所以为了解决错误,我们只需要将in_port的信息提取出来就可以了。为此,我改进了一下代码,多了处理字符串的部分,提取出in_port的值。改进后的完整代码如下。(当然,这里完全可用json解析出in_port数据,但是为了更清晰看出示例,我使用字符串一点点解析的)

from ryu.base import app_manager
from ryu.controller.handler import set_ev_cls
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller import ofp_event
from ryu.ofproto import ofproto_v1_3
'''
实现了一个hub的功能,
即控制器通过packet_in数据包的解析,进行命令的下发,
这个程序的一切指令处理均在控制器进行,并不会进行流表的下发,只会下发转发指令,
所以,在运行之前需要手动在ovs上添加默认controller的流表
'''


class L2Switch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def packet_in_handler(self, ev):
        msg = ev.msg
        dp = msg.datapath
        ofp = dp.ofproto
        ofp_parser = dp.ofproto_parser
        dp_id = dp.id

        # 计算出in_port
        start = str(msg).index('oxm_fields') + 11
        end = str(msg).index('),reason')
        inport_str = str(msg)[start:end]
        instr = eval(inport_str)
        in_port = instr['in_port']

        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]

        data = None
        if msg.buffer_id == ofp.OFP_NO_BUFFER:
             data = msg.data
        print('id:{0}'.format(dp_id))
        print('in_port:{0}'.format(in_port))

        out = ofp_parser.OFPPacketOut(
            datapath=dp, buffer_id=msg.buffer_id, in_port=in_port,
            actions=actions, data=data)
        dp.send_msg(out)

接下来,重新在Ubuntu控制台运行程序。在mininet命令行中输入命令 h1 ping h3,此时可以看出h1 h3之间和相互ping通了,切换h2主机也可以,至此,HUB程序就实现了,各位也可以使用tcpdump监控一下链路的数据流,看看是否真的实现了,这里我就不在赘述。

github地址:https://github.com/Yang-Jianlin/ryu/blob/master/ryu/app/hub1_yjl.py

你可能感兴趣的:(SDN,计算机网络,Python,ryu,ovs,SDN,hub)