diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java index 6b165b5cab61b015996b90c3d9fd5af976e7bc38..7f3463bddb7f97410f1252afd51dd8021b16726d 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java @@ -2,7 +2,9 @@ package net.floodlightcontroller.core.internal; import java.io.IOException; import java.nio.channels.ClosedChannelException; +import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.concurrent.RejectedExecutionException; @@ -20,6 +22,7 @@ import org.jboss.netty.handler.timeout.IdleStateEvent; import org.jboss.netty.handler.timeout.IdleStateHandler; import org.jboss.netty.handler.timeout.ReadTimeoutException; import org.jboss.netty.util.Timer; + import net.floodlightcontroller.core.IOFConnectionBackend; import net.floodlightcontroller.core.OFConnection; import net.floodlightcontroller.core.annotations.LogMessageDoc; @@ -29,6 +32,7 @@ import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHa import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleReadTimeout; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleWriteTimeout; import net.floodlightcontroller.debugcounter.IDebugCounterService; + import org.projectfloodlight.openflow.exceptions.OFParseError; import org.projectfloodlight.openflow.protocol.OFEchoReply; import org.projectfloodlight.openflow.protocol.OFEchoRequest; @@ -39,9 +43,15 @@ import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFeaturesReply; import org.projectfloodlight.openflow.protocol.OFFeaturesRequest; import org.projectfloodlight.openflow.protocol.OFHello; +import org.projectfloodlight.openflow.protocol.OFHelloElem; +import org.projectfloodlight.openflow.protocol.OFHelloElemVersionbitmap; import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.ver13.OFHelloElemTypeSerializerVer13; +import org.projectfloodlight.openflow.protocol.ver14.OFHelloElemTypeSerializerVer14; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.U32; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,10 +72,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { private Channel channel; private final Timer timer; private volatile OFChannelState state; - private OFFactory factory = OFFactories.getFactory(OFVersion.OF_14); + private OFFactory factory; private OFFeaturesReply featuresReply; private volatile OFConnection connection; private final IDebugCounterService debugCounters; + private final List<U32> ofBitmaps; /** transaction Ids to use during handshake. Since only one thread * calls into the OFChannelHandler we don't need atomic. @@ -111,6 +122,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { // we only expect features reply in the WAIT_FEATURES_REPLY state illegalMessageReceived(m); } + + void processOFPortStatus(OFPortStatus m) { + unhandledMessageReceived(m); + } private final boolean channelHandshakeComplete; @@ -253,13 +268,16 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { case EXPERIMENTER: processOFExperimenter((OFExperimenter)m); break; - /* echos can be sent at any time */ + /* echos can be sent at any time */ case ECHO_REPLY: processOFEchoReply((OFEchoReply)m); break; case ECHO_REQUEST: processOFEchoRequest((OFEchoRequest)m); break; + case PORT_STATUS: + processOFPortStatus((OFPortStatus)m); + break; default: illegalMessageReceived(m); break; @@ -305,14 +323,49 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { @Override void processOFHello(OFHello m) throws IOException { - OFVersion version = m.getVersion(); - /* Choose the lower of the two supported versions. */ - if (version.compareTo(factory.getVersion()) < 0) { - factory = OFFactories.getFactory(version); + OFVersion theirVersion = m.getVersion(); + OFVersion commonVersion = null; + /* First, check if there's a version bitmap supplied. WE WILL ALWAYS HAVE a controller-provided version bitmap. */ + if (theirVersion.compareTo(OFVersion.OF_13) >= 0 && !m.getElements().isEmpty()) { + List<U32> bitmaps = new ArrayList<U32>(); + List<OFHelloElem> elements = m.getElements(); + /* Grab all bitmaps supplied */ + for (OFHelloElem e : elements) { + if (m.getVersion().equals(OFVersion.OF_13) + && e.getType() == OFHelloElemTypeSerializerVer13.VERSIONBITMAP_VAL) { + bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps()); + } else if (m.getVersion().equals(OFVersion.OF_14) + && e.getType() == OFHelloElemTypeSerializerVer14.VERSIONBITMAP_VAL) { + bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps()); + } + } + /* Lookup highest, common supported OpenFlow version */ + commonVersion = computeOFVersionFromBitmap(bitmaps); + if (commonVersion == null) { + log.error("Could not negotiate common OpenFlow version for {} with greatest version bitmap algorithm.", channel.getRemoteAddress()); + channel.disconnect(); + return; + } else { + log.info("Negotiated OpenFlow version of {} for {} with greatest version bitmap algorithm.", commonVersion.toString(), channel.getRemoteAddress()); + factory = OFFactories.getFactory(commonVersion); + OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class); + decoder.setVersion(commonVersion); + } + } + /* If there's not a bitmap present, choose the lower of the two supported versions. */ + else if (theirVersion.compareTo(factory.getVersion()) < 0) { + log.info("Negotiated down to switch OpenFlow version of {} for {} using lesser hello header algorithm.", theirVersion.toString(), channel.getRemoteAddress()); + factory = OFFactories.getFactory(theirVersion); + OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class); + decoder.setVersion(theirVersion); } /* else The controller's version is < or = the switch's, so keep original controller factory. */ - - OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class); - decoder.setVersion(version); + else if (theirVersion.equals(factory.getVersion())) { + log.info("Negotiated equal OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress()); + } + else { + log.info("Negotiated down to controller OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress()); + } + setState(new WaitFeaturesReplyState()); } @@ -341,6 +394,37 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { setState(new CompleteState()); } + + @Override + void processOFHello(OFHello m) throws IOException { + /* + * Brocade switches send a second hello after + * the controller responds with its hello. This + * might be to confirm the protocol version used, + * but isn't defined in the OF specification. + * + * We will ignore such hello messages assuming + * the version of the hello is correct according + * to the algorithm in the spec. + * + * TODO Brocade also sets the XID of this second + * hello as the same XID the controller used. + * Checking for this might help to assure we're + * really dealing with the situation we think + * we are. + */ + if (m.getVersion().equals(factory.getVersion())) { + log.warn("Ignoring second hello from {} in state {}. Might be a Brocade.", channel.getRemoteAddress(), state.toString()); + } else { + super.processOFHello(m); /* Versions don't match as they should; abort */ + } + } + + @Override + void processOFPortStatus(OFPortStatus m) { + log.warn("Ignoring PORT_STATUS message from {} during OpenFlow channel establishment. Ports will be explicitly queried in a later state.", channel.getRemoteAddress()); + } + @Override void enterState() throws IOException { sendFeaturesRequest(); @@ -402,7 +486,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { @Nonnull INewOFConnectionListener newConnectionListener, @Nonnull ChannelPipeline pipeline, @Nonnull IDebugCounterService debugCounters, - @Nonnull Timer timer) { + @Nonnull Timer timer, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory) { Preconditions.checkNotNull(switchManager, "switchManager"); Preconditions.checkNotNull(newConnectionListener, "connectionOpenedListener"); @@ -416,10 +502,45 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { this.counters = switchManager.getCounters(); this.state = new InitState(); this.timer = timer; + this.ofBitmaps = ofBitmaps; + this.factory = defaultFactory; log.debug("constructor on OFChannelHandler {}", String.format("%08x", System.identityHashCode(this))); } + /** + * Determine the highest supported version of OpenFlow in common + * between both our OFVersion bitmap and the switch's. + * + * @param theirs, the version bitmaps of the switch + * @return the highest OFVersion in common b/t the two + */ + private OFVersion computeOFVersionFromBitmap(List<U32> theirs) { + Iterator<U32> theirsItr = theirs.iterator(); + Iterator<U32> oursItr = ofBitmaps.iterator(); + OFVersion version = null; + int pos = 0; + int size = 32; + while (theirsItr.hasNext() && oursItr.hasNext()) { + int t = theirsItr.next().getRaw(); + int o = oursItr.next().getRaw(); + + int common = t & o; /* Narrow down the results to the common bits */ + for (int i = 0; i < size; i++) { /* Iterate over and locate the 1's */ + int tmp = common & (1 << i); /* Select the bit of interest, 0-31 */ + if (tmp != 0) { /* Is the version of this bit in common? */ + for (OFVersion v : OFVersion.values()) { /* Which version does this bit represent? */ + if (v.getWireVersion() == i + (size * pos)) { + version = v; + } + } + } + } + pos++; /* OFVersion position. 1-31 = 1, 32 - 63 = 2, etc. Inc at end so it starts at 0. */ + } + return version; + } + /** * Determines if the entire switch handshake is complete (channel+switch). * If the channel handshake is complete the call is forwarded to the @@ -733,10 +854,13 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { */ private void sendHelloMessage() throws IOException { // Send initial hello message - // FIXME:LOJI: Haven't negotiated version yet, assume 1.3 + List<OFHelloElem> he = new ArrayList<OFHelloElem>(); + he.add(factory.buildHelloElemVersionbitmap() + .setBitmaps(ofBitmaps) + .build()); OFHello.Builder builder = factory.buildHello() - .setXid(handshakeTransactionIds--); - // FIXME: Need to add code here to set the version bitmap hello element + .setXid(handshakeTransactionIds--) + .setElements(he); OFHello m = builder.build(); channel.write(Collections.singletonList(m)); log.debug("Send hello: {}", m); diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java index 99a1c4e80585d87a9f9297e7a5089a629644c8ca..532e895ef7b80a7f9ce74310004e656c1af411ca 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java @@ -3,8 +3,11 @@ package net.floodlightcontroller.core.internal; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -52,6 +55,7 @@ import net.floodlightcontroller.debugevent.IEventCategory; import net.floodlightcontroller.debugevent.MockDebugEventService; import org.projectfloodlight.openflow.protocol.OFControllerRole; +import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFeaturesReply; import org.projectfloodlight.openflow.protocol.OFMessage; @@ -60,6 +64,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U32; import org.sdnplatform.sync.IStoreClient; import org.sdnplatform.sync.IStoreListener; import org.sdnplatform.sync.ISyncService; @@ -107,6 +112,9 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen protected static Map<DatapathId, TableId> forwardToControllerFlowsUpToTableByDpid; protected static TableId forwardToControllerFlowsUpToTable = TableId.of(4); /* this should cover most HW switches that have a couple SW-based flow tables */ + protected static List<U32> ofBitmaps; + protected static OFFactory defaultFactory; + private ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler> switchHandlers; private ConcurrentHashMap<DatapathId, IOFSwitchBackend> switches; private ConcurrentHashMap<DatapathId, IOFSwitch> syncedSwitches; @@ -770,6 +778,127 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen * By default, */ forwardToControllerFlowsUpToTableByDpid = jsonToSwitchTableIdMap(configParams.get("maxTableToReceiveTableMissFlowPerDpid")); + + /* + * Get config to determine what versions of OpenFlow we will + * support. The versions will determine the hello's header + * version as well as the OF1.3.1 version bitmap contents. + */ + String protocols = configParams.get("supportedOpenFlowVersions"); + Set<OFVersion> ofVersions = new HashSet<OFVersion>(); + if (protocols != null && !protocols.isEmpty()) { + protocols = protocols.toLowerCase(); + /* + * Brute-force check for all known versions. + */ + if (protocols.contains("1.0") || protocols.contains("10")) { + ofVersions.add(OFVersion.OF_10); + } + if (protocols.contains("1.1") || protocols.contains("11")) { + ofVersions.add(OFVersion.OF_11); + } + if (protocols.contains("1.2") || protocols.contains("12")) { + ofVersions.add(OFVersion.OF_12); + } + if (protocols.contains("1.3") || protocols.contains("13")) { + ofVersions.add(OFVersion.OF_13); + } + if (protocols.contains("1.4") || protocols.contains("14")) { + ofVersions.add(OFVersion.OF_14); + } + /* + * TODO This will need to be updated if/when + * Loxi is updated to support > 1.4. + * + * if (protocols.contains("1.5") || protocols.contains("15")) { + * ofVersions.add(OFVersion.OF_15); + * } + */ + } else { + log.warn("Supported OpenFlow versions not specified. Using Loxi-defined {}", OFVersion.values()); + ofVersions.addAll(Arrays.asList(OFVersion.values())); + } + /* Sanity check */ + if (ofVersions.isEmpty()) { + throw new IllegalStateException("OpenFlow version list should never be empty at this point. Make sure it's being populated in OFSwitchManager's init function."); + } + defaultFactory = computeInitialFactory(ofVersions); + ofBitmaps = computeOurVersionBitmaps(ofVersions); + } + + /** + * Find the max version supplied in the supported + * versions list and use it as the default, which + * will subsequently be used in our hello message + * header's version field. + * + * The factory can be later "downgraded" to a lower + * version depending on what's computed during the + * version-negotiation part of the handshake. + * + * Assumption: The Set of OFVersion ofVersions + * variable has been set already and is NOT EMPTY. + * + * @return the highest-version OFFactory we support + */ + private OFFactory computeInitialFactory(Set<OFVersion> ofVersions) { + /* This should NEVER happen. Double-checking. */ + if (ofVersions == null || ofVersions.isEmpty()) { + throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager."); + } + OFVersion highest = null; + for (OFVersion v : ofVersions) { + if (highest == null) { + highest = v; + } else if (v.compareTo(highest) > 0) { + highest = v; + } + } + /* + * This assumes highest != null, which + * it won't be if the list of versions + * is not empty. + */ + return OFFactories.getFactory(highest); + } + + /** + * Based on the list of OFVersions provided as input (or from Loxi), + * create a list of bitmaps for use in version negotiation during a + * cross-version OpenFlow handshake where both parties support + * OpenFlow versions >= 1.3.1. + * + * Type Set is used as input to guarantee all unique versions. + * + * @param ofVersions, the list of bitmaps. Supply to an OFHello message. + * @return list of bitmaps for the versions of OpenFlow we support + */ + private List<U32> computeOurVersionBitmaps(Set<OFVersion> ofVersions) { + /* This should NEVER happen. Double-checking. */ + if (ofVersions == null || ofVersions.isEmpty()) { + throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager."); + } + + int pos = 1; /* initial bitmap in list */ + int size = 32; /* size of a U32 */ + int tempBitmap = 0; /* maintain the current bitmap we're working on */ + List<U32> bitmaps = new ArrayList<U32>(); + ArrayList<OFVersion> sortedVersions = new ArrayList<OFVersion>(ofVersions); + Collections.sort(sortedVersions); + for (OFVersion v : sortedVersions) { + /* Move on to another bitmap */ + if (v.getWireVersion() > pos * size - 1 ) { + bitmaps.add(U32.ofRaw(tempBitmap)); + tempBitmap = 0; + pos++; + } + tempBitmap = tempBitmap | (1 << (v.getWireVersion() % size)); + } + if (tempBitmap != 0) { + bitmaps.add(U32.ofRaw(tempBitmap)); + } + log.info("Computed OpenFlow version bitmap as {}", Arrays.asList(tempBitmap)); + return bitmaps; } private static Map<DatapathId, TableId> jsonToSwitchTableIdMap(String json) { @@ -871,9 +1000,9 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen bootstrap.setOption("child.keepAlive", true); bootstrap.setOption("child.tcpNoDelay", true); bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE); - - ChannelPipelineFactory pfact = useSsl ? new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, keyStore, keyStorePassword) : - new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService); + + ChannelPipelineFactory pfact = useSsl ? new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory, keyStore, keyStorePassword) : + new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory); bootstrap.setPipelineFactory(pfact); InetSocketAddress sa = new InetSocketAddress(floodlightProvider.getOFPort()); diff --git a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java index 7dbcbf32eb6869671a4859fed538986016ef1092..11827d02fb50a490c7661179bab38e9b2b46292a 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java @@ -19,6 +19,7 @@ package net.floodlightcontroller.core.internal; import java.io.FileInputStream; import java.security.KeyStore; +import java.util.List; import javax.annotation.Nonnull; import javax.net.ssl.KeyManager; @@ -36,6 +37,8 @@ import org.jboss.netty.handler.timeout.IdleStateHandler; import org.jboss.netty.handler.timeout.ReadTimeoutHandler; import org.jboss.netty.util.ExternalResourceReleasable; import org.jboss.netty.util.Timer; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.types.U32; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,14 +59,20 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { protected IDebugCounterService debugCounters; private String keyStore; private String keyStorePassword; + private OFFactory defaultFactory; + private List<U32> ofBitmaps; private void init(IOFSwitchManager switchManager, Timer timer, INewOFConnectionListener connectionListener, - IDebugCounterService debugCounters) { + IDebugCounterService debugCounters, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory) { this.switchManager = switchManager; this.connectionListener = connectionListener; this.timer = timer; this.debugCounters = debugCounters; + this.defaultFactory = defaultFactory; + this.ofBitmaps = ofBitmaps; this.idleHandler = new IdleStateHandler( timer, PipelineIdleReadTimeout.MAIN, @@ -74,9 +83,11 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer, INewOFConnectionListener connectionListener, - IDebugCounterService debugCounters) { + IDebugCounterService debugCounters, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory) { super(); - init(switchManager,timer, connectionListener, debugCounters); + init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory); this.keyStore = null; this.keyStorePassword = null; } @@ -84,9 +95,11 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer, INewOFConnectionListener connectionListener, IDebugCounterService debugCounters, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory, @Nonnull String keyStore, @Nonnull String keyStorePassword) { super(); - init(switchManager,timer, connectionListener, debugCounters); + init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory); this.keyStore = keyStore; this.keyStorePassword = keyStorePassword; } @@ -98,7 +111,9 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { connectionListener, pipeline, debugCounters, - timer); + timer, + ofBitmaps, + defaultFactory); if (keyStore != null && keyStorePassword != null) { try { diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..d0271d6838aec13d8ce97ec389ad5aeb6991f228 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java @@ -0,0 +1,436 @@ +package net.floodlightcontroller.core.web.serializers; + +import net.floodlightcontroller.util.MatchUtils; + +import org.projectfloodlight.openflow.protocol.ver14.OFOxmClassSerializerVer14; +import org.projectfloodlight.openflow.types.ArpOpcode; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.ICMPv4Code; +import org.projectfloodlight.openflow.types.ICMPv4Type; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.IPv6FlowLabel; +import org.projectfloodlight.openflow.types.IpDscp; +import org.projectfloodlight.openflow.types.IpEcn; +import org.projectfloodlight.openflow.types.IpProtocol; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFBooleanValue; +import org.projectfloodlight.openflow.types.OFMetadata; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.projectfloodlight.openflow.types.VlanVid; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +/** + * A handy class to convert OXMs (and NXMs) to and from + * their IDs (U32=[class]+[field]+[has_mask]+[length]) + * and their string representations. The strings are + * defined in {@link net.floodlightcontroller.util.MatchUtils}, + * with the exception of some unique NXMs and OpenFlow 1.5 OXMs + * not technically supported by Loxi/Floodlight yet. Any NXM + * string is prefixed with an "nxm_" followed by the string + * used for the corresponding OXM. For example, an ingress port + * OXM is "in_port", while the NXM version is "nxm_in_port". + * + * Any unique NXM without an OXM counterpart is assigned a string + * to inform of the NXM. For example, NXM 33, "nxm_pkt_mark", marks + * or matches on marked packets as Linux IP tables can do. This is + * not supported in the OpenFlow spec, thus the NXM is assigned the + * string "nxm_pkt_mark" within this class and does not have a string + * predefined in {@link net.floodlightcontroller.util.MatchUtils}. + * + * Any OXM with the HAS_MASKED bit set will have it's string + * representation appended with an "_masked" or the string defined in + * the STR_MASKED class variable below. + * + * As an implementation note, a Google BiMap is used as the storage + * mechanism. This allows fast lookup in both directions from U32 to + * String or from String back to U32. Note that unique keys AND values + * are required in a BiMap. + * + * @author Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswitch.com + * + */ +public class OXMSerializer { + + /* + * OXM IDs for OpenFlow versions 1.5 and below are the same, + * where each version simply adds to the already populated + * list of OXM IDs. (IDs don't change b/t OF versions.) + * As such, we'll assume OF1.4 here, which includes 1.3. + * + * TODO Later, when Loxi supports 1.5, we'll change to + * OFOxmClassSerializerVer15. + */ + + /* String constants */ + private static final String STR_MASKED = "_masked"; + private static final String STR_NXM = "nxm_"; + + /* Bit shifting constants */ + private static final int SHIFT_FIELD = 9; + private static final int SHIFT_CLASS = 16; + private static final int SHIFT_HAS_MASK = 8; + + /* Mask present bit */ + private static final int MASKED = (1 << SHIFT_HAS_MASK); + + /* Classes */ + private static final int OF_BASIC = (OFOxmClassSerializerVer14.OPENFLOW_BASIC_VAL << SHIFT_CLASS) & 0xFFffFFff; + //TODO private static final int OF_PKT_REG = (0x8001 << SHIFT_CLASS) & 0xFFffFFff; + private static final int NXM_0 = (OFOxmClassSerializerVer14.NXM_0_VAL << SHIFT_CLASS) & 0xFFffFFff; + private static final int NXM_1 = (OFOxmClassSerializerVer14.NXM_1_VAL << SHIFT_CLASS) & 0xFFffFFff; + + /* The bi-directional map we'll use to make this process more efficient */ + private static final BiMap<U32, String> map = HashBiMap.create(); + static { + /* + * OXM header is 4 bytes as follows, where C=oxm_class, + * F=oxm_field, M=has_mask, and L=data_length. + * + * CCCC CCCC CCCC CCCC FFFF FFFM LLLL LLLL + * + * The OXM class has already been shifted to the proper + * position and is represented as an integer (4 bytes), + * but we need to shift the OXM field over. The length + * does not need to be shifted. + * + */ + + /* + * OpenFlow Basic OXM Definitions + */ + map.put(U32.ofRaw(OF_BASIC | (0 << SHIFT_FIELD) | OFPort.ZERO.getLength()), MatchUtils.STR_IN_PORT); + map.put(U32.ofRaw(OF_BASIC | (0 << SHIFT_FIELD) | OFPort.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_IN_PORT + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (1 << SHIFT_FIELD) | OFPort.ZERO.getLength()), MatchUtils.STR_IN_PHYS_PORT); + map.put(U32.ofRaw(OF_BASIC | (1 << SHIFT_FIELD) | OFPort.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_IN_PHYS_PORT + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (2 << SHIFT_FIELD) | OFMetadata.NONE.getLength()), MatchUtils.STR_METADATA); + map.put(U32.ofRaw(OF_BASIC | (2 << SHIFT_FIELD) | OFMetadata.NONE.getLength() * 2 | MASKED), MatchUtils.STR_METADATA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (3 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_DL_DST); + map.put(U32.ofRaw(OF_BASIC | (3 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (4 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_DL_SRC); + map.put(U32.ofRaw(OF_BASIC | (4 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (5 << SHIFT_FIELD) | EthType.NONE.getLength()), MatchUtils.STR_DL_TYPE); + map.put(U32.ofRaw(OF_BASIC | (5 << SHIFT_FIELD) | EthType.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_TYPE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (6 << SHIFT_FIELD) | VlanVid.ZERO.getLength()), MatchUtils.STR_DL_VLAN); + map.put(U32.ofRaw(OF_BASIC | (6 << SHIFT_FIELD) | VlanVid.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_DL_VLAN + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (7 << SHIFT_FIELD) | VlanPcp.NONE.getLength()), MatchUtils.STR_DL_VLAN_PCP); + map.put(U32.ofRaw(OF_BASIC | (7 << SHIFT_FIELD) | VlanPcp.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_VLAN_PCP + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (8 << SHIFT_FIELD) | IpDscp.NONE.getLength()), MatchUtils.STR_NW_DSCP); + map.put(U32.ofRaw(OF_BASIC | (8 << SHIFT_FIELD) | IpDscp.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_DSCP + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (9 << SHIFT_FIELD) | IpEcn.NONE.getLength()), MatchUtils.STR_NW_ECN); + map.put(U32.ofRaw(OF_BASIC | (9 << SHIFT_FIELD) | IpEcn.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_ECN + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (10 << SHIFT_FIELD) | IpProtocol.NONE.getLength()), MatchUtils.STR_NW_PROTO); + map.put(U32.ofRaw(OF_BASIC | (10 << SHIFT_FIELD) | IpProtocol.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_PROTO + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (11 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_NW_SRC); + map.put(U32.ofRaw(OF_BASIC | (11 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (12 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_NW_DST); + map.put(U32.ofRaw(OF_BASIC | (12 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (13 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_TCP_SRC); + map.put(U32.ofRaw(OF_BASIC | (13 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TCP_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (14 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_TCP_DST); + map.put(U32.ofRaw(OF_BASIC | (14 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TCP_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (15 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_UDP_SRC); + map.put(U32.ofRaw(OF_BASIC | (15 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_UDP_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (16 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_UDP_DST); + map.put(U32.ofRaw(OF_BASIC | (16 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_UDP_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (17 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_SCTP_SRC); + map.put(U32.ofRaw(OF_BASIC | (17 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_SCTP_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (18 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_SCTP_DST); + map.put(U32.ofRaw(OF_BASIC | (18 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_SCTP_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (19 << SHIFT_FIELD) | ICMPv4Type.NONE.getLength()), MatchUtils.STR_ICMP_TYPE); + map.put(U32.ofRaw(OF_BASIC | (19 << SHIFT_FIELD) | ICMPv4Type.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ICMP_TYPE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (20 << SHIFT_FIELD) | ICMPv4Code.NONE.getLength()), MatchUtils.STR_ICMP_CODE); + map.put(U32.ofRaw(OF_BASIC | (20 << SHIFT_FIELD) | ICMPv4Code.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ICMP_CODE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (21 << SHIFT_FIELD) | ArpOpcode.NONE.getLength()), MatchUtils.STR_ARP_OPCODE); + map.put(U32.ofRaw(OF_BASIC | (21 << SHIFT_FIELD) | ArpOpcode.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_OPCODE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (22 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_ARP_SPA); + map.put(U32.ofRaw(OF_BASIC | (22 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_SPA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (23 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_ARP_DPA); + map.put(U32.ofRaw(OF_BASIC | (23 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_DPA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_ARP_SHA); + map.put(U32.ofRaw(OF_BASIC | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_SHA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_ARP_DHA); + map.put(U32.ofRaw(OF_BASIC | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_DHA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_SRC); + map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_SRC + STR_MASKED); + + + map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_DST); + map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (28 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength()), MatchUtils.STR_IPV6_FLOW_LABEL); + map.put(U32.ofRaw(OF_BASIC | (28 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_FLOW_LABEL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (29 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_ICMPV6_TYPE); + map.put(U32.ofRaw(OF_BASIC | (29 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_ICMPV6_TYPE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (30 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_ICMPV6_CODE); + map.put(U32.ofRaw(OF_BASIC | (30 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_ICMPV6_CODE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_ND_TARGET); + map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_SSL); + map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_SSL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_TTL); + map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TTL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength()), MatchUtils.STR_MPLS_LABEL); + map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_LABEL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (35 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_MPLS_TC); + map.put(U32.ofRaw(OF_BASIC | (35 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_TC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength()), MatchUtils.STR_MPLS_BOS); + map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_BOS + STR_MASKED); + + /* + * TODO in Loxi and MatchUtils + * map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | ), MatchUtils.STR_PBB_ISID); + */ + map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 ), MatchUtils.STR_PBB_ISID); /* need to update after Loxi support */ + map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 * 2 | MASKED ), MatchUtils.STR_PBB_ISID + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength()), MatchUtils.STR_TUNNEL_ID); + map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_ID + STR_MASKED); + + /* + * TODO in MatchUtils + * map.put(U32.ofRaw(OF_BASIC | 39), MatchUtils.STR_IPV6_EXTHDR); + */ + map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2), "ipv6_exthdr"); /* need to update after Loxi support */ + map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2 * 2 | MASKED), "ipv6_exthdr" + STR_MASKED); + + /* note skip of ID 40 here according to spec */ + + /* + * TODO in MatchUtils + * map.put(U32.ofRaw(OF_BASIC | 41), MatchUtils.STR_PBB_UCA); + */ + map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1), "pbb_uca"); + map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1 * 2 | MASKED), "pbb_uca" + STR_MASKED); + + /* + * NXM_0 Definitions + */ + map.put(U32.ofRaw(NXM_0 | (0 << SHIFT_FIELD) | 2 /* old ports are len=2 */), STR_NXM + MatchUtils.STR_IN_PORT); + map.put(U32.ofRaw(NXM_0 | (0 << SHIFT_FIELD) | 2 * 2 /* old ports are len=2 */ | MASKED), STR_NXM + MatchUtils.STR_IN_PORT + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (1 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_DST); + map.put(U32.ofRaw(NXM_0 | (1 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (2 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_SRC); + map.put(U32.ofRaw(NXM_0 | (2 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (3 << SHIFT_FIELD) | EthType.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_TYPE); + map.put(U32.ofRaw(NXM_0 | (3 << SHIFT_FIELD) | EthType.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_TYPE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (4 << SHIFT_FIELD) | VlanVid.ZERO.getLength()), STR_NXM + MatchUtils.STR_DL_VLAN); + map.put(U32.ofRaw(NXM_0 | (4 << SHIFT_FIELD) | VlanVid.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_VLAN + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (5 << SHIFT_FIELD) | 1), STR_NXM + MatchUtils.STR_NW_TOS); + map.put(U32.ofRaw(NXM_0 | (5 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_TOS + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (6 << SHIFT_FIELD) | IpProtocol.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_PROTO); + map.put(U32.ofRaw(NXM_0 | (6 << SHIFT_FIELD) | IpProtocol.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_PROTO + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (7 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_SRC); + map.put(U32.ofRaw(NXM_0 | (7 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (8 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_DST); + map.put(U32.ofRaw(NXM_0 | (8 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (9 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_TCP_SRC); + map.put(U32.ofRaw(NXM_0 | (9 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TCP_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (10 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_TCP_DST); + map.put(U32.ofRaw(NXM_0 | (10 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TCP_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (11 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_UDP_SRC); + map.put(U32.ofRaw(NXM_0 | (11 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_UDP_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (12 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_UDP_DST); + map.put(U32.ofRaw(NXM_0 | (12 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_UDP_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (13 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMP_TYPE); + map.put(U32.ofRaw(NXM_0 | (13 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMP_TYPE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (14 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMP_CODE); + map.put(U32.ofRaw(NXM_0 | (14 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMP_CODE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (15 << SHIFT_FIELD) | ArpOpcode.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_OPCODE); + map.put(U32.ofRaw(NXM_0 | (15 << SHIFT_FIELD) | ArpOpcode.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_OPCODE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (16 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_SPA); + map.put(U32.ofRaw(NXM_0 | (16 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_SPA + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (17 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_DPA); + map.put(U32.ofRaw(NXM_0 | (17 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_DPA + STR_MASKED); + + /* + * NXM_1 Definitions + */ + map.put(U32.ofRaw(NXM_1 | (0 << SHIFT_FIELD) | 4), STR_NXM + "reg_0"); + map.put(U32.ofRaw(NXM_1 | (0 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_0" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (1 << SHIFT_FIELD) | 4), STR_NXM + "reg_1"); + map.put(U32.ofRaw(NXM_1 | (1 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_1" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (2 << SHIFT_FIELD) | 4), STR_NXM + "reg_2"); + map.put(U32.ofRaw(NXM_1 | (2 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_2" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (3 << SHIFT_FIELD) | 4), STR_NXM + "reg_3"); + map.put(U32.ofRaw(NXM_1 | (3 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_3" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (4 << SHIFT_FIELD) | 4), STR_NXM + "reg_4"); + map.put(U32.ofRaw(NXM_1 | (4 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_4" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (5 << SHIFT_FIELD) | 4), STR_NXM + "reg_5"); + map.put(U32.ofRaw(NXM_1 | (5 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_5" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (6 << SHIFT_FIELD) | 4), STR_NXM + "reg_6"); + map.put(U32.ofRaw(NXM_1 | (6 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_6" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (7 << SHIFT_FIELD) | 4), STR_NXM + "reg_7"); + map.put(U32.ofRaw(NXM_1 | (7 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_7" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (16 << SHIFT_FIELD) | U64.ZERO.getLength()), STR_NXM + MatchUtils.STR_TUNNEL_ID); + map.put(U32.ofRaw(NXM_1 | (16 << SHIFT_FIELD) | U64.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TUNNEL_ID + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (17 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_SHA); + map.put(U32.ofRaw(NXM_1 | (17 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_SHA + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (18 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_DHA); + map.put(U32.ofRaw(NXM_1 | (18 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_DHA + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (19 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_SRC); + map.put(U32.ofRaw(NXM_1 | (19 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (20 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_DST); + map.put(U32.ofRaw(NXM_1 | (20 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (21 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMPV6_TYPE); + map.put(U32.ofRaw(NXM_1 | (21 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMPV6_TYPE + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (22 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMPV6_CODE); + map.put(U32.ofRaw(NXM_1 | (22 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMPV6_CODE + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET); + map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_SSL); + map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_SSL + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TTL); + map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TTL + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1), STR_NXM + "ip_frag"); + map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + "ip_frag" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (27 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_FLOW_LABEL); + map.put(U32.ofRaw(NXM_1 | (27 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_FLOW_LABEL + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (28 << SHIFT_FIELD) | IpEcn.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_ECN); + map.put(U32.ofRaw(NXM_1 | (28 << SHIFT_FIELD) | IpEcn.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_ECN + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (29 << SHIFT_FIELD) | 1), STR_NXM + "ip_ttl"); + map.put(U32.ofRaw(NXM_1 | (29 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + "ip_ttl" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (30 << SHIFT_FIELD) | 8), STR_NXM + "flow_cookie"); + map.put(U32.ofRaw(NXM_1 | (30 << SHIFT_FIELD) | 8 * 2 | MASKED), STR_NXM + "flow_cookie" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (31 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_TUNNEL_IPV4_SRC); + map.put(U32.ofRaw(NXM_1 | (31 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_IPV4_SRC + MASKED); + + map.put(U32.ofRaw(NXM_1 | (32 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_TUNNEL_IPV4_DST); + map.put(U32.ofRaw(NXM_1 | (32 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_IPV4_DST + MASKED); + + map.put(U32.ofRaw(NXM_1 | (33 << SHIFT_FIELD) | 4), STR_NXM + "pkt_mark"); + map.put(U32.ofRaw(NXM_1 | (33 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "pkt_mark" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (34 << SHIFT_FIELD) | 2), STR_NXM + "tcp_flags"); + map.put(U32.ofRaw(NXM_1 | (34 << SHIFT_FIELD) | 2 * 2 | MASKED), STR_NXM + "tcp_flags" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (35 << SHIFT_FIELD) | 4), STR_NXM + "internal_dp_hash"); + map.put(U32.ofRaw(NXM_1 | (35 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "internal_dp_hash" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (36 << SHIFT_FIELD) | 4), STR_NXM + "internal_recirc_id"); + map.put(U32.ofRaw(NXM_1 | (36 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "internal_recirc_id" + STR_MASKED); + } + + /** + * Converts the string representation of an OXM ID as + * defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * to its corresponding U32 OXM ID. + * + * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * is the same as the OXM string with an "nxm_" prefix. + * + * Any OXM or NXM that is masked include a "_masked" postfix. + * + * @param oxmId, the U32 defining the OXM/NXM as [class]+[field]+[has_mask]+[length] + * @return the String representing the OXM/NXM + */ + public static String oxmIdToString(U32 oxmId) { + if (map.containsKey(oxmId)) { + return map.get(oxmId); + } else { + return "Unknown OXM ID: " + oxmId.toString(); + } + } + + /** + * Converts the string representation of an OXM ID as + * defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * to its corresponding U32 OXM ID. + * + * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * is the same as the OXM string with an "nxm_" prefix. + * + * Any OXM or NXM that is masked include a "_masked" postfix. + * + * @param oxmString, the string definging the OXM/NXM + * @return the U32 representing the OXM/NXM as [class]+[field]+[has_mask]+[length] + */ + public static U32 oxmStringToId(String oxmString) { + if (map.inverse().containsKey(oxmString)) { + return map.inverse().get(oxmString); + } else { + return null; + } + } +} diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java index bd728ff80be82b0993fd10385b8dca53c63a0fed..606d42f6baa2a4e3f5923b8ce2c2ed85ed81af00 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java @@ -80,6 +80,7 @@ import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDscpRemark; import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandExperimenter; import org.projectfloodlight.openflow.protocol.ver13.OFFlowModFlagsSerializerVer13; import org.projectfloodlight.openflow.protocol.ver13.OFMeterBandTypeSerializerVer13; +import org.projectfloodlight.openflow.protocol.ver13.OFOxmClassSerializerVer13; // Use Loxigen's serializer import org.projectfloodlight.openflow.protocol.ver13.OFPortFeaturesSerializerVer13; import org.projectfloodlight.openflow.protocol.ver13.OFStatsReplyFlagsSerializerVer13; @@ -158,7 +159,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted jGen.writeStartObject(); - + if (reply.getStatType() == null) { // must be an OFFeaturesReply. getValues() was already checked for null above. serializeFeaturesReply((OFFeaturesReply) reply.getValues(), jGen); } else { @@ -213,7 +214,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { } jGen.writeEndObject(); } - + public static void serializeFeaturesReply(OFFeaturesReply fr, JsonGenerator jGen) throws IOException, JsonProcessingException { /* Common to All OF Versions */ jGen.writeStringField("capabilities", fr.getCapabilities().toString()); @@ -221,7 +222,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { jGen.writeNumberField("buffers", fr.getNBuffers()); jGen.writeNumberField("tables", fr.getNTables()); jGen.writeStringField("version", fr.getVersion().toString()); - + if (fr.getVersion().compareTo(OFVersion.OF_13) < 0) { // OF1.3+ break this out into port_config serializePortDesc(fr.getPorts(), jGen); } @@ -509,208 +510,183 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { */ public static void serializeTableFeaturesReply(List<OFTableFeaturesStatsReply> tableFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{ - OFTableFeaturesStatsReply tableFeaturesReply = tableFeaturesReplies.get(0); - jGen.writeStringField("version", tableFeaturesReply.getVersion().toString()); //return the enum name - jGen.writeFieldName("tableFeatures"); jGen.writeStartArray(); - for(OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) { - jGen.writeStartObject(); - jGen.writeNumberField("tableId", tableFeature.getTableId().getValue()); - jGen.writeStringField("name", tableFeature.getName()); - jGen.writeNumberField("metadataMatch", tableFeature.getMetadataMatch().getValue()); - jGen.writeNumberField("metadataWrite", tableFeature.getMetadataWrite().getValue()); - jGen.writeNumberField("config", tableFeature.getConfig()); - jGen.writeNumberField("maxEntries", tableFeature.getMaxEntries()); - - jGen.writeFieldName("properties"); - jGen.writeStartArray(); - for (OFTableFeatureProp properties : tableFeature.getProperties()) { - jGen.writeStartObject(); - short type = (short)properties.getType(); - jGen.writeNumberField("tableFeaturePropType",type); - - switch (type) { - case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_VAL: - OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties; - jGen.writeFieldName("instructions"); - jGen.writeStartArray(); - for (OFInstructionId id : propInstruct.getInstructionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_MISS_VAL: - OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties; - jGen.writeFieldName("instructionsMiss"); - jGen.writeStartArray(); - for (OFInstructionId id : propInstructMiss.getInstructionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_VAL: - OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties; - jGen.writeFieldName("nextTables"); - jGen.writeStartArray(); - for (U8 id : propNxtTables.getNextTableIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_MISS_VAL: - OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties; - jGen.writeFieldName("nextTablesMiss"); - jGen.writeStartArray(); - for (U8 id : propNxtTablesMiss.getNextTableIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_VAL: - OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; - jGen.writeFieldName("writeActions"); - jGen.writeStartArray(); - for (OFActionId id : propWrAct.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_MISS_VAL: - OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties; - jGen.writeFieldName("writeActionsMiss"); - jGen.writeStartArray(); - for (OFActionId id : propWrActMiss.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_VAL: - OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties; - jGen.writeFieldName("applyActions"); - jGen.writeStartArray(); - for (OFActionId id : propAppAct.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_MISS_VAL: - OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties; - jGen.writeFieldName("applyActionsMiss"); - jGen.writeStartArray(); - for (OFActionId id : propAppActMiss.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.MATCH_VAL: - OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties; - jGen.writeFieldName("match"); - jGen.writeStartArray(); - for (U32 id : propMatch.getOxmIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WILDCARDS_VAL: - OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties; - jGen.writeFieldName("wildcards"); - jGen.writeStartArray(); - for (U32 id : propWildcards.getOxmIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_VAL: - OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties; - jGen.writeFieldName("writeSetfield"); - jGen.writeStartArray(); - for (U32 id : propWrSetfield.getOxmIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_MISS_VAL: - OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; - jGen.writeFieldName("writeSetfieldMiss"); - jGen.writeStartArray(); - for (U32 id : propWrSetfieldMiss.getOxmIds()) { + for (OFTableFeaturesStatsReply tableFeaturesReply : tableFeaturesReplies) { + + for(OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) { + jGen.writeStartObject(); + jGen.writeStringField("version", tableFeature.getVersion().toString()); + jGen.writeNumberField("tableId", tableFeature.getTableId().getValue()); + jGen.writeStringField("name", tableFeature.getName()); + jGen.writeNumberField("metadataMatch", tableFeature.getMetadataMatch().getValue()); + jGen.writeNumberField("metadataWrite", tableFeature.getMetadataWrite().getValue()); + jGen.writeNumberField("config", tableFeature.getConfig()); + jGen.writeNumberField("maxEntries", tableFeature.getMaxEntries()); + + jGen.writeFieldName("properties"); + jGen.writeStartArray(); + for (OFTableFeatureProp properties : tableFeature.getProperties()) { + jGen.writeStartObject(); + + short type = (short)properties.getType(); + switch (type) { + case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_VAL: + OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties; + jGen.writeFieldName("instructions"); + jGen.writeStartArray(); + for (OFInstructionId id : propInstruct.getInstructionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_MISS_VAL: + OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties; + jGen.writeFieldName("instructionsMiss"); + jGen.writeStartArray(); + for (OFInstructionId id : propInstructMiss.getInstructionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_VAL: + OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties; + jGen.writeFieldName("nextTables"); + jGen.writeStartArray(); + for (U8 id : propNxtTables.getNextTableIds()) { + jGen.writeNumber(id.getValue()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_MISS_VAL: + OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties; + jGen.writeFieldName("nextTablesMiss"); + jGen.writeStartArray(); + for (U8 id : propNxtTablesMiss.getNextTableIds()) { + jGen.writeNumber(id.getValue()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_VAL: + OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; + jGen.writeFieldName("writeActions"); + jGen.writeStartArray(); + for (OFActionId id : propWrAct.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_MISS_VAL: + OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties; + jGen.writeFieldName("writeActionsMiss"); + jGen.writeStartArray(); + for (OFActionId id : propWrActMiss.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_VAL: + OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties; + jGen.writeFieldName("applyActions"); + jGen.writeStartArray(); + for (OFActionId id : propAppAct.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_MISS_VAL: + OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties; + jGen.writeFieldName("applyActionsMiss"); + jGen.writeStartArray(); + for (OFActionId id : propAppActMiss.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.MATCH_VAL: + OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties; + jGen.writeFieldName("match"); + jGen.writeStartArray(); + for (U32 id : propMatch.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WILDCARDS_VAL: + OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties; + jGen.writeFieldName("wildcards"); + jGen.writeStartArray(); + for (U32 id : propWildcards.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_VAL: + OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties; + jGen.writeFieldName("writeSetfield"); + jGen.writeStartArray(); + for (U32 id : propWrSetfield.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_MISS_VAL: + OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; + jGen.writeFieldName("writeSetfieldMiss"); + jGen.writeStartArray(); + for (U32 id : propWrSetfieldMiss.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_VAL: + OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties; + jGen.writeFieldName("applySetfield"); + jGen.writeStartArray(); + for (U32 id : propAppSetfield.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_MISS_VAL: + OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties; + jGen.writeFieldName("applySetfieldMiss"); + jGen.writeStartArray(); + for (U32 id : propAppSetfieldMiss.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_VAL: + OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; + jGen.writeFieldName("experimenter"); jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); + jGen.writeNumberField("subType", propExp.getSubtype()); + jGen.writeNumberField("experimenter", propExp.getExperimenter()); + jGen.writeStringField("data", propExp.getExperimenterData().toString()); jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_VAL: - OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties; - jGen.writeFieldName("applySetfield"); - jGen.writeStartArray(); - for (U32 id : propAppSetfield.getOxmIds()) { + break; + case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_MISS_VAL: + OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties; + jGen.writeFieldName("experimenterMiss"); jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); + jGen.writeNumberField("subType", propExpMiss.getSubtype()); + jGen.writeNumberField("experimenter", propExpMiss.getExperimenter()); + jGen.writeStringField("data", propExpMiss.getExperimenterData().toString()); jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_MISS_VAL: - OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties; - jGen.writeFieldName("applySetfieldMiss"); - jGen.writeStartArray(); - for (U32 id : propAppSetfieldMiss.getOxmIds()) { + break; + default: + // shouldn't ever get here jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_VAL: - OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; - jGen.writeFieldName("experimenter"); - jGen.writeStartObject(); - jGen.writeNumberField("subType", propExp.getSubtype()); - jGen.writeNumberField("experimenter", propExp.getExperimenter()); - jGen.writeStringField("subType", propExp.getExperimenterData().toString()); - jGen.writeEndObject(); - break; - case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_MISS_VAL: - OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties; - jGen.writeFieldName("experimenterMiss"); - jGen.writeStartObject(); - jGen.writeNumberField("subType", propExpMiss.getSubtype()); - jGen.writeNumberField("experimenter", propExpMiss.getExperimenter()); - jGen.writeStringField("subType", propExpMiss.getExperimenterData().toString()); + break; + }//end of Switch Case jGen.writeEndObject(); - break; - default: - // shouldn't ever get here - break; - }//end of Switch Case + }//end of for loop - properties + jGen.writeEndArray(); jGen.writeEndObject(); - }//end of for loop - properties - jGen.writeEndObject(); - }//end of for loop - features + }//end of for loop - features + } //end of looping through REQ_MORE flagged message loop jGen.writeEndArray(); } @@ -847,7 +823,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { jGen.writeStringField("version", portDescReply.getVersion().toString()); //return the enum name serializePortDesc(portDescReply.getEntries(), jGen); } - + public static void serializePortDesc(List<OFPortDesc> portDescList, JsonGenerator jGen) throws IOException, JsonProcessingException { jGen.writeFieldName("portDesc"); jGen.writeStartArray(); diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java index ea3d77cf26075acbd1c602052c66e25c04b0156c..028614485c1a1d18300241a43d02e4c15398d5e4 100644 --- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java +++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java @@ -11,6 +11,7 @@ import org.projectfloodlight.openflow.types.ArpOpcode; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.ICMPv4Code; import org.projectfloodlight.openflow.types.ICMPv4Type; +import org.projectfloodlight.openflow.types.IPv4Address; import org.projectfloodlight.openflow.types.IPv4AddressWithMask; import org.projectfloodlight.openflow.types.IPv6AddressWithMask; import org.projectfloodlight.openflow.types.IPv6FlowLabel; @@ -104,6 +105,8 @@ public class MatchUtils { public static final String STR_METADATA = "metadata"; public static final String STR_TUNNEL_ID = "tunnel_id"; + public static final String STR_TUNNEL_IPV4_SRC = "tunnel_ipv4_src"; + public static final String STR_TUNNEL_IPV4_DST = "tunnel_ipv4_dst"; public static final String STR_PBB_ISID = "pbb_isid"; @@ -748,6 +751,20 @@ public class MatchUtils { dataMask[1].contains("0x") ? U64.of(Long.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U64.of(Long.valueOf(dataMask[1]))); } break; + case STR_TUNNEL_IPV4_SRC: + if (dataMask.length == 1) { + mb.setExact(MatchField.TUNNEL_IPV4_SRC, IPv4Address.of(key_value[1])); + } else { + mb.setMasked(MatchField.TUNNEL_IPV4_SRC, IPv4AddressWithMask.of(key_value[1])); + } + break; + case STR_TUNNEL_IPV4_DST: + if (dataMask.length == 1) { + mb.setExact(MatchField.TUNNEL_IPV4_DST, IPv4Address.of(key_value[1])); + } else { + mb.setMasked(MatchField.TUNNEL_IPV4_DST, IPv4AddressWithMask.of(key_value[1])); + } + break; case STR_PBB_ISID: /*TODO no-op. Not implemented. if (key_value[1].startsWith("0x")) { diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties index 2a4786bf21b600aa31c233d9f0378eee61d99948..eb354532c36c036904aafe218478c9970d206dba 100644 --- a/src/main/resources/floodlightdefault.properties +++ b/src/main/resources/floodlightdefault.properties @@ -30,6 +30,7 @@ net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnEachTransiti net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePath=/path/to/your/keystore-file.jks net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePassword=your-keystore-password net.floodlightcontroller.core.internal.OFSwitchManager.useSsl=NO +net.floodlightcontroller.core.internal.OFSwitchManager.supportedOpenFlowVersions=1.0, 1.1, 1.2, 1.3, 1.4 net.floodlightcontroller.restserver.RestApiServer.keyStorePath=/path/to/your/keystore-file.jks net.floodlightcontroller.restserver.RestApiServer.keyStorePassword=your-keystore-password net.floodlightcontroller.restserver.RestApiServer.httpsNeedClientAuthentication=NO diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java index b724ebcd629d4f7ee868888e6fb81546ce061977..96147b02eb52b4323be4f804ffad6014c552809b 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java @@ -35,12 +35,14 @@ import org.jboss.netty.util.Timer; import org.junit.After; import org.junit.Before; import org.junit.Test; + import net.floodlightcontroller.core.IOFConnectionBackend; import net.floodlightcontroller.core.OFConnectionCounters; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout; import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl; import net.floodlightcontroller.debugcounter.IDebugCounterService; + import org.projectfloodlight.openflow.protocol.OFActionType; import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFCapabilities; @@ -63,6 +65,7 @@ import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.U32; import com.google.common.collect.ImmutableList; @@ -137,7 +140,7 @@ public class OFChannelHandlerVer10Test { replay(switchManager); handler = new OFChannelHandler(switchManager, newConnectionListener, pipeline, debugCounterService, - timer); + timer, Collections.singletonList(U32.of(0)), OFFactories.getFactory(OFVersion.OF_14)); verify(switchManager); reset(switchManager); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java index bd499214c6a33c624be829a3fa91a71514acd8ba..9b9b6ce897bf00c210326fed7d49e315500e11eb 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java @@ -35,12 +35,14 @@ import org.jboss.netty.util.Timer; import org.junit.After; import org.junit.Before; import org.junit.Test; + import net.floodlightcontroller.core.IOFConnectionBackend; import net.floodlightcontroller.core.OFConnectionCounters; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout; import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl; import net.floodlightcontroller.debugcounter.IDebugCounterService; + import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFBsnSetAuxCxnsReply; import org.projectfloodlight.openflow.protocol.OFCapabilities; @@ -64,6 +66,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.U32; import com.google.common.collect.ImmutableList; @@ -139,7 +142,9 @@ public class OFChannelHandlerVer13Test { expect(switchManager.getCounters()).andReturn(counters).anyTimes(); replay(switchManager); handler = new OFChannelHandler(switchManager, newConnectionListener, - pipeline, debugCounterService, timer); + pipeline, debugCounterService, /* 62 is OF versions 1.0 thru 1.4 in decimal */ + timer, Collections.singletonList(U32.of(62)), OFFactories.getFactory(OFVersion.OF_14)); + verify(switchManager); reset(switchManager);