example.py

from ryu.base import app_manager
from ryu.ofproto import ofproto_v1_3
from ryu.controller.handler import MAIN_DISPATCHER,CONFIG_DISPATCHER
from ryu.controller import ofp_event
from ryu.controller.handler import set_ev_cls
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet

class ExampleSwitch(app_manager.RyuApp):

  OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

  def __init__(self,*args,**kwargs):
    super(ExampleSwitch, self).__init__(*args, **kwargs)
    self.mac_to_port = {}

  #用作初始化时openflow控制器获取openflow交换机的功能支持消息
  #ofp_event.EventOFPSwitchFeatures:交换机想要上传至控制器的消息事件;CONFIG_DISPATCHER:状态信息:等待接受交换机设备信息
  @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
  def switch_features_handler(self, ev):
    datapath = ev.msg.datapath #switch'id
    #Related to the openflow protocol
    ofproto = datapath.ofproto
    ofp_parser = datapath.ofproto_parser

    #install the table-miss flow entry
    match = ofp_parser.OFPMatch()
    #OFPActionOutput:use with packet-out for Specifing outgoing port
    actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
    ofproto.OFPCML_NO_BUFFER)]
    #add flow:parameter include switch'id,priority,match,actions
    self.add_flow(datapath,0,match,actions)

  #add a flow entry.and install it into datapath
  def add_flow(self,datapath,priority,match,actions):
    # Related to the openflow protocol
    ofproto = datapath.ofproto
    ofp_parser = datapath.ofproto_parser

    #contruct a flow_mod msg and sent it.
    #V1_3版本定义了一个指令instructions,一般是处理内部的动作,如跳转到哪里之类的;
    #OFPIT_APPLY_ACTIONS:其中IT指的就是instruction,指的是一旦调用这个指令,就会立即
    #应用相应的所有的动作集-第二个参数:actions
    inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)]
    #mod is a flow entry
    mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,match=match,instructions=inst)
    #Report the information containing the flow table entry to the controller
    datapath.send_msg(mod)
  @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
  def packet_in_handler(self, ev):
    msg = ev.msg
    datapath = msg.datapath
    ofproto = datapath.ofproto
    ofp_parser = datapath.ofproto_parser

    #The following is the logical process of switch learning
    #get datapath id to identify openflow switch
    dpid = datapath.id
    #设置默认:setdefault第二个参数为字典{}的原因是因为要存的东西有两个:主机mac地址以及对应的端口号
    #相当于这样的形式:datapath[dpid]{mac:port}
    self.mac_to_port.setdefault(dpid,{})
    #parser and analize the received packets
    #将报文的字节流msg.data作为参数,调用packet函数就会自动解析。
    pkt = packet.Packet(msg.data)
    #eth_pkt算是pkt里面的某一个数据结构
    #获取以太网协议,生成以太网报文类型
    eth_pkt = pkt.get_protocol(ethernet.ethernet)
    dst = eth_pkt.dst
    src = eth_pkt.src
    in_port = msg.match['in_port']
    #打印一些消息以更好辨别
    self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

    #datapath[dpid]{host:port};key为主机mac地址,value为端口号;mac_to_port[dpid][src] = in_port,也就是找源主机对应的端口(in_port)
    self.mac_to_port[dpid][src] = in_port

    #如果dst已经学习到了,就直接获得其相应的出端口,否则就泛洪
    if dst in self.mac_to_port[dpid]:
      out_port = self.mac_to_port[dpid][dst]
    else:
      out_port = ofproto.OFPP_FLOOD

    #构造动作集
    actions = [ofp_parser.OFPActionOutput(out_port)]

    #install a flow mod msg
    if out_port != ofproto.OFPP_FLOOD:
    match = ofp_parser.OFPMatch(in_port=in_port,eth_dst=dst)
    #下发到指定的端口,进而在下面处理数据包
    self.add_flow(datapath,1,match,actions)

    #send a packet out:控制器下发处理数据包的行为以packet_out的形式下发
    out = ofp_parser.OFPPacketOut(
    datapath=datapath,buffer_id=msg.buffer_id,in_port=in_port,actions=actions)
    datapath.send_msg(out)

posted @ 2019-12-09 09:59  scnu-yang  阅读(378)  评论(0)    收藏  举报