diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java index 2a70610fef2703ea69322c11062bf58fbe0995b3..2908b6f61681659139c27ede6d12e4b49382db86 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.HashMap; import java.util.List; @@ -35,11 +36,11 @@ import net.floodlightcontroller.storage.IStorageSourceListener; import net.floodlightcontroller.storage.StorageException; -import org.openflow.protocol.OFBarrierRequest; import org.openflow.protocol.OFFlowMod; import org.openflow.protocol.OFFlowRemoved; import org.openflow.protocol.OFMatch; import org.openflow.protocol.OFMessage; +import org.openflow.protocol.OFPort; import org.openflow.protocol.OFType; import org.openflow.util.HexString; import org.openflow.util.U16; @@ -186,7 +187,7 @@ public class StaticFlowEntryPusher protected Map<String, String> computeEntry2DpidMap( Map<String, Map<String, OFFlowMod>> map) { - Map<String, String> ret = new HashMap<String, String>(); + Map<String, String> ret = new ConcurrentHashMap<String, String>(); for(String dpid : map.keySet()) { for( String entry: map.get(dpid).keySet()) ret.put(entry, dpid); @@ -279,8 +280,7 @@ public class StaticFlowEntryPusher flowMod.setCookie( StaticFlowEntries.computeEntryCookie(flowMod, Integer.valueOf((String) row.get(COLUMN_COOKIE)), - entryName) - ); + entryName)); } else if (key.equals(COLUMN_PRIORITY)) { flowMod.setPriority(U16.t(Integer.valueOf((String) row.get(COLUMN_PRIORITY)))); } else { // the rest of the keys are for OFMatch().fromString() @@ -317,7 +317,7 @@ public class StaticFlowEntryPusher @Override public void addedSwitch(IOFSwitch sw) { - log.debug("addedSwitch {}; processing its static entries", sw); + log.debug("Switch {} connected; processing its static entries", HexString.toHexString(sw.getId())); sendEntriesToSwitch(sw); } @@ -355,10 +355,22 @@ public class StaticFlowEntryPusher List<OFMessage> outQueue = new ArrayList<OFMessage>(); for(String entry : entriesToAdd.get(dpid).keySet()) { OFFlowMod newFlowMod = entriesToAdd.get(dpid).get(entry); - OFFlowMod oldFlowMod = entriesFromStorage.get(dpid).get(entry); - if (oldFlowMod != null) { // remove any pre-existing rule + //OFFlowMod oldFlowMod = entriesFromStorage.get(dpid).get(entry); + OFFlowMod oldFlowMod = null; + String dpidOldFlowMod = entry2dpid.get(entry); + if (dpidOldFlowMod != null) { + oldFlowMod = entriesFromStorage.get(dpidOldFlowMod).remove(entry); + } + if (oldFlowMod != null) { + // Remove any pre-existing rule + // If the old rule is on a different switch + // then we have to handle that as well. oldFlowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); - outQueue.add(oldFlowMod); + if (dpidOldFlowMod.equals(dpid)) { + outQueue.add(oldFlowMod); + } else { + writeOFMessageToSwitch(HexString.toLong(dpidOldFlowMod), oldFlowMod); + } } if (newFlowMod != null) { entriesFromStorage.get(dpid).put(entry, newFlowMod); @@ -397,15 +409,16 @@ public class StaticFlowEntryPusher private void deleteStaticFlowEntry(String entryName) { String dpid = entry2dpid.remove(entryName); - if (log.isDebugEnabled()) { - log.debug("Deleting flow {} for switch {}", entryName, dpid); - } if (dpid == null) { // assume state has been cleared by deleteFlowsForSwitch() or // deleteAllFlows() return; } + if (log.isDebugEnabled()) { + log.debug("Sending delete flow mod for flow {} for switch {}", entryName, dpid); + } + // send flow_mod delete OFFlowMod flowMod = entriesFromStorage.get(dpid).get(entryName); flowMod.setCommand(OFFlowMod.OFPFC_DELETE_STRICT); @@ -431,7 +444,7 @@ public class StaticFlowEntryPusher @LogMessageDoc(level="ERROR", message="Tried to write to switch {switch} but got {error}", explanation="An I/O error occured while trying to write a " + - "static flow to a switch", + "static flow to a switch", recommendation=LogMessageDoc.CHECK_SWITCH) private void writeOFMessagesToSwitch(long dpid, List<OFMessage> messages) { IOFSwitch ofswitch = floodlightProvider.getSwitches().get(dpid); @@ -448,6 +461,31 @@ public class StaticFlowEntryPusher } } + /** + * Writes a single OFMessage to a switch + * @param dpid The datapath ID of the switch to write to + * @param message The OFMessage to write. + */ + @LogMessageDoc(level="ERROR", + message="Tried to write to switch {switch} but got {error}", + explanation="An I/O error occured while trying to write a " + + "static flow to a switch", + recommendation=LogMessageDoc.CHECK_SWITCH) + private void writeOFMessageToSwitch(long dpid, OFMessage message) { + IOFSwitch ofswitch = floodlightProvider.getSwitches().get(dpid); + if (ofswitch != null) { // is the switch connected + try { + if (log.isDebugEnabled()) { + log.debug("Sending 1 new entries to {}", HexString.toHexString(dpid)); + } + ofswitch.write(message, null); + ofswitch.flush(); + } catch (IOException e) { + log.error("Tried to write to switch {} but got {}", dpid, e.getMessage()); + } + } + } + /** * Writes an OFFlowMod to a switch. It checks to make sure the switch * exists before it sends @@ -492,29 +530,32 @@ public class StaticFlowEntryPusher return StaticFlowName; } - public Command handleFlowRemoved(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { - OFFlowRemoved flowRemoved = (OFFlowRemoved) msg; - long cookie = flowRemoved.getCookie(); + /** + * Handles a flow removed message from a switch. If the flow was removed + * and we did not explicitly delete it we re-install it. If we explicitly + * removed the flow we stop the processing of the flow removed message. + * @param sw The switch that sent the flow removed message. + * @param msg The flow removed message. + * @param cntx The associated context. + * @return Whether to continue processing this message. + */ + public Command handleFlowRemoved(IOFSwitch sw, OFFlowRemoved msg, FloodlightContext cntx) { + long cookie = msg.getCookie(); /** * This is just to sanity check our assumption that static flows * never expire. */ if (AppCookie.extractApp(cookie) == STATIC_FLOW_APP_ID) { - if (flowRemoved.getReason() != - OFFlowRemoved.OFFlowRemovedReason.OFPRR_DELETE) + if (msg.getReason() != OFFlowRemoved.OFFlowRemovedReason.OFPRR_DELETE) log.error("Got a FlowRemove message for a infinite " + "timeout flow: {} from switch {}", msg, sw); - return Command.STOP; // only for us + // Stop the processing chain since we sent the delete. + return Command.STOP; } return Command.CONTINUE; } - public Command handleBarrierReply(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { - - return Command.CONTINUE; - } - @Override @LogMessageDoc(level="ERROR", message="Got a FlowRemove message for a infinite " + @@ -525,9 +566,7 @@ public class StaticFlowEntryPusher public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case FLOW_REMOVED: - return handleFlowRemoved(sw, msg, cntx); - case BARRIER_REPLY: - return handleBarrierReply(sw, msg, cntx); + return handleFlowRemoved(sw, (OFFlowRemoved) msg, cntx); default: return Command.CONTINUE; } @@ -587,7 +626,6 @@ public class StaticFlowEntryPusher @Override public void startUp(FloodlightModuleContext context) { floodlightProvider.addOFMessageListener(OFType.FLOW_REMOVED, this); - floodlightProvider.addOFMessageListener(OFType.BARRIER_REPLY, this); floodlightProvider.addOFSwitchListener(this); floodlightProvider.addHAListener(this); @@ -623,13 +661,13 @@ public class StaticFlowEntryPusher @Override public void deleteAllFlows() { // Send a delete for each switch + Set<String> swSet = new HashSet<String>(); for (String dpid : entry2dpid.values()) { - sendDeleteByCookie(HexString.toLong(dpid)); - } - - // Delete it from the DB - for (String entry : entry2dpid.keySet()) { - deleteFlow(entry); + // Avoid sending duplicate deletes + if (!swSet.contains(dpid)) { + swSet.add(dpid); + sendDeleteByCookie(HexString.toLong(dpid)); + } } // Clear our map @@ -640,13 +678,15 @@ public class StaticFlowEntryPusher eMap.clear(); } + // Reset our DB + storageSource.deleteMatchingRowsAsync(TABLE_NAME, null); } @Override public void deleteFlowsForSwitch(long dpid) { - String sDpid = HexString.toHexString(dpid); sendDeleteByCookie(dpid); + String sDpid = HexString.toHexString(dpid); // Clear all internal flows for this switch Map<String, OFFlowMod> sMap = entriesFromStorage.get(sDpid); if (sMap != null) { @@ -666,12 +706,11 @@ public class StaticFlowEntryPusher * We send a delete flow mod with the static flow pusher app ID in the cookie. * Since OF1.0 doesn't support masking based on the cookie we have to * disable having flow specific cookies. - * @param dpid + * @param dpid The DPID of the switch to clear all it's flows. */ - public void sendDeleteByCookie(long dpid) { + private void sendDeleteByCookie(long dpid) { if (log.isDebugEnabled()) - log.debug("Delete all static flows on switch {}", - HexString.toHexString(dpid)); + log.debug("Deleting all static flows on switch {}", HexString.toHexString(dpid)); IOFSwitch sw = floodlightProvider.getSwitches().get(dpid); if (sw == null) { @@ -686,15 +725,10 @@ public class StaticFlowEntryPusher fm.setMatch(ofm); fm.setCookie(AppCookie.makeCookie(StaticFlowEntryPusher.STATIC_FLOW_APP_ID, 0)); fm.setCommand(OFFlowMod.OFPFC_DELETE); - - OFBarrierRequest ofbr = (OFBarrierRequest) floodlightProvider.getOFMessageFactory(). - getMessage(OFType.BARRIER_REQUEST); - - List<OFMessage> mList = new ArrayList<OFMessage>(2); - mList.add(fm); - mList.add(ofbr); + fm.setOutPort(OFPort.OFPP_NONE); + try { - sw.write(mList, null); + sw.write(fm, null); sw.flush(); } catch (IOException e1) { log.error("Error deleting all flows for switch {}:\n {}", @@ -712,7 +746,6 @@ public class StaticFlowEntryPusher public Map<String, OFFlowMod> getFlows(String dpid) { return entriesFromStorage.get(dpid); } - // IHAListener diff --git a/src/main/java/org/openflow/protocol/OFFlowMod.java b/src/main/java/org/openflow/protocol/OFFlowMod.java index 51b5de35120cf760f685a385e8063208fcbdedf4..06ef5479d67fe785dc3aa4e23ae6306296dbd0e8 100644 --- a/src/main/java/org/openflow/protocol/OFFlowMod.java +++ b/src/main/java/org/openflow/protocol/OFFlowMod.java @@ -59,6 +59,7 @@ public class OFFlowMod extends OFMessage implements OFActionFactoryAware, Clonea public OFFlowMod() { super(); + this.outPort = OFPort.OFPP_NONE.getValue(); this.type = OFType.FLOW_MOD; this.length = U16.t(MINIMUM_LENGTH); } diff --git a/src/main/java/org/openflow/protocol/OFMatch.java b/src/main/java/org/openflow/protocol/OFMatch.java index bbf6a609abbfe3884a4fd0e8c086a67fccf8ab72..c17bc0d29c0dd2febf7dc58ed792d244b6c5ee4b 100644 --- a/src/main/java/org/openflow/protocol/OFMatch.java +++ b/src/main/java/org/openflow/protocol/OFMatch.java @@ -21,6 +21,8 @@ import java.io.Serializable; import java.nio.ByteBuffer; import java.util.Arrays; +import net.floodlightcontroller.packet.Ethernet; + import org.codehaus.jackson.map.annotate.JsonSerialize; import org.jboss.netty.buffer.ChannelBuffer; import org.openflow.protocol.serializers.OFMatchJSONSerializer; @@ -121,7 +123,7 @@ public class OFMatch implements Cloneable, Serializable { this.dataLayerDestination = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; this.dataLayerSource = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; - this.dataLayerVirtualLan = -1; + this.dataLayerVirtualLan = Ethernet.VLAN_UNTAGGED; this.dataLayerVirtualLanPriorityCodePoint = 0; this.dataLayerType = 0; this.inputPort = 0;