1 .. _openflow-protocol-library-dev-guide:
3 OpenFlow Protocol Library Developer Guide
4 =========================================
9 OpenFlow Protocol Library is component in OpenDaylight, that mediates
10 communication between OpenDaylight controller and hardware devices
11 supporting OpenFlow protocol. Primary goal is to provide user (or upper
12 layers of OpenDaylight) communication channel, that can be used for
13 managing network hardware devices.
18 There are three features inside openflowjava:
20 - **odl-openflowjava-protocol** provides all openflowjava bundles, that
21 are needed for communication with openflow devices. It ensures
22 message translation and handles network connections. It also provides
23 openflow protocol specific model.
25 - **odl-openflowjava-all** currently contains only
26 odl-openflowjava-protocol feature.
28 - **odl-openflowjava-stats** provides mechanism for message counting
29 and reporting. Can be used for performance analysis.
31 odl-openflowjava-protocol Architecture
32 --------------------------------------
34 Basic bundles contained in this feature are openflow-protocol-api,
35 openflow-protocol-impl, openflow-protocol-spi and util.
37 - **openflow-protocol-api** - contains openflow model, constants and
38 keys used for (de)serializer registration.
40 - **openflow-protocol-impl** - contains message factories, that
41 translate binary messages into DataObjects and vice versa. Bundle
42 also contains network connection handlers - servers, netty pipeline
45 - **openflow-protocol-spi** - entry point for openflowjava
46 configuration, startup and close. Basically starts implementation.
48 - **util** - utility classes for binary-Java conversions and to ease
49 experimenter key creation
51 odl-openflowjava-stats Feature
52 ------------------------------
54 Runs over odl-openflowjava-protocol. It counts various message types /
55 events and reports counts in specified time periods. Statistics
56 collection can be configured in
57 openflowjava-config/src/main/resources/45-openflowjava-stats.xml
59 Key APIs and Interfaces
60 -----------------------
62 Basic API / SPI classes are ConnectionAdapter (Rpc/notifications) and
63 SwitchConnectionProcider (configure, start, shutdown)
68 Pull the code and import project into your IDE.
72 git clone ssh://<username>@git.opendaylight.org:29418/openflowjava.git
77 Current implementation allows to configure:
79 - listening port (mandatory)
81 - transfer protocol (mandatory)
83 - switch idle timeout (mandatory)
85 - TLS configuration (optional)
87 - thread count (optional)
89 You can find exemplary Openflow Protocol Library instance configuration
94 <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
95 <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
96 <!-- default OF-switch-connection-provider (port 6633) -->
98 <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
99 <name>openflow-switch-connection-provider-default-impl</name>
101 <!-- Possible transport-protocol options: TCP, TLS, UDP -->
102 <transport-protocol>TCP</transport-protocol>
103 <switch-idle-timeout>15000</switch-idle-timeout>
104 <!-- Exemplary TLS configuration:
105 - uncomment the <tls> tag
106 - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
107 files into your virtual machine
108 - set VM encryption options to use copied keys
109 - start communication
110 Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
111 for detailed information regarding TLS -->
113 <keystore>/exemplary-ctlKeystore</keystore>
114 <keystore-type>JKS</keystore-type>
115 <keystore-path-type>CLASSPATH</keystore-path-type>
116 <keystore-password>opendaylight</keystore-password>
117 <truststore>/exemplary-ctlTrustStore</truststore>
118 <truststore-type>JKS</truststore-type>
119 <truststore-path-type>CLASSPATH</truststore-path-type>
120 <truststore-password>opendaylight</truststore-password>
121 <certificate-password>opendaylight</certificate-password>
123 <!-- Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
125 <boss-threads>2</boss-threads>
126 <worker-threads>8</worker-threads>
132 <!-- default OF-switch-connection-provider (port 6653) -->
134 <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
135 <name>openflow-switch-connection-provider-legacy-impl</name>
137 <!-- Possible transport-protocol options: TCP, TLS, UDP -->
138 <transport-protocol>TCP</transport-protocol>
139 <switch-idle-timeout>15000</switch-idle-timeout>
140 <!-- Exemplary TLS configuration:
141 - uncomment the <tls> tag
142 - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
143 files into your virtual machine
144 - set VM encryption options to use copied keys
145 - start communication
146 Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
147 for detailed information regarding TLS -->
149 <keystore>/exemplary-ctlKeystore</keystore>
150 <keystore-type>JKS</keystore-type>
151 <keystore-path-type>CLASSPATH</keystore-path-type>
152 <keystore-password>opendaylight</keystore-password>
153 <truststore>/exemplary-ctlTrustStore</truststore>
154 <truststore-type>JKS</truststore-type>
155 <truststore-path-type>CLASSPATH</truststore-path-type>
156 <truststore-password>opendaylight</truststore-password>
157 <certificate-password>opendaylight</certificate-password>
159 <!-- Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
161 <boss-threads>2</boss-threads>
162 <worker-threads>8</worker-threads>
169 <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:common:config:impl">prefix:openflow-provider-impl</type>
170 <name>openflow-provider-impl</name>
171 <openflow-switch-connection-provider>
172 <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
173 <name>openflow-switch-connection-provider-default</name>
174 </openflow-switch-connection-provider>
175 <openflow-switch-connection-provider>
176 <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
177 <name>openflow-switch-connection-provider-legacy</name>
178 </openflow-switch-connection-provider>
179 <binding-aware-broker>
180 <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
181 <name>binding-osgi-broker</name>
182 </binding-aware-broker>
186 Possible transport-protocol options:
194 Switch-idle timeout specifies time needed to detect idle state of
195 switch. When no message is received from switch within this time, upper
196 layers are notified on switch idleness. To be able to use this exemplary
199 - uncomment the ``<tls>`` tag
201 - copy *exemplary-switch-privkey.pem*, *exemplary-switch-cert.pem* and
202 *exemplary-cacert.pem* files into your virtual machine
204 - set VM encryption options to use copied keys (please visit TLS
205 support wiki page for detailed information regarding TLS)
207 - start communication
209 Thread model configuration specifies how many threads are desired to
210 perform Netty’s I/O operations.
212 - boss-threads specifies the number of threads that register incoming
215 - worker-threads specifies the number of threads performing read /
216 write (+ serialization / deserialization) operations.
221 Public API ``(openflow-protocol-api)``
222 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224 Set of interfaces and builders for immutable data transfer objects
225 representing Openflow Protocol structures.
227 Transfer objects and service APIs are infered from several YANG models
228 using code generator to reduce verbosity of definition and repeatibility
231 The following YANG modules are defined:
233 - openflow-types - defines common Openflow specific types
235 - openflow-instruction - defines base Openflow instructions
237 - openflow-action - defines base Openflow actions
239 - openflow-augments - defines object augmentations
241 - openflow-extensible-match - defines Openflow OXM match
243 - openflow-protocol - defines Openflow Protocol messages
245 - system-notifications - defines system notification objects
247 - openflow-configuration - defines structures used in ConfigSubsystem
249 This modules also reuse types from following YANG modules:
251 - ietf-inet-types - IP adresses, IP prefixes, IP-protocol related types
253 - ietf-yang-types - Mac Address, etc.
255 The use of predefined types is to make APIs contracts more safe, better
256 readable and documented (e.g using MacAddress instead of byte array…)
258 TCP Channel pipeline ``(openflow-protocol-impl)``
259 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
261 Creates channel processing pipeline based on configuration and support.
263 **TCP Channel pipeline.**
265 imageopenflowjava/500px-TCPChannelPipeline.png[width=500]
267 **Switch Connection Provider.**
269 Implementation of connection point for other projects. Library exposes
270 its functionality through this class. Library can be configured, started
271 and shutdowned here. There are also methods for custom (de)serializer
274 **Tcp Connection Initializer.**
276 In order to initialize TCP connection to a device (switch), OF Plugin
277 calls method ``initiateConnection()`` in ``SwitchConnectionProvider``.
278 This method in turn initializes (Bootstrap) server side channel towards
283 Represents single server that is handling incoming connections over TCP
284 / TLS protocol. TCP Handler creates a single instance of TCP Channel
285 Initializer that will initialize channels. After that it binds to
286 configured InetAddress and port. When a new device connects, TCP Handler
287 registers its channel and passes control to TCP Channel Initializer.
289 **TCP Channel Initializer.**
291 This class is used for channel initialization / rejection and passing
292 arguments. After a new channel has been registered it calls Switch
293 Connection Handler’s (OF Plugin) accept method to decide if the library
294 should keep the newly registered channel or if the channel should be
295 closed. If the channel has been accepted, TCP Channel Initializer
296 creates the whole pipeline with needed handlers and also with
297 ConnectionAdapter instance. After the channel pipeline is ready, Switch
298 Connection Handler is notified with ``onConnectionReady`` notification.
299 OpenFlow Plugin can now start sending messages downstream.
303 If there are no messages received for more than time specified, this
304 handler triggers idle state notification. The switch idle timeout is
305 received as a parameter from ConnectionConfiguration settings. Idle
306 State Handler is inactive while there are messages received within the
307 switch idle timeout. If there are no messages received for more than
308 timeout specified, handler creates SwitchIdleEvent message and sends it
313 It encrypts and decrypts messages over TLS protocol. Engaging TLS
314 Handler into pipeline is matter of configuration (``<tls>`` tag). TLS
315 communication is either unsupported or required. TLS Handler is
316 represented as a Netty’s SslHandler.
318 **OF Frame Decoder.**
320 Parses input stream into correct length message frames for further
321 processing. Framing is based on Openflow header length. If received
322 message is shorter than minimal length of OpenFlow message (8 bytes), OF
323 Frame Decoder waits for more data. After receiving at least 8 bytes the
324 decoder checks length in OpenFlow header. If there are still some bytes
325 missing, the decoder waits for them. Else the OF Frame Decoder sends
326 correct length message to next handler in the channel pipeline.
328 **OF Version Detector.**
330 Detects version of used OpenFlow Protocol and discards unsupported
331 version messages. If the detected version is supported, OF Version
332 Detector creates ``VersionMessageWrapper`` object containing the
333 detected version and byte message and sends this object upstream.
337 Chooses correct deserilization factory (based on message type) and
338 deserializes messages into generated DTOs (Data Transfer Object). OF
339 Decoder receives ``VersionMessageWrapper`` object and passes it to
340 ``DeserializationFactory`` which will return translated DTO.
341 ``DeserializationFactory`` creates ``MessageCodeKey`` object with
342 version and type of received message and Class of object that will be
343 the received message deserialized into. This object is used as key when
344 searching for appropriate decoder in ``DecoderTable``. ``DecoderTable``
345 is basically a map storing decoders. Found decoder translates received
346 message into DTO. If there was no decoder found, null is returned. After
347 returning translated DTO back to OF Decoder, the decoder checks if it is
348 null or not. When the DTO is null, the decoder logs this state and
349 throws an Exception. Else it passes the DTO further upstream. Finally,
350 the OF Decoder releases ByteBuf containing received and decoded byte
355 Chooses correct serialization factory (based on type of DTO) and
356 serializes DTOs into byte messages. OF Encoder does the opposite than
357 the OF Decoder using the same principle. OF Encoder receives DTO, passes
358 it for translation and if the result is not null, it sends translated
359 DTO downstream as a ByteBuf. Searching for appropriate encoder is done
360 via MessageTypeKey, based on version and class of received DTO.
362 **Delegating Inbound Handler.**
364 Delegates received DTOs to Connection Adapter. It also reacts on
365 channelInactive and channelUnregistered events. Upon one of these events
366 is triggered, DelegatingInboundHandler creates DisconnectEvent message
367 and sends it upstream, notifying upper layers about switch
370 **Channel Outbound Queue.**
372 Message flushing handler. Stores outgoing messages (DTOs) and flushes
373 them. Flush is performed based on time expired and on the number of
376 **Connection Adapter.**
378 Provides a facade on top of pipeline, which hides netty.io specifics.
379 Provides a set of methods to register for incoming messages and to send
380 messages to particular channel / session. ConnectionAdapterImpl
381 basically implements three interfaces (unified in one superinterface
388 - OpenflowProtocolService
390 **ConnectionAdapter** interface has methods for setting up listeners
391 (message, system and connection ready listener), method to check if all
392 listeners are set, checking if the channel is alive and disconnect
393 method. Disconnect method clears responseCache and disables consuming of
396 **MessageConsumer** interface holds only one method: ``consume()``.
397 ``Consume()`` method is called from DelegatingInboundHandler. This
398 method processes received DTO’s based on their type. There are three
399 types of received objects:
401 - System notifications - invoke system notifications in OpenFlow Plugin
402 (systemListener set). In case of ``DisconnectEvent`` message, the
403 Connection Adapter clears response cache and disables consume()
406 - OpenFlow asynchronous messages (from switch) - invoke corresponding
407 notifications in OpenFlow Plugin,
409 - OpenFlow symmetric messages (replies to requests) - create
410 ``RpcResponseKey`` with XID and DTO’s class set. This
411 ``RpcResponseKey`` is then used to find corresponding future object
412 in responseCache. Future object is set with success flag, received
413 message and errors (if any occurred). In case no corresponding future
414 was found in responseCache, Connection Adapter logs warning and
415 discards the message. Connection Adapter also logs warning when an
416 unknown DTO is received.
418 **OpenflowProtocolService** interface contains all rpc-methods for
419 sending messages from upper layers (OpenFlow Plugin) downstream and
420 responding. Request messages return Future filled with expected reply
421 message, otherwise the expected Future is of type Void.
423 **NOTE:** MultipartRequest message is the only exception. Basically it
424 is request - reply Message type, but it wouldn’t be able to process more
425 following MultipartReply messages if this was implemented as rpc (only
426 one Future). This is why MultipartReply is implemented as notification.
427 OpenFlow Plugin takes care of correct message processing.
429 UDP Channel pipeline (openflow-protocol-impl)
430 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
432 Creates UDP channel processing pipeline based on configuration and
433 support. **Switch Connection Provider**, **Channel Outbound Queue** and
434 **Connection Adapter** fulfill the same role as in case of TCP
435 connection / channel pipeline (please see above).
437 .. figure:: ./images/openflowjava/500px-UdpChannelPipeline.png
438 :alt: UDP Channel pipeline
444 Represents single server that is handling incoming connections over UDP
445 (DTLS) protocol. UDP Handler creates a single instance of UDP Channel
446 Initializer that will initialize channels. After that it binds to
447 configured InetAddress and port. When a new device connects, UDP Handler
448 registers its channel and passes control to UDP Channel Initializer.
450 **UDP Channel Initializer.**
452 This class is used for channel initialization and passing arguments.
453 After a new channel has been registered (for UDP there is always only
454 one channel) UDP Channel Initializer creates whole pipeline with needed
459 Haven’t been implemented yet. Will take care of secure DTLS connections.
461 **OF Datagram Packet Handler.**
463 Combines functionality of OF Frame Decoder and OF Version Detector.
464 Extracts messages from received datagram packets and checks if message
465 version is supported. If there is a message received from yet unknown
466 sender, OF Datagram Packet Handler creates Connection Adapter for this
467 sender and stores it under sender’s address in ``UdpConnectionMap``.
468 This map is also used for sending the messages and for correct
469 Connection Adapter lookup - to delegate messages from one channel to
472 **OF Datagram Packet Decoder.**
474 Chooses correct deserilization factory (based on message type) and
475 deserializes messages into generated DTOs. OF Decoder receives
476 ``VersionMessageUdpWrapper`` object and passes it to
477 ``DeserializationFactory`` which will return translated DTO.
478 ``DeserializationFactory`` creates ``MessageCodeKey`` object with
479 version and type of received message and Class of object that will be
480 the received message deserialized into. This object is used as key when
481 searching for appropriate decoder in ``DecoderTable``. ``DecoderTable``
482 is basically a map storing decoders. Found decoder translates received
483 message into DTO (DataTransferObject). If there was no decoder found,
484 null is returned. After returning translated DTO back to OF Datagram
485 Packet Decoder, the decoder checks if it is null or not. When the DTO is
486 null, the decoder logs this state. Else it looks up appropriate
487 Connection Adapter in ``UdpConnectionMap`` and passes the DTO to found
488 Connection Adapter. Finally, the OF Decoder releases ``ByteBuf``
489 containing received and decoded byte message.
491 **OF Datagram Packet Encoder.**
493 Chooses correct serialization factory (based on type of DTO) and
494 serializes DTOs into byte messages. OF Datagram Packet Encoder does the
495 opposite than the OF Datagram Packet Decoder using the same principle.
496 OF Encoder receives DTO, passes it for translation and if the result is
497 not null, it sends translated DTO downstream as a datagram packet.
498 Searching for appropriate encoder is done via MessageTypeKey, based on
499 version and class of received DTO.
501 SPI (openflow-protocol-spi)
502 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
504 Defines interface for library’s connection point for other projects.
505 Library exposes its functionality through this interface.
507 Integration test (openflow-protocol-it)
508 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
510 Testing communication with simple client.
512 Simple client(simple-client)
513 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
515 Lightweight switch simulator - programmable with desired scenarios.
520 Contains utility classes, mainly for work with ByteBuf.
525 Steps (after the library’s bundle is started):
527 - [1] Library is configured by ConfigSubsystem (adress, ports,
530 - [2] Plugin injects its SwitchConnectionHandler into the Library
532 - [3] Plugin starts the Library
534 - [4] Library creates configured protocol handler (e.g. TCP Handler)
536 - [5] Protocol Handler creates Channel Initializer
538 - [6] Channel Initializer asks plugin whether to accept incoming
539 connection on each new switch connection
541 - [7] Plugin responds:
543 - true - continue building pipeline
545 - false - reject connection / disconnect channel
547 - [8] Library notifies Plugin with onSwitchConnected(ConnectionAdapter)
548 notification, passing reference to ConnectionAdapter, that will
549 handle the connection
551 - [9] Plugin registers its system and message listeners
553 - [10] FireConnectionReadyNotification() is triggered, announcing that
554 pipeline handlers needed for communication have been created and
555 Plugin can start communication
557 - [11] Plugin shutdowns the Library when desired
559 .. figure:: ./images/openflowjava/Library_lifecycle.png
560 :alt: Library lifecycle
564 Statistics collection
565 ---------------------
570 Statistics collection collects message statistics. Current collected
571 statistics (``DS`` - downstream, ``US`` - upstream):
573 - ``DS_ENTERED_OFJAVA`` - all messages that entered openflowjava
574 (picked up from openflowplugin)
576 - ``DS_ENCODE_SUCCESS`` - successfully encoded messages
578 - ``DS_ENCODE_FAIL`` - messages that failed during encoding
579 (serialization) process
581 - ``DS_FLOW_MODS_ENTERED`` - all flow-mod messages that entered
584 - ``DS_FLOW_MODS_SENT`` - all flow-mod messages that were successfully
587 - ``US_RECEIVED_IN_OFJAVA`` - messages received from switch
589 - ``US_DECODE_SUCCESS`` - successfully decoded messages
591 - ``US_DECODE_FAIL`` - messages that failed during decoding
592 (deserialization) process
594 - ``US_MESSAGE_PASS`` - messages handed over to openflowplugin
599 In orded to start statistics, it is needed to feature:install
600 odl-openflowjava-stats. To see the logs one should use log:set DEBUG
601 org.opendaylight.openflowjava.statistics and than probably log:display
602 (you can log:list to see if the logging has been set). To adjust
603 collection settings it is enough to modify 45-openflowjava-stats.xml.
608 JConsole provides two commands for the statistics collection:
610 - printing current statistics
612 - resetting statistic counters
614 After attaching JConsole to correct process, one only needs to go into
616 ``tab → org.opendaylight.controller → RuntimeBean → statistics-collection-service-impl
617 → statistics-collection-service-impl → Operations`` to be able to use
625 see OpenFlow Plugin Developper Guide
633 Entry point for the extensibility is ``SwitchConnectionProvider``.
634 ``SwitchConnectionProvider`` contains methods for (de)serializer
635 registration. To register deserializer it is needed to use
636 .register\*Deserializer(key, impl). To register serializer one must use
637 .register\*Serializer(key, impl). Registration can occur either during
638 configuration or at runtime.
640 **NOTE**: In case when experimenter message is received and no
641 (de)serializer was registered, the library will throw
642 ``IllegalArgumentException``.
647 In order to use extensions it is needed to augment existing model and
648 register new (de)serializers.
650 Augmenting the model: 1. Create new augmentation
652 Register (de)serializers: 1. Create your (de)serializer 2. Let it
653 implement ``OFDeserializer<>`` / ``OFSerializer<>`` - in case the
654 structure you are (de)serializing needs to be used in Multipart
655 TableFeatures messages, let it implement ``HeaderDeserializer<>`` /
656 ``HeaderSerializer`` 3. Implement prescribed methods 4. Register your
657 deserializer under appropriate key (in our case
658 ``ExperimenterActionDeserializerKey``) 5. Register your serializer under
659 appropriate key (in our case ``ExperimenterActionSerializerKey``) 6.
660 Done, test your implementation
662 **NOTE**: If you don’t know what key should be used with your
663 (de)serializer implementation, please visit `Registration
664 keys <#registration_keys>`__ page.
669 Let’s say we have vendor / experimenter action represented by this
677 uint32_t experimenter;
683 First, we have to augment existing model. We create new module, which
684 imports "``openflow-types.yang``" (don’t forget to update your
685 ``pom.xml`` with api dependency). Now we create foo action identity:
689 import openflow-types {prefix oft;}
691 description "Foo action description";
692 base oft:action-base;
695 This will be used as type in our structure. Now we must augment existing
696 action structure, so that we will have the desired fields first and
697 second. In order to create new augmentation, our module has to import
698 "``openflow-action.yang``". The augment should look like this:
702 import openflow-action {prefix ofaction;}
703 augment "/ofaction:actions-container/ofaction:action" {
704 ext:augment-identifier "foo-action";
713 We are finished with model changes. Run mvn clean compile to generate
714 sources. After generation is done, we need to implement our
721 public class FooActionDeserializer extends OFDeserializer<Action> {
723 public Action deserialize(ByteBuf input) {
724 ActionBuilder builder = new ActionBuilder();
725 input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we know the type of action*
726 builder.setType(Foo.class);
727 input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we don't need length*
728 *// now create experimenterIdAugmentation - so that openflowplugin can
729 differentiate correct vendor codec*
730 ExperimenterIdActionBuilder expIdBuilder = new ExperimenterIdActionBuilder();
731 expIdBuilder.setExperimenter(new ExperimenterId(input.readUnsignedInt()));
732 builder.addAugmentation(ExperimenterIdAction.class, expIdBuilder.build());
733 FooActionBuilder fooBuilder = new FooActionBuilder();
734 fooBuilder.setFirst(input.readUnsignedShort());
735 fooBuilder.setSecond(input.readUnsignedShort());
736 builder.addAugmentation(FooAction.class, fooBuilder.build());
737 input.skipBytes(4); *// padding*
738 return builder.build();
746 public class FooActionSerializer extends OFSerializer<Action> {
748 public void serialize(Action action, ByteBuf outBuffer) {
749 outBuffer.writeShort(FOO_CODE);
750 outBuffer.writeShort(16);
751 *// we don't have to check for ExperimenterIdAction augmentation - our
753 *// was called based on the vendor / experimenter ID, so we simply write
755 outBuffer.writeInt(VENDOR / EXPERIMENTER ID);
756 FooAction foo = action.getAugmentation(FooAction.class);
757 outBuffer.writeShort(foo.getFirst());
758 outBuffer.writeShort(foo.getSecond());
759 outBuffer.writeZero(4); //write padding
763 Register both deserializer and serializer:
764 ``SwitchConnectionProvider.registerDeserializer(new
765 ExperimenterActionDeserializerKey(0x04, VENDOR / EXPERIMENTER ID),
766 new FooActionDeserializer());``
767 ``SwitchConnectionProvider.registerSerializer(new
768 ExperimenterActionSerializerKey(0x04, VENDOR / EXPERIMENTER ID),
769 new FooActionSerializer());``
771 We are ready to test our implementation.
773 **NOTE:** Vendor / Experimenter structures define only vendor /
774 experimenter ID as common distinguisher (besides action type). Vendor /
775 Experimenter ID is unique for all vendor messages - that’s why vendor is
776 able to register only one class under
777 ExperimenterAction(De)SerializerKey. And that’s why vendor has to switch
778 / choose between his subclasses / subtypes on his own.
780 Detailed walkthrough: Deserialization extensibility
781 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
783 **External interface & class description.**
785 **OFGeneralDeserializer:**
787 - ``OFDeserializer<E extends DataObject>``
789 - *deserialize(ByteBuf)* - deserializes given ByteBuf
791 - ``HeaderDeserializer<E extends DataObject>``
793 - *deserializeHeaders(ByteBuf)* - deserializes only E headers (used
794 in Multipart TableFeatures messages)
796 **DeserializerRegistryInjector**
798 - ``injectDeserializerRegistry(DeserializerRegistry)`` - injects
799 deserializer registry into deserializer. Useful when custom
800 deserializer needs access to other deserializers.
802 **NOTE:** DeserializerRegistryInjector is not OFGeneralDeserializer
803 descendand. It is a standalone interface.
805 **MessageCodeKey and its descendants** These keys are used as for
806 deserializer lookup in DeserializerRegistry. MessageCodeKey should is
807 used in general, while its descendants are used in more special cases.
808 For Example ActionDeserializerKey is used for Action deserializer lookup
809 and (de)registration. Vendor is provided with special keys, which
810 contain only the most necessary fields. These keys usually start with
811 "Experimenter" prefix (MatchEntryDeserializerKey is an exception).
813 MessageCodeKey has these fields:
815 - short version - Openflow wire version number
817 - int value - value read from byte message
819 - Class<?> clazz - class of object being creating
821 - [1] The scenario starts in a custom bundle which wants to extend
822 library’s functionality. The custom bundle creates deserializers
823 which implement exposed ``OFDeserializer`` / ``HeaderDeserializer``
824 interfaces (wrapped under ``OFGeneralDeserializer`` unifying super
827 - [2] Created deserializers are paired with corresponding
828 ExperimenterKeys, which are used for deserializer lookup. If you
829 don’t know what key should be used with your (de)serializer
830 implementation, please visit `Registration
831 keys <#registration_keys>`__ page.
833 - [3] Paired deserializers are passed to the OF Library via
834 **SwitchConnectionProvider**.\ *registerCustomDeserializer(key,
835 impl)*. Library registers the deserializer.
837 - While registering, Library checks if the deserializer is an
838 instance of **DeserializerRegistryInjector** interface. If yes,
839 the DeserializerRegistry (which stores all deserializer
840 references) is injected into the deserializer.
842 This is particularly useful when the deserializer needs access to other
843 deserializers. For example ``IntructionsDeserializer`` needs access to
844 ``ActionsDeserializer`` in order to be able to process
845 OFPIT\_WRITE\_ACTIONS/OFPIT\_APPLY\_ACTIONS instructions.
847 .. figure:: ./images/openflowjava/800px-Extensibility.png
848 :alt: Deserialization scenario walkthrough
850 Deserialization scenario walkthrough
852 Detailed walkthrough: Serialization extensibility
853 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
855 **External interface & class description.**
857 **OFGeneralSerializer:**
859 - OFSerializer<E extends DataObject>
861 - *serialize(E,ByteBuf)* - serializes E into given ByteBuf
863 - ``HeaderSerializer<E extends DataObject>``
865 - *serializeHeaders(E,ByteBuf)* - serializes E headers (used in
866 Multipart TableFeatures messages)
868 **SerializerRegistryInjector** \*
869 ``injectSerializerRegistry(SerializerRegistry)`` - injects serializer
870 registry into serializer. Useful when custom serializer needs access to
873 **NOTE:** SerializerRegistryInjector is not OFGeneralSerializer
876 **MessageTypeKey and its descendants** These keys are used as for
877 serializer lookup in SerializerRegistry. MessageTypeKey should is used
878 in general, while its descendants are used in more special cases. For
879 Example ActionSerializerKey is used for Action serializer lookup and
880 (de)registration. Vendor is provided with special keys, which contain
881 only the most necessary fields. These keys usually start with
882 "Experimenter" prefix (MatchEntrySerializerKey is an exception).
884 MessageTypeKey has these fields:
886 - *short version* - Openflow wire version number
888 - *Class<E> msgType* - DTO class
892 - [1] Serialization extensbility principles are similar to the
893 deserialization principles. The scenario starts in a custom bundle.
894 The custom bundle creates serializers which implement exposed
895 OFSerializer / HeaderSerializer interfaces (wrapped under
896 OFGeneralSerializer unifying super interface).
898 - [2] Created serializers are paired with their ExperimenterKeys, which
899 are used for serializer lookup. If you don’t know what key should be
900 used with your serializer implementation, please visit `Registration
901 keys <#registration_keys>`__ page.
903 - [3] Paired serializers are passed to the OF Library via
904 **SwitchConnectionProvider**.\ *registerCustomSerializer(key, impl)*.
905 Library registers the serializer.
907 - While registering, Library checks if the serializer is an instance of
908 **SerializerRegistryInjector** interface. If yes, the
909 SerializerRegistry (which stores all serializer references) is
910 injected into the serializer.
912 This is particularly useful when the serializer needs access to other
913 deserializers. For example IntructionsSerializer needs access to
914 ActionsSerializer in order to be able to process
915 OFPIT\_WRITE\_ACTIONS/OFPIT\_APPLY\_ACTIONS instructions.
917 .. figure:: ./images/openflowjava/800px-Extensibility2.png
918 :alt: Serialization scenario walkthrough
920 Serialization scenario walkthrough
925 **SwitchConnectionProvider** ``SwitchConnectionProvider`` constructs and
926 initializes both deserializer and serializer registries with default
927 (de)serializers. It also injects the ``DeserializerRegistry`` into the
928 ``DeserializationFactory``, the ``SerializerRegistry`` into the
929 ``SerializationFactory``. When call to register custom (de)serializer is
930 made, ``SwitchConnectionProvider`` calls register method on appropriate
933 **DeserializerRegistry / SerializerRegistry** Both registries contain
934 init() method to initialize default (de)serializers. Registration checks
935 if key or (de)serializer implementation are not ``null``. If at least
936 one of the is ``null``, ``NullPointerException`` is thrown. Else the
937 (de)serializer implementation is checked if it is
938 ``(De)SerializerRegistryInjector`` instance. If it is an instance of
939 this interface, the registry is injected into this (de)serializer
942 ``GetSerializer(key)`` or ``GetDeserializer(key)`` performs registry
943 lookup. Because there are two separate interfaces that might be put into
944 the registry, the registry uses their unifying super interface.
945 Get(De)Serializer(key) method casts the super interface to desired type.
946 There is also a null check for the (de)serializer received from the
947 registry. If the deserializer wasn’t found, ``NullPointerException``
948 with key description is thrown.
955 **Possible openflow extensions and their keys**
957 There are three vendor specific extensions in Openflow v1.0 and eight in
958 Openflow v1.3. These extensions are registered under registration keys,
959 that are shown in table below:
961 +----------------+---------+------------------------------+-----------------------+
962 | Extension type | OpenFlo | Registration key | Utility class |
964 +================+=========+==============================+=======================+
965 | Vendor message | 1.0 | ExperimenterIdDeserializerKe | ExperimenterDeseriali |
966 | | | y(1, | zerKeyFactory |
967 | | | experimenterId, | |
968 | | | ExperimenterMessage.class) | |
969 +----------------+---------+------------------------------+-----------------------+
970 | Action | 1.0 | ExperimenterActionDeserializ | . |
972 | | | experimenter ID) | |
973 +----------------+---------+------------------------------+-----------------------+
974 | Stats message | 1.0 | ExperimenterMultipartReplyMe | ExperimenterDeseriali |
975 | | | ssageDeserializerKey(1, | zerKeyFactory |
976 | | | experimenter ID) | |
977 +----------------+---------+------------------------------+-----------------------+
978 | Experimenter | 1.3 | ExperimenterIdDeserializerKe | ExperimenterDeseriali |
979 | message | | y(4, | zerKeyFactory |
980 | | | experimenterId, | |
981 | | | ExperimenterMessage.class) | |
982 +----------------+---------+------------------------------+-----------------------+
983 | Match entry | 1.3 | MatchEntryDeserializerKey(4, | . |
984 | | | (number) ${oxm\_class}, | |
985 | | | (number) ${oxm\_field}); | |
986 +----------------+---------+------------------------------+-----------------------+
987 | | | key.setExperimenterId(experi | . |
990 +----------------+---------+------------------------------+-----------------------+
991 | Action | 1.3 | ExperimenterActionDeserializ | . |
993 | | | experimenter ID) | |
994 +----------------+---------+------------------------------+-----------------------+
995 | Instruction | 1.3 | ExperimenterInstructionDeser | . |
996 | | | ializerKey(4, | |
997 | | | experimenter ID) | |
998 +----------------+---------+------------------------------+-----------------------+
999 | Multipart | 1.3 | ExperimenterIdDeserializerKe | ExperimenterDeseriali |
1000 | | | y(4, | zerKeyFactory |
1001 | | | experimenterId, | |
1002 | | | MultipartReplyMessage.class) | |
1003 +----------------+---------+------------------------------+-----------------------+
1004 | Multipart - | 1.3 | ExperimenterIdDeserializerKe | ExperimenterDeseriali |
1005 | Table features | | y(4, | zerKeyFactory |
1006 | | | experimenterId, | |
1007 | | | TableFeatureProperties.class | |
1009 +----------------+---------+------------------------------+-----------------------+
1010 | Error | 1.3 | ExperimenterIdDeserializerKe | ExperimenterDeseriali |
1011 | | | y(4, | zerKeyFactory |
1012 | | | experimenterId, | |
1013 | | | ErrorMessage.class) | |
1014 +----------------+---------+------------------------------+-----------------------+
1015 | Queue property | 1.3 | ExperimenterIdDeserializerKe | ExperimenterDeseriali |
1016 | | | y(4, | zerKeyFactory |
1017 | | | experimenterId, | |
1018 | | | QueueProperty.class) | |
1019 +----------------+---------+------------------------------+-----------------------+
1020 | Meter band | 1.3 | ExperimenterIdDeserializerKe | ExperimenterDeseriali |
1021 | type | | y(4, | zerKeyFactory |
1022 | | | experimenterId, | |
1023 | | | MeterBandExperimenterCase.cl | |
1025 +----------------+---------+------------------------------+-----------------------+
1027 Table: **Deserialization**
1031 **Possible openflow extensions and their keys**
1033 There are three vendor specific extensions in Openflow v1.0 and seven
1034 Openflow v1.3. These extensions are registered under registration keys,
1035 that are shown in table below:
1037 +----------------+---------+------------------------------+-----------------------+
1038 | Extension type | OpenFlo | Registration key | Utility class |
1040 +================+=========+==============================+=======================+
1041 | Vendor message | 1.0 | ExperimenterIdSerializerKey< | ExperimenterSerialize |
1042 | | | >(1, | rKeyFactory |
1043 | | | experimenterId, | |
1044 | | | ExperimenterInput.class) | |
1045 +----------------+---------+------------------------------+-----------------------+
1046 | Action | 1.0 | ExperimenterActionSerializer | . |
1048 | | | experimenterId, sub-type) | |
1049 +----------------+---------+------------------------------+-----------------------+
1050 | Stats message | 1.0 | ExperimenterMultipartRequest | ExperimenterSerialize |
1051 | | | SerializerKey(1, | rKeyFactory |
1052 | | | experimenter ID) | |
1053 +----------------+---------+------------------------------+-----------------------+
1054 | Experimenter | 1.3 | ExperimenterIdSerializerKey< | ExperimenterSerialize |
1055 | message | | >(4, | rKeyFactory |
1056 | | | experimenterId, | |
1057 | | | ExperimenterInput.class) | |
1058 +----------------+---------+------------------------------+-----------------------+
1059 | Match entry | 1.3 | MatchEntrySerializerKey<>(4, | . |
1060 | | | (class) ${oxm\_class}, | |
1061 | | | (class) ${oxm\_field}); | |
1062 +----------------+---------+------------------------------+-----------------------+
1063 | | | key.setExperimenterId(experi | . |
1066 +----------------+---------+------------------------------+-----------------------+
1067 | Action | 1.3 | ExperimenterActionSerializer | . |
1069 | | | experimenterId, sub-type) | |
1070 +----------------+---------+------------------------------+-----------------------+
1071 | Instruction | 1.3 | ExperimenterInstructionSeria | . |
1072 | | | lizerKey(4, | |
1073 | | | experimenter ID) | |
1074 +----------------+---------+------------------------------+-----------------------+
1075 | Multipart | 1.3 | ExperimenterIdSerializerKey< | ExperimenterSerialize |
1076 | | | >(4, | rKeyFactory |
1077 | | | experimenterId, | |
1078 | | | MultipartRequestExperimenter | |
1079 | | | Case.class) | |
1080 +----------------+---------+------------------------------+-----------------------+
1081 | Multipart - | 1.3 | ExperimenterIdSerializerKey< | ExperimenterSerialize |
1082 | Table features | | >(4, | rKeyFactory |
1083 | | | experimenterId, | |
1084 | | | TableFeatureProperties.class | |
1086 +----------------+---------+------------------------------+-----------------------+
1087 | Meter band | 1.3 | ExperimenterIdSerializerKey< | ExperimenterSerialize |
1088 | type | | >(4, | rKeyFactory |
1089 | | | experimenterId, | |
1090 | | | MeterBandExperimenterCase.cl | |
1092 +----------------+---------+------------------------------+-----------------------+
1094 Table: **Serialization**