OpenFlow Protocol Library Developer Guide

Introduction

OpenFlow Protocol Library is component in OpenDaylight, that mediates communication between OpenDaylight controller and hardware devices supporting OpenFlow protocol. Primary goal is to provide user (or upper layers of OpenDaylight) communication channel, that can be used for managing network hardware devices.

Features Overview

There are three features inside openflowjava:

  • odl-openflowjava-protocol provides all openflowjava bundles, that are needed for communication with openflow devices. It ensures message translation and handles network connections. It also provides openflow protocol specific model.

  • odl-openflowjava-all currently contains only odl-openflowjava-protocol feature.

  • odl-openflowjava-stats provides mechanism for message counting and reporting. Can be used for performance analysis.

odl-openflowjava-protocol Architecture

Basic bundles contained in this feature are openflow-protocol-api, openflow-protocol-impl, openflow-protocol-spi and util.

  • openflow-protocol-api - contains openflow model, constants and keys used for (de)serializer registration.

  • openflow-protocol-impl - contains message factories, that translate binary messages into DataObjects and vice versa. Bundle also contains network connection handlers - servers, netty pipeline handlers, …

  • openflow-protocol-spi - entry point for openflowjava configuration, startup and close. Basically starts implementation.

  • util - utility classes for binary-Java conversions and to ease experimenter key creation

odl-openflowjava-stats Feature

Runs over odl-openflowjava-protocol. It counts various message types / events and reports counts in specified time periods. Statistics collection can be configured in openflowjava-config/src/main/resources/45-openflowjava-stats.xml

Key APIs and Interfaces

Basic API / SPI classes are ConnectionAdapter (Rpc/notifications) and SwitchConnectionProcider (configure, start, shutdown)

Installation

Pull the code and import project into your IDE.

git clone ssh://<username>@git.opendaylight.org:29418/openflowjava.git

Configuration

Current implementation allows to configure:

  • listening port (mandatory)

  • transfer protocol (mandatory)

  • switch idle timeout (mandatory)

  • TLS configuration (optional)

  • thread count (optional)

You can find exemplary Openflow Protocol Library instance configuration below:

<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
    <!-- default OF-switch-connection-provider (port 6633) -->
    <module>
      <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
      <name>openflow-switch-connection-provider-default-impl</name>
      <port>6633</port>
<!--  Possible transport-protocol options: TCP, TLS, UDP -->
      <transport-protocol>TCP</transport-protocol>
      <switch-idle-timeout>15000</switch-idle-timeout>
<!--       Exemplary TLS configuration:
            - uncomment the <tls> tag
            - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
              files into your virtual machine
            - set VM encryption options to use copied keys
            - start communication
           Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
           for detailed information regarding TLS -->
<!--       <tls>
             <keystore>/exemplary-ctlKeystore</keystore>
             <keystore-type>JKS</keystore-type>
             <keystore-path-type>CLASSPATH</keystore-path-type>
             <keystore-password>opendaylight</keystore-password>
             <truststore>/exemplary-ctlTrustStore</truststore>
             <truststore-type>JKS</truststore-type>
             <truststore-path-type>CLASSPATH</truststore-path-type>
             <truststore-password>opendaylight</truststore-password>
             <certificate-password>opendaylight</certificate-password>
           </tls> -->
<!--       Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
<!--       <threads>
             <boss-threads>2</boss-threads>
             <worker-threads>8</worker-threads>
           </threads> -->
    </module>
    <!-- default OF-switch-connection-provider (port 6653) -->
    <module>
      <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
      <name>openflow-switch-connection-provider-legacy-impl</name>
      <port>6653</port>
<!--  Possible transport-protocol options: TCP, TLS, UDP -->
      <transport-protocol>TCP</transport-protocol>
      <switch-idle-timeout>15000</switch-idle-timeout>
<!--       Exemplary TLS configuration:
            - uncomment the <tls> tag
            - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
              files into your virtual machine
            - set VM encryption options to use copied keys
            - start communication
           Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
           for detailed information regarding TLS -->
