How to Write a Module (v0.90, v0.91)

(info)

This tutorial is for Floodlight v0.90 and v0.91 (old master). We recommend you upgrade to Floodlight v1.0 for a better API and to use OpenFlow 1.3+ features. You can find the v1.0 documentation here.

Prerequisites

We are going to create a bundle that will watch for new MAC addresses that have not been seen before, and log the MAC and switch they were seen on.

  • Successfully completed the Getting Started tutorial, including setting up Eclipse
  • Mininet installed and running, or a physical OpenFlow switch

Creating The Listener

Add Class In Eclipse

  1. Expand the "floodlight" item in the Package Explorer and find the "src/main/java" folder.
  2. Right-click on the "src/main/java" folder and choose "New/Class".
  3. Enter "net.floodlightcontroller.mactracker" in the "Package" box.
  4. Enter "MACTracker" in the "Name" box.
  5. Next to the "Interfaces" box, choose "Add...".
  6. Add the "IOFMessageListener" and the "IFloodlightModule", click "OK".
  7. Click "Finish" in the dialog.

You should end up with something like this:

package net.floodlightcontroller.mactracker;

import java.util.Collection;
import java.util.Map;

import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFType;

import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;

public class MACTracker implements IOFMessageListener, IFloodlightModule {

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isCallbackOrderingPrereq(OFType type, String name) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isCallbackOrderingPostreq(OFType type, String name) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void init(FloodlightModuleContext context)
			throws FloodlightModuleException {
		// TODO Auto-generated method stub

	}

	@Override
	public void startUp(FloodlightModuleContext context) {
		// TODO Auto-generated method stub

	}

	@Override
	public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
		// TODO Auto-generated method stub
		return null;
	}

}

Setting Up Module Dependencies And Initialization

Before we get started, we are going to need a number of dependencies for the code to work. A tool like Eclipse should make it easy to add them. However, if you aren't using eclipse, you may just want to add them up front:

import net.floodlightcontroller.core.IFloodlightProviderService;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.Set;
import net.floodlightcontroller.packet.Ethernet;
import org.openflow.util.HexString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Now that we have our skeleton class, we have to implement the correct functions to make the module loadable. Lets start by registering some member variables we'll need into the class. Since we are listening to OpenFlow messages we need to register with the FloodlightProvider (IFloodlightProviderService class). We also need a set to store macAddresses we've seen. Finally, we need a logger to output what we've seen.

protected IFloodlightProviderService floodlightProvider;
protected Set macAddresses;
protected static Logger logger;

Now we need to wire it up to the module loading system. We tell the module loader we depend on it by modifying the getModuleDependencies() function.

@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
    Collection<Class<? extends IFloodlightService>> l =
        new ArrayList<Class<? extends IFloodlightService>>();
    l.add(IFloodlightProviderService.class);
    return l;
}

Now its time to create our Init method. Init is called early in the controller startup process — it primarily is run to load dependencies and initialize datastructures.

@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
    floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
    macAddresses = new ConcurrentSkipListSet<Long>();
    logger = LoggerFactory.getLogger(MACTracker.class);
}

Handling The Packet-In Message

Now it's time to implement the basic listener. We'll register for PACKET_IN messages in our startUp method. Here we are assured other modules we depend on are already initialized.

@Override
public void startUp(FloodlightModuleContext context) {
    floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
}

We also have to put in an ID for our OFMessage listener. This is done in the getName() call.

@Override
public String getName() {
    return MACTracker.class.getSimpleName();
}

Now we have to define the behavior we want for PACKET_IN messages. Note that we return Command.CONTINUE to allow this message to continue to be handled by other PACKET_IN handlers as well.

@Override
   public net.floodlightcontroller.core.IListener.Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
        Ethernet eth =
                IFloodlightProviderService.bcStore.get(cntx,
                                            IFloodlightProviderService.CONTEXT_PI_PAYLOAD);

        Long sourceMACHash = Ethernet.toLong(eth.getSourceMACAddress());
        if (!macAddresses.contains(sourceMACHash)) {
            macAddresses.add(sourceMACHash);
            logger.info("MAC Address: {} seen on switch: {}",
                    HexString.toHexString(sourceMACHash),
                    sw.getId());
        }
        return Command.CONTINUE;
    }

Register The Module

{We're almost done, now we just need to tell Floodlight to load the module on startup. First we have to tell the loader that the module exists. This is done by adding the fully qualified module name on it's own line in src/main/resources/META-INF/services/net.floodlight.core.module.IFloodlightModule. We open that file and append this line.

net.floodlightcontroller.mactracker.MACTracker

Then we tell the module to be loaded. We modify the Floodlight module configuration file to append the MACTracker. The default one is src/main/resources/floodlightdefault.properties. The key is floodlight.modules and the value is a comma separated list of fully qualified module names.

floodlight.modules = <leave the default list of modules in place>, net.floodlightcontroller.mactracker.MACTracker

Finally, let's run the controller by right clicking on Main.java and choose "Run As.../Java Application".

How To Connect Mininet Software OpenFlow Switches To Floodlight

This assumes you are running Mininet inside a VM on your host, and you are running Floodlight from Eclipse on the host.

  • Determine your host's IP address relative to Mininet, in the below example it is set as the Gateway (192.168.110.2)
mininet@mininet:~$ sudo route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.110.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0
0.0.0.0         192.168.110.2   0.0.0.0         UG    0      0        0 eth0

mininet@mininet:~$ sudo mn --mac --controller=remote --ip=192.168.110.2 --port=6633
*** Loading ofdatapath
*** Adding controller
*** Creating network
*** Adding hosts:
h2 h3
*** Adding switches:
s1
*** Adding edges:
(s1, h2) (s1, h3)
*** Configuring hosts
h2 h3
*** Starting controller
*** Starting 1 switches
s1
*** Starting CLI:
mininet>pingall

The Pingall command should generate debug output from your MACTracker on the console.

Have You Gotten This Far?

If you have gotten this far, its time to move over to the Floodlight wiki to learn more. We also have a more advanced tutorial available there as well.