--- /dev/null
+/**
+* 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.factory;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+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;
+import org.openflow.protocol.vendor.OFByteArrayVendorData;
+import org.openflow.protocol.vendor.OFVendorData;
+import org.openflow.protocol.vendor.OFVendorDataType;
+import org.openflow.protocol.vendor.OFVendorId;
+
+
+/**
+ * A basic OpenFlow factory that supports naive creation of both Messages and
+ * Actions.
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ * @author Rob Sherwood (rob.sherwood@stanford.edu)
+ *
+ */
+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
+ * interfaces.
+ *
+ * @return a newly created instance that may be modified / used freely by
+ * the caller
+ */
+ @Override
+ public OFMessage getMessage(OFType t) {
+ OFMessage message = t.newInstance();
+ injectFactories(message);
+ return message;
+ }
+
+ @Override
+ public List<OFMessage> parseMessage(ChannelBuffer data) throws MessageParseException {
+ List<OFMessage> msglist = new ArrayList<OFMessage>();
+ OFMessage msg = null;
+
+ while (data.readableBytes() >= OFMessage.MINIMUM_LENGTH) {
+ data.markReaderIndex();
+ msg = this.parseMessageOne(data);
+ if (msg == null) {
+ data.resetReaderIndex();
+ break;
+ }
+ else {
+ msglist.add(msg);
+ }
+ }
+
+ if (msglist.size() == 0) {
+ return null;
+ }
+ return msglist;
+
+ }
+
+ public OFMessage parseMessageOne(ChannelBuffer data) throws MessageParseException {
+ try {
+ OFMessage demux = new OFMessage();
+ OFMessage ofm = null;
+
+ if (data.readableBytes() < OFMessage.MINIMUM_LENGTH)
+ return ofm;
+
+ data.markReaderIndex();
+ demux.readFrom(data);
+ data.resetReaderIndex();
+
+ if (demux.getLengthU() > data.readableBytes())
+ return ofm;
+
+ ofm = getMessage(demux.getType());
+ if (ofm == null)
+ return null;
+
+ injectFactories(ofm);
+ ofm.readFrom(data);
+ if (OFMessage.class.equals(ofm.getClass())) {
+ // advance the position for un-implemented messages
+ data.readerIndex(data.readerIndex()+(ofm.getLengthU() -
+ OFMessage.MINIMUM_LENGTH));
+ }
+
+ return ofm;
+ } catch (Exception e) {
+ /* Write the offending data along with the error message */
+ data.resetReaderIndex();
+ String msg =
+ "Message Parse Error for packet:" + dumpBuffer(data) +
+ "\nException: " + e.toString();
+ data.resetReaderIndex();
+
+ throw new MessageParseException(msg, e);
+ }
+ }
+
+
+
+
+ private void injectFactories(OFMessage ofm) {
+ if (ofm instanceof OFActionFactoryAware) {
+ ((OFActionFactoryAware)ofm).setActionFactory(this);
+ }
+ if (ofm instanceof OFMessageFactoryAware) {
+ ((OFMessageFactoryAware)ofm).setMessageFactory(this);
+ }
+ if (ofm instanceof OFStatisticsFactoryAware) {
+ ((OFStatisticsFactoryAware)ofm).setStatisticsFactory(this);
+ }
+ if (ofm instanceof OFVendorDataFactoryAware) {
+ ((OFVendorDataFactoryAware)ofm).setVendorDataFactory(this);
+ }
+ }
+
+ @Override
+ public OFAction getAction(OFActionType t) {
+ return t.newInstance();
+ }
+
+ @Override
+ public List<OFAction> parseActions(ChannelBuffer data, int length) {
+ return parseActions(data, length, 0);
+ }
+
+ @Override
+ public List<OFAction> parseActions(ChannelBuffer data, int length, int limit) {
+ List<OFAction> results = new ArrayList<OFAction>();
+ OFAction demux = new OFAction();
+ OFAction ofa;
+ int end = data.readerIndex() + length;
+
+ while (limit == 0 || results.size() <= limit) {
+ if ((data.readableBytes() < OFAction.MINIMUM_LENGTH ||
+ (data.readerIndex() + OFAction.MINIMUM_LENGTH) > end))
+ return results;
+
+ data.markReaderIndex();
+ demux.readFrom(data);
+ data.resetReaderIndex();
+
+ if ((demux.getLengthU() > data.readableBytes() ||
+ (data.readerIndex() + demux.getLengthU()) > end))
+ return results;
+
+ 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;
+ }
+
+ @Override
+ public OFStatistics getStatistics(OFType t, OFStatisticsType st) {
+ return st.newInstance(t);
+ }
+
+ @Override
+ public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st,
+ ChannelBuffer data, int length) {
+ return parseStatistics(t, st, data, length, 0);
+ }
+
+ /**
+ * @param t
+ * OFMessage type: should be one of stats_request or stats_reply
+ * @param st
+ * statistics type of this message, e.g., DESC, TABLE
+ * @param data
+ * buffer to read from
+ * @param length
+ * length of statistics
+ * @param limit
+ * number of statistics to grab; 0 == all
+ *
+ * @return list of statistics
+ */
+
+ @Override
+ public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st,
+ ChannelBuffer data, int length, int limit) {
+ List<OFStatistics> results = new ArrayList<OFStatistics>();
+ OFStatistics statistics = getStatistics(t, st);
+
+ int start = data.readerIndex();
+ int count = 0;
+
+ while (limit == 0 || results.size() <= limit) {
+ // TODO Create a separate MUX/DEMUX path for vendor stats
+ if (statistics instanceof OFVendorStatistics)
+ ((OFVendorStatistics)statistics).setLength(length);
+
+ /**
+ * can't use data.remaining() here, b/c there could be other data
+ * buffered past this message
+ */
+ if ((length - count) >= statistics.getLength()) {
+ if (statistics instanceof OFActionFactoryAware)
+ ((OFActionFactoryAware)statistics).setActionFactory(this);
+ statistics.readFrom(data);
+ results.add(statistics);
+ count += statistics.getLength();
+ statistics = getStatistics(t, st);
+ } else {
+ if (count < length) {
+ /**
+ * Nasty case: partial/incomplete statistic found even
+ * though we have a full message. Found when NOX sent
+ * agg_stats request with wrong agg statistics length (52
+ * instead of 56)
+ *
+ * just throw the rest away, or we will break framing
+ */
+ data.readerIndex(start + length);
+ }
+ return results;
+ }
+ }
+ return results; // empty; no statistics at all
+ }
+
+
+ @Override
+ public OFVendorData getVendorData(OFVendorId vendorId,
+ OFVendorDataType vendorDataType) {
+ if (vendorDataType == null)
+ return null;
+
+ return vendorDataType.newInstance();
+ }
+
+ /**
+ * Attempts to parse and return the OFVendorData contained in the given
+ * ChannelBuffer, beginning right after the vendor id.
+ * @param vendor the vendor id that was parsed from the OFVendor message.
+ * @param data the ChannelBuffer from which to parse the vendor data
+ * @param length the length to the end of the enclosing message.
+ * @return an OFVendorData instance
+ */
+ @Override
+ public OFVendorData parseVendorData(int vendor, ChannelBuffer data,
+ int length) {
+ OFVendorDataType vendorDataType = null;
+ OFVendorId vendorId = OFVendorId.lookupVendorId(vendor);
+ if (vendorId != null) {
+ data.markReaderIndex();
+ vendorDataType = vendorId.parseVendorDataType(data, length);
+ data.resetReaderIndex();
+ }
+
+ OFVendorData vendorData = getVendorData(vendorId, vendorDataType);
+ if (vendorData == null)
+ vendorData = new OFByteArrayVendorData();
+
+ vendorData.readFrom(data, length);
+
+ return vendorData;
+ }
+
+ public static String dumpBuffer(ChannelBuffer data) {
+ // NOTE: Reads all the bytes in buffer from current read offset.
+ // Set/Reset ReaderIndex if you want to read from a different location
+ int len = data.readableBytes();
+ StringBuffer sb = new StringBuffer();
+ for (int i=0 ; i<len; i++) {
+ if (i%32 == 0) sb.append("\n");
+ if (i%4 == 0) sb.append(" ");
+ sb.append(String.format("%02x", data.getUnsignedByte(i)));
+ }
+ return sb.toString();
+ }
+
+}