<!--       <tls>
             <keystore>/exemplary-ctlKeystore</keystore>
             <keystore-type>JKS</keystore-type>
             <keystore-path-type>CLASSPATH</keystore-path-type>
             <keystore-password>opendaylight</keystore-password>
             <truststore>/exemplary-ctlTrustStore</truststore>
             <truststore-type>JKS</truststore-type>
             <truststore-path-type>CLASSPATH</truststore-path-type>
             <truststore-password>opendaylight</truststore-password>
             <certificate-password>opendaylight</certificate-password>
           </tls> -->
<!--       Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
<!--       <threads>
             <boss-threads>2</boss-threads>
             <worker-threads>8</worker-threads>
           </threads> -->
    </module>
  <module>
    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:common:config:impl">prefix:openflow-provider-impl</type>
    <name>openflow-provider-impl</name>
    <openflow-switch-connection-provider>
      <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
      <name>openflow-switch-connection-provider-default</name>
    </openflow-switch-connection-provider>
    <openflow-switch-connection-provider>
      <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
      <name>openflow-switch-connection-provider-legacy</name>
    </openflow-switch-connection-provider>
    <binding-aware-broker>
      <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
      <name>binding-osgi-broker</name>
    </binding-aware-broker>
  </module>
</modules>

Possible transport-protocol options:

  • TCP

  • TLS

  • UDP

Switch-idle timeout specifies time needed to detect idle state of switch. When no message is received from switch within this time, upper layers are notified on switch idleness. To be able to use this exemplary TLS configuration:

  • uncomment the <tls> tag

  • copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem files into your virtual machine

  • set VM encryption options to use copied keys (please visit TLS support wiki page for detailed information regarding TLS)

  • start communication

Thread model configuration specifies how many threads are desired to perform Netty’s I/O operations.

  • boss-threads specifies the number of threads that register incoming connections

  • worker-threads specifies the number of threads performing read / write (+ serialization / deserialization) operations.

Architecture

Public API (openflow-protocol-api)

Set of interfaces and builders for immutable data transfer objects representing Openflow Protocol structures.

Transfer objects and service APIs are infered from several YANG models using code generator to reduce verbosity of definition and repeatibility of code.

The following YANG modules are defined:

  • openflow-types - defines common Openflow specific types

  • openflow-instruction - defines base Openflow instructions

  • openflow-action - defines base Openflow actions

  • openflow-augments - defines object augmentations

  • openflow-extensible-match - defines Openflow OXM match

  • openflow-protocol - defines Openflow Protocol messages

  • system-notifications - defines system notification objects

  • openflow-configuration - defines structures used in ConfigSubsystem

This modules also reuse types from following YANG modules:

  • ietf-inet-types - IP adresses, IP prefixes, IP-protocol related types

  • ietf-yang-types - Mac Address, etc.

The use of predefined types is to make APIs contracts more safe, better readable and documented (e.g using MacAddress instead of byte array…)

TCP Channel pipeline (openflow-protocol-impl)

Creates channel processing pipeline based on configuration and support.

TCP Channel pipeline.

imageopenflowjava/500px-TCPChannelPipeline.png[width=500]

Switch Connection Provider.

Implementation of connection point for other projects. Library exposes its functionality through this class. Library can be configured, started and shutdowned here. There are also methods for custom (de)serializer registration.

Tcp Connection Initializer.

In order to initialize TCP connection to a device (switch), OF Plugin calls method initiateConnection() in SwitchConnectionProvider. This method in turn initializes (Bootstrap) server side channel towards the device.

TCP Handler.

Represents single server that is handling incoming connections over TCP / TLS protocol. TCP Handler creates a single instance of TCP Channel Initializer that will initialize channels. After that it binds to configured InetAddress and port. When a new device connects, TCP Handler registers its channel and passes control to TCP Channel Initializer.

TCP Channel Initializer.

