Tuesday, October 15, 2013

and we attack!

Hi guys..so now that you know what our project is about and thus, this blog is about, I think it’s time we start making progress towards our aim of having multiple controllers. You would have gotten some idea as to how to control flows in Mininet from the previous post. Well then, we don't have to worry for writing code to enable multiple-controller in OpenVSwitch's source since we know that it supports a switch talking to multiple controllers. We hopefully won't be leaving Mininet at all during the development.

We intend to create a dictionary of lists in order to store the Switch:Controller associations. The keys of the dictionary will be the switch names as in s1, s2, etc. Each key will map to a list of remote controller ip-addresses and probably their port numbers too for establishing a connection between the switches and their assigned controllers.

So, in python our variable would look like:

mulitpleController = {
                       's1':['127.0.0.1','128.0.0.1','129.0.0.1'],
                       's2':['127.0.0.1','130.0.0.1']
                       's3':['130.0.0.1','129.0.0.1']
                     }
This dictionary will be passed to the function where the controller-switch connection is established.

 So, we thought of how to attack the problem, and we needed to add an option to allow the users to specify the connectivity of switch to controllers via command-line or through a config file. Command-line seemed to be a good way to start as it is convenient to specify. For this, we have added a new command-line argument, --multictrl, which stands for multiple-controllers. Now if someone wants to specify which controller should talk to which switch, the command will look something like this: 

sudo mn --mutictrl=s1:c1,s2:c2,s3:c3


Now lets get into more interesting topic of all the trouble we faced in order to make this happen and how we resolved it :P
  • Getting the input through command line and storing it in a variable called ‘multictrl’ in the file mn was not all that difficult. We added an option in parseArgs()
    [...]
        opts.add_option( '--multictrl', '-m', type='string', default=False,
                                              help='use switches with multiple controllers' )
[...]
          This will enable parsing with multictrl command line argument during execution. Later we can access the string using self.options.multictrl.

  • Once we got that, we had to pass it to the constructor of the class to which ‘Net’ object belonged to (i.e., either Mininet or MininetWithControlNet). We passed it like this in mn file:
[...]
         mn = Net(
                 topo=topo,
                 switch=switch, host=host, controller=controller,
                 link=link,
                 ipBase=ipBase,
                 inNamespace=inNamespace,
                 xterms=xterms, autoSetMacs=mac,
                 autoStaticArp=arp, autoPinCpus=pin,
                 listenPort=listenPort,
                 multictrl=multictrl                 
                  )


        [...]

  

In the net.py file, there is a contructor __init__() inside Mininet class which accepts the passed keyword arguments. So we added another keyword parameter multictrl and gave it a default value of None. And we wrote a method which would parse the given input, make and return a dictionary object which would hold the associations of Controllers and Switches. The returned dictionary would be stored in a data member called self.multiCtrl.

Now when we ran the program, the program caught an exception :

TypeError: __init__() got an unexpected keyword argument 'multictrl'

So the __init__() method would not accept the newly added parameter! In order to solve this, quite obviously, the first thing we did was we Google-d the error and shockingly we did not get any clear result which would tell us what exactly this error was or how to resolve it. So we read quite a bit about keyword arguments, what is it, how is it done, what are the different methods we can pass the keyword arguments in and things like that. Later we tried to pass our ‘multictrl’ from ‘begin()’ method to ‘__init__()’ method by using *args and **kwargs  method i.e.,

[...]
def __init__( self,topo=None, switch=OVSKernelSwitch, host=Host,
                 controller=Controller, link=Link, intf=Intf,
                 build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
                 inNamespace=False,
                 autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
                 listenPort=None, *args, **kwargs):
[...]

In this, the *kwargs argument hold all the unmatched key:value pairs of keyword arguments. So we were hoping that our input would be stored in this at least. But once we ran, we ended up getting the same error!!

Actually, the main cause of all the problems which we were facing was the self.multiCtrl data member. We noticed how other keyword arguments are getting stored while multictrl is failing. It came to our notice that the other keyword arguments and their respective data members to which they were assigned were just the same. Each character’s case matched exactly with that of the data member.


[...]
def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
              controller=Controller, link=Link, intf=Intf,
              build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
              multictrl=False, inNamespace=False,
              autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
              listenPort=None):
self.topo = topo
    self.switoch = switch
    self.host = host
    self.controller = controller
    self.link = link
    self.intf = intf
    self.ipBase = ipBase
    self.ipBaseNum, self.prefixLen = netParse( self.ipBase )
    self.nextIP = 1  # start for address allocation
    self.inNamespace = inNamespace
    self.xterms = xterms
    self.cleanup = cleanup
    self.autoSetMacs = autoSetMacs
    self.autoStaticArp = autoStaticArp
    self.autoPinCpus = autoPinCpus
    self.numCores = numCores()
    self.nextCore = 0  # next core for pinning hosts to CPUs
    self.listenPort = listenPort
    self.multiCtrl = switchControlPair(multictrl) if multictrl else False
           #^the line causing error
[...]

On changing the keyword argument such that it exactly matched with the data member, the program did not encounter any errors and we could neatly run with whatever changes we had made in the code.

The CRUX of our Project

After a brief introduction to SDN and followed by mininet, lets dive into the main objective of our project. In this particular post we would introduce you to the problem statement and the solution we are trying to provide in order to extend the capabilities of a switch in Mininet.

In SDN we came across the concept of controllers and switches, their interaction with each other through the openFlow protocol. In the first network topology we have seen switches connected to each other resulting in flooding and broadcast storm which was solved by a single controller connected to multiple switches as show in our previous post related to Flooding and Broadcast storm.

