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
class Hub(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] #声明openflow版本
def __init__(self,*args,**kwargs):
super(Hub,self).__init__(*args,**kwargs)#继承父类的初始化
#接下来是交换机发送请求到控制器的处理过程
#ofp_event.EventOFPSwitchFeatures:交换机想要上传至控制器的消息事件
#CONFIG_DISPATCHER:状态信息:等待接受交换机设备信息
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)#设计监听
def switch_features_handler(self, ev):
datapath = ev.msg.datapath #可以暂时理解为提取交换机的id号
#Related to the openflow protocol
ofproto = datapath.ofproto
ofp_parser = datapath.ofproto_parser
#匹配,动作集这些都是通过调用ofp_parser的方法来获得的
#当交换机和控制器连接的时候,控制器会下发一条流表项给交换机,告诉交换机把所有的匹配不到的包上传到控制器上面来。
#安装table-miss flow entry:也就是要向控制器请求的消息-add_flow最后有发送信息给控制器的操作:请求构建流表项。
match = ofp_parser.OFPMatch()
#OFPActionOutput:use with packet-out for Specifing outgoing port
#ofproto.OFPP_CONTROLLER:告诉要发送到的端口是控制器,
#ofproto.OFPCML_NO_BUFFER:全部上传到控制器,不在交换机中存储,所以为no_buffer
#此actions的目的是,将所有匹配不到的包都上传到控制器,OFPActionOutput的第一个参数为转发的入端口,第二个参数是存在哪里,第三个参数是出端口(也就是哪里传出去的)
actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
#add flow:parameter include switch'id,priority,match,actions,优先级为0:默认的流表项
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,一般是处理内部的动作,如跳转到哪里之类的;
#ONFPIT_APPLY_ACTIOS:其中IT指的就是instruction,指的是一旦调用这个指令,就会立即
#应用相应的所有的动作集-第二个参数:actions
inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)]
#将上面的属性进行构建流表项
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)
# ofp_event.EventOFPPacketIn:ryu接收的事件消息;MAIN_DISPATCHER:交换机的一般状态
#下面函数是交换机和控制器协商好之后再执行的块。
def packet_in_handler(self,ev):
#ev:The data structure of the Packet-In packet;
#msg:an instance of the OpenFlow message class
msg = ev.msg
datapath = msg.datapath #datapath = switch'id
# Related to the openflow protocol
ofproto = datapath.ofproto
ofp_parser = datapath.ofproto_parser
#V1_3版本定义在匹配域中取入端口的操作
in_port = msg.match['in_port']
#construct a flow entry
match = ofp_parser.OFPMatch()#OFPMatch():因为收到的都匹配发出去,所以不需要匹配项,直接()就可以了
# OFPActionOutput:use with packet-out for Specifing outgoing port
# OFPP_FLOOD: refers to the flooded port
actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_FLOOD)]
#install flow mod to avoid packet_in next time
#下发流表和下发数据包消息是不一样的。
self.add_flow(datapath,1,match,actions)
# 收到一个数据包packet_in,没有把这个数据包下发下去,只是下发了流表指导后面的数据包处理。
# 所以还需要有一个操作把当下的数据包给处理掉。也就是说控制器收到交换机上传过来的消息并相应下发流表指导交换机后续怎么去执行了,
# 但是也还是需要有操作把当下的数据包给处理
#OFFPacketOut is use to construct a packet-out message
#交换机接收到包(包括控制器下发的指令[以数据包的形式]),数据包不知道怎么处理的时候,
#总得有个地方先存着,所以交换机就有一个buffer,自然就有buffer_id。由于在上面提到的buffer
#为no_buffer(当不指定的时候,交换机默认其id为-1),加上一般的风格不直接赋值为-1,所以就会
#直接提取msg.buffer_id:发送存在这里的数据包。
out = ofp_parser.OFPPacketOut(datapath=datapath,buffer_id=msg.buffer_id,in_port=in_port,actions=actions)
#Send_msg构建在线数据格式以发送到相应的数据路径交换机
datapath.send_msg(out)