SDN experiement using Estinet RT188T switches
[network topology]


In this topology, we have three SDN switches, one normal switch, two hosts and one controller. Their settings are shown in the figure. We try to implement the dijkstra algorithm to find a shortest path from H1 to H2.
[ryu controller: dijkstra_ryu.py] Goal: find a shortest path from source to destination using dijkstra algorithm
|
# 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. from ryu.base import app_manager from ryu.controller import mac_to_port from ryu.controller import ofp_event from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER from ryu.controller.handler import set_ev_cls from ryu.ofproto import ofproto_v1_3 from ryu.lib.mac import haddr_to_bin from ryu.lib.packet import packet from ryu.lib.packet import ethernet from ryu.lib.packet import ether_types from ryu.lib import mac from ryu.topology import event, switches from ryu.topology.api import get_switch, get_link from ryu.app.wsgi import ControllerBase from collections import defaultdict #switches myswitches = [] #mymac[srcmac]->(switch, port) mymac={} #adjacency map [sw1][sw2]->port from sw1 to sw2 adjacency=defaultdict(lambda:defaultdict(lambda:None)) datapath_list={} def minimum_distance(distance, Q): #print "minimum_distance() is called", " distance=", distance, " Q=", Q min = float('Inf') node = 0 for v in Q: if distance[v] < min: min = distance[v] node = v return node def get_path (src,dst,first_port,final_port): #Dijkstra's algorithm global myswitches, adjacency print "get_path is called, src=",src," dst=",dst, " first_port=", first_port, " final_port=", final_port distance = {} previous = {} for dpid in myswitches: distance[dpid] = 9999 previous[dpid] = None distance[src]=0 Q=set(myswitches) #print "Q=", Q while len(Q)>0: u = minimum_distance(distance, Q) #print "u=", u Q.remove(u) #print "After removing ", u, " Q=", Q for p in myswitches: if adjacency[u][p]!=None: #print u, "--------", p w = 1 if distance[u] + w < distance[p]: distance[p] = distance[u] + w previous[p] = u #print "distance=", distance, " previous=", previous r=[] p=dst r.append(p) q=previous[p] while q is not None: if q == src: r.append(q) break p=q r.append(p) q=previous[p] r.reverse() if src==dst: path=[src] else: path=r # Now add the ports r = [] in_port = first_port for s1,s2 in zip(path[:-1],path[1:]): out_port = adjacency[s1][s2] r.append((s1,in_port,out_port)) in_port = adjacency[s2][s1] r.append((dst,in_port,final_port)) return r class ProjectController(app_manager.RyuApp): OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] def __init__(self, *args, **kwargs): super(ProjectController, self).__init__(*args, **kwargs) self.mac_to_port = {} self.topology_api_app = self # Handy function that lists all attributes in the given object def ls(self,obj): print("\n".join([x for x in dir(obj) if x[0] != "_"])) def add_flow(self, datapath, in_port, dst, actions): ofproto = datapath.ofproto parser = datapath.ofproto_parser match = datapath.ofproto_parser.OFPMatch( in_port=in_port, eth_dst=dst) inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = datapath.ofproto_parser.OFPFlowMod( datapath=datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, priority=ofproto.OFP_DEFAULT_PRIORITY, instructions=inst) datapath.send_msg(mod) def install_path(self, p, ev, src_mac, dst_mac): print "install_path is called" #print "p=", p, " src_mac=", src_mac, " dst_mac=", dst_mac msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser for sw, in_port, out_port in p: print src_mac,"->", dst_mac, "via ", sw, " in_port=", in_port, " out_port=", out_port match=parser.OFPMatch(in_port=in_port, eth_src=src_mac, eth_dst=dst_mac) actions=[parser.OFPActionOutput(out_port)] datapath=datapath_list[sw] inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS , actions)] mod = datapath.ofproto_parser.OFPFlowMod( datapath=datapath, match=match, idle_timeout=0, hard_timeout=0, priority=1, instructions=inst) datapath.send_msg(mod) @set_ev_cls(ofp_event.EventOFPSwitchFeatures , CONFIG_DISPATCHER) def switch_features_handler(self , ev): print "switch_features_handler is called" datapath = ev.msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser match = parser.OFPMatch() actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS , actions)] mod = datapath.ofproto_parser.OFPFlowMod( datapath=datapath, match=match, cookie=0, command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, priority=0, instructions=inst) datapath.send_msg(mod) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def _packet_in_handler(self, ev): #print "packet_in event:", ev.msg.datapath.id, " in_port:", ev.msg.match['in_port'] msg = ev.msg datapath = msg.datapath ofproto = datapath.ofproto parser = datapath.ofproto_parser in_port = msg.match['in_port'] pkt = packet.Packet(msg.data) eth = pkt.get_protocol(ethernet.ethernet) #print "eth.ethertype=", eth.ethertype #avodi broadcast from LLDP if eth.ethertype==35020: return dst = eth.dst src = eth.src dpid = datapath.id #print "src=", src, " dst=", dst, " type=", hex(eth.ethertype) #print "adjacency=", adjacency self.mac_to_port.setdefault(dpid, {}) if src not in mymac.keys(): mymac[src]=( dpid, in_port) #print "mymac=", mymac if dst in mymac.keys(): p = get_path(mymac[src][0], mymac[dst][0], mymac[src][1], mymac[dst][1]) print "Path=", p self.install_path(p, ev, src, dst) out_port = p[0][2] else: out_port = ofproto.OFPP_FLOOD actions = [parser.OFPActionOutput(out_port)] # install a flow to avoid packet_in next time if out_port != ofproto.OFPP_FLOOD: match = parser.OFPMatch(in_port=in_port, eth_src=src, eth_dst=dst) data=None if msg.buffer_id==ofproto.OFP_NO_BUFFER: data=msg.data if out_port == ofproto.OFPP_FLOOD: #print "FLOOD" while len(actions) > 0 : actions.pop() for i in range(1,23): actions.append(parser.OFPActionOutput(i)) #print "actions=", actions out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=data) datapath.send_msg(out) else: #print "unicast" out = parser.OFPPacketOut( datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=data) datapath.send_msg(out)
events = [event.EventSwitchEnter, event.EventSwitchLeave, event.EventPortAdd, event.EventPortDelete, event.EventPortModify, event.EventLinkAdd, event.EventLinkDelete] @set_ev_cls(events) def get_topology_data(self, ev): print "get_topology_data() is called" global myswitches, adjacency, datapath_list switch_list = get_switch(self.topology_api_app, None) myswitches=[switch.dp.id for switch in switch_list] for switch in switch_list: datapath_list[switch.dp.id]=switch.dp print "datapath_list=", datapath_list print "myswitches=", myswitches links_list = get_link(self.topology_api_app, None) #print "links_list=", links_list mylinks=[(link.src.dpid,link.dst.dpid,link.src.port_no,link.dst.port_no) for link in links_list] for s1,s2,port1,port2 in mylinks: #print "type(s1)=", type(s1), " type(port1)=", type(port1) adjacency[s1][s2]=port1 adjacency[s2][s1]=port2 print s1,":", port1, "<--->",s2,":",port2 |
[Settings]
First, connect to the sw1 and set its IP to 192.168.1.1. And make sure that the Switch Mode is Out-Band. Keep the rest parameters as default settings. Similarly, settings are applied to sw2 and sw3.


[Running the experiment]
In the controller, use the following command to start the experiment.

Before we test the communications between H1 and H2. We can see that three switches are connected to the controller, i.e. 35776618378, 3577668001, and 35776617856 (from myswitches).
We can also see the links between switches. (Here we can know the network topology).

We start to test the communication between H1 and H2.

In the controller, we can see when a packet wants to from H1 to H2. The path is from the input port 1 of 3577668001, and then go out from port 12. After that, the packet will be sent to input port 12 of 35776618378 and output port 1. Finally, the packet will be arrived at H2.

EstiNet Switch: http://www.estinet.com/es/?page_id=20731
Dr. Chih-Heng Ke (smallko@gmail.com)
Department of Computer
Science and Information Engineering,
National Quemoy University, Kinmen, Taiwan.