5、Ryu应用开发学习第一篇

1、Hub集线器                                                                                                 

1.1 原理

集线器的原理非常简单,就是将数据进行泛洪转发即可。

1.2 实现

'''
openflow常用消息
Hello消息、Feature消息,Echo消息,以及Packet_in、Packet_out和Flow_Removed等
1、Hello:Openflow已经初始化
2、Features: 控制器通过向交换机发送features消息,用于请求交换机身份和基本能力信息,且交换机必须对此类消息进行应答。features消息通常在安全通道建立时执行。
Features Request(controller)、 Features Reply(switch)。
3、Echo:该消息用于测量延迟、带宽等。
4、Packet-In: 交换机收到一个网络数据包,在流表中没有匹配项,则发送Packet-In消息给控制器。如果交换机缓存足够多,网络数据包被临时存放在缓存中,网络数据包部分内容和在交换机缓存中的序号一同发给控制器;如果交换机缓存不足则将整个数据包发送给控制器。
5、Packet-Out: 用于控制器通过交换机指定端口转发数据包。
6、FLow_Mod:相当于流表项。

先进行switch_features_handler
'''
from ryu.base import app_manager
from ryu.ofproto import ofproto_v1_3
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER
import ryu.controller.handler


class Hub(app_manager.RyuApp):
    # 明确使用的协议
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
    # 初始化
    def __init__(self,*args,**kwargs):
        super(Hub,self).__init__(*args,**kwargs)
    
    # @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)
    def switch_features_handler(self,ev):
        msg = ev.msg
        # datapath包含流表管道、流表组、端口等信息
        datapath = msg.datapath
        # 协议
        ofproto = datapath.ofproto
        # 解析协议
        ofp_parser = datapath.ofproto_parser
        
        '''在连接建立成功以后,控制器需要下发一个默认流表'''
        '''流表项由 priority、matchFields、instruction 等信息组成'''
        
        # 匹配域包含mac地址、ip地址...
        match = ofp_parser.OFPMatch()
        # 执行的动作:
        '''
        必备动作:从端口转发、转发到controller(不能处理的)、丢弃
        '''
        # OFPActionOutput将数据包发送出去,
        # 第一个参数OFPP_CONTROLLER是接收端口,
        # 第二个是数据包在交换机上缓存buffer_id,由于我们将数据包全部传送到控制器,所以不在交换机上缓存
        actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)]
        # 优先级设置为0,最后执行
        priority = 0
        remind_content = "default flow entry"
        
        self.add_flow(datapath,priority,match,actions,remind_content)
        
    '''
    1、Datapath是逻辑上的网络设备,负责转发和处理数据,无控制能力。(描述一个交换的网桥,也是和控制器通信的
    实体)
    2、一个SDN DataPath包含控制数据平面接口(Control Data Plane Interface,CDPI)
    、代理、转发引擎(Forwarding Engine)表和处理功能(Processing Function)。
    3、SDN数据面(转发面)的关键技术:对数据面进行抽象建模。
    '''   
    def add_flow(self,datapath,priority,match,actions,remind_content):
        # datapath属性
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        
        inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)]
        # flow_mod 相当于流表项
        # https://www.sdnlab.com/15786.html
        mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,match=match,instructions=inst)
        
        print("install to datapath, "+remind_content)
        
        # 发送数据
        datapath.send_msg(mod)
        
    
    @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPPacketIn,MAIN_DISPATCHER)
    def packet_in_handler(self,ev):
        print("packet_in_handler".center(50,"-"))
        print("packet In message")
        msg = ev.msg
        print("\033[34;1mmsg\033[0m")
        print(msg)
        datapath = msg.datapath
        print("\033[35;1mdatapath\033[0m")
        print(datapath)
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        
        # 获取源端口
        in_port = msg.match['in_port']
        print("get packet in,install flow entry,and lookback packet to datapath")
        
        # 匹配域,为空匹配所有
        match = ofp_parser.OFPMatch()
        # 动作:泛洪法
        actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_FLOOD)]
        # 优先级
        priority = 1
        remind_content = "hub flow entry"
        
        # 把流表项发送,构建流表,指导后续的数据包的转发
        self.add_flow(datapath,priority,match,actions,remind_content)
        
        out = ofp_parser.OFPPacketOut(datapath=datapath,buffer_id = msg.buffer_id,in_port=in_port,
                actions=actions,data= msg.data)        
        # 注意:我们将流表项下发了,但是数据包我们这次接收的,并没有处理
        # 就是再将控制器上的数据包,重新发送给datapath,让他按照流表项处理
        # buffer_id是这个数据包,存放在控制器中的缓冲区位置,是在事件中的buffer_id获取
        print("packet out message".center(50,"-"))
        print("out")
        print(out)
        datapath.send_msg(out)
        
           

1.3 分析

 

 2、Switch交换机                                                                             

2.1 原理

(1)A向B发送数据,向controllerPacket-In报文。但此时不知道B所在的端口,因此向Controller发送packetIn,Controller也不知道B所在的端口,因此A进行泛洪操作。

 

(2)由于A进行泛洪发送,因此B和C都能接到来自A的消息,但只有B向A发送应答的消息。但B不知道A所在的端口,因此向Controller发送packetIn,而此时Controller已经知道了A所在的端口,所以,下发流表项。

 

