Multipath Transmission using RYU

 

[Topology]

 

when h1 sends the UDP traffic to h2 with destination port =5555, switch 1 will forward 70% traffic to switch2 and 30% traffic to switch 3. Then all traffic will forward to host2.

 

[script—topology]

from mininet.topo import Topo

 

 

class MyTopo(Topo):

    "Simple loop topology example."

 

    def __init__(self):

        "Create custom loop topo."

 

        # Initialize topology

        Topo.__init__(self)

 

        # Add hosts and switches

        host1 = self.addHost('h1')

        host2 = self.addHost('h2')

        host3 = self.addHost('h3')

        #host4 = self.addHost('h4')

        switch1 = self.addSwitch("s1")

        switch2 = self.addSwitch("s2")

        switch3 = self.addSwitch("s3")

        switch4 = self.addSwitch("s4")

 

        # Add links

        self.addLink(switch1, host1, 1)

        self.addLink(switch1, switch2, 2, 1)

        self.addLink(switch1, switch3, 3, 1)

        self.addLink(switch2, switch4, 2, 1)

        self.addLink(switch3, switch4, 2, 2)

        self.addLink(switch4, host2, 3)

        self.addLink(switch4, host3, 4)

        #self.addLink(switch3, host4, 3)

 

 

topos = {'mytopo': (lambda: MyTopo())}

 

[script- ryu controller]

# 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.ofproto import ofproto_v1_3_parser

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.api import get_switch, get_link

from ryu.app.wsgi import ControllerBase

from ryu.topology import event, switches