/*EDITED*/Now if the controller fails, it will go into emergency mode providing emergency flows.So how can we change or modify the network in order to make our openVswitches more intelligent? That is the CRUX of our project.
  1. First approach would be introduction of more controllers such that each controller is connected to multiple switches.
  2. Second would be to modify the switch such that multiple controllers can be connected to a single switch.

Approach 1

As the first approach is already implemented and tested in Mininet 2.1, we would be going with the second approach.


Now at this stage, you will be having a lot of questions about - How will the topology look like? What are the advantages? And how are we implementing this? Are there any drawbacks or extensions?

Lets answer them, since Mininet uses OpenVSwitch, it is necessary to first check if the service of multiple controller is provided by the OpenVSwitch. As per the following link it is clear that OpenVSwitch does have a support for multiple controllers.
Hence we can GO ahead with our second approach.


With respect to the benefits, there are two major advantages compared to the switches talking to a single controller:
  1. Decentralization: As we have multiple controllers talking to a switch, there are multiple flow entries provided by different controllers to the switch. If one of the controller fails we can still have the network working which is not possible in the older case. This solves the problem of single point failure.
  2. Load Balancing: As the packet is sent to multiple controllers, we can expect a fast response from at least one of the controllers whereas in the other case a single controller is bombarded with packets from different switches, hence there is a high chance of delay in the response.

The next section will cover the implementation aspect related to mininet and the process of how we are gonna ATTACK !!


Sunday, October 6, 2013

Identifying the flow in the code

In this post, we are going to present an abstract view of the flow of control in the code of Mininet.

Download the source code of Mininet from their GitHub repository. It is also recommended that you play around with the software before starting off to understand the source code.

Mininet has been quickly going through many updates. Mininet version 2.1 was in use on the date on which this post was written. You might see certain variation in the source at the later stages.

cd into the mininet folder. You will see many directories in it but the 2 most important directories are the bin directory and the mininet directory.

Directory bin contains mn, this is the file from where the execution of the program begin.

Lines like:
[...]

from mininet.clean import cleanup
from mininet.cli import CLI

[...]

These files contains the class definition which exist inside the mininet directory. So be prepared to make jumps from one file to another when we walk through the source.

Then we come across codes like these:

[...]

TOPODEF = 'minimal'
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
          'linear': LinearTopo,
          'reversed': SingleSwitchReversedTopo,
          'single': SingleSwitchTopo,
          'tree': TreeTopo }

[...]

Here, we are actually creating a dictionary of classes of type Topo. When we enter command to emulate a linear topology or a tree topology, we are actually mapping it to their corresponding classes in the later stage. If you wish to add a new type of object in the code, you should add a member to the dictionary. It has been similarly used for other object creation like SWITCHES, HOSTS, etc.

Then we have some function definitions that's called later. It all starts from the constructor call to MininetRunner() from the bottom of the source of mn.

This calls the init() method inside MininetRunner class. They initialize some class members and then call the parseArgs() function. parseArgs() parses all the arguments you passed in the command line and stores them for further use. Commands not passed in the command-line have a default value set in the program. If you wish to add a new feature in Mininet, you might want to make some changes in here to provide end users with the option to choose from the command-line.

begin() function makes use of the input stored by parseArgs() to start the process of emulating the network.

[...]

topo = buildTopo( TOPOS, self.options.topo )
switch = customConstructor( SWITCHES, self.options.switch )
host = customConstructor( HOSTS, self.options.host )
controller = customConstructor( CONTROLLERS, self.options.controller )
link = customConstructor( LINKS, self.options.link )

[...]

These functions are defined in another file. One good implementation technique of Mininet which we really appreciate is that the developers have mentioned these function names in the import files. It's really convenient to hop to that file to see what that function does.

Coming back to the code, buildTopo takes in the TOPOS dictionary of classes defined on top of mn file and the options.topo stores the type of topo the user had specified in the command-line.

The buildTopo function, maps the topology passed by the user into it's corresponding class and returns the mapped class call back to the topo variable in mn. After this, topo represents the object of

Other objects like switch, host, controller and link also create objects in the similar manner

We then assign all the command-line or default initialized to variables and call the class using keyword parameters.

[...]

inNamespace = self.options.innamespace
Net = MininetWithControlNet if inNamespace else Mininet

[...]

If you don't pass --innamespace in the command-line, inNamespace is set to false by default. Net is hence assigned to Mininet class that's imported from mininet/net.py.

[...]

mn = Net( topo=topo,
          switch=switch, host=host, controller=controller,
          link=link,
          ipBase=ipBase,
          inNamespace=inNamespace,
          xterms=xterms, autoSetMacs=mac,
          autoStaticArp=arp, autoPinCpus=pin,
          listenPort=listenPort )

[...]

Hence a constructor call to class Net in mininet/net.py is called and an object of the entire emulated network is returned.

In the next post we will cover about what happens in the constructor call to class Net.

Saturday, October 5, 2013

Pre-requisites for Experimenting with SDN

Quickly before we begin with the implementation, following is the check-list one ought to have to experiment with SDN.

✔ Openflow Controller (any one):
✔ Openflow Protocol installation can be found here

✔ We would suggest installing Mininet from the source that can be taken from their GitHub page. Check the instructions given in INSTALL file of the source.

✔ Wireshark to see the captures. Wireshark doesn't come with a filter for Openflow packets, you can download the Wireshark dissector by following the instructions for your machine.

We are using Ubuntu 12.04 as our platform to develop. Feel free to use any linux flavour for playing around.