This class is used for channel initialization / rejection and passing arguments. After a new channel has been registered it calls Switch Connection Handler’s (OF Plugin) accept method to decide if the library should keep the newly registered channel or if the channel should be closed. If the channel has been accepted, TCP Channel Initializer creates the whole pipeline with needed handlers and also with ConnectionAdapter instance. After the channel pipeline is ready, Switch Connection Handler is notified with onConnectionReady notification. OpenFlow Plugin can now start sending messages downstream.

Idle Handler.

If there are no messages received for more than time specified, this handler triggers idle state notification. The switch idle timeout is received as a parameter from ConnectionConfiguration settings. Idle State Handler is inactive while there are messages received within the switch idle timeout. If there are no messages received for more than timeout specified, handler creates SwitchIdleEvent message and sends it upstream.

TLS Handler.

It encrypts and decrypts messages over TLS protocol. Engaging TLS Handler into pipeline is matter of configuration (<tls> tag). TLS communication is either unsupported or required. TLS Handler is represented as a Netty’s SslHandler.

OF Frame Decoder.

Parses input stream into correct length message frames for further processing. Framing is based on Openflow header length. If received message is shorter than minimal length of OpenFlow message (8 bytes), OF Frame Decoder waits for more data. After receiving at least 8 bytes the decoder checks length in OpenFlow header. If there are still some bytes missing, the decoder waits for them. Else the OF Frame Decoder sends correct length message to next handler in the channel pipeline.

OF Version Detector.

Detects version of used OpenFlow Protocol and discards unsupported version messages. If the detected version is supported, OF Version Detector creates VersionMessageWrapper object containing the detected version and byte message and sends this object upstream.

OF Decoder.

Chooses correct deserilization factory (based on message type) and deserializes messages into generated DTOs (Data Transfer Object). OF Decoder receives VersionMessageWrapper object and passes it to DeserializationFactory which will return translated DTO. DeserializationFactory creates MessageCodeKey object with version and type of received message and Class of object that will be the received message deserialized into. This object is used as key when searching for appropriate decoder in DecoderTable. DecoderTable is basically a map storing decoders. Found decoder translates received message into DTO. If there was no decoder found, null is returned. After returning translated DTO back to OF Decoder, the decoder checks if it is null or not. When the DTO is null, the decoder logs this state and throws an Exception. Else it passes the DTO further upstream. Finally, the OF Decoder releases ByteBuf containing received and decoded byte message.

OF Encoder.

Chooses correct serialization factory (based on type of DTO) and serializes DTOs into byte messages. OF Encoder does the opposite than the OF Decoder using the same principle. OF Encoder receives DTO, passes it for translation and if the result is not null, it sends translated DTO downstream as a ByteBuf. Searching for appropriate encoder is done via MessageTypeKey, based on version and class of received DTO.

Delegating Inbound Handler.

Delegates received DTOs to Connection Adapter. It also reacts on channelInactive and channelUnregistered events. Upon one of these events is triggered, DelegatingInboundHandler creates DisconnectEvent message and sends it upstream, notifying upper layers about switch disconnection.

Channel Outbound Queue.

Message flushing handler. Stores outgoing messages (DTOs) and flushes them. Flush is performed based on time expired and on the number of messages enqueued.

Connection Adapter.

Provides a facade on top of pipeline, which hides netty.io specifics. Provides a set of methods to register for incoming messages and to send messages to particular channel / session. ConnectionAdapterImpl basically implements three interfaces (unified in one superinterface ConnectionFacade):

  • ConnectionAdapter

  • MessageConsumer

  • OpenflowProtocolService

ConnectionAdapter interface has methods for setting up listeners (message, system and connection ready listener), method to check if all listeners are set, checking if the channel is alive and disconnect method. Disconnect method clears responseCache and disables consuming of new messages.

MessageConsumer interface holds only one method: consume(). Consume() method is called from DelegatingInboundHandler. This method processes received DTO’s based on their type. There are three types of received objects:

  • System notifications - invoke system notifications in OpenFlow Plugin (systemListener set). In case of DisconnectEvent message, the Connection Adapter clears response cache and disables consume() method processing,

  • OpenFlow asynchronous messages (from switch) - invoke corresponding notifications in OpenFlow Plugin,

  • OpenFlow symmetric messages (replies to requests) - create RpcResponseKey with XID and DTO’s class set. This RpcResponseKey is then used to find corresponding future object in responseCache. Future object is set with success flag, received message and errors (if any occurred). In case no corresponding future was found in responseCache, Connection Adapter logs warning and discards the message. Connection Adapter also logs warning when an unknown DTO is received.