(3)A收到B的应答消息后,A向B发送数据,但此时A还是不知道B所在的端口位置,因此,继续向Controller发送packetIn。而Controller此时已经知道B所在的端口,所以,不在需要洪泛发送。下发流表项。

 

(4)至此,A和B之间的通信已经建立(形象的比喻),因此,以后A、B之间发送数据,只需根据相应的流表项进行转发即可(应该可以认为不再经过Controller)。

2.2 实现

# Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
An OpenFlow 1.0 L2 learning switch implementation.
"""

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



class SimpleSwitch(app_manager.RyuApp):
     # 明确使用的协议
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
    # 初始化
    def __init__(self,*args,**kwargs):
        super(Hub,self).__init__(*args,**kwargs)
    
    # @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    @ryu.controller.handler.set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)
    def switch_features_handler(self,ev):
        msg = ev.msg
        # datapath包含流表管道、流表组、端口等信息
        datapath = msg.datapath
        # 协议
        ofproto = datapath.ofproto
        # 解析协议
        ofp_parser = datapath.ofproto_parser
        
        '''在连接建立成功以后,控制器需要下发一个默认流表'''
        '''流表项由 priority、matchFields、instruction 等信息组成'''
        
        # 匹配域包含mac地址、ip地址...
        match = ofp_parser.OFPMatch()
        # 执行的动作:
        '''
        必备动作:从端口转发、转发到controller(不能处理的)、丢弃
        '''
        # OFPActionOutput将数据包发送出去,
        # 第一个参数OFPP_CONTROLLER是接收端口,
        # 第二个是数据包在交换机上缓存buffer_id,由于我们将数据包全部传送到控制器,所以不在交换机上缓存
        actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,ofproto.OFPCML_NO_BUFFER)]
        # 优先级设置为0,最后执行
        priority = 0
        remind_content = "default flow entry"
        
        self.add_flow(datapath,priority,match,actions,remind_content)
        
    '''
    1、Datapath是逻辑上的网络设备,负责转发和处理数据,无控制能力。(描述一个交换的网桥,也是和控制器通信的
    实体)
    2、一个SDN DataPath包含控制数据平面接口(Control Data Plane Interface,CDPI)
    、代理、转发引擎(Forwarding Engine)表和处理功能(Processing Function)。
    3、SDN数据面(转发面)的关键技术:对数据面进行抽象建模。
    '''   
    def add_flow(self,datapath,priority,match,actions,remind_content):
        # datapath属性
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        
        inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)]
        # flow_mod 相当于流表项
        # https://www.sdnlab.com/15786.html
        mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,match=match,instructions=inst)
        
        print("install to datapath, "+remind_content)
        
        # 发送数据
        datapath.send_msg(mod)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        msg = ev.msg
        # 解析datapath
        datapath = msg.datapath
        ofproto = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        
        '''
        每个交换机都有一个独立的转发表(流表),但是Controller下面会挂载很多Switch
        所以,必须先确定哪个的mac地址(主机)在哪个datapath(交换机)上
        '''
        '''# get datapath id to identify switch'''
        # 获取datapath的id,即交换机
        dpid = datapath.id
        # setdefault:当字典中存在key时,原字典不会改变value,当字典不存在key时,key对应的就为{}
        # 将id存入mac_to_port中
        self.mac_to_port.setdefault(dpid,{})
        
        '''# parser and analyse the receive packet message'''
        # 把二进制数据解析成数据包(报文)
        pkt = packet.Packet(msg.data)
        # 获取以太网的数据包
        eth_pkt = pkt.get_protocols(ethernet.ethernet)[0]
        # 获取源和目的mac地址
        dst = eth_pkt.dst
        src = eth_pkt.src
        # 获取源端口
        ip_port = msg.match['ip_port']
        # 输出消息
        self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)
        
        '''# 学习mac地址到端口的映射关系,避免下次发送相同的端口时还是继续泛洪'''
        self.mac_to_port[dpid][src] = ip_port
        
        '''# 判断流表中(mac_to_port)是否存在此映射,存在直接发送,不存在泛洪'''
        # 存在,不需要洪泛
        if dst in self.mac_to_port[dpid]:
            out_port = self.mac_to_port[dpid][dst]
        
        # 不存在,洪泛
        else:
            out_port = ofproto.FLOOD   
            
        '''# actions:1、转发/2、泛洪'''
        actions = [ofp_parser.OFPActionOutput(out_port)]

        '''
        #####1存在#####
        # 下发flow_mod,指导怎么发送
        '''
        # 如果out_port存在
        if out_port != ofproto.FLOOD:
            # 通过in_port和目的的mac地址匹配
            match = ofp_parser.OFPMatch(in_port = in_port,eth_dst = dst)
            self.add_flow(datapath,1,match,actions)
        '''
        #####2不存在#####
        # 泛洪
        '''
        
        ofp_parser.OFPPacketOut(datapath=datapath,buffer_id=msg.buffer_id,in_port=in_port,actions=actions,data=msg.data)
        
        datapath.send_msg(out)
        
        
  

2.3 分析

启动Minient topo结构

Ryu

 

当h1 ping h2时

 当h1 ping h3 时

当h2 ping h3时

posted @ 2020-12-22 22:00  Dammond  阅读(568)  评论(0)    收藏  举报