Skip to content
Snippets Groups Projects
Commit 3d09ffa4 authored by Andreas Wundsam's avatar Andreas Wundsam
Browse files

openflowj: added parsing infrastructure for custom Vendor Action classes

parent 73e8266b
No related branches found
No related tags found
No related merge requests found
Showing
with 328 additions and 15 deletions
/**
* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
* University
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
......@@ -94,7 +94,7 @@ public enum OFActionType {
VENDOR (0xffff, OFActionVendor.class, new Instantiable<OFAction>() {
@Override
public OFAction instantiate() {
return new OFActionVendor();
return new OFActionVendorGeneric();
}});
protected static OFActionType[] mapping;
......
/**
* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
* University
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
......@@ -24,7 +24,7 @@ import org.jboss.netty.buffer.ChannelBuffer;
*
* @author David Erickson (daviderickson@cs.stanford.edu)
*/
public class OFActionVendor extends OFAction {
public abstract class OFActionVendor extends OFAction {
public static int MINIMUM_LENGTH = 8;
protected int vendor;
......
/**
* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
* University
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/
package org.openflow.protocol.action;
import java.util.Arrays;
import org.jboss.netty.buffer.ChannelBuffer;
/** A generic / unparsed vendor action. This action is returned by
* BasicFactory.readFromWire if no more specific OFVendorActionFactory
* is registered.
*
* @author David Erickson (daviderickson@cs.stanford.edu)
* @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
*/
public class OFActionVendorGeneric extends OFActionVendor {
public static int MINIMUM_LENGTH = 8;
private final static byte[] EMPTY_ARRAY = new byte[0];
protected byte[] vendorData;
public OFActionVendorGeneric() {
super();
}
public byte[] getVendorData() {
return vendorData;
}
public void setVendorData(byte[] vendorData) {
this.vendorData = vendorData;
}
@Override
public void readFrom(ChannelBuffer data) {
super.readFrom(data);
int vendorDataLength = this.getLength() - MINIMUM_LENGTH;
if (vendorDataLength > 0) {
vendorData = new byte[vendorDataLength];
data.readBytes(vendorData);
} else {
vendorData = EMPTY_ARRAY;
}
}
@Override
public void writeTo(ChannelBuffer data) {
super.writeTo(data);
data.writeInt(this.vendor);
data.writeBytes(vendorData);
}
@Override
public int hashCode() {
final int prime = 379;
int result = super.hashCode();
result = prime * result + Arrays.hashCode(vendorData);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof OFActionVendorGeneric)) {
return false;
}
OFActionVendorGeneric other = (OFActionVendorGeneric) obj;
if (!Arrays.equals(vendorData, other.vendorData)) {
return false;
}
return true;
}
}
/**
* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
* University
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
......@@ -25,6 +25,7 @@ import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFType;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionType;
import org.openflow.protocol.action.OFActionVendor;
import org.openflow.protocol.statistics.OFStatistics;
import org.openflow.protocol.statistics.OFStatisticsType;
import org.openflow.protocol.statistics.OFVendorStatistics;
......@@ -45,6 +46,12 @@ import org.openflow.protocol.vendor.OFVendorId;
public class BasicFactory implements OFMessageFactory, OFActionFactory,
OFStatisticsFactory, OFVendorDataFactory {
private final OFVendorActionRegistry vendorActionRegistry;
public BasicFactory() {
vendorActionRegistry = OFVendorActionRegistry.getInstance();
}
/**
* create and return a new instance of a message for OFType t. Also injects
* factories for those message types that implement the *FactoryAware
......@@ -169,19 +176,42 @@ public class BasicFactory implements OFMessageFactory, OFActionFactory,
(data.readerIndex() + demux.getLengthU()) > end))
return results;
ofa = getAction(demux.getType());
ofa.readFrom(data);
if (OFAction.class.equals(ofa.getClass())) {
// advance the position for un-implemented messages
data.readerIndex(data.readerIndex()+(ofa.getLengthU() -
OFAction.MINIMUM_LENGTH));
}
ofa = parseActionOne(demux.getType(), data);
results.add(ofa);
}
return results;
}
private OFAction parseActionOne(OFActionType type, ChannelBuffer data) {
OFAction ofa;
data.markReaderIndex();
ofa = getAction(type);
ofa.readFrom(data);
if(type == OFActionType.VENDOR) {
OFActionVendor vendorAction = (OFActionVendor) ofa;
OFVendorActionFactory vendorActionFactory = vendorActionRegistry.get(vendorAction.getVendor());
if(vendorActionFactory != null) {
// if we have a specific vendorActionFactory for this vendor id,
// delegate to it for vendor-specific reparsing of the message
data.resetReaderIndex();
OFActionVendor newAction = vendorActionFactory.readFrom(data);
if(newAction != null)
ofa = newAction;
}
}
if (OFAction.class.equals(ofa.getClass())) {
// advance the position for un-implemented messages
data.readerIndex(data.readerIndex()+(ofa.getLengthU() -
OFAction.MINIMUM_LENGTH));
}
return ofa;
}
@Override
public OFActionFactory getActionFactory() {
return this;
......
package org.openflow.protocol.factory;
import org.jboss.netty.buffer.ChannelBuffer;
import org.openflow.protocol.action.OFActionVendor;
/** Interface contract for an actionfactory that creates vendor-specific actions.
* VendorActionFactories are registered with the BasicFactory for a specific
* vendor id.
* <p>
* <b>Note:</b> Implementations are expected to be thread-safe.
*
* @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
*/
public interface OFVendorActionFactory {
/** parse the data from the wire, create and return a vendor-specific action.
*
* @param data contains a serialized vendor action at the current readerPosition.
* The full message is guaranteed to be available in the buffer.
*
* @return upon success returns a newly allocated vendor-specific
* action instance, and advances the readerPosition in data for the
* entire length. Upon failure, returns null and leaves the readerPosition
* in data unmodified.
*/
OFActionVendor readFrom(ChannelBuffer data);
}
package org.openflow.protocol.factory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** Singleton registry object that holds a mapping from vendor ids to vendor-specific
* mapping factories. Threadsafe.
*
* @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
*/
public class OFVendorActionRegistry {
private static class InstanceHolder {
private final static OFVendorActionRegistry instance = new OFVendorActionRegistry();
}
public static OFVendorActionRegistry getInstance() {
return InstanceHolder.instance;
}
private final Map <Integer, OFVendorActionFactory> vendorActionFactories;
public OFVendorActionRegistry() {
vendorActionFactories = new ConcurrentHashMap<Integer, OFVendorActionFactory>();
}
public OFVendorActionFactory register(int vendorId, OFVendorActionFactory factory) {
return vendorActionFactories.put(vendorId, factory);
}
public OFVendorActionFactory get(int vendorId) {
return vendorActionFactories.get(vendorId);
}
}
/**
* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
* University
*
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
......@@ -17,16 +17,23 @@
package org.openflow.protocol;
import static org.junit.Assert.assertArrayEquals;
import java.util.List;
import junit.framework.TestCase;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.openflow.protocol.action.MockVendorAction;
import org.openflow.protocol.action.MockVendorActionFactory;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionVendorGeneric;
import org.openflow.protocol.factory.BasicFactory;
import org.openflow.protocol.factory.MessageParseException;
import org.openflow.protocol.factory.OFVendorActionRegistry;
import org.openflow.util.U16;
import junit.framework.TestCase;
public class BasicFactoryTest extends TestCase {
public void testCreateAndParse() throws MessageParseException {
......@@ -78,4 +85,50 @@ public class BasicFactoryTest extends TestCase {
}
}
public void testCustomVendorAction() throws MessageParseException {
BasicFactory factory = new BasicFactory();
OFVendorActionRegistry.getInstance().register(
MockVendorAction.VENDOR_ID, new MockVendorActionFactory());
byte[] deadBeefMessage = {
(byte) 0xff, (byte) 0xff, // action vendor
0x00, 0x10, // length
(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte)0xef, // deadbeaf
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08 // pad
};
ChannelBuffer buf = ChannelBuffers.copiedBuffer(deadBeefMessage);
List<OFAction> actions = factory.parseActions(buf,deadBeefMessage.length);
assertEquals(1, actions.size());
OFAction ofAction = actions.get(0);
assertTrue("Action should be MockVendorAction, but is "+ofAction.getClass(), ofAction instanceof MockVendorAction);
assertArrayEquals( new byte[] { 1,2,3,4,5,6,7,8}, ((MockVendorAction)ofAction).getMockData());
}
public void testGenericVendorAction() throws MessageParseException {
byte[] nonDeadBeefMessage = {
(byte) 0xff, (byte) 0xff, // action vendor
0x00, 0x10, // length
(byte) 0x7e, (byte) 0xe7, (byte) 0xbe, (byte)0xef, // deadbeaf
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08 // pad
};
BasicFactory factory = new BasicFactory();
OFVendorActionRegistry.getInstance().register(
MockVendorAction.VENDOR_ID, new MockVendorActionFactory());
ChannelBuffer buf = ChannelBuffers.copiedBuffer(nonDeadBeefMessage);
List<OFAction> actions = factory.parseActions(buf,nonDeadBeefMessage.length);
assertEquals(1, actions.size());
OFAction ofAction = actions.get(0);
assertTrue("Action should be OFActionVendorGeneric, but is "+ofAction.getClass(), ofAction instanceof OFActionVendorGeneric);
}
}
package org.openflow.protocol.action;
import org.jboss.netty.buffer.ChannelBuffer;
public class MockVendorAction extends OFActionVendor {
public static final int VENDOR_ID = 0xdeadbeef;
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private byte[] mockData;
public byte[] getMockData() {
return mockData;
}
public void setMockData(byte[] mockData) {
this.mockData = mockData;
}
@Override
public void readFrom(ChannelBuffer data) {
super.readFrom(data);
int dataLength = getLength() - MINIMUM_LENGTH;
if(dataLength > 0) {
mockData = new byte[dataLength];
data.readBytes(mockData);
} else {
mockData = EMPTY_BYTE_ARRAY;
}
}
@Override
public void writeTo(ChannelBuffer data) {
super.writeTo(data);
data.writeBytes(mockData);
}
}
package org.openflow.protocol.action;
import org.jboss.netty.buffer.ChannelBuffer;
import org.openflow.protocol.factory.OFVendorActionFactory;
public class MockVendorActionFactory implements OFVendorActionFactory {
@Override
public OFActionVendor readFrom(ChannelBuffer data) {
MockVendorAction action = new MockVendorAction();
action.readFrom(data);
return action;
}
}
package org.openflow.protocol.action;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.openflow.protocol.factory.OFVendorActionRegistry;
public class OFVendorActionRegistryTest {
@Test
public void test() {
MockVendorActionFactory factory = new MockVendorActionFactory();
OFVendorActionRegistry.getInstance().register(MockVendorAction.VENDOR_ID, factory);
assertEquals(factory, OFVendorActionRegistry.getInstance().get(MockVendorAction.VENDOR_ID));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment