wiki:Internal/OpenFlow/Controllers/MultiCtl/FLInternals

Version 7 (modified by akoshibe, 12 years ago) ( diff )

Floodlight Controller Internals

This is a quick recap/overview of the internal workings of OpenFlowHub's Floodlight OpenFlow controller. There is a good amount of official documentation at openflowhub.org; the contents of this page are side-notes compiled based on some code reading done in 2012.

As a convention, classes and interfaces are shown in terminal type, and methods and variables in italics. Furthermore, "module" and "service" may be used interchangeably unless otherwise specified.

I. System Initialization

Docs: http://www.openflowhub.org/display/floodlightcontroller/Module+loading+system

Floodlight can be broken down into two main components: A module loader and all of the modules that implement Floodlight's services and functions. Initialization is characterized by four main steps that bring these service-providing modules up:

  1. Read in configurations
  2. Load modules
  3. Initialize core controller module/service
  4. Register modules with controller service

1.1 The module loading process

The startup process of Floodlight begins with the reading in of configuration files and loading of necessary functional components (modules).

There are two main configuration files:

  1. net.floodlightcontroller.core.module.IFloodlightModule: the list of all available modules
  2. floodlightdefault.properties: a list of service-providing modules and their configurations

The files are read by the module loading system to find and properly load all of the modules. Module interdependencies are found with a DFS search for interdependencies beginning with the service-providing ones listed in the second file. A module is anything that implements the IFloodlightModule interface, and this interface provides a method, getModuleDependencies(), which facilitates the building of this dependency tree by returning a list of modules that a module depends on. the methods in the module loader that are responsible for building the dependency list are loadModulesFromContig() and loadModulesFromList(). The procedure produces the smallest collection of modules to be loaded.

A list of modules that come with the base controller can be found here.

Once the list is complete each module is activated by calling its init() and startUp() methods. init() typically initializes references to services that a module depends on, and other things that do not count on a module's dependencies being fully active; startUp() takes care of actions dependent on fully active services (e.g. callback registration) after everyone's init() are called. In addition to the ordering produced by the DFS process, the division of the module startup process into two steps guarantees that modules are able to properly register with necessary services without worrying about which ones are already initialized.

1.2 Module initialization and registration

The first module to always be activated (by the virtue of being first in floodlightdefault.properties) is the FloodlightProvider, which provides the key service that implements Floodlight's controller functions (managing connections from switches, keepalives, event dispatch, etc). The actual implementation at the heart of the controller service is the class Controller, implementing the IFloodlightProviderService interface. When people mention IFloodlightProvider, they are referring to a collection of methods that are part of this interface, and when floodlightProvider is referenced it is usually an instantiation of Controller (or some other implementation of IFloodlightProviderService).

The module loader calls each module's startUp() method to register them with Controller, typically either as an IOFMessageListener or an IOFSwitchListener. The identification depends on what kind of event a service is interested in receiving from Controller. Services interested in new messages are the former, and those interested in the joining/leaving of switches are the latter. Services may belong to both categories. In Controller, the two groups are organized into two lists, messageListeners and switchListeners. Each method essentially "adds itself" to either or both of these lists when their startUp() method is called, using the add/remove methods for each type of listener provided by IFloodlightProviderService .

For example, taking a look at the Hub module:

   public void init(FloodlightModuleContext context)                              
           throws FloodlightModuleException {
       floodlightProvider =                                                         
               context.getServiceImpl(IFloodlightProviderService.class);
   }

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

Several things should be mentioned here:

  • FloodlightModuleContext maintains a HashMap called serviceMap that keeps a mapping between a service and its implementing class
  • the mapping between the service and its class is in terms of the interface that the class implements
  • Hub initializes a reference to an implementation of IFloodlightProviderService called floodlightProvider in init()
  • Hub is interested in receiving PacketIns from switches, so it registers itself as a IOFMessageListener in startUp()

startUp() is also where service-specific variables in floodlightdefault.properties will be used to do per-module configurations. A module accesses the contents of the file through FloodlightModuleContext via the method getConfigParams().

modules are loaded and initialized, Floodlight will begin listening for incoming connections from switches and sending out LLDP messages to gather topology information.

Handling switches

OFChannelHandler is the inner class of Controller responsible for listening for incoming connections from switches. When a switch establishes a connection, a new instantiation of OFSwitchImpl is created for the switch. OFSwitchImpl is a representation of an individual switch, comprised of the channel, DPID, and information gleaned from the FEATURE_REPLY, among other bits and pieces. Changes are made to a switch through the manipulation of the OFSwitchImpl representing it.

Another thing that occurs upon a switch establishing contact (or disappearing) is the dispatch of a switch event. SwitchUpdate is the Controller inner class responsible for calling a registered IOFSwitchListener's addedSwitch() or removedSwitch() methods.

The OpenFlow Handshake

The switch event is not fired until the switch completes the OpenFlow handshake. The handshake can be outlined as below:

  1. Exchange of HELLO messages
  2. Controller: send FEATURES_REQUEST
  3. switch: send FEATURES_REPLY
  4. Controller: send GET_CONFIG_REQUEST
  5. Switch: send GET_CONFIG_REPLY

Stage 5 is the point where checkSwitchReady() is called, the switch state is set to READY, and listeners are notified of its arrival. The components that execute the handshake are implemented in Controller as a collection of methods that are called from processOFMessage(). processOFMessage() is also responsible for keeping up with the ECHO_REQUEST/REPLY keepalive messages.

Handling Messages

Once the handshake is complete and interested parties aware of it, messages to/from the switch can be dispatched to registered IOFMessageListeners. The high-level view of the message processing chain is the following:

  1. OFChannelHandler receives the message
  2. Controller dispatches the message to interested services
  3. services process the event accordingly
  4. service sends a message to the switch through its OFSwitchImpl
Note: See TracWiki for help on using the wiki.