How to Write a Module (v0.90, v0.91)
|
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
- Expand the "floodlight" item in the Package Explorer and find the "src/main/java" folder.
- Right-click on the "src/main/java" folder and choose "New/Class".
- Enter "net.floodlightcontroller.mactracker" in the "Package" box.
- Enter "MACTracker" in the "Name" box.
- Next to the "Interfaces" box, choose "Add...".
- Add the "IOFMessageListener" and the "IFloodlightModule", click "OK".
- 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.