1 == OpenFlow Protocol Library Developer Guide
4 OpenFlow Protocol Library is component in OpenDaylight, that mediates communication
5 between OpenDaylight controller and hardware devices supporting OpenFlow protocol.
6 Primary goal is to provide user (or upper layers of OpenDaylight) communication
7 channel, that can be used for managing network hardware devices.
10 There are three features inside openflowjava:
12 * *odl-openflowjava-protocol* provides all openflowjava bundles, that are needed
13 for communication with openflow devices. It ensures message translation and
14 handles network connections. It also provides openflow protocol specific
16 * *odl-openflowjava-all* currently contains only odl-openflowjava-protocol feature.
17 * *odl-openflowjava-stats* provides mechanism for message counting and reporting.
18 Can be used for performance analysis.
20 === odl-openflowjava-protocol Architecture
21 Basic bundles contained in this feature are openflow-protocol-api,
22 openflow-protocol-impl, openflow-protocol-spi and util.
24 * *openflow-protocol-api* - contains openflow model, constants and keys used for
25 (de)serializer registration.
26 * *openflow-protocol-impl* - contains message factories, that translate binary
27 messages into DataObjects and vice versa. Bundle also contains network connection
28 handlers - servers, netty pipeline handlers, ...
29 * *openflow-protocol-spi* - entry point for openflowjava configuration,
30 startup and close. Basically starts implementation.
31 * *util* - utility classes for binary-Java conversions and to ease experimenter
34 === odl-openflowjava-stats Feature
35 Runs over odl-openflowjava-protocol. It counts various message types / events
36 and reports counts in specified time periods. Statistics collection can be
37 configured in openflowjava-config/src/main/resources/45-openflowjava-stats.xml
39 === Key APIs and Interfaces
40 Basic API / SPI classes are ConnectionAdapter (Rpc/notifications) and
41 SwitchConnectionProcider (configure, start, shutdown)
43 //=== API Reference Documentation
44 //Provide links to JavaDoc, REST API documentation, etc. [TBD]
47 Pull the code and import project into your IDE.
49 git clone ssh://<username>@git.opendaylight.org:29418/openflowjava.git
52 Current implementation allows to configure:
54 * listening port (mandatory)
55 * transfer protocol (mandatory)
56 * switch idle timeout (mandatory)
57 * TLS configuration (optional)
58 * thread count (optional)
60 You can find exemplary Openflow Protocol Library instance configuration below:
62 <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
63 <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
64 <!-- default OF-switch-connection-provider (port 6633) -->
66 <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
67 <name>openflow-switch-connection-provider-default-impl</name>
69 <!-- Possible transport-protocol options: TCP, TLS, UDP -->
70 <transport-protocol>TCP</transport-protocol>
71 <switch-idle-timeout>15000</switch-idle-timeout>
72 <!-- Exemplary TLS configuration:
73 - uncomment the <tls> tag
74 - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
75 files into your virtual machine
76 - set VM encryption options to use copied keys
78 Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
79 for detailed information regarding TLS -->
81 <keystore>/exemplary-ctlKeystore</keystore>
82 <keystore-type>JKS</keystore-type>
83 <keystore-path-type>CLASSPATH</keystore-path-type>
84 <keystore-password>opendaylight</keystore-password>
85 <truststore>/exemplary-ctlTrustStore</truststore>
86 <truststore-type>JKS</truststore-type>
87 <truststore-path-type>CLASSPATH</truststore-path-type>
88 <truststore-password>opendaylight</truststore-password>
89 <certificate-password>opendaylight</certificate-password>
91 <!-- Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
93 <boss-threads>2</boss-threads>
94 <worker-threads>8</worker-threads>
99 <!-- default OF-switch-connection-provider (port 6653) -->
101 <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider:impl">prefix:openflow-switch-connection-provider-impl</type>
102 <name>openflow-switch-connection-provider-legacy-impl</name>
104 <!-- Possible transport-protocol options: TCP, TLS, UDP -->
105 <transport-protocol>TCP</transport-protocol>
106 <switch-idle-timeout>15000</switch-idle-timeout>
107 <!-- Exemplary TLS configuration:
108 - uncomment the <tls> tag
109 - copy exemplary-switch-privkey.pem, exemplary-switch-cert.pem and exemplary-cacert.pem
110 files into your virtual machine
111 - set VM encryption options to use copied keys
112 - start communication
113 Please visit OpenflowPlugin or Openflow Protocol Library#Documentation wiki pages
114 for detailed information regarding TLS -->
116 <keystore>/exemplary-ctlKeystore</keystore>
117 <keystore-type>JKS</keystore-type>
118 <keystore-path-type>CLASSPATH</keystore-path-type>
119 <keystore-password>opendaylight</keystore-password>
120 <truststore>/exemplary-ctlTrustStore</truststore>
121 <truststore-type>JKS</truststore-type>
122 <truststore-path-type>CLASSPATH</truststore-path-type>
123 <truststore-password>opendaylight</truststore-password>
124 <certificate-password>opendaylight</certificate-password>
126 <!-- Exemplary thread model configuration. Uncomment <threads> tag below to adjust default thread model -->
128 <boss-threads>2</boss-threads>
129 <worker-threads>8</worker-threads>
135 <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:common:config:impl">prefix:openflow-provider-impl</type>
136 <name>openflow-provider-impl</name>
137 <openflow-switch-connection-provider>
138 <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
139 <name>openflow-switch-connection-provider-default</name>
140 </openflow-switch-connection-provider>
141 <openflow-switch-connection-provider>
142 <type xmlns:ofSwitch="urn:opendaylight:params:xml:ns:yang:openflow:switch:connection:provider">ofSwitch:openflow-switch-connection-provider</type>
143 <name>openflow-switch-connection-provider-legacy</name>
144 </openflow-switch-connection-provider>
145 <binding-aware-broker>
146 <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
147 <name>binding-osgi-broker</name>
148 </binding-aware-broker>
152 Possible transport-protocol options:
158 Switch-idle timeout specifies time needed to detect idle state of switch. When
159 no message is received from switch within this time, upper layers are notified
161 To be able to use this exemplary TLS configuration:
163 * uncomment the +<tls>+ tag
164 * copy _exemplary-switch-privkey.pem_, _exemplary-switch-cert.pem_ and
165 _exemplary-cacert.pem_ files into your virtual machine
166 * set VM encryption options to use copied keys (please visit TLS support wiki page
167 for detailed information regarding TLS)
168 * start communication
170 Thread model configuration specifies how many threads are desired to perform
171 Netty's I/O operations.
173 * boss-threads specifies the number of threads that register incoming connections
174 * worker-threads specifies the number of threads performing read / write
175 (+ serialization / deserialization) operations.
180 ==== Public API +(openflow-protocol-api)+
181 Set of interfaces and builders for immutable data transfer objects representing
182 Openflow Protocol structures.
184 Transfer objects and service APIs are infered from several YANG models
185 using code generator to reduce verbosity of definition and repeatibility of code.
187 The following YANG modules are defined:
189 * openflow-types - defines common Openflow specific types
190 * openflow-instruction - defines base Openflow instructions
191 * openflow-action - defines base Openflow actions
192 * openflow-augments - defines object augmentations
193 * openflow-extensible-match - defines Openflow OXM match
194 * openflow-protocol - defines Openflow Protocol messages
195 * system-notifications - defines system notification objects
196 * openflow-configuration - defines structures used in ConfigSubsystem
198 This modules also reuse types from following YANG modules:
200 * ietf-inet-types - IP adresses, IP prefixes, IP-protocol related types
201 * ietf-yang-types - Mac Address, etc.
203 The use of predefined types is to make APIs contracts more safe, better readable
204 and documented (e.g using MacAddress instead of byte array...)
206 ==== TCP Channel pipeline +(openflow-protocol-impl)+
208 Creates channel processing pipeline based on configuration and support.
210 .TCP Channel pipeline
211 imageopenflowjava/500px-TCPChannelPipeline.png[width=500]
213 .Switch Connection Provider
214 Implementation of connection point for other projects. Library exposes its
215 functionality through this class.
216 Library can be configured, started and shutdowned here. There are also methods
217 for custom (de)serializer registration.
219 .Tcp Connection Initializer
220 In order to initialize TCP connection to a device (switch), OF Plugin calls method
221 +initiateConnection()+ in +SwitchConnectionProvider+. This method in turn initializes
222 (Bootstrap) server side channel towards the device.
225 Represents single server that is handling incoming connections over TCP / TLS protocol.
226 TCP Handler creates a single instance of TCP Channel Initializer that will initialize
227 channels. After that it binds to configured InetAddress and port. When a new
228 device connects, TCP Handler registers its channel and passes control to
229 TCP Channel Initializer.
231 .TCP Channel Initializer
232 This class is used for channel initialization / rejection and passing arguments.
233 After a new channel has been registered it calls Switch Connection Handler's
234 (OF Plugin) accept method to decide if the library should keep the newly registered
235 channel or if the channel should be closed. If the channel has been accepted,
236 TCP Channel Initializer creates the whole pipeline with needed handlers and also
237 with ConnectionAdapter instance. After the channel pipeline is ready, Switch
238 Connection Handler is notified with +onConnectionReady+ notification.
239 OpenFlow Plugin can now start sending messages downstream.
242 If there are no messages received for more than time specified, this handler
243 triggers idle state notification.
244 The switch idle timeout is received as a parameter from ConnectionConfiguration
245 settings. Idle State Handler is inactive while there are messages received within
246 the switch idle timeout. If there are no messages received for more than timeout
247 specified, handler creates SwitchIdleEvent message and sends it upstream.
250 It encrypts and decrypts messages over TLS protocol.
251 Engaging TLS Handler into pipeline is matter of configuration (+<tls>+ tag).
252 TLS communication is either unsupported or required. TLS Handler is represented
253 as a Netty's SslHandler.
256 Parses input stream into correct length message frames for further processing.
257 Framing is based on Openflow header length. If received message is shorter than
258 minimal length of OpenFlow message (8 bytes), OF Frame Decoder waits for more data.
259 After receiving at least 8 bytes the decoder checks length in OpenFlow header.
260 If there are still some bytes missing, the decoder waits for them. Else the OF
261 Frame Decoder sends correct length message to next handler in the channel pipeline.
264 Detects version of used OpenFlow Protocol and discards unsupported version messages.
265 If the detected version is supported, OF Version Detector creates
266 +VersionMessageWrapper+ object containing the detected version and byte message
267 and sends this object upstream.
270 Chooses correct deserilization factory (based on message type) and deserializes
271 messages into generated DTOs (Data Transfer Object).
272 OF Decoder receives +VersionMessageWrapper+ object and passes it to
273 +DeserializationFactory+ which will return translated DTO. +DeserializationFactory+
274 creates +MessageCodeKey+ object with version and type of received message and
275 Class of object that will be the received message deserialized into. This object
276 is used as key when searching for appropriate decoder in +DecoderTable+.
277 +DecoderTable+ is basically a map storing decoders. Found decoder translates
278 received message into DTO. If there was no decoder found, null is returned. After
279 returning translated DTO back to OF Decoder, the decoder checks if it is null or not.
280 When the DTO is null, the decoder logs this state and throws an Exception. Else it
281 passes the DTO further upstream. Finally, the OF Decoder releases ByteBuf containing
282 received and decoded byte message.
285 Chooses correct serialization factory (based on type of DTO) and serializes DTOs
287 OF Encoder does the opposite than the OF Decoder using the same principle.
288 OF Encoder receives DTO, passes it for translation and if the result is not null,
289 it sends translated DTO downstream as a ByteBuf. Searching for appropriate encoder
290 is done via MessageTypeKey, based on version and class of received DTO.
292 .Delegating Inbound Handler
293 Delegates received DTOs to Connection Adapter.
294 It also reacts on channelInactive and channelUnregistered events. Upon one of
295 these events is triggered, DelegatingInboundHandler creates DisconnectEvent message
296 and sends it upstream, notifying upper layers about switch disconnection.
298 .Channel Outbound Queue
299 Message flushing handler.
300 Stores outgoing messages (DTOs) and flushes them. Flush is performed based on time
301 expired and on the number of messages enqueued.
304 Provides a facade on top of pipeline, which hides netty.io specifics. Provides a
305 set of methods to register for incoming messages and to send messages to particular
307 ConnectionAdapterImpl basically implements three interfaces (unified in one
308 superinterface ConnectionFacade):
312 * OpenflowProtocolService
315 *ConnectionAdapter* interface has methods for setting up listeners (message,
316 system and connection ready listener), method to check if all listeners are set,
317 checking if the channel is alive and disconnect method. Disconnect method clears
318 responseCache and disables consuming of new messages.
320 *MessageConsumer* interface holds only one method: +consume()+. +Consume()+ method
321 is called from DelegatingInboundHandler. This method processes received DTO's based
322 on their type. There are three types of received objects:
324 * System notifications - invoke system notifications in OpenFlow Plugin
325 (systemListener set). In case of +DisconnectEvent+ message, the Connection Adapter
326 clears response cache and disables consume() method processing,
327 * OpenFlow asynchronous messages (from switch) - invoke corresponding notifications
329 * OpenFlow symmetric messages (replies to requests) - create +RpcResponseKey+
330 with XID and DTO's class set. This +RpcResponseKey+ is then used to find
331 corresponding future object in responseCache. Future object is set with success
332 flag, received message and errors (if any occurred). In case no corresponding
333 future was found in responseCache, Connection Adapter logs warning and discards
334 the message. Connection Adapter also logs warning when an unknown DTO is received.
336 *OpenflowProtocolService* interface contains all rpc-methods for sending messages
337 from upper layers (OpenFlow Plugin) downstream and responding. Request messages
338 return Future filled with expected reply message, otherwise the expected Future
342 MultipartRequest message is the only exception. Basically it is request - reply
343 Message type, but it wouldn't be able to process more following MultipartReply
344 messages if this was implemented as rpc (only one Future). This is why MultipartReply
345 is implemented as notification. OpenFlow Plugin takes care of correct message
349 ==== UDP Channel pipeline (openflow-protocol-impl)
350 Creates UDP channel processing pipeline based on configuration and support.
351 *Switch Connection Provider*, *Channel Outbound Queue* and *Connection Adapter*
352 fulfill the same role as in case of TCP connection / channel pipeline (please
355 .UDP Channel pipeline
356 image::openflowjava/500px-UdpChannelPipeline.png[width=500]
360 Represents single server that is handling incoming connections over UDP (DTLS)
362 UDP Handler creates a single instance of UDP Channel Initializer that will
363 initialize channels. After that it binds to configured InetAddress and port.
364 When a new device connects, UDP Handler registers its channel and passes control
365 to UDP Channel Initializer.
367 .UDP Channel Initializer
368 This class is used for channel initialization and passing arguments.
369 After a new channel has been registered (for UDP there is always only one channel)
370 UDP Channel Initializer creates whole pipeline with needed handlers.
373 Haven't been implemented yet. Will take care of secure DTLS connections.
375 .OF Datagram Packet Handler
376 Combines functionality of OF Frame Decoder and OF Version Detector. Extracts
377 messages from received datagram packets and checks if message version is supported.
378 If there is a message received from yet unknown sender, OF Datagram Packet Handler
379 creates Connection Adapter for this sender and stores it under sender's address in
380 +UdpConnectionMap+. This map is also used for sending the messages and for correct
381 Connection Adapter lookup - to delegate messages from one channel to multiple sessions.
383 .OF Datagram Packet Decoder
384 Chooses correct deserilization factory (based on message type) and deserializes
385 messages into generated DTOs.
386 OF Decoder receives +VersionMessageUdpWrapper+ object and passes it to
387 +DeserializationFactory+ which will return translated DTO. +DeserializationFactory+
388 creates +MessageCodeKey+ object with version and type of received message and
389 Class of object that will be the received message deserialized into. This object
390 is used as key when searching for appropriate decoder in +DecoderTable+.
391 +DecoderTable+ is basically a map storing decoders. Found decoder translates
392 received message into DTO (DataTransferObject). If there was no decoder found,
393 null is returned. After returning translated DTO back to OF Datagram Packet Decoder,
394 the decoder checks if it is null or not. When the DTO is null, the decoder logs
395 this state. Else it looks up appropriate Connection Adapter in +UdpConnectionMap+
396 and passes the DTO to found Connection Adapter. Finally, the OF Decoder releases
397 +ByteBuf+ containing received and decoded byte message.
399 .OF Datagram Packet Encoder
400 Chooses correct serialization factory (based on type of DTO) and serializes DTOs
402 OF Datagram Packet Encoder does the opposite than the OF Datagram Packet Decoder
403 using the same principle. OF Encoder receives DTO, passes it for translation and
404 if the result is not null, it sends translated DTO downstream as a datagram packet.
405 Searching for appropriate encoder is done via MessageTypeKey, based on version
406 and class of received DTO.
408 ==== SPI (openflow-protocol-spi)
409 Defines interface for library's connection point for other projects. Library
410 exposes its functionality through this interface.
412 ==== Integration test (openflow-protocol-it)
413 Testing communication with simple client.
415 ==== Simple client(simple-client)
416 Lightweight switch simulator - programmable with desired scenarios.
419 Contains utility classes, mainly for work with ByteBuf.
422 === Library's lifecycle
424 Steps (after the library's bundle is started):
426 * [1] Library is configured by ConfigSubsystem (adress, ports, encryption, ...)
427 * [2] Plugin injects its SwitchConnectionHandler into the Library
428 * [3] Plugin starts the Library
429 * [4] Library creates configured protocol handler (e.g. TCP Handler)
430 * [5] Protocol Handler creates Channel Initializer
431 * [6] Channel Initializer asks plugin whether to accept incoming connection on
432 each new switch connection
433 * [7] Plugin responds:
434 - true - continue building pipeline
435 - false - reject connection / disconnect channel
436 * [8] Library notifies Plugin with onSwitchConnected(ConnectionAdapter)
437 notification, passing reference to ConnectionAdapter, that will handle the connection
438 * [9] Plugin registers its system and message listeners
439 * [10] FireConnectionReadyNotification() is triggered, announcing that pipeline
440 handlers needed for communication have been created and Plugin can start
442 * [11] Plugin shutdowns the Library when desired
445 image::openflowjava/Library_lifecycle.png[width=500]
448 === Statistics collection
451 Statistics collection collects message statistics.
452 Current collected statistics (+DS+ - downstream, +US+ - upstream):
454 * +DS_ENTERED_OFJAVA+ - all messages that entered openflowjava (picked up from
456 * +DS_ENCODE_SUCCESS+ - successfully encoded messages
457 * +DS_ENCODE_FAIL+ - messages that failed during encoding (serialization) process
458 * +DS_FLOW_MODS_ENTERED+ - all flow-mod messages that entered openflowjava
459 * +DS_FLOW_MODS_SENT+ - all flow-mod messages that were successfully sent
460 * +US_RECEIVED_IN_OFJAVA+ - messages received from switch
461 * +US_DECODE_SUCCESS+ - successfully decoded messages
462 * +US_DECODE_FAIL+ - messages that failed during decoding (deserialization) process
463 * +US_MESSAGE_PASS+ - messages handed over to openflowplugin
466 In orded to start statistics, it is needed to feature:install odl-openflowjava-stats.
467 To see the logs one should use log:set DEBUG org.opendaylight.openflowjava.statistics
468 and than probably log:display (you can log:list to see if the logging has been set).
469 To adjust collection settings it is enough to modify 45-openflowjava-stats.xml.
472 JConsole provides two commands for the statistics collection:
474 * printing current statistics
475 * resetting statistic counters
477 After attaching JConsole to correct process, one only needs to go into MBeans
478 +tab -> org.opendaylight.controller -> RuntimeBean -> statistics-collection-service-impl
479 -> statistics-collection-service-impl -> Operations+ to be able to use this commands.
482 NOTE: see OpenFlow Plugin Developper Guide
488 Entry point for the extensibility is +SwitchConnectionProvider+.
489 +SwitchConnectionProvider+ contains methods for (de)serializer registration.
490 To register deserializer it is needed to use .register*Deserializer(key, impl).
491 To register serializer one must use .register*Serializer(key, impl). Registration
492 can occur either during configuration or at runtime.
494 *NOTE*: In case when experimenter message is received and no (de)serializer was
495 registered, the library will throw +IllegalArgumentException+.
498 In order to use extensions it is needed to augment existing model and register new (de)serializers.
500 Augmenting the model:
501 1. Create new augmentation
503 Register (de)serializers:
504 1. Create your (de)serializer
505 2. Let it implement +OFDeserializer<>+ / +OFSerializer<>+
506 - in case the structure you are (de)serializing needs to be used in Multipart
507 TableFeatures messages, let it implement +HeaderDeserializer<>+ / +HeaderSerializer+
508 3. Implement prescribed methods
509 4. Register your deserializer under appropriate key (in our case
510 +ExperimenterActionDeserializerKey+)
511 5. Register your serializer under appropriate key (in our case
512 +ExperimenterActionSerializerKey+)
513 6. Done, test your implementation
515 *NOTE*: If you don't know what key should be used with your (de)serializer
516 implementation, please visit <<registration_keys, Registration keys>> page.
519 Let's say we have vendor / experimenter action represented by this structure:
524 uint32_t experimenter;
530 First, we have to augment existing model. We create new module, which imports
531 "+openflow-types.yang+" (don't forget to update your +pom.xml+ with api dependency).
532 Now we create foo action identity:
534 import openflow-types {prefix oft;}
536 description "Foo action description";
537 base oft:action-base;
541 This will be used as type in our structure. Now we must augment existing action
542 structure, so that we will have the desired fields first and second. In order to
543 create new augmentation, our module has to import "+openflow-action.yang+". The
544 augment should look like this:
546 import openflow-action {prefix ofaction;}
547 augment "/ofaction:actions-container/ofaction:action" {
548 ext:augment-identifier "foo-action";
557 We are finished with model changes. Run mvn clean compile to generate sources.
558 After generation is done, we need to implement our (de)serializer.
562 public class FooActionDeserializer extends OFDeserializer<Action> {
564 public Action deserialize(ByteBuf input) {
565 ActionBuilder builder = new ActionBuilder();
566 input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we know the type of action*
567 builder.setType(Foo.class);
568 input.skipBytes(SIZE_OF_SHORT_IN_BYTES); *// we don't need length*
569 *// now create experimenterIdAugmentation - so that openflowplugin can
570 differentiate correct vendor codec*
571 ExperimenterIdActionBuilder expIdBuilder = new ExperimenterIdActionBuilder();
572 expIdBuilder.setExperimenter(new ExperimenterId(input.readUnsignedInt()));
573 builder.addAugmentation(ExperimenterIdAction.class, expIdBuilder.build());
574 FooActionBuilder fooBuilder = new FooActionBuilder();
575 fooBuilder.setFirst(input.readUnsignedShort());
576 fooBuilder.setSecond(input.readUnsignedShort());
577 builder.addAugmentation(FooAction.class, fooBuilder.build());
578 input.skipBytes(4); *// padding*
579 return builder.build();
585 public class FooActionSerializer extends OFSerializer<Action> {
587 public void serialize(Action action, ByteBuf outBuffer) {
588 outBuffer.writeShort(FOO_CODE);
589 outBuffer.writeShort(16);
590 *// we don't have to check for ExperimenterIdAction augmentation - our
592 *// was called based on the vendor / experimenter ID, so we simply write
594 outBuffer.writeInt(VENDOR / EXPERIMENTER ID);
595 FooAction foo = action.getAugmentation(FooAction.class);
596 outBuffer.writeShort(foo.getFirst());
597 outBuffer.writeShort(foo.getSecond());
598 outBuffer.writeZero(4); //write padding
602 Register both deserializer and serializer:
603 +SwitchConnectionProvider.registerDeserializer(new
604 ExperimenterActionDeserializerKey(0x04, VENDOR / EXPERIMENTER ID),
605 new FooActionDeserializer());+
606 +SwitchConnectionProvider.registerSerializer(new
607 ExperimenterActionSerializerKey(0x04, VENDOR / EXPERIMENTER ID),
608 new FooActionSerializer());+
610 We are ready to test our implementation.
612 *NOTE:* Vendor / Experimenter structures define only vendor / experimenter ID as
613 common distinguisher (besides action type). Vendor / Experimenter ID is unique
614 for all vendor messages - that's why vendor is able to register only one class
615 under ExperimenterAction(De)SerializerKey. And that's why vendor has to switch
616 / choose between his subclasses / subtypes on his own.
618 ==== Detailed walkthrough: Deserialization extensibility
620 .External interface & class description
621 *OFGeneralDeserializer:*
623 * +OFDeserializer<E extends DataObject>+
624 ** _deserialize(ByteBuf)_ - deserializes given ByteBuf
625 * +HeaderDeserializer<E extends DataObject>+
626 ** _deserializeHeaders(ByteBuf)_ - deserializes only E headers (used in Multipart
627 TableFeatures messages)
629 *DeserializerRegistryInjector*
631 * +injectDeserializerRegistry(DeserializerRegistry)+ - injects deserializer
632 registry into deserializer. Useful when custom deserializer needs access to
635 *NOTE:* DeserializerRegistryInjector is not OFGeneralDeserializer descendand.
636 It is a standalone interface.
638 *MessageCodeKey and its descendants*
639 These keys are used as for deserializer lookup in DeserializerRegistry.
640 MessageCodeKey should is used in general, while its descendants are used in more
641 special cases. For Example ActionDeserializerKey is used for Action deserializer
642 lookup and (de)registration. Vendor is provided with special keys, which contain
643 only the most necessary fields. These keys usually start with "Experimenter"
644 prefix (MatchEntryDeserializerKey is an exception).
646 MessageCodeKey has these fields:
648 * short version - Openflow wire version number
649 * int value - value read from byte message
650 * Class<?> clazz - class of object being creating
652 .Scenario walkthrough
653 * [1] The scenario starts in a custom bundle which wants to extend library's
654 functionality. The custom bundle creates deserializers which implement exposed
655 +OFDeserializer+ / +HeaderDeserializer+ interfaces (wrapped under
656 +OFGeneralDeserializer+ unifying super interface).
657 * [2] Created deserializers are paired with corresponding ExperimenterKeys,
658 which are used for deserializer lookup.
659 If you don't know what key should be used with your (de)serializer implementation,
660 please visit <<registration_keys, Registration keys>> page.
661 * [3] Paired deserializers are passed to the OF Library
662 via *SwitchConnectionProvider*._registerCustomDeserializer(key, impl)_.
663 Library registers the deserializer.
664 ** While registering, Library checks if the deserializer is an instance of
665 *DeserializerRegistryInjector* interface. If yes, the DeserializerRegistry
666 (which stores all deserializer references) is injected into the deserializer.
668 This is particularly useful when the deserializer needs access to other
669 deserializers. For example +IntructionsDeserializer+ needs access to
670 +ActionsDeserializer+ in order to be able to process
671 OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS instructions.
673 .Deserialization scenario walkthrough
674 image::openflowjava/800px-Extensibility.png[width=500]
676 ==== Detailed walkthrough: Serialization extensibility
677 .External interface & class description
679 *OFGeneralSerializer:*
681 * OFSerializer<E extends DataObject>
682 ** _serialize(E,ByteBuf)_ - serializes E into given ByteBuf
683 * +HeaderSerializer<E extends DataObject>+
684 ** _serializeHeaders(E,ByteBuf)_ - serializes E headers (used in Multipart
685 TableFeatures messages)
687 *SerializerRegistryInjector*
688 * +injectSerializerRegistry(SerializerRegistry)+ - injects serializer registry
689 into serializer. Useful when custom serializer needs access to other serializers.
691 *NOTE:* SerializerRegistryInjector is not OFGeneralSerializer descendand.
693 *MessageTypeKey and its descendants*
694 These keys are used as for serializer lookup in SerializerRegistry.
695 MessageTypeKey should is used in general, while its descendants are used in more
696 special cases. For Example ActionSerializerKey is used for Action serializer
697 lookup and (de)registration. Vendor is provided with special keys, which contain
698 only the most necessary fields. These keys usually start with "Experimenter"
699 prefix (MatchEntrySerializerKey is an exception).
701 MessageTypeKey has these fields:
703 * _short version_ - Openflow wire version number
704 * _Class<E> msgType_ - DTO class
708 * [1] Serialization extensbility principles are similar to the deserialization
709 principles. The scenario starts in a custom bundle. The custom bundle creates
710 serializers which implement exposed OFSerializer / HeaderSerializer interfaces
711 (wrapped under OFGeneralSerializer unifying super interface).
712 * [2] Created serializers are paired with their ExperimenterKeys, which are used
713 for serializer lookup.
714 If you don't know what key should be used with your serializer implementation,
715 please visit <<registration_keys, Registration keys>> page.
716 * [3] Paired serializers are passed to the OF Library via
717 *SwitchConnectionProvider*._registerCustomSerializer(key, impl)_. Library
718 registers the serializer.
719 * While registering, Library checks if the serializer is an instance of
720 *SerializerRegistryInjector* interface. If yes, the SerializerRegistry (which
721 stores all serializer references) is injected into the serializer.
723 This is particularly useful when the serializer needs access to other deserializers.
724 For example IntructionsSerializer needs access to ActionsSerializer in order to
725 be able to process OFPIT_WRITE_ACTIONS/OFPIT_APPLY_ACTIONS instructions.
727 .Serialization scenario walkthrough
728 image::openflowjava/800px-Extensibility2.png[width=500]
730 ==== Internal description
732 *SwitchConnectionProvider*
733 +SwitchConnectionProvider+ constructs and initializes both deserializer and
734 serializer registries with default (de)serializers. It also injects the
735 +DeserializerRegistry+ into the +DeserializationFactory+, the +SerializerRegistry+
736 into the +SerializationFactory+.
737 When call to register custom (de)serializer is made, +SwitchConnectionProvider+
738 calls register method on appropriate registry.
740 *DeserializerRegistry / SerializerRegistry*
741 Both registries contain init() method to initialize default (de)serializers.
742 Registration checks if key or (de)serializer implementation are not +null+. If at
743 least one of the is +null+, +NullPointerException+ is thrown. Else the
744 (de)serializer implementation is checked if it is +(De)SerializerRegistryInjector+
745 instance. If it is an instance of this interface, the registry is injected into
746 this (de)serializer implementation.
748 +GetSerializer(key)+ or +GetDeserializer(key)+ performs registry lookup. Because
749 there are two separate interfaces that might be put into the registry, the
750 registry uses their unifying super interface. Get(De)Serializer(key) method casts
751 the super interface to desired type. There is also a null check for the
752 (de)serializer received from the registry. If the deserializer wasn't found,
753 +NullPointerException+ with key description is thrown.
756 [[registration_keys]]
757 ==== Registration keys
761 *Possible openflow extensions and their keys*
763 There are three vendor specific extensions in Openflow v1.0 and eight in
764 Openflow v1.3. These extensions are registered under registration keys,
765 that are shown in table below:
768 [options="header",cols="20%,10%,40%,30%"]
769 |========================================================================================================================================================
770 |Extension type |OpenFlow|Registration key |Utility class
771 |Vendor message |1.0 |ExperimenterIdDeserializerKey(1, experimenterId, ExperimenterMessage.class) |ExperimenterDeserializerKeyFactory
772 |Action |1.0 |ExperimenterActionDeserializerKey(1, experimenter ID) |.
773 |Stats message |1.0 |ExperimenterMultipartReplyMessageDeserializerKey(1, experimenter ID) |ExperimenterDeserializerKeyFactory
774 |Experimenter message |1.3 |ExperimenterIdDeserializerKey(4, experimenterId, ExperimenterMessage.class) |ExperimenterDeserializerKeyFactory
775 |Match entry |1.3 |MatchEntryDeserializerKey(4, (number) ${oxm_Class}, (number) ${oxm_Field}); |.
776 | | |key.setExperimenterId(experimenter ID); |.
777 |Action |1.3 |ExperimenterActionDeserializerKey(4, experimenter ID) |.
778 |Instruction |1.3 |ExperimenterInstructionDeserializerKey(4, experimenter ID) |.
779 |Multipart |1.3 |ExperimenterIdDeserializerKey(4, experimenterId, MultipartReplyMessage.class) |ExperimenterDeserializerKeyFactory
780 |Multipart - Table features|1.3 |ExperimenterIdDeserializerKey(4, experimenterId, TableFeatureProperties.class) |ExperimenterDeserializerKeyFactory
781 |Error |1.3 |ExperimenterIdDeserializerKey(4, experimenterId, ErrorMessage.class) |ExperimenterDeserializerKeyFactory
782 |Queue property |1.3 |ExperimenterIdDeserializerKey(4, experimenterId, QueueProperty.class) |ExperimenterDeserializerKeyFactory
783 |Meter band type |1.3 |ExperimenterIdDeserializerKey(4, experimenterId, MeterBandExperimenterCase.class)|ExperimenterDeserializerKeyFactory
784 |========================================================================================================================================================
788 *Possible openflow extensions and their keys*
790 There are three vendor specific extensions in Openflow v1.0 and seven Openflow
791 v1.3. These extensions are registered under registration keys, that are shown in
796 [options="header",cols="20%,10%,40%,30%"]
797 |=============================================================================================================================================================
798 |Extension type |OpenFlow|Registration key |Utility class
799 |Vendor message |1.0 |ExperimenterIdSerializerKey<>(1, experimenterId, ExperimenterInput.class) |ExperimenterSerializerKeyFactory
800 |Action |1.0 |ExperimenterActionSerializerKey(1, experimenterId, sub-type) |.
801 |Stats message |1.0 |ExperimenterMultipartRequestSerializerKey(1, experimenter ID) |ExperimenterSerializerKeyFactory
802 |Experimenter message |1.3 |ExperimenterIdSerializerKey<>(4, experimenterId, ExperimenterInput.class) |ExperimenterSerializerKeyFactory
803 |Match entry |1.3 |MatchEntrySerializerKey<>(4, (class) ${oxm_Class}, (class) ${oxm_Field}); |.
804 | | |key.setExperimenterId(experimenter ID) |.
805 |Action |1.3 |ExperimenterActionSerializerKey(4, experimenterId, sub-type) |.
806 |Instruction |1.3 |ExperimenterInstructionSerializerKey(4, experimenter ID) |.
807 |Multipart |1.3 |ExperimenterIdSerializerKey<>(4, experimenterId, MultipartRequestExperimenterCase.class)|ExperimenterSerializerKeyFactory
808 |Multipart - Table features|1.3 |ExperimenterIdSerializerKey<>(4, experimenterId, TableFeatureProperties.class) |ExperimenterSerializerKeyFactory
809 |Meter band type |1.3 |ExperimenterIdSerializerKey<>(4, experimenterId, MeterBandExperimenterCase.class) |ExperimenterSerializerKeyFactory
810 |=============================================================================================================================================================