shortest_forwording.py

from ryu.base import app_manager
from ryu.ofproto import ofproto_v1_3
from ryu.controller import ofp_event
from ryu.controller.handler import set_ev_cls
from ryu.controller.handler import CONFIG_DISPATCHER,MAIN_DISPATCHER
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.topology import event
from ryu.topology.api import get_switch, get_link
import networkx as nx

#原理:
# 1)拓扑信息发现:周期发送LLDP报文,发现链路信息,使用networkx来存储拓扑信息。
# 2)根据链路信息计算最佳转发路径:使用networkx实现最短路径计算
# 3)根据最短路径,安装流表项,实现批量下发流表项方法

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


def __init__(self,*args,**kwargs):
super(ShortestForwarding, self).__init__(*args,**kwargs)
self.network = nx.DiGraph()#声明一个有向图
self.topology_api_app = self#用自己的拓扑
self.paths = {}

 

#handle switch features info
@set_ev_cls(ofp_event.EventOFPSwitchFeatures,CONFIG_DISPATCHER)
def switch_features_handler(self,ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
ofp_parser = datapath.ofproto_parser

#install a table-miss flow entry for each datapath
match = ofp_parser.OFPMatch()
#看这action动作的参数,就知道是下面的send_msg(mod)是提交到控制器的请求了
actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]

self.add_flow(datapath,0,match,actions)

#流表都是在交换机中安装的
def add_flow(self,datapath,priority,match,actions):
ofproto = datapath.ofproto
ofp_parser = datapath.ofproto_parser
#inst,mod都是在已经获取到的match,action的基础下得到的,所以是可以通过参数的方式传递进来
#contruct a flow_mod msg and send it to datapath
inst = [ofp_parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,
actions)]
mod = ofp_parser.OFPFlowMod(datapath=datapath,priority=priority,
match=match,instructions=inst)

#发送信息给控制器
datapath.send_msg(mod)

#get topology and store it into network object.
#两种状态都可能会出现,所以就是[]
@set_ev_cls(event.EventSwitchEnter,[CONFIG_DISPATCHER,MAIN_DISPATCHER])
def get_topology(self,ev):

#get nodes
switch_list = get_switch(self.topology_api_app,None)
switches = [switch.dp.id for switch in switch_list]#datapath是以键值对的形式储存
self.network.add_nodes_from(switches)

#get links
links_list = get_link(self.topology_api_app,None)
links = [(link.src.dpid,link.dst.dpid,{'port':link.src.port_no}) for link in links_list]
self.network.add_edges_from(links)

#get reverse links
links = [(link.dst.dpid,link.src.dpid,{'port':link.dst.port_no}) for link in links_list]
self.network.add_edges_from(links)

#get out_port by using networkx's Dijkstra algorithm.
def get_out_port(self,datapath,src,dst,in_port):
dpid = datapath.id
#add links between host and access switch
if src not in self.network:
self.network.add_node(src)
self.network.add_edge(dpid,src,{'port':in_port})
self.network.add_edge(src,dpid)#主机到交换机的链路一般是没用的,一般是网卡的名字。
self.paths.setdefault(src,{})

if dst in self.network:
if dst not in self.paths[src]:
path = nx.shortest_path(self.network,src,dst)
self.paths[src][dst] = path

path = self.paths[src][dst]#要记得这是个二维数组
#因为我们要找的是out_port,要找的是出口。
next_hop = path[path.index(dpid) + 1]
#['port']是[next_hop]里面存在的一个属性
out_port = self.network[dpid][next_hop]['port']
print("path:",path)
else:
out_port = datapath.ofproto.OFPP_FLOOD
return out_port

#handle packet in msg
@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

#还要对网络数据包进行解析
pkt = packet.Packet(msg.data)
eth = pkt.get_protocol(ethernet.ethernet)
in_port = msg.match["in_port"]

#get out_port
out_port = self.get_out_port(datapath,eth.src,eth.dst,in_port)#也是需要通过入端口找到出端口的
actions = [ofp_parser.OFPActionOutput[out_port]]

#install flow entries
if out_port != ofproto.OFPP_FLOOD:
#因为我们是要找指定端口,匹配参数自然少不了端口匹配。
match = ofp_parser.OFPMatch(in_port=in_port,eth_dst=eth_dst)
#下发流表指导后续操作
self.add_flow(datapath,1,match,actions)

#控制器会下发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 10:00  scnu-yang  阅读(300)  评论(0编辑  收藏  举报