Adding a new Switch class to Mininet.

This is a quick post about making Mininet support new network objects, like switches.

Depending on the amount of features that a software switch (or other program) supports, and the degree to which you want to access those features through Mininet, adding a custom node type to Mininet can be a relatively simple task. For something as simple as a classic learning switch, this may be a matter of of implementing just a handful of methods within a child class of Switch.

An example: the LinuxBridge class

A good example of such a custom node is the LinuxBridge, prepackaged with Mininet and found in nodelib.py. This node, which implements a non-OpenFlow learning switch using the classic Linux bridge(8) device, just implements five methods, ignoring its constructor:

  • connected( self ):
    Returns True if the switch is connected to a controller or False if not. For a non-SDN switch, a connected state is when it is fully started up and therefore, ready for interaction. For the LinuxBridge, this is when brctl(8) shows that it is in the “forwarding” state.

  • start( self, _controllers ):
    Initializes and configures the software switch underlying the Mininet Switch object. For our example, this method invokes a series of brctl commands to initialize the switch, add ports, and to apply the necessary configurations (e.g. enable STP) to get it to the connected state. An OpenFlow switch such as OVS would also be configured to connect with a set of controllers passed into this method.

  • stop( self, deleteIntfs=True ):
    Carries out the teardown and removal of the underlying software switch backing the Mininet object. This method is the complement to start.

  • dpctl( self, *args ):
    Invokes the configuration utility for the software switch, parameterized to point to the particular instance associated with the Mininet object. In our case, this is, again, brctl.

  • setup( cls ):
    Checks that all prerequisites are met in order to start up and configure the software switch. This may entail checking that certain kernel modules are loaded, packages like bridge-utils are installed, and certain features, such as the firewall, are configured to allow the switch to function.

A bit of detail to keep note of is that the Switch class does its own housekeeping in its __init__ and stop methods, so these should be called from your custom class as well:

class LinuxBridge( Switch ):

    def __init__( self, name, stp=False, prio=None, **kwargs ):
        # snipped out initialization things here
        Switch.__init__( self, name, **kwargs )

    # snip...

    def stop( self, deleteIntfs=True ):
        # snipped out cleanup things here
        super( LinuxBridge, self ).stop( deleteIntfs )

Specifically, __init__ would be tied to initializing any major lightweight virtualization functions and facilities for sending commands to the process(es) underlying the switch object, and stop, the teardown and cleanup of the interfaces on the switch.

This meant that, by implementing the above methods in a IfBridge class, I was able to quickly put together a learning switch using if_bridge(4) and ifconfig(8) for my experimental Mininet port – barring one easily circumvented but odd bit of behavior. As a convention, custom nodes usually end up in nodelib.py, so that is where I ended up adding my IfBridge node.

Integrating a custom element into `mn`

mn allows you to launch a Mininet topology from the shell, and is one of the first things that the Mininet quickstart has you run to sanity check an install. It’s also handy for launching a topology to test with, without having to write a Python script. Among the options that mn comes with is the class of various network elements (switches, links, etc) to use when building the topology:

$ mn --help
Usage: mn [options]
(type mn -h for details)

The mn utility creates Mininet network from the command line. It can create
parametrized topologies, invoke the Mininet CLI, and run tests.

Options:
  -h, --help            show this help message and exit
  --switch=SWITCH       default|ivs|lxbr|ovs|ovsbr|ovsk|user[,param=value...]
                        ovs=OVSSwitch default=OVSSwitch ovsk=OVSSwitch
                        lxbr=LinuxBridge user=UserSwitch ivs=IVSSwitch
                        ovsbr=OVSBridge
  --host=HOST           cfs|proc|rt[,param=value...]
                        rt=CPULimitedHost{'sched': 'rt'} proc=Host
                        cfs=CPULimitedHost{'sched': 'cfs'}
...

Adding your custom Switch to this list is a matter of a few changes in mn. mn keeps a mapping between the various Node and Link classes and their aliases (e.g. default for OVS, lxbr for LinuxBridge …). So in my case, I have my IfBridge custom switch, which I aliased to ‘ifbr’ in the SWITCHES map:

SWITCHES = { 'user': UserSwitch,
             'ovs': OVSSwitch,
             ...
             'ifbr': IfBridge }

At this point, your custom class should be displayed alongside the other choices in the help message:

  --switch=SWITCH       default|ifbr|ivs|lxbr|ovs|ovsbr|ovsk|user[,param=value
                        ...] ovs=OVSSwitch lxbr=LinuxBridge user=UserSwitch
                        ivs=IVSSwitch default=OVSSwitch ovsk=OVSSwitch
                        ovsbr=OVSBridge ifbr=IfBridge

The rest of the script more or less sanity-checks the combination of classes that had been chosen during invocation, before launching a Mininet object. The sanity checks done for each network element type depends on their features. For example, if --switch=default is specified, mn checks for an OpenFlow controller, since the OVS switch would need a controller to connect to. If a non-functional dummy controller (--controller=none) had been used in the invocation, it will fall back to using bridge-mode OVS (ovsbr) even if ‘default’ was chosen for the switch.

In the case of my custom ‘ifbr’ switch, since it is a good-old-fashioned learning switch, the only extra work I needed to do was to add it to the list of non-OpenFlow switch types that are available for use with the ‘none’ controller:

    def begin( self ):
        "Create and run mininet."
        # snip...
        if not self.options.controller:
            if not CONTROLLERS[ 'default' ]:
            # snip...
                elif self.options.switch not in ( 'ovsbr', 'lxbr', 'ifbr' ):
                    raise Exception( "Could not find a default controller "
                                     "for switch %s" %
                                     self.options.switch )

At this point, I can run mn with my custom switch as so:

sudo ./mn --switch=ifbr --controller=none --test=pingall
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s