Round-Robin Ryu controller for mininet

107 views Asked by At

Fat tree topology

I have successfully managed to generate the topology in mininet but I am struggling to create a controller that uses Round-Robin load balancing over all available links to send packets.

This is my topology file:

from mininet.topo import Topo
from mininet.net import Mininet
from mininet.log import setLogLevel
from mininet.cli import CLI
from mininet.node import RemoteController

class MyTopo(Topo):
    def __init__(self):
        super(MyTopo, self).__init__()

        switches = []
        for i in range(1, 20 + 1):
            switch = self.addSwitch('s{}'.format(i))
            switches.append(switch)

        hosts = []
        for i in range(1, 16 + 1):
            host = self.addHost('h{}'.format(i))
            hosts.append(host)

        # Connect the first 8 switches to the first 16 hosts
        for i, switch in enumerate(switches[:8]):
            self.addLink(switch, hosts[i * 2])
            self.addLink(switch, hosts[i * 2 + 1])

        # Connect switches 9-16 to switches 1-8
        for i in range(4):
            for j in range(2):
                self.addLink(switches[i * 2 + j], switches[i * 2 + 8])
                self.addLink(switches[i * 2 + j], switches[i * 2 + 9])

        # Connect switches 17-20 to switches 9-16
        for i in range(2):
            for j in range(4):
                self.addLink(switches[16 + i], switches[2 * j + 8])
            for j in range(4):
                self.addLink(switches[18 + i], switches[2 * j + 9])

def main():
    setLogLevel('info')

    topo = MyTopo()
    net = Mininet(topo=topo, controller=RemoteController) 

    net.start()

    CLI(net)

    net.stop()

if __name__ == '__main__':
    main()

Tried editing the pre-existing SimpleSwitch13, but it is probably an abomination:

from ryu.base import app_manager
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.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ether_types

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

    def __init__(self, *args, **kwargs):
        super(SimpleSwitch13RR, self).__init__(*args, **kwargs)
        self.mac_to_port = {}
        self.port_round_robin = {}

    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def switch_features_handler(self, ev):
        datapath = ev.msg.datapath
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        # install table-miss flow entry
        match = parser.OFPMatch()
        actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)]
        self.add_flow(datapath, 0, match, actions)

    def add_flow(self, datapath, priority, match, actions, buffer_id=None):
        ofproto = datapath.ofproto
        parser = datapath.ofproto_parser

        inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]
        if buffer_id:
            mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, priority=priority, match=match, instructions=inst)
        else:
            mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst)
        datapath.send_msg(mod)

    @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
    def _packet_in_handler(self, ev):
        if ev.msg.msg_len < ev.msg.total_len:
            self.logger.debug("packet truncated: only %s of %s bytes", ev.msg.msg_len, ev.msg.total_len)
        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_protocols(ethernet.ethernet)[0]

        if eth.ethertype == ether_types.ETH_TYPE_LLDP or eth.ethertype == ether_types.ETH_TYPE_IPV6:
            return
        dst = eth.dst
        src = eth.src

        dpid = datapath.id
        self.mac_to_port.setdefault(dpid, {})

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

        # Round Robin logic
        available_ports = [port.port_no for port in datapath.ports.values() 
                           if port.port_no not in [ofproto.OFPP_LOCAL, ofproto.OFPP_CONTROLLER]]

        if (src, dst) not in self.port_round_robin:
            self.port_round_robin[(src, dst)] = iter(available_ports)

        try:
            out_port = next(self.port_round_robin[(src, dst)])
        except StopIteration:
            self.port_round_robin[(src, dst)] = iter(available_ports)
            out_port = next(self.port_round_robin[(src, dst)])

        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_dst=dst, eth_src=src)
            if msg.buffer_id != ofproto.OFP_NO_BUFFER:
                self.add_flow(datapath, 1, match, actions, msg.buffer_id)
                return
            else:
                self.add_flow(datapath, 1, match, actions)

        data = None
        if msg.buffer_id == ofproto.OFP_NO_BUFFER:
            data = msg.data

        out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
                                  in_port=in_port, actions=actions, data=data)
        datapath.send_msg(out)
0

There are 0 answers