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)