diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index ccb68176f6086b187071cb4fc9fbf01e9e3df40d..df2c58a743918cc6c27aa89ec7e94dd064b65ed6 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -116,7 +116,13 @@ public interface IOFSwitch extends IOFMessageWriter { Set<OFCapabilities> getCapabilities(); - short getTables(); + /** + * Get the specific TableIds according to the ofp_table_features. + * Not all switches have sequential TableIds, so this will give the + * specific TableIds used by the switch. + * @return + */ + Collection<TableId> getTables(); /** * @return a copy of the description statistics for this switch @@ -350,5 +356,11 @@ public interface IOFSwitch extends IOFMessageWriter { * @return The table features or null if no features are known for the table requested. */ public TableFeatures getTableFeatures(TableId table); + + /** + * Get the number of tables as returned by the ofp_features_reply. + * @return + */ + short getNumTables(); } diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitch.java b/src/main/java/net/floodlightcontroller/core/OFSwitch.java index eeb33525b5b2b49a43bfd085af18e7068e861672..146bfe19179531501d1f36c66a0404575f70c7ce 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitch.java @@ -97,7 +97,8 @@ public class OFSwitch implements IOFSwitchBackend { protected Set<OFCapabilities> capabilities; protected long buffers; protected Set<OFActionType> actions; - protected short tables; + protected Collection<TableId> tables; + protected short nTables; protected final DatapathId datapathId; private Map<TableId, TableFeatures> tableFeaturesByTableId; @@ -164,6 +165,7 @@ public class OFSwitch implements IOFSwitchBackend { this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, Boolean.TRUE); this.tableFeaturesByTableId = new HashMap<TableId, TableFeatures>(); + this.tables = new ArrayList<TableId>(); } private static int ident(int i) { @@ -829,7 +831,8 @@ public class OFSwitch implements IOFSwitchBackend { /* OF1.3+ Per-table actions are set later in the OFTableFeaturesRequest/Reply */ this.actions = featuresReply.getActions(); } - this.tables = featuresReply.getNTables(); + + this.nTables = featuresReply.getNTables(); } @Override @@ -879,6 +882,7 @@ public class OFSwitch implements IOFSwitchBackend { List<OFTableFeatures> tfs = reply.getEntries(); for (OFTableFeatures tf : tfs) { tableFeaturesByTableId.put(tf.getTableId(), TableFeatures.of(tf)); + tables.add(tf.getTableId()); log.trace("Received TableFeatures for TableId {}, TableName {}", tf.getTableId().toString(), tf.getName()); } } @@ -1085,9 +1089,18 @@ public class OFSwitch implements IOFSwitchBackend { } + /** + * This performs a copy on each 'get'. + * Use sparingly for good performance. + */ + @Override + public Collection<TableId> getTables() { + return new ArrayList<TableId>(tables); + } + @Override - public short getTables() { - return tables; + public short getNumTables() { + return this.nTables; } @Override @@ -1226,8 +1239,8 @@ public class OFSwitch implements IOFSwitchBackend { @Override public TableId setMaxTableForTableMissFlow(TableId max) { - if (max.getValue() >= tables) { - maxTableToGetTableMissFlow = TableId.of(tables - 1 < 0 ? 0 : tables - 1); + if (max.getValue() >= nTables) { + maxTableToGetTableMissFlow = TableId.of(nTables - 1 < 0 ? 0 : nTables - 1); } else { maxTableToGetTableMissFlow = max; } diff --git a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java index ea583510844a32655d7019afe2a10d620a02c892..c537ddd64ecd177c46cbbeca41bd88a6873a5b8b 100644 --- a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java +++ b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java @@ -136,7 +136,7 @@ public class SwitchSyncRepresentation { public SwitchSyncRepresentation(IOFSwitch sw) { this.dpid = sw.getId(); this.buffers = sw.getBuffers(); - this.tables = sw.getTables(); + this.tables = (short) sw.getNumTables(); this.capabilities = sw.getCapabilities(); this.actions = sw.getActions(); this.ports = toSyncedPortList(sw.getPorts()); diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java index 667ae245ff239e046d80a3b03efc23509748f044..a6702b74f6b014a498c41c0f9263e5cba8742eaa 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java @@ -46,7 +46,6 @@ 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.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java index 05e56c5f47e1f9b082f66321e16092f70b4eb6f6..0e6c15daebc9bb3ba5266359eeae403f01f0af6c 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java @@ -27,6 +27,7 @@ import net.floodlightcontroller.core.annotations.LogMessageDocs; import net.floodlightcontroller.core.internal.OFSwitchAppHandshakePlugin.PluginResultType; import net.floodlightcontroller.util.OFDPAUtils; +import org.projectfloodlight.openflow.protocol.OFActionType; import org.projectfloodlight.openflow.protocol.OFBadRequestCode; import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFBarrierRequest; @@ -71,6 +72,8 @@ import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsRequest; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.actionid.OFActionId; +import org.projectfloodlight.openflow.protocol.actionid.OFActionIdOutput; import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg; import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; @@ -482,7 +485,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { .setGroupType(OFGroupType.SELECT) .build(); this.sw.write(delgroup); - + /* * Make sure we allow these operations to complete before proceeding. */ @@ -523,17 +526,39 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { ArrayList<OFAction> actions = new ArrayList<OFAction>(1); actions.add(factory.actions().output(OFPort.CONTROLLER, 0xffFFffFF)); ArrayList<OFMessage> flows = new ArrayList<OFMessage>(); - /* Use <, not <=, since we want to allow for zero table miss flows */ - for (int tableId = 0; tableId < this.sw.getMaxTableForTableMissFlow().getValue(); tableId++) { - /* Only add the flow if the table exists and if it supports sending to the controller */ - TableFeatures tf = this.sw.getTableFeatures(TableId.of(tableId)); - if (/* TODO tf != null*/ true /* TODO && tf.supportsSendingToController() -- we need something like this, but how? */) { - OFFlowAdd defaultFlow = this.factory.buildFlowAdd() - .setTableId(TableId.of(tableId)) - .setPriority(0) - .setActions(actions) - .build(); - flows.add(defaultFlow); + + /* If we received a table features reply, iterate over the tables */ + if (!this.sw.getTables().isEmpty()) { + short missCount = 0; + for (TableId tid : this.sw.getTables()) { + /* Only add the flow if the table exists and if it supports sending to the controller */ + TableFeatures tf = this.sw.getTableFeatures(tid); + if (tf != null && (missCount < this.sw.getMaxTableForTableMissFlow().getValue())) { + for (OFActionId aid : tf.getPropApplyActionsMiss().getActionIds()) { + if (aid.getType() == OFActionType.OUTPUT) { /* The assumption here is that OUTPUT includes the special port CONTROLLER... */ + OFFlowAdd defaultFlow = this.factory.buildFlowAdd() + .setTableId(tid) + .setPriority(0) + .setActions(actions) + .build(); + flows.add(defaultFlow); + break; /* Stop searching for actions and go to the next table in the list */ + } + } + } + missCount++; + } + } else { /* Otherwise, use the number of tables starting at TableId=0 as indicated in the features reply */ + short missCount = 0; + for (short tid = 0; tid < this.sw.getNumTables(); tid++, missCount++) { + if (missCount < this.sw.getMaxTableForTableMissFlow().getValue()) { /* Only insert if we want it */ + OFFlowAdd defaultFlow = this.factory.buildFlowAdd() + .setTableId(TableId.of(tid)) + .setPriority(0) + .setActions(actions) + .build(); + flows.add(defaultFlow); + } } } this.sw.write(flows); @@ -1175,6 +1200,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { OFDescStatsReply descStatsReply = (OFDescStatsReply) m; SwitchDescription description = new SwitchDescription(descStatsReply); sw = switchManager.getOFSwitchInstance(mainConnection, description, factory, featuresReply.getDatapathId()); + // set switch information // set features reply and channel first so we a DPID and // channel info. @@ -1479,11 +1505,11 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { * SLAVE. */ public class MasterState extends OFSwitchHandshakeState { - + MasterState() { super(true); } - + private long sendBarrier() { long xid = handshakeTransactionIds--; OFBarrierRequest barrier = factory.buildBarrierRequest() @@ -1503,7 +1529,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { log.info("Clearing flow tables of {} on upcoming initial role as MASTER.", sw.getId().toString()); clearAllTables(); } - + sendBarrier(); /* Need to make sure the tables are clear before adding default flows */ addDefaultFlows(); @@ -1513,7 +1539,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { * the clear/default flow operations above to have completed. */ sendBarrier(); - + setSwitchStatus(SwitchStatus.MASTER); } diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java index 5e311a3d426fbbe678b0e393ae3dab3d234e938d..bace34438cd4a7c8080e3d36dcbc5dbe48c3fc58 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java @@ -184,9 +184,9 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED)); oldSw.disconnect(); } - + /* - * Set other config options for this switch. + * Set some other config options for this switch. */ if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) { if (forwardToControllerFlowsUpToTableByDpid.containsKey(sw.getId())) { @@ -504,6 +504,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen public IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection, SwitchDescription description, OFFactory factory, DatapathId datapathId) { + return this.driverRegistry.getOFSwitchInstance(connection, description, factory, datapathId); } diff --git a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java index 03a46f462b68300c61639052b03a7931bfcc4861..7cb96bcee1f84545bcf4069d4aba091503d73cce 100644 --- a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java +++ b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java @@ -47,7 +47,7 @@ public class SwitchRepresentation { // IOFSwitch this.buffers = sw.getBuffers(); this.capabilities = sw.getCapabilities(); - this.tables = sw.getTables(); + this.tables = sw.getNumTables(); this.inetAddress = sw.getInetAddress(); this.sortedPorts = sw.getSortedPorts(); this.isConnected = sw.isConnected(); diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java index 18b097db1e6fd1332459689375e20e7ffdc81f2a..c64f1a821be38a7c7fa49f1c7f21c51f106e6185 100644 --- a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java @@ -16,16 +16,13 @@ package net.floodlightcontroller.core.web; -import java.io.IOException; import java.util.HashMap; import java.util.Map; -import org.restlet.data.Status; import org.restlet.resource.ServerResource; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.RoleInfo; -import net.floodlightcontroller.core.HARole; import net.floodlightcontroller.core.annotations.LogMessageDoc; import org.restlet.resource.Get; @@ -34,7 +31,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.MappingJsonFactory; public class ControllerRoleResource extends ServerResource { diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java index 753685a358b7a5de190694a032327a7b362c216c..d67307676a9d2aa0386f7d4e32d4dee2fd0e644a 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java @@ -60,7 +60,7 @@ public class IOFSwitchSerializer extends JsonSerializer<IOFSwitch> { serializePorts(sw.getPorts(),jGen); jGen.writeNumberField("buffers",sw.getBuffers()); jGen.writeStringField("inetAddress",sw.getInetAddress().toString()); - jGen.writeNumberField("tables",sw.getTables()); + jGen.writeNumberField("tables",sw.getNumTables()); jGen.writeNumberField("connectedSince",sw.getConnectedSince().getTime()); jGen.writeEndObject(); } 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 606d42f6baa2a4e3f5923b8ce2c2ed85ed81af00..67474cbab8a5801178a56b5598e980a1f1fc403a 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java @@ -80,7 +80,6 @@ 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; diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index b06cfebaad75a075f8be894f7b27b490acfeb7b4..d331fe2af4f4b6f1843470fd9b051fbc02465455 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -42,6 +42,7 @@ import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.IPv6; import net.floodlightcontroller.packet.TCP; import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.routing.ForwardingBase; @@ -60,6 +61,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBufferId; @@ -181,6 +183,8 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) { DatapathId dstSwDpid = dstDap.getSwitchDPID(); DatapathId dstIsland = topologyService.getL2DomainId(dstSwDpid); + log.trace("Source Island: {}, Destination Island: {}", srcIsland, dstIsland); + log.trace("Source Device: {}, Destination Device: {}", srcDevice, dstDevice); if ((dstIsland != null) && dstIsland.equals(srcIsland)) { on_same_island = true; if (sw.getId().equals(dstSwDpid) && inPort.equals(dstDap.getPort())) { @@ -337,6 +341,38 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { } } else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */ mb.setExact(MatchField.ETH_TYPE, EthType.ARP); + } else if (eth.getEtherType() == EthType.IPv6) { + IPv6 ip = (IPv6) eth.getPayload(); + IPv6Address srcIp = ip.getSourceAddress(); + IPv6Address dstIp = ip.getDestinationAddress(); + + if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv6) + .setExact(MatchField.IPV6_SRC, srcIp) + .setExact(MatchField.IPV6_DST, dstIp); + } + + if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) { + /* + * Take care of the ethertype if not included earlier, + * since it's a prerequisite for transport ports. + */ + if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv4); + } + + if (ip.getNextHeader().equals(IpProtocol.TCP)) { + TCP tcp = (TCP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP) + .setExact(MatchField.TCP_SRC, tcp.getSourcePort()) + .setExact(MatchField.TCP_DST, tcp.getDestinationPort()); + } else if (ip.getNextHeader().equals(IpProtocol.UDP)) { + UDP udp = (UDP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP) + .setExact(MatchField.UDP_SRC, udp.getSourcePort()) + .setExact(MatchField.UDP_DST, udp.getDestinationPort()); + } + } } return mb.build(); } diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index d0e6e0eda91ae7d418706395c9a8d17c1255b4e7..175f464b25a974709caf1c47cbffe0916d132d7e 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -259,8 +259,8 @@ public abstract class ForwardingBase implements IOFMessageListener { OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput(); List<OFAction> actions = new ArrayList<OFAction>(); - Match.Builder mb = MatchUtils.createRetentiveBuilder(match); - + Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion()); + // set input and output ports on the switch OFPort outPort = switchPortList.get(indx).getPortId(); OFPort inPort = switchPortList.get(indx - 1).getPortId(); diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java index 028614485c1a1d18300241a43d02e4c15398d5e4..c458a9f2e852e5ac79827828de0a8ce2b3bf4054 100644 --- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java +++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java @@ -155,27 +155,27 @@ public class MatchUtils { } return mb.build(); } - + + /** + * Retains all fields in the Match parent. Converts the parent to an + * equivalent Match of OFVersion version. No polite check is done to verify + * if MatchFields in parent are supported in a Match of OFVersion + * version. An exception will be thrown if there are any unsupported + * fields during the conversion process. * - * Workaround for bug in Loxi: - * - * Create a builder from an existing Match object. Unlike Match's - * createBuilder(), this utility function will preserve all of - * Match m's MatchFields, even if new MatchFields are set or modified - * with the builder after it is returned to the calling function. - * - * All original MatchFields in m will be set if the build() method is - * invoked upon the returned builder. After the builder is returned, if - * a MatchField is modified via setExact(), setMasked(), or wildcard(), - * the newly modified MatchField will replace the original found in m. + * Note that a Match.Builder is returned. This is a convenience for cases + * where MatchFields might be modified, added, or removed prior to being + * built (e.g. in Forwarding/Routing between switches of different OFVersions). + * Simply build the returned Match.Builder if you would like to treat this + * function as a strict copy-to-version. * - * @param m; the match to create the builder from - * @return Match.Builder; the builder that can be modified, and when built, - * will retain all of m's MatchFields, unless you explicitly overwrite them. + * @param parent, the Match to convert + * @param version, the OFVersion to convert parent to + * @return a Match.Builder of the newly-converted Match */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Match.Builder createRetentiveBuilder(Match m) { + @SuppressWarnings("unchecked") + public static Match.Builder convertToVersion(Match parent, OFVersion version) { /* Builder retains a parent MatchField list, but list will not be used to * build the new match if the builder's set methods have been invoked; only * additions will be built, and all parent MatchFields will be ignored, @@ -185,14 +185,15 @@ public class MatchUtils { * in the Match built from this builder if the user decides to add or subtract * from the MatchField list. */ - Match.Builder mb = m.createBuilder(); - Iterator<MatchField<?>> itr = m.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded) + Match.Builder mb = OFFactories.getFactory(version).buildMatch(); + Iterator<MatchField<?>> itr = parent.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded) while(itr.hasNext()) { + @SuppressWarnings("rawtypes") MatchField mf = itr.next(); - if (m.isExact(mf)) { - mb.setExact(mf, m.get(mf)); - } else if (m.isPartiallyMasked(mf)) { - mb.setMasked(mf, m.getMasked(mf)); + if (parent.isExact(mf)) { + mb.setExact(mf, parent.get(mf)); + } else if (parent.isPartiallyMasked(mf)) { + mb.setMasked(mf, parent.getMasked(mf)); } else { // it's either exact, masked, or wildcarded // itr only contains exact and masked MatchFields @@ -202,6 +203,28 @@ public class MatchUtils { return mb; } + /** + * + * Workaround for bug in Loxi: + * + * Create a builder from an existing Match object. Unlike Match's + * createBuilder(), this utility function will preserve all of + * Match m's MatchFields, even if new MatchFields are set or modified + * with the builder after it is returned to the calling function. + * + * All original MatchFields in m will be set if the build() method is + * invoked upon the returned builder. After the builder is returned, if + * a MatchField is modified via setExact(), setMasked(), or wildcard(), + * the newly modified MatchField will replace the original found in m. + * + * @param m; the match to create the builder from + * @return Match.Builder; the builder that can be modified, and when built, + * will retain all of m's MatchFields, unless you explicitly overwrite them. + */ + public static Match.Builder createRetentiveBuilder(Match m) { + return convertToVersion(m, m.getVersion()); + } + /** * Create a Match builder the same OF version as Match m. The returned builder * will not retain any MatchField information from Match m and will diff --git a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java index f4276466454a492dd65921625886ca3badd3cc43..f2744985b46813c045828174c8142dab3f418e54 100644 --- a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java +++ b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java @@ -229,8 +229,10 @@ public class OFDPAUtils { public static boolean addLearningSwitchPrereqs(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) { /* * Both of these must complete. If the first fails, the second will not be executed. (AND short-circuit) + * + * Groups must be added last, since they require the VLANs to be added first. */ - return addLearningSwitchPrereqGroups(sw, vlan, ports) && addLearningSwitchPrereqFlows(sw, vlan, ports); + return addLearningSwitchPrereqFlows(sw, vlan, ports) && addLearningSwitchPrereqGroups(sw, vlan, ports) ; } /** @@ -369,14 +371,14 @@ public class OFDPAUtils { ArrayList<OFAction> actions = new ArrayList<OFAction>(); Match.Builder mb = sw.getOFFactory().buildMatch(); OFFlowAdd.Builder fab = sw.getOFFactory().buildFlowAdd(); - + /* These are common to all flows for VLAN flow table. */ fab.setBufferId(OFBufferId.NO_BUFFER) - .setCookie(COOKIE) - .setHardTimeout(HARD_TIMEOUT) - .setIdleTimeout(IDLE_TIMEOUT) - .setPriority(PRIORITY) - .setTableId(Tables.VLAN); + .setCookie(COOKIE) + .setHardTimeout(HARD_TIMEOUT) + .setIdleTimeout(IDLE_TIMEOUT) + .setPriority(PRIORITY) + .setTableId(Tables.VLAN); for (OFPortModeTuple p : ports) { /* If VLAN tag not present add a tag (internal=1 or defined) */ @@ -392,13 +394,17 @@ public class OFDPAUtils { /* No matter what, we need to match on the ingress port */ mb.setExact(MatchField.IN_PORT, p.getPort()); + /* We have to do this for OF-DPA. It's like adding the VLAN to the switch on that port. */ + instructions.add(sw.getOFFactory().instructions().applyActions(Collections.singletonList((OFAction) + sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))) + ))); /* No matter what, output to the next table */ instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC)); /* Set the new stuff. */ fab.setInstructions(instructions) - .setMatch(mb.build()) - .build(); + .setMatch(mb.build()) + .build(); sw.write(fab.build()); log.debug("Writing prereq flow to VLAN flow table {}", fab.build().toString()); @@ -410,7 +416,7 @@ public class OFDPAUtils { /* * We will insert a DLF flow to send to controller in the Policy ACL table (60). - * TODO Maybe this isn't the best choice, since we assume bypass of bridging and unicast/mulicast routing tables. + * TODO Maybe this isn't the best choice, since we assume bypass/auto-forwarding of bridging and unicast/mulicast routing tables. */ actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xffFFffFF)); instructions.add(sw.getOFFactory().instructions().applyActions(actions)); diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties index b1ae5c73a3600930ad1ffb44aed4e41f97c34338..8c644c730d806b8140cd24cd19409d23d4ccc00c 100644 --- a/src/main/resources/floodlightdefault.properties +++ b/src/main/resources/floodlightdefault.properties @@ -23,8 +23,8 @@ org.sdnplatform.sync.internal.SyncManager.port=6642 net.floodlightcontroller.forwarding.Forwarding.match=vlan, mac, ip, transport net.floodlightcontroller.core.internal.FloodlightProvider.openflowPort=6653 net.floodlightcontroller.core.internal.FloodlightProvider.role=ACTIVE -net.floodlightcontroller.core.internal.OFSwitchManager.defaultMaxTablesToReceiveTableMissFlow=4 -net.floodlightcontroller.core.internal.OFSwitchManager.maxTablesToReceiveTableMissFlowPerDpid={"00:00:00:00:00:00:00:01":"4","2":"4"} +net.floodlightcontroller.core.internal.OFSwitchManager.defaultMaxTablesToReceiveTableMissFlow=2 +net.floodlightcontroller.core.internal.OFSwitchManager.maxTablesToReceiveTableMissFlowPerDpid={"00:00:00:00:00:00:00:01":"1","2":"1"} net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnInitialHandshakeAsMaster=YES net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnEachTransitionToMaster=YES net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePath=/path/to/your/keystore-file.jks diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml index 95ba59679ace01c1d8334da23b24909768cd8d25..21c48f39c6a6b94e501b3bafd9a97528b25f2b02 100644 --- a/src/main/resources/logback-test.xml +++ b/src/main/resources/logback-test.xml @@ -17,7 +17,6 @@ <logger name="net.floodlightcontroller.devicemanager" level="INFO"></logger> <logger name="net.floodlightcontroller.packet" level="INFO"></logger> <logger name="net.floodlightcontroller.forwarding" level="INFO"></logger> - <logger name="net.floodlightcontroller.devicemanager" level="INFO"></logger> <logger name="net.floodlightcontroller.core.internal" level="INFO"></logger> <logger level="INFO" name="net.floodlightcontroller.learningswitch"></logger> <logger level="INFO" name="org.projectfloodlight.openflow"></logger> diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index d0a4a963d1b202cf9d8fdd0ce3453204a0196b53..669876e7904ba16f5db21284869fc387724be6d3 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -244,7 +244,7 @@ public class ControllerTest extends FloodlightTestCase { expect(sw.getSwitchDescription()).andReturn(description).atLeastOnce(); expect(sw.getBuffers()) .andReturn(featuresReply.getNBuffers()).atLeastOnce(); - expect(sw.getTables()) + expect(sw.getNumTables()) .andReturn(featuresReply.getNTables()).atLeastOnce(); expect(sw.getCapabilities()) .andReturn(featuresReply.getCapabilities()).atLeastOnce(); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java index 9c67de3b2eb584cb94e5fc2d883a92819ed96d7b..675873a5eeac46c7e8db6cef30e31ea02680fd08 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java @@ -525,12 +525,12 @@ public abstract class OFSwitchHandlerTestBase { expectLastCall().anyTimes(); sw.write(anyObject(Iterable.class)); expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); + expect(sw.getNumTables()).andStubReturn((short)0); sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, supportsNxRole); expectLastCall().anyTimes(); if (SwitchStatus.MASTER == newStatus) { if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { - expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(1); + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); } } @@ -603,7 +603,8 @@ public abstract class OFSwitchHandlerTestBase { expectLastCall().anyTimes(); sw.write(anyObject(Iterable.class)); expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); + expect(sw.getTables()).andStubReturn(Collections.EMPTY_LIST); + expect(sw.getNumTables()).andStubReturn((short) 0); sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); expectLastCall().once(); sw.setControllerRole(OFControllerRole.ROLE_MASTER); @@ -613,7 +614,7 @@ public abstract class OFSwitchHandlerTestBase { expectLastCall().once(); if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { - expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(1); + //expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(1); expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); } replay(sw); @@ -627,11 +628,6 @@ public abstract class OFSwitchHandlerTestBase { /* Go into the MasterState */ switchHandler.processOFMessage(reply); - /* Now, trigger transition to master */ - OFBarrierReply br = getFactory().buildBarrierReply() - .build(); - switchHandler.processOFMessage(br); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); } @@ -703,7 +699,7 @@ public abstract class OFSwitchHandlerTestBase { expectLastCall().anyTimes(); sw.write(anyObject(Iterable.class)); expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); + expect(sw.getNumTables()).andStubReturn((short)0); sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); expectLastCall().once(); sw.setControllerRole(OFControllerRole.ROLE_MASTER); @@ -712,7 +708,7 @@ public abstract class OFSwitchHandlerTestBase { sw.setStatus(SwitchStatus.MASTER); expectLastCall().once(); if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { - expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(1); + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); } replay(sw); @@ -734,11 +730,6 @@ public abstract class OFSwitchHandlerTestBase { /* Go into the MasterState */ switchHandler.processOFMessage(err); - - /* Now, trigger transition to master */ - OFBarrierReply br = getFactory().buildBarrierReply() - .build(); - switchHandler.processOFMessage(br); assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); } @@ -771,7 +762,7 @@ public abstract class OFSwitchHandlerTestBase { expectLastCall().anyTimes(); sw.write(anyObject(Iterable.class)); expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); + expect(sw.getNumTables()).andStubReturn((short)0); sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); expectLastCall().once(); sw.setControllerRole(OFControllerRole.ROLE_MASTER); @@ -780,7 +771,7 @@ public abstract class OFSwitchHandlerTestBase { sw.setStatus(SwitchStatus.MASTER); expectLastCall().once(); if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { - expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(1); + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); } replay(sw); @@ -794,10 +785,6 @@ public abstract class OFSwitchHandlerTestBase { expectLastCall().once(); replay(switchManager); switchHandler.processOFMessage(m); - - /* Send another barrier to trigger becoming master */ - switchHandler.processOFMessage(m); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); @@ -939,7 +926,7 @@ public abstract class OFSwitchHandlerTestBase { expectLastCall().anyTimes(); sw.write(anyObject(Iterable.class)); expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); + expect(sw.getNumTables()).andStubReturn((short)0); sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); expectLastCall().once(); sw.setControllerRole(OFControllerRole.ROLE_MASTER); @@ -947,7 +934,7 @@ public abstract class OFSwitchHandlerTestBase { expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); sw.setStatus(SwitchStatus.MASTER); expectLastCall().once(); - expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(2); + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); replay(sw); reset(switchManager); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java index 0de8b2b66bae472f183b8255429f7c40bf830bf9..62d43ab96a1305cb89e0df40ae14df5ee9f89fd2 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java @@ -198,7 +198,7 @@ public class OFSwitchManagerTest{ expect(sw.getSwitchDescription()).andReturn(description).anyTimes(); expect(sw.getBuffers()) .andReturn(featuresReply.getNBuffers()).anyTimes(); - expect(sw.getTables()) + expect(sw.getNumTables()) .andReturn(featuresReply.getNTables()).anyTimes(); expect(sw.getCapabilities()) .andReturn(featuresReply.getCapabilities()).anyTimes(); diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java index c11a14372eb33ad7d007e6d75c05bb2d1d68cc73..1416e8353b59fb7b82bf42a5ad95a8d37c2eb10e 100644 --- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java +++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java @@ -183,10 +183,16 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { } @Override - public short getTables() { + public short getNumTables() { fail("Unexpected method call"); return 0; } + + @Override + public Collection<TableId> getTables() { + fail("Unexpected method call"); + return null; + } @Override public boolean attributeEquals(String name, Object other) {