OpenflowProtocolService interface contains all rpc-methods for sending messages from upper layers (OpenFlow Plugin) downstream and responding. Request messages return Future filled with expected reply message, otherwise the expected Future is of type Void.

NOTE: MultipartRequest message is the only exception. Basically it is request - reply Message type, but it wouldn’t be able to process more following MultipartReply messages if this was implemented as rpc (only one Future). This is why MultipartReply is implemented as notification. OpenFlow Plugin takes care of correct message processing.

UDP Channel pipeline (openflow-protocol-impl)

Creates UDP channel processing pipeline based on configuration and support. Switch Connection Provider, Channel Outbound Queue and Connection Adapter fulfill the same role as in case of TCP connection / channel pipeline (please see above).

UDP Channel pipeline

UDP Channel pipeline

UDP Handler.

Represents single server that is handling incoming connections over UDP (DTLS) protocol. UDP Handler creates a single instance of UDP Channel Initializer that will initialize channels. After that it binds to configured InetAddress and port. When a new device connects, UDP Handler registers its channel and passes control to UDP Channel Initializer.

UDP Channel Initializer.

This class is used for channel initialization and passing arguments. After a new channel has been registered (for UDP there is always only one channel) UDP Channel Initializer creates whole pipeline with needed handlers.

DTLS Handler.

Haven’t been implemented yet. Will take care of secure DTLS connections.

OF Datagram Packet Handler.

Combines functionality of OF Frame Decoder and OF Version Detector. Extracts messages from received datagram packets and checks if message version is supported. If there is a message received from yet unknown sender, OF Datagram Packet Handler creates Connection Adapter for this sender and stores it under sender’s address in UdpConnectionMap. This map is also used for sending the messages and for correct Connection Adapter lookup - to delegate messages from one channel to multiple sessions.

OF Datagram Packet Decoder.

Chooses correct deserilization factory (based on message type) and deserializes messages into generated DTOs. OF Decoder receives VersionMessageUdpWrapper object and passes it to DeserializationFactory which will return translated DTO. DeserializationFactory creates MessageCodeKey object with version and type of received message and Class of object that will be the received message deserialized into. This object is used as key when searching for appropriate decoder in DecoderTable. DecoderTable is basically a map storing decoders. Found decoder translates received message into DTO (DataTransferObject). If there was no decoder found, null is returned. After returning translated DTO back to OF Datagram Packet Decoder, the decoder checks if it is null or not. When the DTO is null, the decoder logs this state. Else it looks up appropriate Connection Adapter in UdpConnectionMap and passes the DTO to found Connection Adapter. Finally, the OF Decoder releases ByteBuf containing received and decoded byte message.

OF Datagram Packet Encoder.

Chooses correct serialization factory (based on type of DTO) and serializes DTOs into byte messages. OF Datagram Packet Encoder does the opposite than the OF Datagram Packet Decoder using the same principle. OF Encoder receives DTO, passes it for translation and if the result is not null, it sends translated DTO downstream as a datagram packet. Searching for appropriate encoder is done via MessageTypeKey, based on version and class of received DTO.

SPI (openflow-protocol-spi)

Defines interface for library’s connection point for other projects. Library exposes its functionality through this interface.

Integration test (openflow-protocol-it)

Testing communication with simple client.

Simple client(simple-client)

Lightweight switch simulator - programmable with desired scenarios.

Utility (util)

Contains utility classes, mainly for work with ByteBuf.

Library’s lifecycle

