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