Changes between Version 9 and Version 10 of Internal/OpenFlow/VendorTutorial


Ignore:
Timestamp:
Jan 4, 2013, 11:46:33 PM (11 years ago)
Author:
akoshibe
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Internal/OpenFlow/VendorTutorial

    v9 v10  
    2121Where the numbers in the parenthesis denote the field size in bytes. The vendor message header and payload in combination make up the full payload of the !OpenFlow message, the vendor data.
    2222
    23 == 2. In openflowj == #iface
    24 In openflowj, the base interface and framework needed for creating vendor messages are found in the package org.openflow.protocol.vendor. Specifically, openflowj provides developers with a way to define custom vendor data classes. 
    25 
     23== 2. openflowj == #iface
     24In openflowj, the base interface and framework needed for creating vendor messages are found in the package org.openflow.protocol.vendor. Specifically, openflowj provides developers with a way to define custom vendor data classes. We start with a brief overview of the interface provided for this purpose and the general approach used for vendor data implementation, after which we describe the detailed requirements for implementing vendor data.   
     25
     26==== Interface OFVendorData ====
    2627`OFVendorData` is the Java interface required by any class defining custom vendor data. `OFVendorData` imposes just a few methods on a class implementing it:
    2728 
     
    3233These methods are needed for the serialization/deserialization of the message.
    3334
    34 === 2.1 The Vendor Message Header ===
    35 What we refer to as the "vendor message header" is typically a set of variables defined in the class that implements `OFVendorData`. The class in which these variables are declared typically becomes the base class for further message types.
     35==== Structuring the code ====
     36A custom vendor message is comprised of the !OpenFlow header that identifies it as a vendor type message, and the vendor data. The vendor data is usually implemented as a collection of classes - one base class that implements `OFVendorData` and defines the Vendor ID, and the various subclasses that extend the base class to implement the individual message types.   
     37
     38This organization isn't a requirement, but makes code reuse easier. The "nesting" of message classes can be thought of as the implementation of the message structure in layers - Each subclass implements either 1) message components that are encapsulated by components in its parent class, and/or 2) methods that assign values to variables declared in its super-classes. We will see the this in the code snippets to follow.
     39
     40=== 2.1 The Nicira vendor messages ===
     41As mentioned earlier, the Nicira vendor messages implement newer protocol features in an older version of !OpenFlow that does not support it. This vendor message's two message types are implemented across four classes with the following parent-child relations:
     42{{{
     43                                                             
     44    OFNiciraVendorData--->OFRoleVendorData--+--->OFRoleRequestVendorData
     45                                            |
     46    ---> = "parent class of"                +--->OFRoleReplyVendorData
     47
     48}}}
     49Where `OFNiciraVendorData` is the base class implementing `OFVendorData`, and `OFRoleRequestVendorData` and `OFRoleReplyVendorData` implement the two message types. This chart will hopefully make more sense with the following sections.
     50
     51=== 2.1 Vendor Data Components ===
     52What we refer to as the "vendor message header" is typically a set of variables defined in the class that implements `OFVendorData`.
    3653
    3754For example, the vendor ID and data type are part of `OFNiciraVendorData`, the base class for all Nicira vendor messages:
     
    4764...
    4865}}}
    49 `OFNiciraVendorData` is extended to implement the Request and Reply message types (`OFRoleRequestVendorData` and `OFRoleReplyVendorData`, respectively). Note how at (1) the value of dataType is not set within this class - this value is set through the base class's constructor, which takes an integer value for the dataType:
     66As explained in the start of the section, `OFNiciraVendorData` is extended to implement the Request and Reply message types (`OFRoleRequestVendorData` and `OFRoleReplyVendorData`, respectively). Note how at (1) the value of dataType is not set within this class - this value is set through the base class's constructor, which takes an integer value:
    5067{{{
    5168   /**
     
    7390If we trace back, we learn that super() above refers to the constructor of `OFRoleVendorData`, a subclass of `OFNiciraVendorData`. `OFRoleVendorData` takes the value passed to it by `OFRoleReplyVendorData` and passes it to the constructor of its parent class.
    7491
    75 This organization isn't a requirement, but makes code reuse easier. The "nesting" of message classes can be thought of as implementing the message structure in layers - Each subclass implements either message components that are encapsulated by components in its parent class, or methods that assign values to variables declared in its super-classes, like in the example above.   
    76 
    77 The Vendor ID and data type are the only requirements in terms of vendor header content. Given that the methods required by `OFVendorData` are provided, along with those required for message registration, the message implementation may be structured as needed. The usual additions are various message fields and their getters and setters. 
     92You may wonder what the purpose of `OFRoleVendorData` is - for now, it is okay to think of it as the class that defines the single-integer vendor data payload common to both Request and Reply message types, and the values that it can take ("role values" as commented below):
     93{{{
     94    public class OFRoleVendorData extends OFNiciraVendorData {
     95   
     96        /**
     97         * Role value indicating that the controller is in the OTHER role.
     98         */
     99        public static final int NX_ROLE_OTHER = 0;
     100   
     101        /**
     102         * Role value indicating that the controller is in the MASTER role.
     103         */
     104        public static final int NX_ROLE_MASTER = 1;
     105   
     106        /**
     107         * Role value indicating that the controller is in the SLAVE role.
     108         */
     109        public static final int NX_ROLE_SLAVE = 2;
     110
     111        protected int role;
     112
     113     ...
     114}}}
     115There is more to this class, but that is covered later, under [#serial serialization].   
     116
     117The Vendor ID and data type are the only requirements in terms of vendor header content. Given that the methods required by `OFVendorData` are provided, along with those required for message registration (which we cover next), the message implementation may be structured as needed.
    78118
    79119=== 2.2 Message Registration ===
     
    139179As an example, we can look at `OFRoleVendorData`, the parent class of the Nicira Request and Reply messages. Since both messages only differ in the values of the ''role'' field, the readFrom(), writeTo(), and getLength() methods for the final packet structure are defined in this class. The message structure looks like this:
    140180{{{
    141                      |<-Vendor ID->||<-dataType->|<----payload(4)--->|                             
     181                     |<-Vendor ID->||<-dataType->||<---payload(4)--->|                             
    142182    [OpenFlow Header][ 0x00002320  ][    10|11   ][      0|1|2       ]
    143183}}}
     
    181221    }
    182222}}}
    183 
    184 == 3. The full Vendor Message ==
    185 This section describes how to construct the full vendor type message once we have a structured vendor data object, or alternatively, parse a vendor type message containing a known type of vendor data. 
     223Now that we have vendor data, we can complete the full !OpenFlow message -- which brings us to the next section. 
     224
     225== 3. The OpenFlow Message ==
     226This section describes how to construct the full vendor type message once we have a structured vendor data object, or alternatively, parse a vendor type message containing a known (e.g. registered) type of vendor data.   
     227
     228The class `OFVendor` implements the full !OpenFlow vendor message. `OFVendor` itself is a subclass of `OFMessage`, which implements the generic !OpenFlow message header. As with other !OpenFlow messages, the vendor messages are received by the controller via the `OFChannelHandler`, an inner class of `Controller`, and sent out via the ''write()'' methods in `OFSwitchImpl`, the class that represents a switch connected to the controller. Description of the full system is better left to another tutorial, as it will detract from our main topic. 
    186229
    187230=== 3.1 Message construction ===
    188 The vendor data is just the payload of an !OpenFlow vendor type message. The !OpenFlow header must be added to the vendor data before it can be sent out on the channel.   
     231We sidestep from the Nicira messages for this section to look at `OFVendorTest`, the unit test for Vendor messages, found in `/src/test/java` of Floodlight source. Aside from their [http://www.openflowhub.org/display/floodlightcontroller/Unit+Tests intended purpose], the unit tests are fairly good references for figuring out how various components of the controller and openflowj are instantiated and/or used.
     232
     233We can reference the first few lines of ''testVendorData()'' of `OFVendorTest` to see how a vendor message may be constructed in full.
     234{{{
     235    OFVendor msg = makeVendorMessage(ACME_VENDOR_ID);                         (1)
     236    OFVendorData vendorData = new AcmeVendorData1((short)11, (short)22);      (2)
     237    msg.setVendorData(vendorData);                                            (3)
     238    msg.setLengthU(OFVendor.MINIMUM_LENGTH + vendorData.getLength());         (4)
     239}}}
     240We see four basic steps to the process:
     241 1. Create an "empty" OFVendor message. the method ''makeVendorMessage()'' is a helper function:
     242 {{{
     243     private OFVendor makeVendorMessage(int vendor) {
     244         OFVendor msg = (OFVendor) messageFactory.getMessage(OFType.VENDOR);   (1)
     245         msg.setVendorDataFactory(new BasicFactory());                         (2)
     246         msg.setVendor(vendor);                                                (3)
     247         return msg;
     248     }
     249 }}}
     250 This function is (1) wired to an !OpenFlow message factory that supplies us with the empty OFVendor message, and also does some prep work on the OFVendor message, such as (2) setting its message factory and (3) the Vendor ID field.   
     251 2. Create the vendor data, and set its fields as needed.
     252 3. set the vendor data of the message created in 1. to the vendor data created in 2.
     253 4. set the length of the total message. OFVendor.MINIMUM_LENGTH counts just the length of the !OpenFlow header (8 bytes) and the Vendor ID and is 12 bytes. 
     254
     255At this point, the message is ready to be sent. Sending the message to a switch is a one-liner, in theory:
     256{{{
     257    sw.write(msg, null);
     258}}}
    189259
    190260=== 3.2 Reading message contents ===
     261`OFVendor` provides the following methods for deconstructing a message:
     262 * getVendor() : return the Vendor ID
     263 * getVendorData() : return the vendor data
     264Once the vendor data is extracted, it may be cast to the appropriate message type for further parsing. A good reference for this process is the method ''handleVendorMessage()'', found in `OFChannelHandler`. It extracts the dataType from the message, and passes it to a switch statement to cast it to the correct message type and to pass it to appropriate methods:
     265{{{
     266    int dataType = niciraVendorData.getDataType();
     267    switch (dataType) {
     268        case OFRoleReplyVendorData.NXT_ROLE_REPLY:
     269             OFRoleReplyVendorData roleReplyVendorData =
     270                    (OFRoleReplyVendorData) niciraVendorData;
     271             handleRoleReplyMessage(vendorMessage,
     272                                   roleReplyVendorData);
     273             break;
     274        default:
     275             log.warn("Unhandled Nicira VENDOR message; " +
     276                      "data type = {}", dataType);
     277             break;
     278    }
     279}}}
     280This method processes all vendor messages that the controller receives. As it is currently implemented to handle only Nicira vendor messages, it must be modified to also handle your messages. This can be as simple as adding your Vendor ID to the first switch statement in the method:
     281{{{
     282    int vendor = vendorMessage.getVendor();
     283    switch (vendor) {
     284        case OFNiciraVendorData.NX_VENDOR_ID:
     285         ...
     286             break;
     287        case YourVendorData.VENDOR_ID:
     288         ....
     289             break;
     290        default:
     291            log.warn("Unhandled VENDOR message; vendor id = {}", vendor);
     292            break;
     293    }
     294}}}