Steps (after the library’s bundle is started):

  • [1] Library is configured by ConfigSubsystem (adress, ports, encryption, …)

  • [2] Plugin injects its SwitchConnectionHandler into the Library

  • [3] Plugin starts the Library

  • [4] Library creates configured protocol handler (e.g. TCP Handler)

  • [5] Protocol Handler creates Channel Initializer

  • [6] Channel Initializer asks plugin whether to accept incoming connection on each new switch connection

  • [7] Plugin responds:

    • true - continue building pipeline

    • false - reject connection / disconnect channel

  • [8] Library notifies Plugin with onSwitchConnected(ConnectionAdapter) notification, passing reference to ConnectionAdapter, that will handle the connection

  • [9] Plugin registers its system and message listeners

  • [10] FireConnectionReadyNotification() is triggered, announcing that pipeline handlers needed for communication have been created and Plugin can start communication

  • [11] Plugin shutdowns the Library when desired

Library lifecycle

Library lifecycle

Statistics collection

Introduction

Statistics collection collects message statistics. Current collected statistics (DS - downstream, US - upstream):

  • DS_ENTERED_OFJAVA - all messages that entered openflowjava (picked up from openflowplugin)

  • DS_ENCODE_SUCCESS - successfully encoded messages

  • DS_ENCODE_FAIL - messages that failed during encoding (serialization) process

  • DS_FLOW_MODS_ENTERED - all flow-mod messages that entered openflowjava

  • DS_FLOW_MODS_SENT - all flow-mod messages that were successfully sent

  • US_RECEIVED_IN_OFJAVA - messages received from switch

  • US_DECODE_SUCCESS - successfully decoded messages

  • US_DECODE_FAIL - messages that failed during decoding (deserialization) process

  • US_MESSAGE_PASS - messages handed over to openflowplugin

Karaf

In orded to start statistics, it is needed to feature:install odl-openflowjava-stats. To see the logs one should use log:set DEBUG org.opendaylight.openflowjava.statistics and than probably log:display (you can log:list to see if the logging has been set). To adjust collection settings it is enough to modify 45-openflowjava-stats.xml.

JConsole

JConsole provides two commands for the statistics collection:

  • printing current statistics

  • resetting statistic counters

After attaching JConsole to correct process, one only needs to go into MBeans tab org.opendaylight.controller RuntimeBean statistics-collection-service-impl statistics-collection-service-impl Operations to be able to use this commands.

TLS Support

Note

see OpenFlow Plugin Developper Guide

Extensibility

Introduction

Entry point for the extensibility is SwitchConnectionProvider. SwitchConnectionProvider contains methods for (de)serializer registration. To register deserializer it is needed to use .register*Deserializer(key, impl). To register serializer one must use .register*Serializer(key, impl). Registration can occur either during configuration or at runtime.

NOTE: In case when experimenter message is received and no (de)serializer was registered, the library will throw IllegalArgumentException.

Basic Principle

In order to use extensions it is needed to augment existing model and register new (de)serializers.

Augmenting the model: 1. Create new augmentation

Register (de)serializers: 1. Create your (de)serializer 2. Let it implement OFDeserializer<> / OFSerializer<> - in case the structure you are (de)serializing needs to be used in Multipart TableFeatures messages, let it implement HeaderDeserializer<> / HeaderSerializer 3. Implement prescribed methods 4. Register your deserializer under appropriate key (in our case ExperimenterActionDeserializerKey) 5. Register your serializer under appropriate key (in our case ExperimenterActionSerializerKey) 6. Done, test your implementation

NOTE: If you don’t know what key should be used with your (de)serializer implementation, please visit Registration keys page.

Example

Let’s say we have vendor / experimenter action represented by this structure:

struct foo_action {
    uint16_t type;
    uint16_t length;
    uint32_t experimenter;
    uint16_t first;
    uint16_t second;
    uint8_t  pad[4];
}

First, we have to augment existing model. We create new module, which imports “openflow-types.yang” (don’t forget to update your pom.xml with api dependency). Now we create foo action identity:

import openflow-types {prefix oft;}
identity foo {
    description "Foo action description";
    base oft:action-base;
}