import networkx as nx

 

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

        self.net=nx.DiGraph()

        self.nodes = {}

        self.links = {}

        self.no_of_nodes = 0

        self.no_of_links = 0

        self.i=0

  

    # 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=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)

  

        ###add rule for multipath transmission in s1

        if ev.msg.datapath.id == 1:

          #in_port=1,src=10.0.0.1,dst=10.0.0.2,udp,udp_dst=5555--->group id 50

          ofproto = datapath.ofproto

          parser = datapath.ofproto_parser

    

          #group 50--> port3:30%, port2:70%  

          port_1 = 3

          queue_1 = parser.OFPActionSetQueue(0)

          actions_1 = [queue_1, parser.OFPActionOutput(port_1)]

 

          port_2 = 2

          queue_2 = parser.OFPActionSetQueue(0)

          actions_2 = [queue_2, parser.OFPActionOutput(port_2)]

 

          weight_1 = 30

          weight_2 = 70

 

          watch_port = ofproto_v1_3.OFPP_ANY

          watch_group = ofproto_v1_3.OFPQ_ALL

 

          buckets = [

            parser.OFPBucket(weight_1, watch_port, watch_group, actions_1),

            parser.OFPBucket(weight_2, watch_port, watch_group, actions_2)]

 

          group_id = 50

          req = parser.OFPGroupMod(

            datapath, datapath.ofproto.OFPFC_ADD,

            datapath.ofproto.OFPGT_SELECT, group_id, buckets)

          datapath.send_msg(req)

 

          match = parser.OFPMatch(in_port=1, eth_type=0x0800, ipv4_src="10.0.0.1", ipv4_dst="10.0.0.2", ip_proto=17, udp_dst=5555)

          actions = [datapath.ofproto_parser.OFPActionGroup(50)]

          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=3, instructions=inst)

          datapath.send_msg(mod)

   

        ###add rule for multipath transmission in s2     

        if ev.msg.datapath.id == 2:

          #in_port=1,src=10.0.0.1,dst=10.0.0.2--->output port:2

          ofproto = datapath.ofproto

          parser = datapath.ofproto_parser

          match = parser.OFPMatch(in_port=1, eth_type=0x0800, ipv4_src="10.0.0.1", ipv4_dst="10.0.0.2", ip_proto=17, udp_dst=5555)

          actions = [parser.OFPActionOutput(2)]

          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=3, instructions=inst)

          datapath.send_msg(mod)

       

        ###add rule for multipath transmission in s3

        if ev.msg.datapath.id == 3:

          #in_port=1,src=10.0.0.1,dst=10.0.0.2--->output port:2

          ofproto = datapath.ofproto

          parser = datapath.ofproto_parser

          match = parser.OFPMatch(in_port=1, eth_type=0x0800, ipv4_src="10.0.0.1", ipv4_dst="10.0.0.2", ip_proto=17, udp_dst=5555)

          actions = [parser.OFPActionOutput(2)]

          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=3, instructions=inst)

          datapath.send_msg(mod)

       

        ###add rule for multipath transmission in s4

        if ev.msg.datapath.id == 4:

          #in_port=1,src=10.0.0.1,dst=10.0.0.2--->output port:3

          ofproto = datapath.ofproto

          parser = datapath.ofproto_parser

          match = parser.OFPMatch(in_port=1, eth_type=0x0800, ipv4_src="10.0.0.1", ipv4_dst="10.0.0.2", ip_proto=17, udp_dst=5555)

          actions = [parser.OFPActionOutput(3)]

          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=3, instructions=inst)

          datapath.send_msg(mod)

         

          #in_port=2,src=10.0.0.1,dst=10.0.0.2--->output port:3

          ofproto = datapath.ofproto

          parser = datapath.ofproto_parser

          match = parser.OFPMatch(in_port=2, eth_type=0x0800, ipv4_src="10.0.0.1", ipv4_dst="10.0.0.2", ip_proto=17, udp_dst=5555)

          actions = [parser.OFPActionOutput(3)]

          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=3, instructions=inst)

          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

        parser = datapath.ofproto_parser

        in_port = msg.match['in_port']

 

        pkt = packet.Packet(msg.data)

        eth = pkt.get_protocol(ethernet.ethernet)

 

        dst = eth.dst

        src = eth.src

        dpid = datapath.id

        self.mac_to_port.setdefault(dpid, {})

        #print "nodes"

        #print self.net.nodes()

        #print "edges"

        #print self.net.edges()

        #self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

       

        if src not in self.net:

            self.net.add_node(src)

            self.net.add_edge(dpid,src,{'port':in_port})

            self.net.add_edge(src,dpid)

        if dst in self.net:

            #print (src in self.net)

            #print nx.shortest_path(self.net,1,4)

            #print nx.shortest_path(self.net,4,1)

            #print nx.shortest_path(self.net,src,4)

 

            path=nx.shortest_path(self.net,src,dst)  

            next=path[path.index(dpid)+1]

            out_port=self.net[dpid][next]['port']

        else:

            out_port = ofproto.OFPP_FLOOD

 

        actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]

        # install a flow to avoid packet_in next time

        if out_port != ofproto.OFPP_FLOOD:

            self.add_flow(datapath, in_port, dst, actions)

 

        out = datapath.ofproto_parser.OFPPacketOut(

            datapath=datapath, buffer_id=msg.buffer_id, in_port=in_port,

            actions=actions)

        datapath.send_msg(out)

   

    @set_ev_cls(event.EventSwitchEnter)

    def get_topology_data(self, ev):

        switch_list = get_switch(self.topology_api_app, None)  

        switches=[switch.dp.id for switch in switch_list]

        self.net.add_nodes_from(switches)

        

        print "**********List of switches"

        for switch in switch_list:

          #self.ls(switch)

          print switch

          #self.nodes[self.no_of_nodes] = switch

          #self.no_of_nodes += 1

       

        links_list = get_link(self.topology_api_app, None)

        #print links_list

        links=[(link.src.dpid,link.dst.dpid,{'port':link.src.port_no}) for link in links_list]

        #print links

        self.net.add_edges_from(links)

        links=[(link.dst.dpid,link.src.dpid,{'port':link.dst.port_no}) for link in links_list]

        #print links

        self.net.add_edges_from(links)

        print "**********List of links"

        print self.net.edges()

 

[Execution]

1.      Open a terminal to run the ryu controller

 

2.      Open another to generate mininet topology.

 

3.      Check the rules in switch1.

 

4.      Check the rules in switch 2, switch 3, and switch 4.

 

5.      Using the iperf to transmit udp traffic from h1 to h2.

 

6.      Check the results in switch 1 (check the tx_pkts in port 2 and por 3. The results are close to 70:30)

 

[Reference]

1.      http://www.muzixing.com/pages/2014/11/07/load-balancemultipath-application-on-ryu.html

2.      https://github.com/muzixing/ryu/blob/master/ryu/app/multipath.py

 

  

Dr. Chih-Heng Ke

Department of Computer Science and Information Engineering, National Quemoy University, Kinmen, Taiwan

Email: smallko@gmail.com