This will be used as type in our structure. Now we must augment existing action structure, so that we will have the desired fields first and second. In order to create new augmentation, our module has to import “openflow-action.yang”. The augment should look like this:

import openflow-action {prefix ofaction;}
augment "/ofaction:actions-container/ofaction:action" {
    ext:augment-identifier "foo-action";
        leaf first {
            type uint16;
        }
        leaf second {
            type uint16;
        }
    }

We are finished with model changes. Run mvn clean compile to generate sources. After generation is done, we need to implement our (de)serializer.

Deserializer:

public class FooActionDeserializer extends OFDeserializer<Action> {
   @Override
   public Action deserialize(ByteBuf input) {
       ActionBuilder builder = new ActionBuilder();
       input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we know the type of action*
       builder.setType(Foo.class);
       input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we don't need length*
       *// now create experimenterIdAugmentation - so that openflowplugin can
       differentiate correct vendor codec*
       ExperimenterIdActionBuilder expIdBuilder = new ExperimenterIdActionBuilder();
       expIdBuilder.setExperimenter(new ExperimenterId(input.readUnsignedInt()));
       builder.addAugmentation(ExperimenterIdAction.class, expIdBuilder.build());
       FooActionBuilder fooBuilder = new FooActionBuilder();
       fooBuilder.setFirst(input.readUnsignedShort());
       fooBuilder.setSecond(input.readUnsignedShort());
       builder.addAugmentation(FooAction.class, fooBuilder.build());
       input.skipBytes(4); *// padding*
       return builder.build();
   }
}

Serializer:

public class FooActionSerializer extends OFSerializer<Action> {
   @Override
   public void serialize(Action action, ByteBuf outBuffer) {
       outBuffer.writeShort(FOO_CODE);
       outBuffer.writeShort(16);
       *// we don't have to check for ExperimenterIdAction augmentation - our
       serializer*
       *// was called based on the vendor / experimenter ID, so we simply write
       it to buffer*
       outBuffer.writeInt(VENDOR / EXPERIMENTER ID);
       FooAction foo = action.getAugmentation(FooAction.class);
       outBuffer.writeShort(foo.getFirst());
       outBuffer.writeShort(foo.getSecond());
       outBuffer.writeZero(4); //write padding
   }
}

Register both deserializer and serializer: SwitchConnectionProvider.registerDeserializer(new ExperimenterActionDeserializerKey(0x04, VENDOR / EXPERIMENTER ID), new FooActionDeserializer()); SwitchConnectionProvider.registerSerializer(new ExperimenterActionSerializerKey(0x04, VENDOR / EXPERIMENTER ID), new FooActionSerializer());

We are ready to test our implementation.

NOTE: Vendor / Experimenter structures define only vendor / experimenter ID as common distinguisher (besides action type). Vendor / Experimenter ID is unique for all vendor messages - that’s why vendor is able to register only one class under ExperimenterAction(De)SerializerKey. And that’s why vendor has to switch / choose between his subclasses / subtypes on his own.

Detailed walkthrough: Deserialization extensibility

External interface & class description.

OFGeneralDeserializer:

  • OFDeserializer<E extends DataObject>

    • deserialize(ByteBuf) - deserializes given ByteBuf

  • HeaderDeserializer<E extends DataObject>

    • deserializeHeaders(ByteBuf) - deserializes only E headers (used in Multipart TableFeatures messages)

DeserializerRegistryInjector

  • injectDeserializerRegistry(DeserializerRegistry) - injects deserializer registry into deserializer. Useful when custom deserializer needs access to other deserializers.

NOTE: DeserializerRegistryInjector is not OFGeneralDeserializer descendand. It is a standalone interface.

MessageCodeKey and its descendants These keys are used as for deserializer lookup in DeserializerRegistry. MessageCodeKey should is used in general, while its descendants are used in more special cases. For Example ActionDeserializerKey is used for Action deserializer lookup and (de)registration. Vendor is provided with special keys, which contain only the most necessary fields. These keys usually start with “Experimenter” prefix (MatchEntryDeserializerKey is an exception).

MessageCodeKey has these fields:

  • short version - Openflow wire version number

  • int value - value read from byte message

  • Class<?> clazz - class of object being creating

  • [1] The scenario starts in a custom bundle which wants to extend library’s functionality. The custom bundle creates deserializers which implement exposed OFDeserializer / HeaderDeserializer interfaces (wrapped under OFGeneralDeserializer unifying super interface).

  • [2] Created deserializers are paired with corresponding ExperimenterKeys, which are used for deserializer lookup. If you don’t know what key should be used with your (de)serializer implementation, please visit Registration keys page.

  • [3] Paired deserializers are passed to the OF Library via SwitchConnectionProvider.registerCustomDeserializer(key, impl). Library registers the deserializer.

    • While registering, Library checks if the deserializer is an instance of DeserializerRegistryInjector interface. If yes, the DeserializerRegistry (which stores all deserializer references) is injected into the deserializer.

This is particularly useful when the deserializer needs access to other deserializers. For example IntructionsDeserializer needs access to ActionsDeserializer in order to be able to process OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS instructions.

Deserialization scenario walkthrough

Deserialization scenario walkthrough

Detailed walkthrough: Serialization extensibility

External interface & class description.

OFGeneralSerializer:

  • OFSerializer<E extends DataObject>

    • serialize(E,ByteBuf) - serializes E into given ByteBuf

  • HeaderSerializer<E extends DataObject>

    • serializeHeaders(E,ByteBuf) - serializes E headers (used in Multipart TableFeatures messages)

SerializerRegistryInjector * injectSerializerRegistry(SerializerRegistry) - injects serializer registry into serializer. Useful when custom serializer needs access to other serializers.

NOTE: SerializerRegistryInjector is not OFGeneralSerializer descendand.

MessageTypeKey and its descendants These keys are used as for serializer lookup in SerializerRegistry. MessageTypeKey should is used in general, while its descendants are used in more special cases. For Example ActionSerializerKey is used for Action serializer lookup and (de)registration. Vendor is provided with special keys, which contain only the most necessary fields. These keys usually start with “Experimenter” prefix (MatchEntrySerializerKey is an exception).

MessageTypeKey has these fields:

  • short version - Openflow wire version number

  • Class<E> msgType - DTO class

Scenario walkthrough

  • [1] Serialization extensbility principles are similar to the deserialization principles. The scenario starts in a custom bundle. The custom bundle creates serializers which implement exposed OFSerializer / HeaderSerializer interfaces (wrapped under OFGeneralSerializer unifying super interface).

  • [2] Created serializers are paired with their ExperimenterKeys, which are used for serializer lookup. If you don’t know what key should be used with your serializer implementation, please visit Registration keys page.

  • [3] Paired serializers are passed to the OF Library via SwitchConnectionProvider.registerCustomSerializer(key, impl). Library registers the serializer.

  • While registering, Library checks if the serializer is an instance of SerializerRegistryInjector interface. If yes, the SerializerRegistry (which stores all serializer references) is injected into the serializer.

This is particularly useful when the serializer needs access to other deserializers. For example IntructionsSerializer needs access to ActionsSerializer in order to be able to process OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS instructions.

Serialization scenario walkthrough

Serialization scenario walkthrough

Internal description

SwitchConnectionProvider SwitchConnectionProvider constructs and initializes both deserializer and serializer registries with default (de)serializers. It also injects the DeserializerRegistry into the DeserializationFactory, the SerializerRegistry into the SerializationFactory. When call to register custom (de)serializer is made, SwitchConnectionProvider calls register method on appropriate registry.

DeserializerRegistry / SerializerRegistry Both registries contain init() method to initialize default (de)serializers. Registration checks if key or (de)serializer implementation are not null. If at least one of the is null, NullPointerException is thrown. Else the (de)serializer implementation is checked if it is (De)SerializerRegistryInjector instance. If it is an instance of this interface, the registry is injected into this (de)serializer implementation.

GetSerializer(key) or GetDeserializer(key) performs registry lookup. Because there are two separate interfaces that might be put into the registry, the registry uses their unifying super interface. Get(De)Serializer(key) method casts the super interface to desired type. There is also a null check for the (de)serializer received from the registry. If the deserializer wasn’t found, NullPointerException with key description is thrown.

Registration keys

Deserialization.

Possible openflow extensions and their keys

There are three vendor specific extensions in Openflow v1.0 and eight in Openflow v1.3. These extensions are registered under registration keys, that are shown in table below:

Extension type

OpenFlo w

Registration key

Utility class

Vendor message

1.0

ExperimenterIdDeserializerKe y(1, experimenterId, ExperimenterMessage.class)

ExperimenterDeseriali zerKeyFactory

Action

1.0

ExperimenterActionDeserializ erKey(1, experimenter ID)

.

Stats message

1.0

ExperimenterMultipartReplyMe ssageDeserializerKey(1, experimenter ID)

ExperimenterDeseriali zerKeyFactory

Experimenter message

1.3

ExperimenterIdDeserializerKe y(4, experimenterId, ExperimenterMessage.class)

ExperimenterDeseriali zerKeyFactory

Match entry

1.3

MatchEntryDeserializerKey(4, (number) ${oxm_class}, (number) ${oxm_field});

.

key.setExperimenterId(experi menter ID);

.

Action

1.3

ExperimenterActionDeserializ erKey(4, experimenter ID)

.

Instruction

1.3

ExperimenterInstructionDeser ializerKey(4, experimenter ID)

.

Multipart

1.3

ExperimenterIdDeserializerKe y(4, experimenterId, MultipartReplyMessage.class)

ExperimenterDeseriali zerKeyFactory

Multipart - Table features

1.3

ExperimenterIdDeserializerKe y(4, experimenterId, TableFeatureProperties.class )

ExperimenterDeseriali zerKeyFactory

Error

1.3

ExperimenterIdDeserializerKe y(4, experimenterId, ErrorMessage.class)

ExperimenterDeseriali zerKeyFactory

Queue property

1.3

ExperimenterIdDeserializerKe y(4, experimenterId, QueueProperty.class)

ExperimenterDeseriali zerKeyFactory

Meter band type

1.3

ExperimenterIdDeserializerKe y(4, experimenterId, MeterBandExperimenterCase.cl ass)

ExperimenterDeseriali zerKeyFactory

Table: Deserialization

Serialization.

Possible openflow extensions and their keys

There are three vendor specific extensions in Openflow v1.0 and seven Openflow v1.3. These extensions are registered under registration keys, that are shown in table below:

Extension type

OpenFlo w

Registration key

Utility class

Vendor message

1.0

ExperimenterIdSerializerKey< >(1, experimenterId, ExperimenterInput.class)

ExperimenterSerialize rKeyFactory

Action

1.0

ExperimenterActionSerializer Key(1, experimenterId, sub-type)

.

Stats message

1.0

ExperimenterMultipartRequest SerializerKey(1, experimenter ID)

ExperimenterSerialize rKeyFactory

Experimenter message

1.3

ExperimenterIdSerializerKey< >(4, experimenterId, ExperimenterInput.class)

ExperimenterSerialize rKeyFactory

Match entry

1.3

MatchEntrySerializerKey<>(4, (class) ${oxm_class}, (class) ${oxm_field});

.

key.setExperimenterId(experi menter ID)

.

Action

1.3

ExperimenterActionSerializer Key(4, experimenterId, sub-type)

.

Instruction

1.3

ExperimenterInstructionSeria lizerKey(4, experimenter ID)

.

Multipart

1.3

ExperimenterIdSerializerKey< >(4, experimenterId, MultipartRequestExperimenter Case.class)

ExperimenterSerialize rKeyFactory

Multipart - Table features

1.3

ExperimenterIdSerializerKey< >(4, experimenterId, TableFeatureProperties.class )

ExperimenterSerialize rKeyFactory

Meter band type

1.3

ExperimenterIdSerializerKey< >(4, experimenterId, MeterBandExperimenterCase.cl ass)

ExperimenterSerialize rKeyFactory

Table: Serialization