diff --git a/.gitignore b/.gitignore
index dcc106a8e1436ff767ad147545bdfe308800b665..6bb47c581906a2d592173e1b0532486f4ace9bdc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ thrift
 *.swp
 *.pyc
 findbugs-results
+*.launch
diff --git a/build.xml b/build.xml
index 79d64b1f1c29c68f6cfd490e8e6a9942f1463856..0a3eb4bcdf095b74b5a1eb32ba8d19bbc63b33c9 100644
--- a/build.xml
+++ b/build.xml
@@ -283,10 +283,12 @@
     <target name="findbugs-xml" depends="init,compile">
         <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" classpath="${findbugs.home}/lib/findbugs-ant.jar"/>
         <mkdir dir="${findbugs.results}"/>
-        <findbugs home="${findbugs.home}"
+        <findbugs
+            home="${findbugs.home}"
             output="xml"
             excludeFilter="${basedir}/findbugs-exclude.xml"
-            outputFile="${findbugs.results}/results.xml" reportLevel='high'>
+            jvmargs="-Xmx1024m"
+            outputFile="${findbugs.results}/results.xml">
            <sourcePath path="${source}" />
            <sourcePath path="${thrift.out.dir}" />
            <class location="${build}" />
@@ -299,9 +301,11 @@
     <target name="findbugs" depends="init,compile">
         <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" classpath="${findbugs.home}/lib/findbugs-ant.jar"/>
         <mkdir dir="${findbugs.results}"/>
-        <findbugs home="${findbugs.home}"
+        <findbugs
+            home="${findbugs.home}"
             output="html"
             excludeFilter="${basedir}/findbugs-exclude.xml"
+            jvmargs="-Xmx1024m"
             outputFile="${findbugs.results}/results.html" >
            <sourcePath path="${source}" />
            <sourcePath path="${thrift.out.dir}" />
diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml
index bb2e5f327c1d1c56d911e9c3f38351b233727cce..9baffdda0e4c7805c197c34025aae9274b3a0075 100644
--- a/findbugs-exclude.xml
+++ b/findbugs-exclude.xml
@@ -1,10 +1,20 @@
 <FindBugsFilter>
     <!--- Checks disabled on generated code -->
+
     <Match>
         <!-- don't complain about switch statements / external representation exposed
              by generated parser files -->
         <Class name="~net\.bigdb\.(query|yang)\.parser\..*(Lexer|Parser)" />
-        <Bug pattern="SF_SWITCH_NO_DEFAULT,EI_EXPOSE_REP" />
+        <Or>
+            <Bug pattern="SF_SWITCH_NO_DEFAULT,EI_EXPOSE_REP,SBSC_USE_STRINGBUFFER_CONCATENATION" />
+            <Bug category="PERFORMANCE,MALICIOUS_CODE" />
+        </Or>
+    </Match>
+
+    <Match>
+        <!-- Exclude warnings in generated thrift class files -->
+        <Class name="~net\.floodlightcontroller\.packetstreamer\.thrift\..*" />
+        <Bug pattern="CN_IDIOM,DLS_DEAD_LOCAL_STORE" />
     </Match>
 
     <!-- checks disabled because of too many false positives -->
diff --git a/setup-eclipse.sh b/setup-eclipse.sh
index 01a6a95ef0f34cee64e85d0707c3d5b29ca0d219..4224a3c5c982f4fee5768a0e5bd788ea36f0b765 100755
--- a/setup-eclipse.sh
+++ b/setup-eclipse.sh
@@ -25,6 +25,36 @@ cat >"$d/.project" <<EOF
 </projectDescription>
 EOF
 
+cat >"$d/Floodlight-Default-Conf.launch" << EOF
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+        <listEntry value="/floodlight/src/main/java/net/floodlightcontroller/core/Main.java"/>
+    </listAttribute>
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+        <listEntry value="1"/>
+    </listAttribute>
+    <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="net.floodlightcontroller.core.Main"/>
+    <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="floodlight"/>
+    <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/>
+</launchConfiguration>
+EOF
+
+cat > "$d/Floodlight-Quantum-Conf.launch" << EOF
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+        <listEntry value="/floodlight/src/main/java/net/floodlightcontroller/core/Main.java"/>
+    </listAttribute>
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+        <listEntry value="1"/>
+    </listAttribute>
+    <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="net.floodlightcontroller.core.Main"/>
+    <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-cf src/main/resources/quantum.properties"/>
+    <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="floodlight"/>
+    <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/>
+</launchConfiguration>
+EOF
 
 cat >"$d/.classpath" <<EOF
 <?xml version="1.0" encoding="UTF-8"?>
diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
index e73972ac6fe5f857e7de69fec56b47b79851cdb5..683e0dee8436e941e5663378d3e352f38cafafc9 100644
--- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
+++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java
@@ -88,6 +88,11 @@ public interface IFloodlightProviderService extends
      */
     public Role getRole();
     
+    /**
+     * Get the current role of the controller
+     */
+    public RoleInfo getRoleInfo();
+
     /**
      * Get the current mapping of controller IDs to their IP addresses
      * Returns a copy of the current mapping. 
@@ -95,15 +100,13 @@ public interface IFloodlightProviderService extends
      */
     public Map<String,String> getControllerNodeIPs();
     
-    /**
-     * Gets the ID of the controller
-     */
-    public String getControllerId();
     
     /**
      * Set the role of the controller
+     * @param role The new role for the controller node
+     * @param changeDescription The reason or other information for this role change
      */
-    public void setRole(Role role);
+    public void setRole(Role role, String changeDescription);
     
     /**
      * Add a switch listener
@@ -208,6 +211,17 @@ public interface IFloodlightProviderService extends
     */
    public void setAlwaysClearFlowsOnSwAdd(boolean value);
    
+   /**
+    * Get controller memory information
+    */
+   public Map<String, Long> getMemory();
+
+   /**
+    * returns the uptime of this controller.
+    * @return
+    */
+   public Long getUptime();
+   
    /**
     * Adds an OFSwitch driver
     * @param desc The starting portion of switch's manufacturer string
diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java
index 98eb372404b3bbcf490d5dcde5bd43baaee43afc..8fa298ddc3bd96fb260373b0757291725f6833ff 100644
--- a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java
+++ b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java
@@ -48,6 +48,7 @@ import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 import org.codehaus.jackson.map.ser.ToStringSerializer;
 import org.jboss.netty.channel.Channel;
+import org.openflow.protocol.OFBarrierRequest;
 import org.openflow.protocol.OFFeaturesReply;
 import org.openflow.protocol.OFFlowMod;
 import org.openflow.protocol.OFMatch;
@@ -509,10 +510,18 @@ public abstract class OFSwitchBase implements IOFSwitch {
             .setCommand(OFFlowMod.OFPFC_DELETE)
             .setOutPort(OFPort.OFPP_NONE)
             .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH));
+        fm.setXid(getNextTransactionId());
+        OFMessage barrierMsg = (OFBarrierRequest)
+                floodlightProvider.getOFMessageFactory().getMessage(
+                        OFType.BARRIER_REQUEST);
+        barrierMsg.setXid(getNextTransactionId());
         try {
             List<OFMessage> msglist = new ArrayList<OFMessage>(1);
             msglist.add(fm);
             channel.write(msglist);
+            msglist = new ArrayList<OFMessage>(1);
+            msglist.add(barrierMsg);
+            channel.write(msglist);
         } catch (Exception e) {
             log.error("Failed to clear all flows on switch " + this, e);
         }
diff --git a/src/main/java/net/floodlightcontroller/core/RoleInfo.java b/src/main/java/net/floodlightcontroller/core/RoleInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..26c1a510bd556e5cedb5eb0b91f45e45ebe0e456
--- /dev/null
+++ b/src/main/java/net/floodlightcontroller/core/RoleInfo.java
@@ -0,0 +1,76 @@
+/**
+ *    Copyright 2013, Big Switch Networks, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License"); you may
+ *    not use this file except in compliance with the License. You may obtain
+ *    a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ *    License for the specific language governing permissions and limitations
+ *    under the License.
+ **/
+
+package net.floodlightcontroller.core;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import net.floodlightcontroller.core.IFloodlightProviderService.Role;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+
+
+public class RoleInfo {
+    protected String role;
+    protected String roleChangeDescription;
+    protected Date roleChangeDateTime;
+
+    public RoleInfo() {
+    }
+
+    public RoleInfo(String role) {
+        setRole(role);
+    }
+
+    public RoleInfo(Role role, String description) {
+        this.role = (role != null) ? role.name() : "DISABLED";
+        this.roleChangeDescription = description;
+    }
+
+    public RoleInfo(Role role, String description, Date dt) {
+        this.role = (role != null) ? role.name() : "DISABLED";
+        this.roleChangeDescription = description;
+        this.roleChangeDateTime = dt;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+
+    @JsonProperty(value="change-description")
+    public String getRoleChangeDescription() {
+        return roleChangeDescription;
+    }
+    @JsonProperty(value="change-description")
+    public void setRoleChangeDescription(String roleChangeDescription) {
+        this.roleChangeDescription = roleChangeDescription;
+    }
+    @JsonProperty(value="change-date-time")
+    public String getRoleChangeDateTime() {
+        SimpleDateFormat formatter =
+                new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+        formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+        return roleChangeDateTime == null ?
+                  "" : formatter.format(roleChangeDateTime);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
index 1a41b9bb7a6c1b5a8850becf8629fd8a358aa278..607835b71a36ed3c01addf25894f7dba9274548b 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java
@@ -19,11 +19,14 @@ package net.floodlightcontroller.core.internal;
 
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
 import java.net.InetSocketAddress;
 import java.nio.channels.ClosedChannelException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -55,6 +58,7 @@ import net.floodlightcontroller.core.IOFSwitchDriver;
 import net.floodlightcontroller.core.IOFSwitchFilter;
 import net.floodlightcontroller.core.IOFSwitchListener;
 import net.floodlightcontroller.core.OFSwitchBase;
+import net.floodlightcontroller.core.RoleInfo;
 import net.floodlightcontroller.core.annotations.LogMessageDoc;
 import net.floodlightcontroller.core.annotations.LogMessageDocs;
 import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState;
@@ -176,13 +180,12 @@ public class Controller implements IFloodlightProviderService,
     // Configuration options
     protected int openFlowPort = 6633;
     protected int workerThreads = 0;
-    // The id for this controller node. Should be unique for each controller
-    // node in a controller cluster.
-    protected String controllerId = "localhost";
 
     // The current role of the controller.
     // If the controller isn't configured to support roles, then this is null.
     protected Role role;
+    protected String lastRoleChangeDescription = "Controller startup.";
+    protected Date roleChangeDateTime = new Date();
     // This is the role of the controller based on HARoleChange notifications
     // we have sent. I.e., this field reflects the last role notification
     // we have sent to the listeners. On a transition to slave we first set
@@ -191,14 +194,13 @@ public class Controller implements IFloodlightProviderService,
     // OF messages while the modules are in slave role. 
     // The pendingRole is a role change just received, but not sent out
     // notifications yet.
-    protected Role pendingRole;protected volatile Role notifiedRole;
+    protected Role pendingRole;
+    protected String pendRoleChangeDescription;
+    protected volatile Role notifiedRole;
     // A helper that handles sending and timeout handling for role requests
     protected RoleChanger roleChanger;
     protected SingletonTask roleChangeDamper;
 
-    // Start time of the controller
-    protected long systemStartTime;
-
     // Flag to always flush flow table on switch reconnect (HA or otherwise)
     protected boolean alwaysClearFlowsOnSwAdd = false;
 
@@ -385,12 +387,20 @@ public class Controller implements IFloodlightProviderService,
     }
 
     @Override
-    public void setRole(Role role) {
+    public RoleInfo getRoleInfo() {
+        synchronized(roleChanger) {
+            return new RoleInfo(role, lastRoleChangeDescription, roleChangeDateTime);
+        }
+    }
+
+    @Override
+    public void setRole(Role role, String roleChangeDescription) {
         if (role == null) throw new NullPointerException("Role can not be null.");
 
         // If role is changed in quick succession for some reason,
         // the 2 second delay will dampen the frequency.
         this.pendingRole = role;
+        pendRoleChangeDescription = roleChangeDescription;
         roleChangeDamper.reschedule(2000, TimeUnit.MILLISECONDS);
     }
 
@@ -408,6 +418,9 @@ public class Controller implements IFloodlightProviderService,
 
             Role oldRole = this.role;
             this.role = pendingRole;
+            this.lastRoleChangeDescription = this.pendRoleChangeDescription;
+            this.pendRoleChangeDescription = null;
+            this.roleChangeDateTime = new Date();
 
             log.debug("Submitting role change request to role {}", role);
             roleChanger.submitRequest(connectedSwitches, role);
@@ -987,9 +1000,9 @@ public class Controller implements IFloodlightProviderService,
                     if (roleChanger.checkFirstPendingRoleRequestXid(
                             sw, error.getXid())) {
                         roleChanger.deliverRoleRequestError(sw, error);
-                    } else if (error.getErrorCode() ==
+                    } else if (error.getErrorType() ==
                             OFErrorType.OFPET_BAD_REQUEST.getValue() &&
-                            error.getErrorType() ==
+                            error.getErrorCode() ==
                             OFBadRequestCode.OFPBRC_EPERM.ordinal() &&
                             role.equals(Role.MASTER)) {
                         // We are the master and the switch returned permission
@@ -1235,7 +1248,10 @@ public class Controller implements IFloodlightProviderService,
                     }
                     pktinProcTime.recordEndTimePktIn(sw, m, bc);
                 } else {
-                    log.warn("Unhandled OF Message: {} from {}", m, sw);
+                    if (m.getType() != OFType.BARRIER_REPLY) 
+                        log.warn("Unhandled OF Message: {} from {}", m, sw);
+                    else 
+                        log.debug("Received a Barrier Reply, no listeners for it");
                 }
 
                 if ((bContext == null) && (bc != null)) flcontext_free(bc);
@@ -1586,23 +1602,10 @@ public class Controller implements IFloodlightProviderService,
         return factory;
     }
 
-    @Override
-    public String getControllerId() {
-        return controllerId;
-    }
-
     // **************
     // Initialization
     // **************
 
-    protected void updateControllerInfo() {
-        // Write out the controller info to the storage source
-        Map<String, Object> controllerInfo = new HashMap<String, Object>();
-        String id = getControllerId();
-        controllerInfo.put(CONTROLLER_ID, id);
-        storageSource.updateRow(CONTROLLER_TABLE_NAME, controllerInfo);
-    }
-
 
     /**
      * Sets the initial role based on properties in the config params.
@@ -1755,11 +1758,7 @@ public class Controller implements IFloodlightProviderService,
             this.workerThreads = Integer.parseInt(threads);
         }
         log.debug("Number of worker threads set to {}", this.workerThreads);
-        String controllerId = configParams.get("controllerid");
-        if (controllerId != null) {
-            this.controllerId = controllerId;
-        }
-        log.debug("ControllerId set to {}", this.controllerId);
+        
     }
 
     private void initVendorMessages() {
@@ -1794,7 +1793,6 @@ public class Controller implements IFloodlightProviderService,
         this.notifiedRole = this.role;
         this.roleChanger = new RoleChanger(this);
         initVendorMessages();
-        this.systemStartTime = System.currentTimeMillis();
 
         String option = configParams.get("flushSwitchesOnReconnect");
 
@@ -1824,20 +1822,6 @@ public class Controller implements IFloodlightProviderService,
                                              CONTROLLER_ID);
         storageSource.addListener(CONTROLLER_INTERFACE_TABLE_NAME, this);
 
-        while (true) {
-            try {
-                updateControllerInfo();
-                break;
-            }
-            catch (StorageException e) {
-                log.info("Waiting for storage source");
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e1) {
-                }
-            }
-        }
-
         // Startup load monitoring
         if (overload_drop) {
             this.loadmonitor.startMonitoring(
@@ -1989,7 +1973,8 @@ public class Controller implements IFloodlightProviderService,
 
     @Override
     public long getSystemStartTime() {
-        return (this.systemStartTime);
+        RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
+        return rb.getStartTime();
     }
 
     @Override
@@ -2001,6 +1986,21 @@ public class Controller implements IFloodlightProviderService,
         return this.alwaysClearFlowsOnSwAdd;
     }
 
+    @Override
+    public Map<String, Long> getMemory() {
+        Map<String, Long> m = new HashMap<String, Long>();
+        Runtime runtime = Runtime.getRuntime();
+        m.put("total", runtime.totalMemory());
+        m.put("free", runtime.freeMemory());
+        return m;
+    }
+
+    @Override
+    public Long getUptime() {
+        RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
+        return rb.getUptime();
+    }
+
     @Override
     public void addOFSwitchDriver(String description, IOFSwitchDriver driver) {
         IOFSwitchDriver existingDriver = switchBindingMap.get(description);
diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java
index 003fbbc25fbfcd6e8755f078ab62bfdcdcc25217..e31c9cdae08403b59b3726f9aabbf3b4c5d33b79 100644
--- a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java
+++ b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java
@@ -20,6 +20,7 @@ import org.restlet.data.Status;
 import org.restlet.resource.ServerResource;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
+import net.floodlightcontroller.core.RoleInfo;
 import net.floodlightcontroller.core.IFloodlightProviderService.Role;
 import net.floodlightcontroller.core.annotations.LogMessageDoc;
 
@@ -37,7 +38,7 @@ public class ControllerRoleResource extends ServerResource {
         IFloodlightProviderService floodlightProvider = 
                 (IFloodlightProviderService)getContext().getAttributes().
                     get(IFloodlightProviderService.class.getCanonicalName());
-        return new RoleInfo(floodlightProvider.getRole());
+        return floodlightProvider.getRoleInfo();
     }
     
     @Post("json")
@@ -68,6 +69,6 @@ public class ControllerRoleResource extends ServerResource {
                 (IFloodlightProviderService)getContext().getAttributes().
                     get(IFloodlightProviderService.class.getCanonicalName());
         
-        floodlightProvider.setRole(role);
+        floodlightProvider.setRole(role, roleInfo.getRoleChangeDescription());
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java b/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java
deleted file mode 100644
index c8fa0c1a49a6bf564dee230cfa38b7abbfca1c31..0000000000000000000000000000000000000000
--- a/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- *    Copyright 2013, Big Switch Networks, Inc.
- *
- *    Licensed under the Apache License, Version 2.0 (the "License"); you may
- *    not use this file except in compliance with the License. You may obtain
- *    a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- *    License for the specific language governing permissions and limitations
- *    under the License.
- **/
-
-package net.floodlightcontroller.core.web;
-
-import net.floodlightcontroller.core.IFloodlightProviderService.Role;
-
-public class RoleInfo {
-    protected String role;
-    
-    public RoleInfo() {
-    }
-    
-    public RoleInfo(String role) {
-        setRole(role);
-    }
-    
-    public RoleInfo(Role role) {
-        this.role = (role != null) ? role.name() : "DISABLED";
-    }
-    
-    public String getRole() {
-        return role;
-    }
-    
-    public void setRole(String role) {
-        this.role = role;
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java
index 5da407b94ba0203274de890eaea89c3e31e47ffe..74a228eea5858a61cc50dee54aecdc310d5bd120 100644
--- a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java
+++ b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java
@@ -23,6 +23,7 @@ import org.restlet.resource.ServerResource;
 
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFSwitch;
+import net.floodlightcontroller.core.RoleInfo;
 
 import org.restlet.resource.Get;
 import org.slf4j.Logger;
@@ -46,7 +47,7 @@ public class SwitchRoleResource extends ServerResource {
             HashMap<String,RoleInfo> model = new HashMap<String,RoleInfo>();
             for (IOFSwitch sw: floodlightProvider.getSwitches().values()) {
                 switchId = sw.getStringId();
-                roleInfo = new RoleInfo(sw.getHARole());
+                roleInfo = new RoleInfo(sw.getHARole(), null);
                 model.put(switchId, roleInfo);
             }
             return model;
@@ -56,7 +57,7 @@ public class SwitchRoleResource extends ServerResource {
         IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
         if (sw == null)
             return null;
-        roleInfo = new RoleInfo(sw.getHARole());
+        roleInfo = new RoleInfo(sw.getHARole(), null);
         return roleInfo;
     }
 }
diff --git a/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java b/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java
index 978419873037467e4ad67439905fae510c24ea72..b6981ef252fc7c6c5a8ad69e39e3197ecf30c06c 100644
--- a/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java
+++ b/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java
@@ -23,25 +23,24 @@ import org.restlet.resource.ServerResource;
 
 
 public class SystemUptimeResource extends ServerResource {
-	
-	public class UptimeRest {
-		long systemUptimeMsec;
-
-		public long getSystemUptimeMsec() {
-			return systemUptimeMsec;
-		}
-	}
-	
-	@Get("json")
-	public UptimeRest retrieve() {
-		IFloodlightProviderService floodlightProvider = 
-			(IFloodlightProviderService)getContext().getAttributes().
-			get(IFloodlightProviderService.class.getCanonicalName());
-		
-		UptimeRest uptime = new UptimeRest();
-		uptime.systemUptimeMsec = 
-		   System.currentTimeMillis() - floodlightProvider.getSystemStartTime();
-		
-		return (uptime);
-	}
+
+    public class UptimeRest {
+        long systemUptimeMsec;
+
+        public long getSystemUptimeMsec() {
+            return systemUptimeMsec;
+        }
+    }
+
+    @Get("json")
+    public UptimeRest retrieve() {
+        IFloodlightProviderService floodlightProvider =
+            (IFloodlightProviderService)getContext().getAttributes().
+            get(IFloodlightProviderService.class.getCanonicalName());
+
+        UptimeRest uptime = new UptimeRest();
+        uptime.systemUptimeMsec = floodlightProvider.getUptime();
+
+        return (uptime);
+    }
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
index 69fac694665f14e6df08ada226d0a5496e2b9884..5e8f221a9829bdcf25d0f37a41fd33047db3affe 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java
@@ -51,6 +51,12 @@ public interface IDeviceService extends IFloodlightService {
     public static final String CONTEXT_DST_DEVICE = 
             "net.floodlightcontroller.devicemanager.dstDevice"; 
 
+    /**
+     * The original destination device for the current packet-in
+     */
+    public static final String CONTEXT_ORIG_DST_DEVICE =
+            "net.floodlightcontroller.devicemanager.origDstDevice";
+
     /**
      * A FloodlightContextStore object that can be used to interact with the 
      * FloodlightContext information created by BVS manager.
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java
index 6029af1b6c855460a45b502e4c4f568761fc648f..1b53550f6fe5f047db8160e4399e005c85e08dda 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -20,8 +20,8 @@ package net.floodlightcontroller.devicemanager;
 import java.util.Set;
 
 /**
- * Implementors of this interface can receive updates from DeviceManager about
- * the changes entity Classes.
+ * Implementors of this interface can receive updates from the Entity Classifier about
+ * the changes to entity Classes.
  *
  * @author Ananth Suryanarayana (Ananth.Suryanarayana@bigswitch.com)
  */
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
index d2b712de83d068685d87e1b6b1bcc1c49141a9c4..e6083f93acf39a4f356443e051d9ef3d4ee3177d 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java
@@ -63,8 +63,6 @@ public class Device implements IDevice {
     // the vlan Ids from the entities of this device
     protected Short[] vlanIds;
     protected String dhcpClientName;
-    
-   
 
     /**
      * These are the old attachment points for the device that were
@@ -75,6 +73,7 @@ public class Device implements IDevice {
      * The current attachment points for the device.
      */
     protected List<AttachmentPoint> attachmentPoints;
+
     // ************
     // Constructors
     // ************
@@ -110,7 +109,7 @@ public class Device implements IDevice {
             if (deviceManager.isValidAttachmentPoint(sw, port)) {
                 AttachmentPoint ap;
                 ap = new AttachmentPoint(sw, port,
-entity.getLastSeenTimestamp().getTime());
+                                         entity.getLastSeenTimestamp().getTime());
 
                 this.attachmentPoints = new ArrayList<AttachmentPoint>();
                 this.attachmentPoints.add(ap);
@@ -157,7 +156,7 @@ entity.getLastSeenTimestamp().getTime());
     /**
      * Construct a new device consisting of the entities from the old device
      * plus an additional entity.
-     * The caller needs to ensure that the additional entity is not already 
+     * The caller needs to ensure that the additional entity is not already
      * present in the array
      * @param device the old device object
      * @param newEntity the entity to add. newEntity must be have the same
@@ -172,23 +171,23 @@ entity.getLastSeenTimestamp().getTime());
         this.deviceManager = device.deviceManager;
         this.deviceKey = device.deviceKey;
         this.dhcpClientName = device.dhcpClientName;
-        
+
         this.entities = new Entity[device.entities.length + 1];
         if (insertionpoint < 0) {
-            insertionpoint = -(Arrays.binarySearch(device.entities, 
+            insertionpoint = -(Arrays.binarySearch(device.entities,
                                                    newEntity)+1);
         }
         if (insertionpoint > 0) {
             // insertion point is not the beginning:
             // copy up to insertion point
-            System.arraycopy(device.entities, 0, 
+            System.arraycopy(device.entities, 0,
                              this.entities, 0,
                              insertionpoint);
         }
         if (insertionpoint < device.entities.length) {
-            // insertion point is not the end 
+            // insertion point is not the end
             // copy from insertion point
-            System.arraycopy(device.entities, insertionpoint, 
+            System.arraycopy(device.entities, insertionpoint,
                              this.entities, insertionpoint+1,
                              device.entities.length-insertionpoint);
         }
@@ -216,7 +215,7 @@ entity.getLastSeenTimestamp().getTime());
         this.entityClass = device.entityClass;
         computeVlandIds();
     }
-    
+
     private void computeVlandIds() {
         if (entities.length == 1) {
             if (entities[0].getVlan() != null) {
@@ -224,6 +223,7 @@ entity.getLastSeenTimestamp().getTime());
             } else {
                 vlanIds = new Short[] { Short.valueOf((short)-1) };
             }
+            return;
         }
 
         TreeSet<Short> vals = new TreeSet<Short>();
@@ -236,7 +236,6 @@ entity.getLastSeenTimestamp().getTime());
         vlanIds = vals.toArray(new Short[vals.size()]);
     }
 
-
     /**
      * Given a list of attachment points (apList), the procedure would return
      * a map of attachment points for each L2 domain.  L2 domain id is the key.
@@ -419,7 +418,9 @@ entity.getLastSeenTimestamp().getTime());
         if (apMap == null || apMap.isEmpty()) {
             apList.add(newAP);
             attachmentPoints = apList;
-            return true;
+            // there are no old attachment points - we should not treat this
+            // as a device moved.
+            return false;
         }
 
         long id = topology.getL2DomainId(sw);
@@ -555,6 +556,9 @@ entity.getLastSeenTimestamp().getTime());
         return false;
     }
 
+    // *******
+    // IDevice
+    // *******
 
     @Override
     public SwitchPort[] getAttachmentPoints() {
@@ -568,16 +572,11 @@ entity.getLastSeenTimestamp().getTime());
         if (attachmentPoints == null) return returnSwitchPorts;
         if (attachmentPoints.isEmpty()) return returnSwitchPorts;
 
-
         // copy ap list.
-        List<AttachmentPoint> apList;
-        apList = new ArrayList<AttachmentPoint>();
-        if (attachmentPoints != null) apList.addAll(attachmentPoints);
-        // get AP map.
-        Map<Long, AttachmentPoint> apMap = getAPMap(apList);
+        List<AttachmentPoint> apList = attachmentPoints;
 
-        if (apMap != null) {
-            for(AttachmentPoint ap: apMap.values()) {
+        if (apList != null) {
+            for(AttachmentPoint ap: apList) {
                 SwitchPort swport = new SwitchPort(ap.getSw(),
                                                    ap.getPort());
                     sp.add(swport);
@@ -596,6 +595,8 @@ entity.getLastSeenTimestamp().getTime());
             this.oldAPs = oldAPList;
 
         List<AttachmentPoint> dupList;
+        // get AP map.
+        Map<Long, AttachmentPoint> apMap = getAPMap(apList);
         dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap);
         if (dupList != null) {
             for(AttachmentPoint ap: dupList) {
@@ -608,10 +609,6 @@ entity.getLastSeenTimestamp().getTime());
         return sp.toArray(new SwitchPort[sp.size()]);
     }
 
-    // *******
-    // IDevice
-    // *******
-
     @Override
     public Long getDeviceKey() {
         return deviceKey;
@@ -651,7 +648,7 @@ entity.getLastSeenTimestamp().getTime());
                     deviceManager.queryClassByEntity(entityClass, ipv4Fields, e);
             while (devices.hasNext()) {
                 Device d = devices.next();
-                if (deviceKey.equals(d.getDeviceKey())) 
+                if (deviceKey.equals(d.getDeviceKey()))
                     continue;
                 for (Entity se : d.entities) {
                     if (se.getIpv4Address() != null &&
@@ -678,7 +675,7 @@ entity.getLastSeenTimestamp().getTime());
     public Short[] getSwitchPortVlanIds(SwitchPort swp) {
         TreeSet<Short> vals = new TreeSet<Short>();
         for (Entity e : entities) {
-            if (e.switchDPID == swp.getSwitchDPID() 
+            if (e.switchDPID == swp.getSwitchDPID()
                     && e.switchPort == swp.getPort()) {
                 if (e.getVlan() == null)
                     vals.add(Ethernet.VLAN_UNTAGGED);
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
index 5796830ba388189cb15ee339bf4891c2a8cb6ce6..c58999b17733f2878b146633124483a2f73c0988 100755
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java
@@ -241,16 +241,16 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
             return "DeviceUpdate [device=" + devIdStr + ", change=" + change
                    + ", fieldsChanged=" + fieldsChanged + "]";
         }
-        
+
     }
 
     /**
      * AttachmentPointComparator
-     * 
+     *
      * Compares two attachment points and returns the latest one.
      * It is assumed that the two attachment points are in the same
      * L2 domain.
-     * 
+     *
      * @author srini
      */
     protected class AttachmentPointComparator
@@ -261,7 +261,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
         @Override
         public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) {
-            //First compare based on L2 domain ID; 
+            //First compare based on L2 domain ID;
 
             long oldSw = oldAP.getSw();
             short oldPort = oldAP.getPort();
@@ -362,7 +362,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
     @Override
     public IDevice findClassDevice(IEntityClass entityClass, long macAddress,
-                                  Short vlan, Integer ipv4Address) 
+                                  Short vlan, Integer ipv4Address)
                                   throws IllegalArgumentException {
         if (vlan != null && vlan.shortValue() <= 0)
             vlan = null;
@@ -370,10 +370,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
             ipv4Address = null;
         Entity e = new Entity(macAddress, vlan, ipv4Address,
                               null, null, null);
-        if (entityClass == null || 
+        if (entityClass == null ||
                 !allKeyFieldsPresent(e, entityClass.getKeyFields())) {
             throw new IllegalArgumentException("Not all key fields and/or "
-                    + " no source device specified. Required fields: " + 
+                    + " no source device specified. Required fields: " +
                     entityClassifier.getKeyFields());
         }
         return findDestByEntity(entityClass, e);
@@ -446,7 +446,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         ArrayList<Iterator<Device>> iterators =
                 new ArrayList<Iterator<Device>>();
         ClassState classState = getClassState(entityClass);
-        
+
         DeviceIndex index = null;
         if (classState.secondaryIndexMap.size() > 0) {
             EnumSet<DeviceField> keys =
@@ -454,7 +454,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                                   switchDPID, switchPort);
             index = classState.secondaryIndexMap.get(keys);
         }
-        
+
         Iterator<Device> iter;
         if (index == null) {
             index = classState.classIndex;
@@ -481,10 +481,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                                             index.queryByEntity(entity));
         }
         iterators.add(iter);
-        
+
         return new MultiIterator<Device>(iterators.iterator());
     }
-    
+
     protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress,
                                                         Short vlan,
                                                         Integer ipv4Address,
@@ -531,6 +531,16 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
          logListeners();
     }
 
+    @Override
+    public void addSuppressAPs(long swId, short port) {
+        suppressAPs.add(new SwitchPort(swId, port));
+    }
+
+    @Override
+    public void removeSuppressAPs(long swId, short port) {
+        suppressAPs.remove(new SwitchPort(swId, port));
+    }
+
     private void logListeners() {
         List<IDeviceListener> listeners = deviceListeners.getOrderedListeners();
         if (listeners != null) {
@@ -599,13 +609,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         ListIterator<OFMatchReconcile> iter = ofmRcList.listIterator();
         while (iter.hasNext()) {
             OFMatchReconcile ofm = iter.next();
-            
+
             // Remove the STOPPed flow.
             if (Command.STOP == reconcileFlow(ofm)) {
                 iter.remove();
             }
         }
-        
+
         if (ofmRcList.size() > 0) {
             return Command.CONTINUE;
         } else {
@@ -638,10 +648,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice);
         }
         if (logger.isTraceEnabled()) {
-            logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " 
+            logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, "
                          + "dstEntity={}, dstDev={}",
                          new Object[] {ofm.ofmWithSwDpid.getOfMatch(),
-                                       srcEntity, srcDevice, 
+                                       srcEntity, srcDevice,
                                        dstEntity, dstDevice } );
         }
         return Command.CONTINUE;
@@ -784,6 +794,14 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         if (srcEntity == null)
             return Command.STOP;
 
+        // Learn from ARP packet for special VRRP settings.
+        // In VRRP settings, the source MAC address and sender MAC
+        // addresses can be different.  In such cases, we need to learn
+        // the IP to MAC mapping of the VRRP IP address.  The source
+        // entity will not have that information.  Hence, a separate call
+        // to learn devices in such cases.
+        learnDeviceFromArpResponseData(eth, sw.getId(), pi.getInPort());
+
         // Learn/lookup device information
         Device srcDevice = learnDeviceByEntity(srcEntity);
         if (srcDevice == null)
@@ -860,8 +878,8 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
     }
 
     /**
-     * Get sender IP address from packet if the packet is either an ARP 
-     * packet. 
+     * Get sender IP address from packet if the packet is either an ARP
+     * packet.
      * @param eth
      * @param dlAddr
      * @return
@@ -873,7 +891,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                     (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) {
                 return IPv4.toIPv4Address(arp.getSenderProtocolAddress());
             }
-        } 
+        }
         return 0;
     }
 
@@ -904,6 +922,43 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                           new Date());
     }
 
+    /**
+     * Learn device from ARP data in scenarios where the
+     * Ethernet source MAC is different from the sender hardware
+     * address in ARP data.
+     */
+    protected void learnDeviceFromArpResponseData(Ethernet eth,
+                                            long swdpid,
+                                            int port) {
+
+        if (!(eth.getPayload() instanceof ARP)) return;
+        ARP arp = (ARP) eth.getPayload();
+
+        byte[] dlAddrArr = eth.getSourceMACAddress();
+        long dlAddr = Ethernet.toLong(dlAddrArr);
+
+        byte[] senderHardwareAddr = arp.getSenderHardwareAddress();
+        long senderAddr = Ethernet.toLong(senderHardwareAddr);
+
+        if (dlAddr == senderAddr) return;
+
+        // Ignore broadcast/multicast source
+        if ((senderHardwareAddr[0] & 0x1) != 0)
+            return;
+
+        short vlan = eth.getVlanID();
+        int nwSrc = IPv4.toIPv4Address(arp.getSenderProtocolAddress());
+
+        Entity e =  new Entity(senderAddr,
+                ((vlan >= 0) ? vlan : null),
+                ((nwSrc != 0) ? nwSrc : null),
+                swdpid,
+                port,
+                new Date());
+
+        learnDeviceByEntity(e);
+    }
+
     /**
      * Get a (partial) entity for the destination from the packet.
      * @param eth
@@ -954,7 +1009,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
         Long swDpid = null;
         Short inPort = null;
-        
+
         if (isSource) {
             swDpid = ofmWithSwDpid.getSwitchDataPathId();
             inPort = ofmWithSwDpid.getOfMatch().getInputPort();
@@ -981,11 +1036,12 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                           (learnap ? (int)inPort : null),
                           new Date());
     }
+
     /**
      * Look up a {@link Device} based on the provided {@link Entity}. We first
      * check the primary index. If we do not find an entry there we classify
-     * the device into its IEntityClass and query the classIndex. 
-     * This implies that all key field of the current IEntityClassifier must 
+     * the device into its IEntityClass and query the classIndex.
+     * This implies that all key field of the current IEntityClassifier must
      * be present in the entity for the lookup to succeed!
      * @param entity the entity to search for
      * @return The {@link Device} object if found
@@ -1021,18 +1077,19 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
      * the given source device.  The source device is important since
      * there could be ambiguity in the destination device without the
      * attachment point information.
-     * @param source the source device.  The returned destination will be
-     * in the same entity class as the source.
-     * @param dstEntity the entity to look up
+     * @param reference  the source device's entity class.
+     *                   The returned destination will be
+     *                   in the same entity class as the source.
+     * @param dstEntity  the entity to look up
      * @return an {@link Device} or null if no device is found.
      */
     protected Device findDestByEntity(IEntityClass reference,
                                       Entity dstEntity) {
-        
-        // Look  up the fully-qualified entity to see if it 
+
+        // Look  up the fully-qualified entity to see if it
         // exists in the primary entity index
         Long deviceKey = primaryIndex.findByEntity(dstEntity);
-         
+
         if (deviceKey == null) {
             // This could happen because:
             // 1) no destination known, or a broadcast destination
@@ -1051,7 +1108,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         if (deviceKey == null) return null;
         return deviceMap.get(deviceKey);
     }
-    
 
     /**
      * Look up a {@link Device} within a particular entity class based on
@@ -1115,20 +1171,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 // device map, and use the referenced Device below.
                 device = deviceMap.get(deviceKey);
                 if (device == null) {
-                    // This can happen due to concurrent modification 
+                    // This can happen due to concurrent modification
                     if (logger.isDebugEnabled()) {
                         logger.debug("No device for deviceKey {} while "
                                      + "while processing entity {}",
                                      deviceKey, entity);
                     }
+                    // if so, then try again till we don't even get the device key
+                    // and so we recreate the device
+                    continue;
                 }
             } else {
                 // If the secondary index does not contain the entity,
                 // create a new Device object containing the entity, and
-                // generate a new device ID if the the entity is on an 
-                // attachment point port. Otherwise ignore. 
+                // generate a new device ID if the the entity is on an
+                // attachment point port. Otherwise ignore.
                 if (entity.hasSwitchPort() &&
-                        !topology.isAttachmentPointPort(entity.getSwitchDPID(), 
+                        !topology.isAttachmentPointPort(entity.getSwitchDPID(),
                                                  entity.getSwitchPort().shortValue())) {
                     if (logger.isDebugEnabled()) {
                         logger.debug("Not learning new device on internal"
@@ -1137,10 +1196,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                     device = null;
                     break;
                 }
-                // Before we create the new device also check if 
+                // Before we create the new device also check if
                 // the entity is allowed (e.g., for spoofing protection)
                 if (!isEntityAllowed(entity, entityClass)) {
-                    logger.info("PacketIn is not allowed {} {}", 
+                    logger.info("PacketIn is not allowed {} {}",
                                 entityClass.getName(), entity);
                     device = null;
                     break;
@@ -1174,23 +1233,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
                 break;
             }
-
+            // if it gets here, we have a pre-existing Device for this Entity
             if (!isEntityAllowed(entity, device.getEntityClass())) {
-                logger.info("PacketIn is not allowed {} {}", 
+                logger.info("PacketIn is not allowed {} {}",
                             device.getEntityClass().getName(), entity);
                 return null;
             }
-            // If this is an internal port we don't learn the new entity
-            // and don't update indexes. We only learn on attachment point
-            // ports.
+            // If this is not an attachment point port we don't learn the new entity
+            // and don't update indexes. But we do allow the device to continue up
+            // the chain.
             if (entity.hasSwitchPort() &&
-                    !topology.isAttachmentPointPort(entity.getSwitchDPID(), 
+                    !topology.isAttachmentPointPort(entity.getSwitchDPID(),
                                                  entity.getSwitchPort().shortValue())) {
                 break;
             }
             int entityindex = -1;
             if ((entityindex = device.entityIndex(entity)) >= 0) {
-                // Entity already exists 
+                // Entity already exists
                 // update timestamp on the found entity
                 Date lastSeen = entity.getLastSeenTimestamp();
                 if (lastSeen == null) {
@@ -1198,15 +1257,14 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                     entity.setLastSeenTimestamp(lastSeen);
                 }
                 device.entities[entityindex].setLastSeenTimestamp(lastSeen);
-                // we break the loop after the else block and after checking
-                // for new AP
+                // we break the loop after checking for changes to the AP
             } else {
-                // New entity for this devce
+                // New entity for this device
                 // compute the insertion point for the entity.
                 // see Arrays.binarySearch()
                 entityindex = -(entityindex + 1);
                 Device newDevice = allocateDevice(device, entity, entityindex);
- 
+
                 // generate updates
                 EnumSet<DeviceField> changedFields =
                         findChangedFields(device, entity);
@@ -1227,23 +1285,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                 updateSecondaryIndices(entity,
                                        device.getEntityClass(),
                                        deviceKey);
-                
+
                 if (changedFields.size() > 0) {
                     deviceUpdates =
                     updateUpdates(deviceUpdates,
                                   new DeviceUpdate(newDevice, CHANGE,
                                                    changedFields));
                 }
-                // we break the loop after checking for changed AP 
+                // we break the loop after checking for changed AP
             }
-            // Update attachment point (will only be hit if the device 
-            // already existed and no concurrent modification
+            // Update attachment point (will only be hit if the device
+            // already existed and no concurrent modification)
             if (entity.hasSwitchPort()) {
-                boolean moved = 
+                boolean moved =
                         device.updateAttachmentPoint(entity.getSwitchDPID(),
                                 entity.getSwitchPort().shortValue(),
                                 entity.getLastSeenTimestamp().getTime());
-                // TODO: use update mechanism instead of sending the 
+                // TODO: use update mechanism instead of sending the
                 // notification directly
                 if (moved) {
                     sendDeviceMovedNotification(device);
@@ -1261,7 +1319,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                     }
                 }
             }
-            break; 
+            break;
         }
 
         if (deleteQueue != null) {
@@ -1372,7 +1430,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
 
     /**
      * Check if the entity e has all the keyFields set. Returns false if not
-     * @param e entity to check 
+     * @param e entity to check
      * @param keyFields the key fields to check e against
      * @return
      */
@@ -1397,7 +1455,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                     //if (e.vlan == null) return false;
                     break;
                 default:
-                    // we should never get here. unless somebody extended 
+                    // we should never get here. unless somebody extended
                     // DeviceFields
                     throw new IllegalStateException();
             }
@@ -1475,23 +1533,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         }
     }
 
-    // *********************
-    // IEntityClassListener
-    // *********************
-    @Override
-    public void entityClassChanged (Set<String> entityClassNames) {
-        /* iterate through the devices, reclassify the devices that belong
-         * to these entity class names
-         */
-        Iterator<Device> diter = deviceMap.values().iterator();
-        while (diter.hasNext()) {
-            Device d = diter.next();
-            if (d.getEntityClass() == null ||
-                entityClassNames.contains(d.getEntityClass().getName()))
-                reclassifyDevice(d);
-        }
-    }
-
     /**
      * Clean up expired entities/devices
      */
@@ -1599,7 +1640,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                                                        others);
         }
     }
-    
+
     /**
      * method to delete a given device, remove all entities first and then
      * finally delete the device itself.
@@ -1608,12 +1649,12 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
     protected void deleteDevice(Device device) {
         ArrayList<Entity> emptyToKeep = new ArrayList<Entity>();
         for (Entity entity : device.getEntities()) {
-            this.removeEntity(entity, device.getEntityClass(), 
+            this.removeEntity(entity, device.getEntityClass(),
                 device.getDeviceKey(), emptyToKeep);
         }
         if (!deviceMap.remove(device.getDeviceKey(), device)) {
             if (logger.isDebugEnabled())
-                logger.debug("device map does not have this device -" + 
+                logger.debug("device map does not have this device -" +
                     device.toString());
         }
     }
@@ -1624,7 +1665,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                                                Long switchDPID,
                                                Integer switchPort) {
         // FIXME: vlan==null is a valid search. Need to handle this
-        // case correctly. Note that the code will still work correctly. 
+        // case correctly. Note that the code will still work correctly.
         // But we might do a full device search instead of using an index.
         EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class);
         if (macAddress != null) keys.add(DeviceField.MAC);
@@ -1635,7 +1676,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         return keys;
     }
 
-
     protected Iterator<Device> queryClassByEntity(IEntityClass clazz,
                                                   EnumSet<DeviceField> keyFields,
                                                   Entity entity) {
@@ -1667,16 +1707,17 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
                                     int insertionpoint) {
         return new Device(device, entity, insertionpoint);
     }
-    
+
+    //not used
     protected Device allocateDevice(Device device, Set <Entity> entities) {
-        List <AttachmentPoint> newPossibleAPs = 
+        List <AttachmentPoint> newPossibleAPs =
                 new ArrayList<AttachmentPoint>();
-        List <AttachmentPoint> newAPs = 
+        List <AttachmentPoint> newAPs =
                 new ArrayList<AttachmentPoint>();
-        for (Entity entity : entities) { 
+        for (Entity entity : entities) {
             if (entity.switchDPID != null && entity.switchPort != null) {
-                AttachmentPoint aP = 
-                        new AttachmentPoint(entity.switchDPID.longValue(), 
+                AttachmentPoint aP =
+                        new AttachmentPoint(entity.switchDPID.longValue(),
                                     entity.switchPort.shortValue(), 0);
                 newPossibleAPs.add(aP);
             }
@@ -1697,15 +1738,9 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         return d;
     }
 
-    @Override
-    public void addSuppressAPs(long swId, short port) {
-        suppressAPs.add(new SwitchPort(swId, port));
-    }
-
-    @Override
-    public void removeSuppressAPs(long swId, short port) {
-        suppressAPs.remove(new SwitchPort(swId, port));
-    }
+    // *********************
+    // ITopologyListener
+    // *********************
 
     /**
      * Topology listener method.
@@ -1745,10 +1780,28 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
             }
         }
     }
-    
+
+    // *********************
+    // IEntityClassListener
+    // *********************
+
+    @Override
+    public void entityClassChanged (Set<String> entityClassNames) {
+        /* iterate through the devices, reclassify the devices that belong
+         * to these entity class names
+         */
+        Iterator<Device> diter = deviceMap.values().iterator();
+        while (diter.hasNext()) {
+            Device d = diter.next();
+            if (d.getEntityClass() == null ||
+                entityClassNames.contains(d.getEntityClass().getName()))
+                reclassifyDevice(d);
+        }
+    }
+
     /**
      * this method will reclassify and reconcile a device - possibilities
-     * are - create new device(s), remove entities from this device. If the 
+     * are - create new device(s), remove entities from this device. If the
      * device entity class did not change then it returns false else true.
      * @param device
      */
@@ -1761,10 +1814,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         }
         boolean needToReclassify = false;
         for (Entity entity : device.entities) {
-            IEntityClass entityClass = 
+            IEntityClass entityClass =
                     this.entityClassifier.classifyEntity(entity);
             if (entityClass == null || device.getEntityClass() == null) {
-                needToReclassify = true;                
+                needToReclassify = true;
                 break;
             }
             if (!entityClass.getName().
@@ -1776,12 +1829,12 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
         if (needToReclassify == false) {
             return false;
         }
-            
+
         LinkedList<DeviceUpdate> deviceUpdates =
                 new LinkedList<DeviceUpdate>();
         // delete this device and then re-learn all the entities
         this.deleteDevice(device);
-        deviceUpdates.add(new DeviceUpdate(device, 
+        deviceUpdates.add(new DeviceUpdate(device,
                 DeviceUpdate.Change.DELETE, null));
         if (!deviceUpdates.isEmpty())
             processUpdates(deviceUpdates);
@@ -1789,5 +1842,5 @@ IFlowReconcileListener, IInfoProvider, IHAListener {
             this.learnDeviceByEntity(entity);
         }
         return true;
-    }   
+    }
 }
diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java
index 4f2d3f8c4995b466f2fc5e22edbf11160fbaf3ab..4811013e77258654c9577b0271a51f72c31bfd8d 100644
--- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java
+++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2012 Big Switch Networks, Inc. 
+*    Copyright 2012 Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -31,7 +31,7 @@ public class DeviceUniqueIndex extends DeviceIndex {
     /**
      * The index
      */
-    private ConcurrentHashMap<IndexedEntity, Long> index;
+    private final ConcurrentHashMap<IndexedEntity, Long> index;
 
     /**
      * Construct a new device index using the provided key fields
@@ -51,10 +51,10 @@ public class DeviceUniqueIndex extends DeviceIndex {
         final Long deviceKey = findByEntity(entity);
         if (deviceKey != null)
             return Collections.<Long>singleton(deviceKey).iterator();
-        
+
         return Collections.<Long>emptySet().iterator();
     }
-    
+
     @Override
     public Iterator<Long> getAll() {
         return index.values().iterator();
@@ -68,15 +68,15 @@ public class DeviceUniqueIndex extends DeviceIndex {
 
             Long ret = index.putIfAbsent(ie, deviceKey);
             if (ret != null && !ret.equals(deviceKey)) {
-                // If the return value is non-null, then fail the insert 
-                // (this implies that a device using this entity has 
+                // If the return value is non-null, then fail the insert
+                // (this implies that a device using this entity has
                 // already been created in another thread).
                 return false;
             }
         }
         return true;
     }
-    
+
     @Override
     public void updateIndex(Entity entity, Long deviceKey) {
         IndexedEntity ie = new IndexedEntity(keyFields, entity);
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
index 4e2ffb1dbe11c3ce4d89fa110adbc42287c611e7..1ad6f4320866479f5f0ef3dcfb1001d3ef9e3c86 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -57,7 +57,7 @@ public interface ILinkDiscoveryService extends IFloodlightService {
                                            boolean isReverse);
 
     /**
-     * Returns an unmodifiable map from switch id to a set of all links with it 
+     * Returns an unmodifiable map from switch id to a set of all links with it
      * as an endpoint.
      */
     public Map<Long, Set<Link>> getSwitchLinks();
@@ -74,7 +74,8 @@ public interface ILinkDiscoveryService extends IFloodlightService {
     public Set<NodePortTuple> getSuppressLLDPsInfo();
 
     /**
-     * Adds a switch port to suppress lldp set
+     * Adds a switch port to suppress lldp set. LLDPs and BDDPs will not be sent
+     * out, and if any are received on this port then they will be dropped.
      */
     public void AddToSuppressLLDPs(long sw, short port);
 
@@ -105,8 +106,13 @@ public interface ILinkDiscoveryService extends IFloodlightService {
     public Map<NodePortTuple, Set<Link>> getPortLinks();
 
     /**
+     * addMACToIgnoreList is a service provided by LinkDiscovery to ignore
+     * certain packets early in the packet-in processing chain. Since LinkDiscovery
+     * is first in the packet-in processing pipeline, it can efficiently drop these
+     * packets. Currently these packets are identified only by their source MAC address.
+     *
      * Add a MAC address range to ignore list. All packet ins from this range
-     * will be dropped
+     * will be dropped - use with care!
      * @param mac The base MAC address that is to be ignored
      * @param ignoreBits The number of LSBs to ignore. A value of 0 will add
      *        only one MAC address 'mac' to ignore list. A value of 48 will add
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index f35a556f621a38fdad5d442c5ec7ca63a0268edd..2e11e995deea7e12a938367937dfd35f182c9f43 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -35,6 +35,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
@@ -143,7 +144,8 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     protected IRestApiService restApi;
 
     // LLDP and BDDP fields
-    private static final byte[] LLDP_STANDARD_DST_MAC_STRING = HexString.fromHexString("01:80:c2:00:00:0e");
+    private static final byte[] LLDP_STANDARD_DST_MAC_STRING =
+            HexString.fromHexString("01:80:c2:00:00:0e");
     private static final long LINK_LOCAL_MASK = 0xfffffffffff0L;
     private static final long LINK_LOCAL_VALUE = 0x0180c2000000L;
     protected static int EVENT_HISTORY_SIZE = 1024; // in seconds
@@ -159,12 +161,12 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte
     private static final byte TLV_DIRECTION_VALUE_FORWARD[] = { 0x01 };
     private static final byte TLV_DIRECTION_VALUE_REVERSE[] = { 0x02 };
-    private static final LLDPTLV forwardTLV = new LLDPTLV().setType((byte) TLV_DIRECTION_TYPE)
-                                                           .setLength((short) TLV_DIRECTION_LENGTH)
+    private static final LLDPTLV forwardTLV = new LLDPTLV().setType(TLV_DIRECTION_TYPE)
+                                                           .setLength(TLV_DIRECTION_LENGTH)
                                                            .setValue(TLV_DIRECTION_VALUE_FORWARD);
 
-    private static final LLDPTLV reverseTLV = new LLDPTLV().setType((byte) TLV_DIRECTION_TYPE)
-                                                           .setLength((short) TLV_DIRECTION_LENGTH)
+    private static final LLDPTLV reverseTLV = new LLDPTLV().setType(TLV_DIRECTION_TYPE)
+                                                           .setLength(TLV_DIRECTION_LENGTH)
                                                            .setValue(TLV_DIRECTION_VALUE_REVERSE);
 
     // Link discovery task details.
@@ -252,9 +254,123 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     }
     protected Set<MACRange> ignoreMACSet;
 
+    //*********************
+    // ILinkDiscoveryService
+    //*********************
+
+    @Override
+    public OFPacketOut generateLLDPMessage(long sw, short port,
+                                       boolean isStandard, boolean isReverse) {
+
+        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+
+        if (log.isTraceEnabled()) {
+            log.trace("Sending LLDP packet out of swich: {}, port: {}",
+                      HexString.toHexString(sw), port);
+        }
+
+        // using "nearest customer bridge" MAC address for broadest possible
+        // propagation
+        // through provider and TPMR bridges (see IEEE 802.1AB-2009 and
+        // 802.1Q-2011),
+        // in particular the Linux bridge which behaves mostly like a provider
+        // bridge
+        byte[] chassisId = new byte[] { 4, 0, 0, 0, 0, 0, 0 }; // filled in
+                                                               // later
+        byte[] portId = new byte[] { 2, 0, 0 }; // filled in later
+        byte[] ttlValue = new byte[] { 0, 0x78 };
+        // OpenFlow OUI - 00-26-E1
+        byte[] dpidTLVValue = new byte[] { 0x0, 0x26, (byte) 0xe1, 0, 0, 0,
+                                          0, 0, 0, 0, 0, 0 };
+        LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127)
+                                       .setLength((short) dpidTLVValue.length)
+                                       .setValue(dpidTLVValue);
+
+        byte[] dpidArray = new byte[8];
+        ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
+        ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
+
+        Long dpid = sw;
+        dpidBB.putLong(dpid);
+        // set the chassis id's value to last 6 bytes of dpid
+        System.arraycopy(dpidArray, 2, chassisId, 1, 6);
+        // set the optional tlv to the full dpid
+        System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
+
+        // TODO: Consider remove this block of code.
+        // It's evil to overwrite port object. The the old code always
+        // overwrote mac address, we now only overwrite zero macs and
+        // log a warning, mostly for paranoia.
+        byte[] srcMac = ofpPort.getHardwareAddress();
+        byte[] zeroMac = { 0, 0, 0, 0, 0, 0 };
+        if (Arrays.equals(srcMac, zeroMac)) {
+            log.warn("Port {}/{} has zero hareware address"
+                             + "overwrite with lower 6 bytes of dpid",
+                     HexString.toHexString(dpid), ofpPort.getPortNumber());
+            System.arraycopy(dpidArray, 2, srcMac, 0, 6);
+        }
+
+        // set the portId to the outgoing port
+        portBB.putShort(port);
+        if (log.isTraceEnabled()) {
+            log.trace("Sending LLDP out of interface: {}/{}",
+                      HexString.toHexString(sw), port);
+        }
+
+        LLDP lldp = new LLDP();
+        lldp.setChassisId(new LLDPTLV().setType((byte) 1)
+                                       .setLength((short) chassisId.length)
+                                       .setValue(chassisId));
+        lldp.setPortId(new LLDPTLV().setType((byte) 2)
+                                    .setLength((short) portId.length)
+                                    .setValue(portId));
+        lldp.setTtl(new LLDPTLV().setType((byte) 3)
+                                 .setLength((short) ttlValue.length)
+                                 .setValue(ttlValue));
+        lldp.getOptionalTLVList().add(dpidTLV);
+
+        // Add the controller identifier to the TLV value.
+        lldp.getOptionalTLVList().add(controllerTLV);
+        if (isReverse) {
+            lldp.getOptionalTLVList().add(reverseTLV);
+        } else {
+            lldp.getOptionalTLVList().add(forwardTLV);
+        }
+
+        Ethernet ethernet;
+        if (isStandard) {
+            ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress())
+                                     .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
+                                     .setEtherType(Ethernet.TYPE_LLDP);
+            ethernet.setPayload(lldp);
+        } else {
+            BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
+            bsn.setPayload(lldp);
+
+            ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress())
+                                     .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
+                                     .setEtherType(Ethernet.TYPE_BSN);
+            ethernet.setPayload(bsn);
+        }
+
+        // serialize and wrap in a packet out
+        byte[] data = ethernet.serialize();
+        OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory()
+                                                         .getMessage(OFType.PACKET_OUT);
+        po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
+        po.setInPort(OFPort.OFPP_NONE);
+
+        // set data and data length
+        po.setLengthU(OFPacketOut.MINIMUM_LENGTH + data.length);
+        po.setPacketData(data);
+
+        return po;
+    }
+
     /**
      * Get the LLDP sending period in seconds.
-     * 
+     *
      * @return LLDP sending period in seconds.
      */
     public int getLldpFrequency() {
@@ -263,17 +379,19 @@ public class LinkDiscoveryManager implements IOFMessageListener,
 
     /**
      * Get the LLDP timeout value in seconds
-     * 
+     *
      * @return LLDP timeout value in seconds
      */
     public int getLldpTimeout() {
         return LINK_TIMEOUT;
     }
 
+    @Override
     public Map<NodePortTuple, Set<Link>> getPortLinks() {
         return portLinks;
     }
 
+    @Override
     public Set<NodePortTuple> getSuppressLLDPsInfo() {
         return suppressLinkDiscovery;
     }
@@ -282,6 +400,7 @@ public class LinkDiscoveryManager implements IOFMessageListener,
      * Add a switch port to the suppressed LLDP list. Remove any known links on
      * the switch port.
      */
+    @Override
     public void AddToSuppressLLDPs(long sw, short port) {
         NodePortTuple npt = new NodePortTuple(sw, port);
         this.suppressLinkDiscovery.add(npt);
@@ -292,6 +411,7 @@ public class LinkDiscoveryManager implements IOFMessageListener,
      * Remove a switch port from the suppressed LLDP list. Discover links on
      * that switchport.
      */
+    @Override
     public void RemoveFromSuppressLLDPs(long sw, short port) {
         NodePortTuple npt = new NodePortTuple(sw, port);
         this.suppressLinkDiscovery.remove(npt);
@@ -302,10 +422,12 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         return shuttingDown;
     }
 
+    @Override
     public boolean isTunnelPort(long sw, short port) {
         return false;
     }
 
+    @Override
     public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
         if (info.getUnicastValidTime() != null) {
             return ILinkDiscovery.LinkType.DIRECT_LINK;
@@ -315,809 +437,889 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         return ILinkDiscovery.LinkType.INVALID_LINK;
     }
 
-    @LogMessageDoc(level = "ERROR",
-                   message = "Error in link discovery updates loop",
-                   explanation = "An unknown error occured while dispatching "
-                                 + "link update notifications",
-                   recommendation = LogMessageDoc.GENERIC_ACTION)
-    private
-            void doUpdatesThread() throws InterruptedException {
-        do {
-            LDUpdate update = updates.take();
-            List<LDUpdate> updateList = new ArrayList<LDUpdate>();
-            updateList.add(update);
-
-            // Add all the pending updates to the list.
-            while (updates.peek() != null) {
-                updateList.add(updates.remove());
-            }
+    @Override
+    public Set<Short> getQuarantinedPorts(long sw) {
+        Set<Short> qPorts = new HashSet<Short>();
 
-            if (linkDiscoveryAware != null) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
-                              new Object[] {
-                                            update.getOperation(),
-                                            HexString.toHexString(update.getSrc()),
-                                            update.getSrcPort(),
-                                            HexString.toHexString(update.getDst()),
-                                            update.getDstPort(),
-                                            linkDiscoveryAware });
-                }
-                try {
-                    for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order
-                                                                            // maintained
-                        lda.linkDiscoveryUpdate(updateList);
-                    }
-                } catch (Exception e) {
-                    log.error("Error in link discovery updates loop", e);
-                }
+        Iterator<NodePortTuple> iter = quarantineQueue.iterator();
+        while (iter.hasNext()) {
+            NodePortTuple npt = iter.next();
+            if (npt.getNodeId() == sw) {
+                qPorts.add(npt.getPortId());
             }
-        } while (updates.peek() != null);
+        }
+        return qPorts;
     }
 
-    protected boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
-        return this.suppressLinkDiscovery.contains(new NodePortTuple(sw,
-                                                                     portNumber));
+    @Override
+    public Map<Long, Set<Link>> getSwitchLinks() {
+        return this.switchLinks;
     }
 
-    protected void discoverLinks() {
+    @Override
+    public void addMACToIgnoreList(long mac, int ignoreBits) {
+        MACRange range = new MACRange();
+        range.baseMAC = mac;
+        range.ignoreBits = ignoreBits;
+        ignoreMACSet.add(range);
+    }
 
-        // timeout known links.
-        timeoutLinks();
+    @Override
+    public boolean isAutoPortFastFeature() {
+        return autoPortFastFeature;
+    }
 
-        // increment LLDP clock
-        lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL;
+    @Override
+    public void setAutoPortFastFeature(boolean autoPortFastFeature) {
+        this.autoPortFastFeature = autoPortFastFeature;
+    }
 
-        if (lldpClock == 0) {
-            log.debug("Sending LLDP out on all ports.");
-            discoverOnAllPorts();
-        }
+    @Override
+    public void addListener(ILinkDiscoveryListener listener) {
+        linkDiscoveryAware.add(listener);
     }
 
-    /**
-     * Quarantine Ports.
-     */
-    protected class QuarantineWorker implements Runnable {
-        @Override
-        public void run() {
-            try {
-                processBDDPLists();
-            } catch (Exception e) {
-                log.error("Error in quarantine worker thread", e);
-            } finally {
-                bddpTask.reschedule(BDDP_TASK_INTERVAL,
-                                    TimeUnit.MILLISECONDS);
-            }
+    @Override
+    public Map<Link, LinkInfo> getLinks() {
+        lock.readLock().lock();
+        Map<Link, LinkInfo> result;
+        try {
+            result = new HashMap<Link, LinkInfo>(links);
+        } finally {
+            lock.readLock().unlock();
         }
+        return result;
     }
 
-    /**
-     * Add a switch port to the quarantine queue. Schedule the quarantine task
-     * if the quarantine queue was empty before adding this switch port.
-     * 
-     * @param npt
-     */
-    protected void addToQuarantineQueue(NodePortTuple npt) {
-        if (quarantineQueue.contains(npt) == false)
-                                                   quarantineQueue.add(npt);
+    // currently not called
+    public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
+        return portBroadcastDomainLinks;
     }
 
-    /**
-     * Remove a switch port from the quarantine queue.
-     */
-    protected void removeFromQuarantineQueue(NodePortTuple npt) {
-        // Remove all occurrences of the node port tuple from the list.
-        while (quarantineQueue.remove(npt))
-            ;
+    @Override
+    public String getName() {
+        return "linkdiscovery";
     }
 
-    /**
-     * Add a switch port to maintenance queue.
-     * 
-     * @param npt
-     */
-    protected void addToMaintenanceQueue(NodePortTuple npt) {
-        // TODO We are not checking if the switch port tuple is already
-        // in the maintenance list or not. This will be an issue for
-        // really large number of switch ports in the network.
-        if (maintenanceQueue.contains(npt) == false)
-                                                    maintenanceQueue.add(npt);
-    }
+    //*********************
+    //   OFMessage Listener
+    //*********************
 
-    /**
-     * Remove a switch port from maintenance queue.
-     * 
-     * @param npt
-     */
-    protected void removeFromMaintenanceQueue(NodePortTuple npt) {
-        // Remove all occurrences of the node port tuple from the queue.
-        while (maintenanceQueue.remove(npt))
-            ;
+    @Override
+    public Command receive(IOFSwitch sw, OFMessage msg,
+                           FloodlightContext cntx) {
+        switch (msg.getType()) {
+            case PACKET_IN:
+                return this.handlePacketIn(sw.getId(), (OFPacketIn) msg,
+                                           cntx);
+            case PORT_STATUS:
+                return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
+            default:
+                break;
+        }
+        return Command.CONTINUE;
     }
 
-    /**
-     * This method processes the quarantine list in bursts. The task is at most
-     * once per BDDP_TASK_INTERVAL. One each call, BDDP_TASK_SIZE number of
-     * switch ports are processed. Once the BDDP packets are sent out through
-     * the switch ports, the ports are removed from the quarantine list.
-     */
+    @Override
+    public boolean isCallbackOrderingPrereq(OFType type, String name) {
+        return false;
+    }
 
-    protected void processBDDPLists() {
-        int count = 0;
-        Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
+    @Override
+    public boolean isCallbackOrderingPostreq(OFType type, String name) {
+        return false;
+    }
 
-        while (count < BDDP_TASK_SIZE && quarantineQueue.peek() != null) {
-            NodePortTuple npt;
-            npt = quarantineQueue.remove();
-            sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false,
-                                 false);
-            nptList.add(npt);
-            count++;
-        }
+    //***********************************
+    //  Internal Methods - Packet-in Processing Related
+    //***********************************
 
-        count = 0;
-        while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
-            NodePortTuple npt;
-            npt = maintenanceQueue.remove();
-            sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false,
-                                 false);
-            count++;
+    protected Command handlePacketIn(long sw, OFPacketIn pi,
+                                     FloodlightContext cntx) {
+        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
+                           IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+
+        if (eth.getEtherType() == Ethernet.TYPE_BSN) {
+            BSN bsn = (BSN) eth.getPayload();
+            if (bsn == null) return Command.STOP;
+            if (bsn.getPayload() == null) return Command.STOP;
+            // It could be a packet other than BSN LLDP, therefore
+            // continue with the regular processing.
+            if (bsn.getPayload() instanceof LLDP == false)
+                return Command.CONTINUE;
+            return handleLldp((LLDP) bsn.getPayload(), sw, pi.getInPort(), false, cntx);
+        } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
+            return handleLldp((LLDP) eth.getPayload(), sw, pi.getInPort(), true, cntx);
+        } else if (eth.getEtherType() < 1500) {
+            long destMac = eth.getDestinationMAC().toLong();
+            if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Ignoring packet addressed to 802.1D/Q "
+                              + "reserved address.");
+                }
+                return Command.STOP;
+            }
         }
 
-        for (NodePortTuple npt : nptList) {
-            generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
+        if (ignorePacketInFromSource(eth.getSourceMAC().toLong())) {
+            return Command.STOP;
         }
-    }
 
-    public Set<Short> getQuarantinedPorts(long sw) {
-        Set<Short> qPorts = new HashSet<Short>();
+        // If packet-in is from a quarantine port, stop processing.
+        NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
+        if (quarantineQueue.contains(npt)) return Command.STOP;
 
-        Iterator<NodePortTuple> iter = quarantineQueue.iterator();
-        while (iter.hasNext()) {
-            NodePortTuple npt = iter.next();
-            if (npt.getNodeId() == sw) {
-                qPorts.add(npt.getPortId());
+        return Command.CONTINUE;
+    }
+
+    private boolean ignorePacketInFromSource(long srcMAC) {
+        Iterator<MACRange> it = ignoreMACSet.iterator();
+        while (it.hasNext()) {
+            MACRange range = it.next();
+            long mask = ~0;
+            if (range.ignoreBits >= 0 && range.ignoreBits <= 48) {
+                mask = mask << range.ignoreBits;
+                if ((range.baseMAC & mask) == (srcMAC & mask)) {
+                    return true;
+                }
             }
         }
-        return qPorts;
+        return false;
     }
 
-    private void generateSwitchPortStatusUpdate(long sw, short port) {
-        UpdateOperation operation;
-
+    private Command handleLldp(LLDP lldp, long sw, short inPort,
+                               boolean isStandard, FloodlightContext cntx) {
+        // If LLDP is suppressed on this port, ignore received packet as well
         IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        if (iofSwitch == null) return;
-
-        OFPhysicalPort ofp = iofSwitch.getPort(port);
-        if (ofp == null) return;
-
-        int srcPortState = ofp.getState();
-        boolean portUp = ((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) != OFPortState.OFPPS_STP_BLOCK.getValue());
-
-        if (portUp)
-            operation = UpdateOperation.PORT_UP;
-        else
-            operation = UpdateOperation.PORT_DOWN;
-
-        updates.add(new LDUpdate(sw, port, operation));
-    }
 
-    /**
-     * Send LLDP on known ports
-     */
-    protected void discoverOnKnownLinkPorts() {
-        // Copy the port set.
-        Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
-        nptSet.addAll(portLinks.keySet());
+        if (!isIncomingDiscoveryAllowed(sw, inPort, isStandard))
+            return Command.STOP;
 
-        // Send LLDP from each of them.
-        for (NodePortTuple npt : nptSet) {
-            discover(npt);
+        // If this is a malformed LLDP exit
+        if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) {
+            return Command.STOP;
         }
-    }
-
-    protected void discover(NodePortTuple npt) {
-        discover(npt.getNodeId(), npt.getPortId());
-    }
 
-    protected void discover(long sw, short port) {
-        sendDiscoveryMessage(sw, port, true, false);
-    }
+        long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
+        long otherId = 0;
+        boolean myLLDP = false;
+        Boolean isReverse = null;
 
-    /**
-     * This method is used to specifically ignore/consider specific
-     * links.
-     * @param src
-     * @param srcPort
-     * @param dst
-     * @param dstPort
-     * @return
-     */
-    protected boolean isLinkAllowed(long src, short srcPort,
-                                    long dst, short dstPort) {
-        return true;
-    }
+        ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
+        portBB.position(1);
 
-    /**
-     * Check if incoming discovery messages are enabled or not.
-     * @param sw
-     * @param port
-     * @param isStandard
-     * @return
-     */
-    protected boolean isIncomingDiscoveryAllowed(long sw, short port,
-                                                 boolean isStandard) {
+        Short remotePort = portBB.getShort();
+        IOFSwitch remoteSwitch = null;
 
-        if (isLinkDiscoverySuppressed(sw, port)) {
-            /* Do not process LLDPs from this port as suppressLLDP is set */
-            return false;
+        // Verify this LLDP packet matches what we're looking for
+        for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
+            if (lldptlv.getType() == 127 && lldptlv.getLength() == 12
+                && lldptlv.getValue()[0] == 0x0
+                && lldptlv.getValue()[1] == 0x26
+                && lldptlv.getValue()[2] == (byte) 0xe1
+                && lldptlv.getValue()[3] == 0x0) {
+                ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
+                remoteSwitch = floodlightProvider.getSwitches()
+                                                 .get(dpidBB.getLong(4));
+            } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8) {
+                otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
+                if (myId == otherId) myLLDP = true;
+            } else if (lldptlv.getType() == TLV_DIRECTION_TYPE
+                       && lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
+                if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
+                    isReverse = false;
+                else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
+                    isReverse = true;
+            }
         }
 
-        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        if (iofSwitch == null) {
-            return false;
+        if (myLLDP == false) {
+            // This is not the LLDP sent by this controller.
+            // If the LLDP message has multicast bit set, then we need to
+            // broadcast the packet as a regular packet (after checking IDs)
+            if (isStandard) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Got a standard LLDP=[{}] that was not sent by" +
+                              " this controller. Not fowarding it.", lldp.toString());
+                }
+                return Command.STOP;
+            } else if (myId < otherId) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Getting BDDP packets from a different controller"
+                              + "and letting it go through normal processing chain.");
+                }
+                return Command.CONTINUE;
+            }
+            return Command.STOP;
         }
 
-        if (port == OFPort.OFPP_LOCAL.getValue()) return false;
-
-        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
-        if (ofpPort == null) {
+        if (remoteSwitch == null) {
+            // Ignore LLDPs not generated by Floodlight, or from a switch that
+            // has recently
+            // disconnected, or from a switch connected to another Floodlight
+            // instance
             if (log.isTraceEnabled()) {
-                log.trace("Null physical port. sw={}, port={}",
-                          HexString.toHexString(sw), port);
+                log.trace("Received LLDP from remote switch not connected to the controller");
             }
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Check if outgoing discovery messages are enabled or not.
-     * @param sw
-     * @param port
-     * @param isStandard
-     * @param isReverse
-     * @return
-     */
-    protected boolean isOutgoingDiscoveryAllowed(long sw, short port,
-                                                 boolean isStandard,
-                                                 boolean isReverse) {
-
-        if (isLinkDiscoverySuppressed(sw, port)) {
-            /* Dont send LLDPs out of this port as suppressLLDP is set */
-            return false;
-        }
-
-        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        if (iofSwitch == null) {
-            return false;
+            return Command.STOP;
         }
 
-        if (port == OFPort.OFPP_LOCAL.getValue()) return false;
-
-        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
-        if (ofpPort == null) {
+        if (!remoteSwitch.portEnabled(remotePort)) {
             if (log.isTraceEnabled()) {
-                log.trace("Null physical port. sw={}, port={}",
-                          HexString.toHexString(sw), port);
+                log.trace("Ignoring link with disabled source port: switch {} port {}",
+                          remoteSwitch.getStringId(), remotePort);
             }
-            return false;
+            return Command.STOP;
+        }
+        if (suppressLinkDiscovery.contains(new NodePortTuple(
+                                                             remoteSwitch.getId(),
+                                                             remotePort))) {
+            if (log.isTraceEnabled()) {
+                log.trace("Ignoring link with suppressed src port: switch {} port {}",
+                          remoteSwitch.getStringId(), remotePort);
+            }
+            return Command.STOP;
+        }
+        if (!iofSwitch.portEnabled(inPort)) {
+            if (log.isTraceEnabled()) {
+                log.trace("Ignoring link with disabled dest port: switch {} port {}",
+                          HexString.toHexString(sw), inPort);
+            }
+            return Command.STOP;
         }
 
-        // For fast ports, do not send forward LLDPs or BDDPs.
-        if (!isReverse && autoPortFastFeature && iofSwitch.isFastPort(port))
-            return false;
-        return true;
-    }
-
-    /**
-     * Get the actions for packet-out corresponding to a specific port.
-     * This is a placeholder for adding actions if any port-specific
-     * actions are desired.  The default action is simply to output to
-     * the given port.
-     * @param port
-     * @return
-     */
-    protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPhysicalPort port){
-        // set actions
-        List<OFAction> actions = new ArrayList<OFAction>();
-        actions.add(new OFActionOutput(port.getPortNumber(), (short) 0));
-        return actions;
-    }
+        OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
+        int srcPortState = (physicalPort != null) ? physicalPort.getState()
+                                                 : 0;
+        physicalPort = iofSwitch.getPort(inPort);
+        int dstPortState = (physicalPort != null) ? physicalPort.getState()
+                                                 : 0;
 
-    public OFPacketOut generateLLDPMessage(long sw, short port,
-                                       boolean isStandard, boolean isReverse) {
+        // Store the time of update to this link, and push it out to
+        // routingEngine
+        Link lt = new Link(remoteSwitch.getId(), remotePort,
+                           iofSwitch.getId(), inPort);
 
-        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+        if (!isLinkAllowed(lt.getSrc(), lt.getSrcPort(),
+                           lt.getDst(), lt.getDstPort()))
+            return Command.STOP;
 
-        if (log.isTraceEnabled()) {
-            log.trace("Sending LLDP packet out of swich: {}, port: {}",
-                      HexString.toHexString(sw), port);
-            return null;
-        }
+        // Continue only if link is allowed.
+        Long lastLldpTime = null;
+        Long lastBddpTime = null;
 
-        // using "nearest customer bridge" MAC address for broadest possible
-        // propagation
-        // through provider and TPMR bridges (see IEEE 802.1AB-2009 and
-        // 802.1Q-2011),
-        // in particular the Linux bridge which behaves mostly like a provider
-        // bridge
-        byte[] chassisId = new byte[] { 4, 0, 0, 0, 0, 0, 0 }; // filled in
-                                                               // later
-        byte[] portId = new byte[] { 2, 0, 0 }; // filled in later
-        byte[] ttlValue = new byte[] { 0, 0x78 };
-        // OpenFlow OUI - 00-26-E1
-        byte[] dpidTLVValue = new byte[] { 0x0, 0x26, (byte) 0xe1, 0, 0, 0,
-                                          0, 0, 0, 0, 0, 0 };
-        LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127)
-                                       .setLength((short) dpidTLVValue.length)
-                                       .setValue(dpidTLVValue);
+        Long firstSeenTime = System.currentTimeMillis();
 
-        byte[] dpidArray = new byte[8];
-        ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
-        ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
+        if (isStandard)
+            lastLldpTime = System.currentTimeMillis();
+        else
+            lastBddpTime = System.currentTimeMillis();
 
-        Long dpid = sw;
-        dpidBB.putLong(dpid);
-        // set the chassis id's value to last 6 bytes of dpid
-        System.arraycopy(dpidArray, 2, chassisId, 1, 6);
-        // set the optional tlv to the full dpid
-        System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
+        LinkInfo newLinkInfo = new LinkInfo(firstSeenTime, lastLldpTime,
+                                            lastBddpTime, srcPortState,
+                                            dstPortState);
 
-        // TODO: Consider remove this block of code.
-        // It's evil to overwrite port object. The the old code always
-        // overwrote mac address, we now only overwrite zero macs and
-        // log a warning, mostly for paranoia.
-        byte[] srcMac = ofpPort.getHardwareAddress();
-        byte[] zeroMac = { 0, 0, 0, 0, 0, 0 };
-        if (Arrays.equals(srcMac, zeroMac)) {
-            log.warn("Port {}/{} has zero hareware address"
-                             + "overwrite with lower 6 bytes of dpid",
-                     HexString.toHexString(dpid), ofpPort.getPortNumber());
-            System.arraycopy(dpidArray, 2, srcMac, 0, 6);
-        }
+        addOrUpdateLink(lt, newLinkInfo);
 
-        // set the portId to the outgoing port
-        portBB.putShort(port);
-        if (log.isTraceEnabled()) {
-            log.trace("Sending LLDP out of interface: {}/{}",
-                      HexString.toHexString(sw), port);
+        // Check if reverse link exists.
+        // If it doesn't exist and if the forward link was seen
+        // first seen within a small interval, send probe on the
+        // reverse link.
+        newLinkInfo = links.get(lt);
+        if (newLinkInfo != null && isStandard && isReverse == false) {
+            Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
+                                        lt.getSrc(), lt.getSrcPort());
+            LinkInfo reverseInfo = links.get(reverseLink);
+            if (reverseInfo == null) {
+                // the reverse link does not exist.
+                if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis()
+                                                     - LINK_TIMEOUT) {
+                    this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(),
+                                              isStandard, true);
+                }
+            }
         }
 
-        LLDP lldp = new LLDP();
-        lldp.setChassisId(new LLDPTLV().setType((byte) 1)
-                                       .setLength((short) chassisId.length)
-                                       .setValue(chassisId));
-        lldp.setPortId(new LLDPTLV().setType((byte) 2)
-                                    .setLength((short) portId.length)
-                                    .setValue(portId));
-        lldp.setTtl(new LLDPTLV().setType((byte) 3)
-                                 .setLength((short) ttlValue.length)
-                                 .setValue(ttlValue));
-        lldp.getOptionalTLVList().add(dpidTLV);
-
-        // Add the controller identifier to the TLV value.
-        lldp.getOptionalTLVList().add(controllerTLV);
-        if (isReverse) {
-            lldp.getOptionalTLVList().add(reverseTLV);
-        } else {
-            lldp.getOptionalTLVList().add(forwardTLV);
-        }
+        // If the received packet is a BDDP packet, then create a reverse BDDP
+        // link as well.
+        if (!isStandard) {
+            Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
+                                        lt.getSrc(), lt.getSrcPort());
 
-        Ethernet ethernet;
-        if (isStandard) {
-            ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress())
-                                     .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
-                                     .setEtherType(Ethernet.TYPE_LLDP);
-            ethernet.setPayload(lldp);
-        } else {
-            BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
-            bsn.setPayload(lldp);
+            // srcPortState and dstPort state are reversed.
+            LinkInfo reverseInfo = new LinkInfo(firstSeenTime, lastLldpTime,
+                                                lastBddpTime, dstPortState,
+                                                srcPortState);
 
-            ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress())
-                                     .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
-                                     .setEtherType(Ethernet.TYPE_BSN);
-            ethernet.setPayload(bsn);
+            addOrUpdateLink(reverseLink, reverseInfo);
         }
 
-        // serialize and wrap in a packet out
-        byte[] data = ethernet.serialize();
-        OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory()
-                                                         .getMessage(OFType.PACKET_OUT);
-        po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
-        po.setInPort(OFPort.OFPP_NONE);
-
-        // set data and data length
-        po.setLengthU(OFPacketOut.MINIMUM_LENGTH + data.length);
-        po.setPacketData(data);
+        // Remove the node ports from the quarantine and maintenance queues.
+        NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(),
+                                                 lt.getSrcPort());
+        NodePortTuple nptDst = new NodePortTuple(lt.getDst(),
+                                                 lt.getDstPort());
+        removeFromQuarantineQueue(nptSrc);
+        removeFromMaintenanceQueue(nptSrc);
+        removeFromQuarantineQueue(nptDst);
+        removeFromMaintenanceQueue(nptDst);
 
-        return po;
+        // Consume this message
+        return Command.STOP;
     }
 
+    //***********************************
+    //  Internal Methods - Port Status/ New Port Processing Related
+    //***********************************
+
     /**
-     * Send link discovery message out of a given switch port. The discovery
-     * message may be a standard LLDP or a modified LLDP, where the dst mac
-     * address is set to :ff. TODO: The modified LLDP will updated in the future
-     * and may use a different eth-type.
+     * Handles an OFPortStatus message from a switch. We will add or delete
+     * LinkTupes as well re-compute the topology if needed.
      *
      * @param sw
-     * @param port
-     * @param isStandard
-     *            indicates standard or modified LLDP
-     * @param isReverse
-     *            indicates whether the LLDP was sent as a response
+     *            The IOFSwitch that sent the port status message
+     * @param ps
+     *            The OFPortStatus message
+     * @return The Command to continue or stop after we process this message
      */
-    @LogMessageDoc(level = "ERROR",
-                   message = "Failure sending LLDP out port {port} on switch {switch}",
-                   explanation = "An I/O error occured while sending LLDP message "
-                                 + "to the switch.",
-                   recommendation = LogMessageDoc.CHECK_SWITCH)
-    protected
-            void sendDiscoveryMessage(long sw, short port,
-                                      boolean isStandard, boolean isReverse) {
-
-        // Takes care of all checks including null pointer checks.
-        if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse))
-            return;
+    protected Command handlePortStatus(long sw, OFPortStatus ps) {
 
         IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+        if (iofSwitch == null) return Command.CONTINUE;
 
         if (log.isTraceEnabled()) {
-            log.trace("Sending LLDP packet out of swich: {}, port: {}",
-                      HexString.toHexString(sw), port);
-            return;
-        }
-        OFPacketOut po = generateLLDPMessage(sw, port, isStandard, isReverse);
-
-        // Add actions
-        List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort);
-        po.setActions(actions);
-        short  actionLength = 0;
-        Iterator <OFAction> actionIter = actions.iterator();
-        while (actionIter.hasNext()) {
-            actionLength += actionIter.next().getLength();
+            log.trace("handlePortStatus: Switch {} port #{} reason {}; "
+                              + "config is {} state is {}",
+                      new Object[] { iofSwitch.getStringId(),
+                                    ps.getDesc().getPortNumber(),
+                                    ps.getReason(),
+                                    ps.getDesc().getConfig(),
+                                    ps.getDesc().getState() });
         }
-        po.setActionsLength(actionLength);
 
-        // po already has the minimum length + data length set
-        // simply add the actions length to this.
-        po.setLengthU(po.getLengthU() + po.getActionsLength());
+        short port = ps.getDesc().getPortNumber();
+        NodePortTuple npt = new NodePortTuple(sw, port);
+        boolean linkDeleted = false;
+        boolean linkInfoChanged = false;
 
-        // send
+        lock.writeLock().lock();
         try {
-            iofSwitch.write(po, null);
-            iofSwitch.flush();
-        } catch (IOException e) {
-            log.error("Failure sending LLDP out port {} on switch {}",
-                      new Object[] { port, iofSwitch.getStringId() }, e);
+            // if ps is a delete, or a modify where the port is down or
+            // configured down
+            if ((byte) OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason()
+                || ((byte) OFPortReason.OFPPR_MODIFY.ordinal() == ps.getReason() && !portEnabled(ps.getDesc()))) {
+                deleteLinksOnPort(npt, "Port Status Changed");
+                LDUpdate update = new LDUpdate(sw, port,
+                                               UpdateOperation.PORT_DOWN);
+                updates.add(update);
+                linkDeleted = true;
+            } else if (ps.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
+                // If ps is a port modification and the port state has changed
+                // that affects links in the topology
+
+                if (this.portLinks.containsKey(npt)) {
+                    for (Link lt : this.portLinks.get(npt)) {
+                        LinkInfo linkInfo = links.get(lt);
+                        assert (linkInfo != null);
+                        Integer updatedSrcPortState = null;
+                        Integer updatedDstPortState = null;
+                        if (lt.getSrc() == npt.getNodeId()
+                            && lt.getSrcPort() == npt.getPortId()
+                            && (linkInfo.getSrcPortState() != ps.getDesc()
+                                                                .getState())) {
+                            updatedSrcPortState = ps.getDesc().getState();
+                            linkInfo.setSrcPortState(updatedSrcPortState);
+                        }
+                        if (lt.getDst() == npt.getNodeId()
+                            && lt.getDstPort() == npt.getPortId()
+                            && (linkInfo.getDstPortState() != ps.getDesc()
+                                                                .getState())) {
+                            updatedDstPortState = ps.getDesc().getState();
+                            linkInfo.setDstPortState(updatedDstPortState);
+                        }
+                        if ((updatedSrcPortState != null)
+                            || (updatedDstPortState != null)) {
+                            // The link is already known to link discovery
+                            // manager and the status has changed, therefore
+                            // send an LDUpdate.
+                            UpdateOperation operation = getUpdateOperation(linkInfo.getSrcPortState(),
+                                                                           linkInfo.getDstPortState());
+                            updates.add(new LDUpdate(lt.getSrc(),
+                                                     lt.getSrcPort(),
+                                                     lt.getDst(),
+                                                     lt.getDstPort(),
+                                                     getLinkType(lt,
+                                                                 linkInfo),
+                                                     operation));
+                            writeLinkToStorage(lt, linkInfo);
+                            linkInfoChanged = true;
+                        }
+                    }
+                }
+
+                UpdateOperation operation = getUpdateOperation(ps.getDesc()
+                                                                 .getState());
+                updates.add(new LDUpdate(sw, port, operation));
+            }
+
+            if (!linkDeleted && !linkInfoChanged) {
+                if (log.isTraceEnabled()) {
+                    log.trace("handlePortStatus: Switch {} port #{} reason {};"
+                                      + " no links to update/remove",
+                              new Object[] { HexString.toHexString(sw),
+                                            ps.getDesc().getPortNumber(),
+                                            ps.getReason() });
+                }
+            }
+        } finally {
+            lock.writeLock().unlock();
+        }
+
+        if (!linkDeleted) {
+            // Send LLDP right away when port state is changed for faster
+            // cluster-merge. If it is a link delete then there is not need
+            // to send the LLDPs right away and instead we wait for the LLDPs
+            // to be sent on the timer as it is normally done
+            // do it outside the write-lock
+            // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
+            processNewPort(npt.getNodeId(), npt.getPortId());
         }
+        return Command.CONTINUE;
     }
 
     /**
-     * Send LLDPs to all switch-ports
+     * Process a new port. If link discovery is disabled on the port, then do
+     * nothing. If autoportfast feature is enabled and the port is a fast port,
+     * then do nothing. Otherwise, send LLDP message. Add the port to
+     * quarantine.
+     *
+     * @param sw
+     * @param p
      */
-    protected void discoverOnAllPorts() {
-        if (log.isTraceEnabled()) {
-            log.trace("Sending LLDP packets out of all the enabled ports on switch {}");
+    private void processNewPort(long sw, short p) {
+        if (isLinkDiscoverySuppressed(sw, p)) {
+            // Do nothing as link discovery is suppressed.
+            return;
         }
-        Set<Long> switches = floodlightProvider.getSwitches().keySet();
-        // Send standard LLDPs
-        for (long sw : switches) {
-            IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-            if (iofSwitch == null) continue;
-            if (iofSwitch.getEnabledPorts() != null) {
-                for (OFPhysicalPort ofp : iofSwitch.getEnabledPorts()) {
-                    if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
-                                                                           continue;
-                    if (autoPortFastFeature
-                        && iofSwitch.isFastPort(ofp.getPortNumber()))
-                                                                     continue;
 
-                    // sends forward LLDP only non-fastports.
-                    sendDiscoveryMessage(sw, ofp.getPortNumber(), true,
-                                         false);
+        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+        if (iofSwitch == null) return;
 
-                    // If the switch port is not alreayd in the maintenance
-                    // queue, add it.
-                    NodePortTuple npt = new NodePortTuple(
-                                                          sw,
-                                                          ofp.getPortNumber());
-                    addToMaintenanceQueue(npt);
-                }
-            }
+        if (autoPortFastFeature && iofSwitch.isFastPort(p)) {
+            // Do nothing as the port is a fast port.
+            return;
+        }
+        NodePortTuple npt = new NodePortTuple(sw, p);
+        discover(sw, p);
+        // if it is not a fast port, add it to quarantine.
+        if (!iofSwitch.isFastPort(p)) {
+            addToQuarantineQueue(npt);
+        } else {
+            // Add to maintenance queue to ensure that BDDP packets
+            // are sent out.
+            addToMaintenanceQueue(npt);
         }
     }
 
-    protected void setControllerTLV() {
-        // Setting the controllerTLVValue based on current nano time,
-        // controller's IP address, and the network interface object hash
-        // the corresponding IP address.
+    private boolean portEnabled(OFPhysicalPort port) {
+        if (port == null) return false;
+        if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0)
+                                                                             return false;
+        if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0)
+                                                                           return false;
+        // Port STP state doesn't work with multiple VLANs, so ignore it for now
+        // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) ==
+        // OFPortState.OFPPS_STP_BLOCK.getValue())
+        // return false;
+        return true;
+    }
 
-        final int prime = 7867;
-        InetAddress localIPAddress = null;
-        NetworkInterface localInterface = null;
+    //***********************************
+    //  Internal Methods - Discovery Related
+    //***********************************
 
-        byte[] controllerTLVValue = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; // 8
-                                                                           // byte
-                                                                           // value.
-        ByteBuffer bb = ByteBuffer.allocate(10);
+    @LogMessageDoc(level = "ERROR",
+                   message = "Error in link discovery updates loop",
+                   explanation = "An unknown error occured while dispatching "
+                                 + "link update notifications",
+                   recommendation = LogMessageDoc.GENERIC_ACTION)
+    private void doUpdatesThread() throws InterruptedException {
+        do {
+            LDUpdate update = updates.take();
+            List<LDUpdate> updateList = new ArrayList<LDUpdate>();
+            updateList.add(update);
 
-        try {
-            localIPAddress = java.net.InetAddress.getLocalHost();
-            localInterface = NetworkInterface.getByInetAddress(localIPAddress);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
+            // Add all the pending updates to the list.
+            while (updates.peek() != null) {
+                updateList.add(updates.remove());
+            }
 
-        long result = System.nanoTime();
-        if (localIPAddress != null)
-                                   result = result
-                                            * prime
-                                            + IPv4.toIPv4Address(localIPAddress.getHostAddress());
-        if (localInterface != null)
-                                   result = result * prime
-                                            + localInterface.hashCode();
-        // set the first 4 bits to 0.
-        result = result & (0x0fffffffffffffffL);
+            if (linkDiscoveryAware != null) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Dispatching link discovery update {} {} {} {} {} for {}",
+                              new Object[] {
+                                            update.getOperation(),
+                                            HexString.toHexString(update.getSrc()),
+                                            update.getSrcPort(),
+                                            HexString.toHexString(update.getDst()),
+                                            update.getDstPort(),
+                                            linkDiscoveryAware });
+                }
+                try {
+                    for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order
+                                                                            // maintained
+                        lda.linkDiscoveryUpdate(updateList);
+                    }
+                } catch (Exception e) {
+                    log.error("Error in link discovery updates loop", e);
+                }
+            }
+        } while (updates.peek() != null);
+    }
 
-        bb.putLong(result);
+    protected boolean isLinkDiscoverySuppressed(long sw, short portNumber) {
+        return this.suppressLinkDiscovery.contains(new NodePortTuple(sw,
+                                                                     portNumber));
+    }
 
-        bb.rewind();
-        bb.get(controllerTLVValue, 0, 8);
+    protected void discoverLinks() {
 
-        this.controllerTLV = new LLDPTLV().setType((byte) 0x0c)
-                                          .setLength((short) controllerTLVValue.length)
-                                          .setValue(controllerTLVValue);
-    }
+        // timeout known links.
+        timeoutLinks();
 
-    @Override
-    public String getName() {
-        return "linkdiscovery";
-    }
+        // increment LLDP clock
+        lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL;
 
-    @Override
-    public Command receive(IOFSwitch sw, OFMessage msg,
-                           FloodlightContext cntx) {
-        switch (msg.getType()) {
-            case PACKET_IN:
-                return this.handlePacketIn(sw.getId(), (OFPacketIn) msg,
-                                           cntx);
-            case PORT_STATUS:
-                return this.handlePortStatus(sw.getId(), (OFPortStatus) msg);
-            default:
-                break;
+        if (lldpClock == 0) {
+            log.debug("Sending LLDP out on all ports.");
+            discoverOnAllPorts();
         }
-        return Command.CONTINUE;
     }
 
-    private Command handleLldp(LLDP lldp, long sw, short inPort,
-                               boolean isStandard, FloodlightContext cntx) {
-        // If LLDP is suppressed on this port, ignore received packet as well
-        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-
-        if (!isIncomingDiscoveryAllowed(sw, inPort, isStandard))
-            return Command.STOP;
-
-        // If this is a malformed LLDP, or not from us, exit
-        if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) {
-            return Command.CONTINUE;
+    /**
+     * Quarantine Ports.
+     */
+    protected class QuarantineWorker implements Runnable {
+        @Override
+        public void run() {
+            try {
+                processBDDPLists();
+            } catch (Exception e) {
+                log.error("Error in quarantine worker thread", e);
+            } finally {
+                bddpTask.reschedule(BDDP_TASK_INTERVAL,
+                                    TimeUnit.MILLISECONDS);
+            }
         }
+    }
 
-        long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
-        long otherId = 0;
-        boolean myLLDP = false;
-        Boolean isReverse = null;
+    /**
+     * Add a switch port to the quarantine queue. Schedule the quarantine task
+     * if the quarantine queue was empty before adding this switch port.
+     *
+     * @param npt
+     */
+    protected void addToQuarantineQueue(NodePortTuple npt) {
+        if (quarantineQueue.contains(npt) == false)
+                                                   quarantineQueue.add(npt);
+    }
 
-        ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
-        portBB.position(1);
+    /**
+     * Remove a switch port from the quarantine queue.
+     */
+    protected void removeFromQuarantineQueue(NodePortTuple npt) {
+        // Remove all occurrences of the node port tuple from the list.
+        while (quarantineQueue.remove(npt))
+            ;
+    }
 
-        Short remotePort = portBB.getShort();
-        IOFSwitch remoteSwitch = null;
+    /**
+     * Add a switch port to maintenance queue.
+     *
+     * @param npt
+     */
+    protected void addToMaintenanceQueue(NodePortTuple npt) {
+        // TODO We are not checking if the switch port tuple is already
+        // in the maintenance list or not. This will be an issue for
+        // really large number of switch ports in the network.
+        if (maintenanceQueue.contains(npt) == false)
+                                                    maintenanceQueue.add(npt);
+    }
 
-        // Verify this LLDP packet matches what we're looking for
-        for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
-            if (lldptlv.getType() == 127 && lldptlv.getLength() == 12
-                && lldptlv.getValue()[0] == 0x0
-                && lldptlv.getValue()[1] == 0x26
-                && lldptlv.getValue()[2] == (byte) 0xe1
-                && lldptlv.getValue()[3] == 0x0) {
-                ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue());
-                remoteSwitch = floodlightProvider.getSwitches()
-                                                 .get(dpidBB.getLong(4));
-            } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8) {
-                otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
-                if (myId == otherId) myLLDP = true;
-            } else if (lldptlv.getType() == TLV_DIRECTION_TYPE
-                       && lldptlv.getLength() == TLV_DIRECTION_LENGTH) {
-                if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0])
-                    isReverse = false;
-                else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0])
-                                                                                 isReverse = true;
-            }
-        }
+    /**
+     * Remove a switch port from maintenance queue.
+     *
+     * @param npt
+     */
+    protected void removeFromMaintenanceQueue(NodePortTuple npt) {
+        // Remove all occurrences of the node port tuple from the queue.
+        while (maintenanceQueue.remove(npt))
+            ;
+    }
 
-        if (myLLDP == false) {
-            // This is not the LLDP sent by this controller.
-            // If the LLDP message has multicast bit set, then we need to
-            // broadcast
-            // the packet as a regular packet.
-            if (isStandard) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Got a standard LLDP=[{}]. Not fowarding it.", lldp.toString());
-                }
-                return Command.STOP;
-            } else if (myId < otherId) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Getting BDDP packets from a different controller"
-                              + "and letting it go through normal processing chain.");
-                }
-                return Command.CONTINUE;
-            }
-        }
+    /**
+     * This method processes the quarantine list in bursts. The task is at most
+     * once per BDDP_TASK_INTERVAL. One each call, BDDP_TASK_SIZE number of
+     * switch ports are processed. Once the BDDP packets are sent out through
+     * the switch ports, the ports are removed from the quarantine list.
+     */
+    protected void processBDDPLists() {
+        int count = 0;
+        Set<NodePortTuple> nptList = new HashSet<NodePortTuple>();
 
-        if (remoteSwitch == null) {
-            // Ignore LLDPs not generated by Floodlight, or from a switch that
-            // has recently
-            // disconnected, or from a switch connected to another Floodlight
-            // instance
-            if (log.isTraceEnabled()) {
-                log.trace("Received LLDP from remote switch not connected to the controller");
-            }
-            return Command.STOP;
+        while (count < BDDP_TASK_SIZE && quarantineQueue.peek() != null) {
+            NodePortTuple npt;
+            npt = quarantineQueue.remove();
+            sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false,
+                                 false);
+            nptList.add(npt);
+            count++;
         }
 
-        if (!remoteSwitch.portEnabled(remotePort)) {
-            if (log.isTraceEnabled()) {
-                log.trace("Ignoring link with disabled source port: switch {} port {}",
-                          remoteSwitch.getStringId(), remotePort);
-            }
-            return Command.STOP;
-        }
-        if (suppressLinkDiscovery.contains(new NodePortTuple(
-                                                             remoteSwitch.getId(),
-                                                             remotePort))) {
-            if (log.isTraceEnabled()) {
-                log.trace("Ignoring link with suppressed src port: switch {} port {}",
-                          remoteSwitch.getStringId(), remotePort);
-            }
-            return Command.STOP;
-        }
-        if (!iofSwitch.portEnabled(inPort)) {
-            if (log.isTraceEnabled()) {
-                log.trace("Ignoring link with disabled dest port: switch {} port {}",
-                          HexString.toHexString(sw), inPort);
-            }
-            return Command.STOP;
+        count = 0;
+        while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) {
+            NodePortTuple npt;
+            npt = maintenanceQueue.remove();
+            sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false,
+                                 false);
+            count++;
         }
 
-        OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
-        int srcPortState = (physicalPort != null) ? physicalPort.getState()
-                                                 : 0;
-        physicalPort = iofSwitch.getPort(inPort);
-        int dstPortState = (physicalPort != null) ? physicalPort.getState()
-                                                 : 0;
+        for (NodePortTuple npt : nptList) {
+            generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId());
+        }
+    }
 
-        // Store the time of update to this link, and push it out to
-        // routingEngine
-        Link lt = new Link(remoteSwitch.getId(), remotePort,
-                           iofSwitch.getId(), inPort);
+    private void generateSwitchPortStatusUpdate(long sw, short port) {
+        UpdateOperation operation;
 
-        if (!isLinkAllowed(lt.getSrc(), lt.getSrcPort(),
-                           lt.getDst(), lt.getDstPort()))
-            return Command.STOP;
+        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+        if (iofSwitch == null) return;
 
-        // Continue only if link is allowed.
-        Long lastLldpTime = null;
-        Long lastBddpTime = null;
+        OFPhysicalPort ofp = iofSwitch.getPort(port);
+        if (ofp == null) return;
 
-        Long firstSeenTime = System.currentTimeMillis();
+        int srcPortState = ofp.getState();
+        boolean portUp = ((srcPortState & OFPortState.OFPPS_STP_MASK.getValue())
+                               != OFPortState.OFPPS_STP_BLOCK.getValue());
 
-        if (isStandard)
-            lastLldpTime = System.currentTimeMillis();
+        if (portUp)
+            operation = UpdateOperation.PORT_UP;
         else
-            lastBddpTime = System.currentTimeMillis();
+            operation = UpdateOperation.PORT_DOWN;
 
-        LinkInfo newLinkInfo = new LinkInfo(firstSeenTime, lastLldpTime,
-                                            lastBddpTime, srcPortState,
-                                            dstPortState);
+        updates.add(new LDUpdate(sw, port, operation));
+    }
 
+    /**
+     * Send LLDP on known ports
+     */
+    protected void discoverOnKnownLinkPorts() {
+        // Copy the port set.
+        Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
+        nptSet.addAll(portLinks.keySet());
 
-        addOrUpdateLink(lt, newLinkInfo);
+        // Send LLDP from each of them.
+        for (NodePortTuple npt : nptSet) {
+            discover(npt);
+        }
+    }
 
-        // Continue only if addOrUpdateLink was successful.
+    protected void discover(NodePortTuple npt) {
+        discover(npt.getNodeId(), npt.getPortId());
+    }
 
-        // Check if reverse link exists.
-        // If it doesn't exist and if the forward link was seen
-        // first seen within a small interval, send probe on the
-        // reverse link.
+    protected void discover(long sw, short port) {
+        sendDiscoveryMessage(sw, port, true, false);
+    }
 
-        newLinkInfo = links.get(lt);
-        if (newLinkInfo != null && isStandard && isReverse == false) {
-            Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
-                                        lt.getSrc(), lt.getSrcPort());
-            LinkInfo reverseInfo = links.get(reverseLink);
-            if (reverseInfo == null) {
-                // the reverse link does not exist.
-                if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis()
-                                                     - LINK_TIMEOUT) {
-                    this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(),
-                                              isStandard, true);
-                }
+    /**
+     * Check if incoming discovery messages are enabled or not.
+     * @param sw
+     * @param port
+     * @param isStandard
+     * @return
+     */
+    protected boolean isIncomingDiscoveryAllowed(long sw, short port,
+                                                 boolean isStandard) {
+
+        if (isLinkDiscoverySuppressed(sw, port)) {
+            /* Do not process LLDPs from this port as suppressLLDP is set */
+            return false;
+        }
+
+        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+        if (iofSwitch == null) {
+            return false;
+        }
+
+        if (port == OFPort.OFPP_LOCAL.getValue()) return false;
+
+        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+        if (ofpPort == null) {
+            if (log.isTraceEnabled()) {
+                log.trace("Null physical port. sw={}, port={}",
+                          HexString.toHexString(sw), port);
             }
+            return false;
         }
 
-        // If the received packet is a BDDP packet, then create a reverse BDDP
-        // link as well.
-        if (!isStandard) {
-            Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
-                                        lt.getSrc(), lt.getSrcPort());
+        return true;
+    }
 
-            // srcPortState and dstPort state are reversed.
-            LinkInfo reverseInfo = new LinkInfo(firstSeenTime, lastLldpTime,
-                                                lastBddpTime, dstPortState,
-                                                srcPortState);
+    /**
+     * Check if outgoing discovery messages are enabled or not.
+     * @param sw
+     * @param port
+     * @param isStandard
+     * @param isReverse
+     * @return
+     */
+    protected boolean isOutgoingDiscoveryAllowed(long sw, short port,
+                                                 boolean isStandard,
+                                                 boolean isReverse) {
 
-            addOrUpdateLink(reverseLink, reverseInfo);
+        if (isLinkDiscoverySuppressed(sw, port)) {
+            /* Dont send LLDPs out of this port as suppressLLDP is set */
+            return false;
         }
 
-        // Remove the node ports from the quarantine and maintenance queues.
-        NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(),
-                                                 lt.getSrcPort());
-        NodePortTuple nptDst = new NodePortTuple(lt.getDst(),
-                                                 lt.getDstPort());
-        removeFromQuarantineQueue(nptSrc);
-        removeFromMaintenanceQueue(nptSrc);
-        removeFromQuarantineQueue(nptDst);
-        removeFromMaintenanceQueue(nptDst);
+        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+        if (iofSwitch == null) {
+            return false;
+        }
 
-        // Consume this message
-        return Command.STOP;
+        if (port == OFPort.OFPP_LOCAL.getValue()) return false;
+
+        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+        if (ofpPort == null) {
+            if (log.isTraceEnabled()) {
+                log.trace("Null physical port. sw={}, port={}",
+                          HexString.toHexString(sw), port);
+            }
+            return false;
+        }
+
+        // For fast ports, do not send forward LLDPs or BDDPs.
+        if (!isReverse && autoPortFastFeature && iofSwitch.isFastPort(port))
+            return false;
+        return true;
     }
 
-    protected Command handlePacketIn(long sw, OFPacketIn pi,
-                                     FloodlightContext cntx) {
-        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
-                                                              IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+    /**
+     * Get the actions for packet-out corresponding to a specific port.
+     * This is a placeholder for adding actions if any port-specific
+     * actions are desired.  The default action is simply to output to
+     * the given port.
+     * @param port
+     * @return
+     */
+    protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPhysicalPort port){
+        // set actions
+        List<OFAction> actions = new ArrayList<OFAction>();
+        actions.add(new OFActionOutput(port.getPortNumber(), (short) 0));
+        return actions;
+    }
 
-        if (eth.getEtherType() == Ethernet.TYPE_BSN) {
-            BSN bsn = (BSN) eth.getPayload();
-            if (bsn == null) return Command.STOP;
-            if (bsn.getPayload() == null) return Command.STOP;
-            // It could be a packet other than BSN LLDP, therefore
-            // continue with the regular processing.
-            if (bsn.getPayload() instanceof LLDP == false)
-                                                          return Command.CONTINUE;
-            return handleLldp((LLDP) bsn.getPayload(), sw, pi.getInPort(), false, cntx);
-        } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) {
-            return handleLldp((LLDP) eth.getPayload(), sw, pi.getInPort(), true, cntx);
-        } else if (eth.getEtherType() < 1500) {
-            long destMac = eth.getDestinationMAC().toLong();
-            if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Ignoring packet addressed to 802.1D/Q "
-                              + "reserved address.");
-                }
-                return Command.STOP;
-            }
+    /**
+     * Send link discovery message out of a given switch port. The discovery
+     * message may be a standard LLDP or a modified LLDP, where the dst mac
+     * address is set to :ff. TODO: The modified LLDP will updated in the future
+     * and may use a different eth-type.
+     *
+     * @param sw
+     * @param port
+     * @param isStandard
+     *            indicates standard or modified LLDP
+     * @param isReverse
+     *            indicates whether the LLDP was sent as a response
+     */
+    @LogMessageDoc(level = "ERROR",
+                   message = "Failure sending LLDP out port {port} on switch {switch}",
+                   explanation = "An I/O error occured while sending LLDP message "
+                                 + "to the switch.",
+                   recommendation = LogMessageDoc.CHECK_SWITCH)
+    protected void sendDiscoveryMessage(long sw, short port,
+                                        boolean isStandard, boolean isReverse) {
+
+        // Takes care of all checks including null pointer checks.
+        if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse))
+            return;
+
+        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+
+        if (log.isTraceEnabled()) {
+            log.trace("Sending LLDP packet out of swich: {}, port: {}",
+                      HexString.toHexString(sw), port);
+        }
+        OFPacketOut po = generateLLDPMessage(sw, port, isStandard, isReverse);
+
+        // Add actions
+        List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort);
+        po.setActions(actions);
+        short  actionLength = 0;
+        Iterator <OFAction> actionIter = actions.iterator();
+        while (actionIter.hasNext()) {
+            actionLength += actionIter.next().getLength();
         }
+        po.setActionsLength(actionLength);
 
-        if (ignorePacketInFromSource(eth.getSourceMAC().toLong())) {
-            return Command.STOP;
+        // po already has the minimum length + data length set
+        // simply add the actions length to this.
+        po.setLengthU(po.getLengthU() + po.getActionsLength());
+
+        // send
+        try {
+            iofSwitch.write(po, null);
+            iofSwitch.flush();
+        } catch (IOException e) {
+            log.error("Failure sending LLDP out port {} on switch {}",
+                      new Object[] { port, iofSwitch.getStringId() }, e);
+        }
+    }
+
+    /**
+     * Send LLDPs to all switch-ports
+     */
+    protected void discoverOnAllPorts() {
+        if (log.isTraceEnabled()) {
+            log.trace("Sending LLDP packets out of all the enabled ports on switch {}");
         }
+        Set<Long> switches = floodlightProvider.getSwitches().keySet();
+        // Send standard LLDPs
+        for (long sw : switches) {
+            IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
+            if (iofSwitch == null) continue;
+            if (iofSwitch.getEnabledPorts() != null) {
+                for (OFPhysicalPort ofp : iofSwitch.getEnabledPorts()) {
+                    if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber()))
+                                                                           continue;
+                    if (autoPortFastFeature
+                        && iofSwitch.isFastPort(ofp.getPortNumber()))
+                                                                     continue;
 
-        // If packet-in is from a quarantine port, stop processing.
-        NodePortTuple npt = new NodePortTuple(sw, pi.getInPort());
-        if (quarantineQueue.contains(npt)) return Command.STOP;
+                    // sends forward LLDP only non-fastports.
+                    sendDiscoveryMessage(sw, ofp.getPortNumber(), true,
+                                         false);
 
-        return Command.CONTINUE;
+                    // If the switch port is not alreayd in the maintenance
+                    // queue, add it.
+                    NodePortTuple npt = new NodePortTuple(
+                                                          sw,
+                                                          ofp.getPortNumber());
+                    addToMaintenanceQueue(npt);
+                }
+            }
+        }
     }
 
     protected UpdateOperation getUpdateOperation(int srcPortState,
                                                  int dstPortState) {
-        boolean added = (((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) != OFPortState.OFPPS_STP_BLOCK.getValue()) && ((dstPortState & OFPortState.OFPPS_STP_MASK.getValue()) != OFPortState.OFPPS_STP_BLOCK.getValue()));
+        boolean added = (((srcPortState & OFPortState.OFPPS_STP_MASK.getValue())
+                                != OFPortState.OFPPS_STP_BLOCK.getValue()) &&
+                          ((dstPortState & OFPortState.OFPPS_STP_MASK.getValue())
+                                != OFPortState.OFPPS_STP_BLOCK.getValue()));
 
         if (added) return UpdateOperation.LINK_UPDATED;
         return UpdateOperation.LINK_REMOVED;
@@ -1132,6 +1334,17 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             return UpdateOperation.PORT_DOWN;
     }
 
+    //************************************
+    // Internal Methods - Link Operations Related
+    //************************************
+
+    /**
+     * This method is used to specifically ignore/consider specific links.
+     */
+    protected boolean isLinkAllowed(long src, short srcPort,
+                                    long dst, short dstPort) {
+        return true;
+    }
     protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) {
 
         NodePortTuple srcNpt, dstNpt;
@@ -1142,8 +1355,8 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             // put the new info. if an old info exists, it will be returned.
             LinkInfo oldInfo = links.put(lt, newInfo);
             if (oldInfo != null
-                && oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
-                                                                           newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
+                    && oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime())
+                newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
 
             if (log.isTraceEnabled()) {
                 log.trace("addOrUpdateLink: {} {}",
@@ -1161,30 +1374,26 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             if (oldInfo == null) {
                 // index it by switch source
                 if (!switchLinks.containsKey(lt.getSrc()))
-                                                          switchLinks.put(lt.getSrc(),
-                                                                          new HashSet<Link>());
+                    switchLinks.put(lt.getSrc(), new HashSet<Link>());
                 switchLinks.get(lt.getSrc()).add(lt);
 
                 // index it by switch dest
                 if (!switchLinks.containsKey(lt.getDst()))
-                                                          switchLinks.put(lt.getDst(),
-                                                                          new HashSet<Link>());
+                    switchLinks.put(lt.getDst(), new HashSet<Link>());
                 switchLinks.get(lt.getDst()).add(lt);
 
                 // index both ends by switch:port
                 if (!portLinks.containsKey(srcNpt))
-                                                   portLinks.put(srcNpt,
-                                                                 new HashSet<Link>());
+                    portLinks.put(srcNpt, new HashSet<Link>());
                 portLinks.get(srcNpt).add(lt);
 
                 if (!portLinks.containsKey(dstNpt))
-                                                   portLinks.put(dstNpt,
-                                                                 new HashSet<Link>());
+                    portLinks.put(dstNpt, new HashSet<Link>());
                 portLinks.get(dstNpt).add(lt);
 
                 // Add to portNOFLinks if the unicast valid time is null
                 if (newInfo.getUnicastValidTime() == null)
-                                                          addLinkToBroadcastDomain(lt);
+                    addLinkToBroadcastDomain(lt);
 
                 writeLinkToStorage(lt, newInfo);
                 updateOperation = UpdateOperation.LINK_UPDATED;
@@ -1273,13 +1482,9 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         return linkChanged;
     }
 
-    public Map<Long, Set<Link>> getSwitchLinks() {
-        return this.switchLinks;
-    }
-
     /**
      * Removes links from memory and storage.
-     * 
+     *
      * @param links
      *            The List of @LinkTuple to delete.
      */
@@ -1289,7 +1494,7 @@ public class LinkDiscoveryManager implements IOFMessageListener,
 
     /**
      * Removes links from memory and storage.
-     * 
+     *
      * @param links
      *            The List of @LinkTuple to delete.
      */
@@ -1343,243 +1548,22 @@ public class LinkDiscoveryManager implements IOFMessageListener,
                 removeLinkFromStorage(lt);
 
                 // TODO Whenever link is removed, it has to checked if
-                // the switchports must be added to quarantine.
-
-                if (log.isTraceEnabled()) {
-                    log.trace("Deleted link {}", lt);
-                }
-            }
-        } finally {
-            if (updateList != null) linkUpdateList.addAll(updateList);
-            updates.addAll(linkUpdateList);
-            lock.writeLock().unlock();
-        }
-    }
-
-    /**
-     * Handles an OFPortStatus message from a switch. We will add or delete
-     * LinkTupes as well re-compute the topology if needed.
-     * 
-     * @param sw
-     *            The IOFSwitch that sent the port status message
-     * @param ps
-     *            The OFPortStatus message
-     * @return The Command to continue or stop after we process this message
-     */
-    protected Command handlePortStatus(long sw, OFPortStatus ps) {
-
-        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        if (iofSwitch == null) return Command.CONTINUE;
-
-        if (log.isTraceEnabled()) {
-            log.trace("handlePortStatus: Switch {} port #{} reason {}; "
-                              + "config is {} state is {}",
-                      new Object[] { iofSwitch.getStringId(),
-                                    ps.getDesc().getPortNumber(),
-                                    ps.getReason(),
-                                    ps.getDesc().getConfig(),
-                                    ps.getDesc().getState() });
-        }
-
-        short port = ps.getDesc().getPortNumber();
-        NodePortTuple npt = new NodePortTuple(sw, port);
-        boolean linkDeleted = false;
-        boolean linkInfoChanged = false;
-
-        lock.writeLock().lock();
-        try {
-            // if ps is a delete, or a modify where the port is down or
-            // configured down
-            if ((byte) OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason()
-                || ((byte) OFPortReason.OFPPR_MODIFY.ordinal() == ps.getReason() && !portEnabled(ps.getDesc()))) {
-                deleteLinksOnPort(npt, "Port Status Changed");
-                LDUpdate update = new LDUpdate(sw, port,
-                                               UpdateOperation.PORT_DOWN);
-                updates.add(update);
-                linkDeleted = true;
-            } else if (ps.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
-                // If ps is a port modification and the port state has changed
-                // that affects links in the topology
-
-                if (this.portLinks.containsKey(npt)) {
-                    for (Link lt : this.portLinks.get(npt)) {
-                        LinkInfo linkInfo = links.get(lt);
-                        assert (linkInfo != null);
-                        Integer updatedSrcPortState = null;
-                        Integer updatedDstPortState = null;
-                        if (lt.getSrc() == npt.getNodeId()
-                            && lt.getSrcPort() == npt.getPortId()
-                            && (linkInfo.getSrcPortState() != ps.getDesc()
-                                                                .getState())) {
-                            updatedSrcPortState = ps.getDesc().getState();
-                            linkInfo.setSrcPortState(updatedSrcPortState);
-                        }
-                        if (lt.getDst() == npt.getNodeId()
-                            && lt.getDstPort() == npt.getPortId()
-                            && (linkInfo.getDstPortState() != ps.getDesc()
-                                                                .getState())) {
-                            updatedDstPortState = ps.getDesc().getState();
-                            linkInfo.setDstPortState(updatedDstPortState);
-                        }
-                        if ((updatedSrcPortState != null)
-                            || (updatedDstPortState != null)) {
-                            // The link is already known to link discovery
-                            // manager and the status has changed, therefore
-                            // send an LDUpdate.
-                            UpdateOperation operation = getUpdateOperation(linkInfo.getSrcPortState(),
-                                                                           linkInfo.getDstPortState());
-                            updates.add(new LDUpdate(lt.getSrc(),
-                                                     lt.getSrcPort(),
-                                                     lt.getDst(),
-                                                     lt.getDstPort(),
-                                                     getLinkType(lt,
-                                                                 linkInfo),
-                                                     operation));
-                            writeLinkToStorage(lt, linkInfo);
-                            linkInfoChanged = true;
-                        }
-                    }
-                }
-
-                UpdateOperation operation = getUpdateOperation(ps.getDesc()
-                                                                 .getState());
-                updates.add(new LDUpdate(sw, port, operation));
-            }
-
-            if (!linkDeleted && !linkInfoChanged) {
-                if (log.isTraceEnabled()) {
-                    log.trace("handlePortStatus: Switch {} port #{} reason {};"
-                                      + " no links to update/remove",
-                              new Object[] { HexString.toHexString(sw),
-                                            ps.getDesc().getPortNumber(),
-                                            ps.getReason() });
-                }
-            }
-        } finally {
-            lock.writeLock().unlock();
-        }
-
-        if (!linkDeleted) {
-            // Send LLDP right away when port state is changed for faster
-            // cluster-merge. If it is a link delete then there is not need
-            // to send the LLDPs right away and instead we wait for the LLDPs
-            // to be sent on the timer as it is normally done
-            // do it outside the write-lock
-            // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS);
-            processNewPort(npt.getNodeId(), npt.getPortId());
-        }
-        return Command.CONTINUE;
-    }
-
-    /**
-     * Process a new port. If link discovery is disabled on the port, then do
-     * nothing. If autoportfast feature is enabled and the port is a fast port,
-     * then do nothing. Otherwise, send LLDP message. Add the port to
-     * quarantine.
-     * 
-     * @param sw
-     * @param p
-     */
-    private void processNewPort(long sw, short p) {
-        if (isLinkDiscoverySuppressed(sw, p)) {
-            // Do nothing as link discovery is suppressed.
-            return;
-        }
-
-        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        if (iofSwitch == null) return;
-
-        if (autoPortFastFeature && iofSwitch.isFastPort(p)) {
-            // Do nothing as the port is a fast port.
-            return;
-        }
-        NodePortTuple npt = new NodePortTuple(sw, p);
-        discover(sw, p);
-        // if it is not a fast port, add it to quarantine.
-        if (!iofSwitch.isFastPort(p)) {
-            addToQuarantineQueue(npt);
-        } else {
-            // Add to maintenance queue to ensure that BDDP packets
-            // are sent out.
-            addToMaintenanceQueue(npt);
-        }
-    }
-
-    /**
-     * We send out LLDP messages when a switch is added to discover the topology
-     * 
-     * @param sw
-     *            The IOFSwitch that connected to the controller
-     */
-    @Override
-    public void addedSwitch(IOFSwitch sw) {
-
-        if (sw.getEnabledPortNumbers() != null) {
-            for (Short p : sw.getEnabledPortNumbers()) {
-                processNewPort(sw.getId(), p);
-            }
-        }
-        // Update event history
-        evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
-        LDUpdate update = new LDUpdate(sw.getId(), null,
-                                       UpdateOperation.SWITCH_UPDATED);
-        updates.add(update);
-    }
-
-    /**
-     * When a switch disconnects we remove any links from our map and notify.
-     * 
-     * @param The
-     *            id of the switch
-     */
-    @Override
-    public void removedSwitch(IOFSwitch iofSwitch) {
-        // Update event history
-        long sw = iofSwitch.getId();
-        evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
-        List<Link> eraseList = new ArrayList<Link>();
-        lock.writeLock().lock();
-        try {
-            if (switchLinks.containsKey(sw)) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Handle switchRemoved. Switch {}; removing links {}",
-                              HexString.toHexString(sw), switchLinks.get(sw));
-                }
-
-                List<LDUpdate> updateList = new ArrayList<LDUpdate>();
-                updateList.add(new LDUpdate(sw, null,
-                                            UpdateOperation.SWITCH_REMOVED));
-                // add all tuples with an endpoint on this switch to erase list
-                eraseList.addAll(switchLinks.get(sw));
-
-                // Sending the updateList, will ensure the updates in this
-                // list will be added at the end of all the link updates.
-                // Thus, it is not necessary to explicitly add these updates
-                // to the queue.
-                deleteLinks(eraseList, "Switch Removed", updateList);
-            } else {
-                // Switch does not have any links.
-                updates.add(new LDUpdate(sw, null,
-                                         UpdateOperation.SWITCH_REMOVED));
+                // the switchports must be added to quarantine.
+
+                if (log.isTraceEnabled()) {
+                    log.trace("Deleted link {}", lt);
+                }
             }
         } finally {
+            if (updateList != null) linkUpdateList.addAll(updateList);
+            updates.addAll(linkUpdateList);
             lock.writeLock().unlock();
         }
     }
 
-    /**
-     * We don't react the port changed notifications here. we listen for
-     * OFPortStatus messages directly. Might consider using this notifier
-     * instead
-     */
-    @Override
-    public void switchPortChanged(Long switchId) {
-        // no-op
-    }
-
     /**
      * Delete links incident on a given switch port.
-     * 
+     *
      * @param npt
      * @param reason
      */
@@ -1667,72 +1651,282 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         }
     }
 
-    private boolean portEnabled(OFPhysicalPort port) {
-        if (port == null) return false;
-        if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0)
-                                                                             return false;
-        if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0)
-                                                                           return false;
-        // Port STP state doesn't work with multiple VLANs, so ignore it for now
-        // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) ==
-        // OFPortState.OFPPS_STP_BLOCK.getValue())
-        // return false;
-        return true;
+    protected void addLinkToBroadcastDomain(Link lt) {
+
+        NodePortTuple srcNpt, dstNpt;
+        srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
+        dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
+
+        if (!portBroadcastDomainLinks.containsKey(srcNpt))
+            portBroadcastDomainLinks.put(srcNpt,
+                                         new HashSet<Link>());
+        portBroadcastDomainLinks.get(srcNpt).add(lt);
+
+        if (!portBroadcastDomainLinks.containsKey(dstNpt))
+            portBroadcastDomainLinks.put(dstNpt,
+                                         new HashSet<Link>());
+        portBroadcastDomainLinks.get(dstNpt).add(lt);
     }
 
-    public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() {
-        return portBroadcastDomainLinks;
+    protected void removeLinkFromBroadcastDomain(Link lt) {
+
+        NodePortTuple srcNpt, dstNpt;
+        srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
+        dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
+
+        if (portBroadcastDomainLinks.containsKey(srcNpt)) {
+            portBroadcastDomainLinks.get(srcNpt).remove(lt);
+            if (portBroadcastDomainLinks.get(srcNpt).isEmpty())
+                                                               portBroadcastDomainLinks.remove(srcNpt);
+        }
+
+        if (portBroadcastDomainLinks.containsKey(dstNpt)) {
+            portBroadcastDomainLinks.get(dstNpt).remove(lt);
+            if (portBroadcastDomainLinks.get(dstNpt).isEmpty())
+                                                               portBroadcastDomainLinks.remove(dstNpt);
+        }
+    }
+
+    //******************
+    // Internal Helper Methods
+    //******************
+
+    protected void setControllerTLV() {
+        // Setting the controllerTLVValue based on current nano time,
+        // controller's IP address, and the network interface object hash
+        // the corresponding IP address.
+
+        final int prime = 7867;
+        InetAddress localIPAddress = null;
+        NetworkInterface localInterface = null;
+
+        byte[] controllerTLVValue = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; // 8
+                                                                           // byte
+                                                                           // value.
+        ByteBuffer bb = ByteBuffer.allocate(10);
+
+        try {
+            localIPAddress = java.net.InetAddress.getLocalHost();
+            localInterface = NetworkInterface.getByInetAddress(localIPAddress);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        long result = System.nanoTime();
+        if (localIPAddress != null)
+                                   result = result
+                                            * prime
+                                            + IPv4.toIPv4Address(localIPAddress.getHostAddress());
+        if (localInterface != null)
+                                   result = result * prime
+                                            + localInterface.hashCode();
+        // set the first 4 bits to 0.
+        result = result & (0x0fffffffffffffffL);
+
+        bb.putLong(result);
+
+        bb.rewind();
+        bb.get(controllerTLVValue, 0, 8);
+
+        this.controllerTLV = new LLDPTLV().setType((byte) 0x0c)
+                                          .setLength((short) controllerTLVValue.length)
+                                          .setValue(controllerTLVValue);
     }
 
+    //******************
+    // IOFSwitchListener
+    //******************
+
+    /**
+     * We send out LLDP messages when a switch is added to discover the topology
+     *
+     * @param sw
+     *            The IOFSwitch that connected to the controller
+     */
     @Override
-    public Map<Link, LinkInfo> getLinks() {
-        lock.readLock().lock();
-        Map<Link, LinkInfo> result;
+    public void addedSwitch(IOFSwitch sw) {
+
+        if (sw.getEnabledPortNumbers() != null) {
+            for (Short p : sw.getEnabledPortNumbers()) {
+                processNewPort(sw.getId(), p);
+            }
+        }
+        // Update event history
+        evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None");
+        LDUpdate update = new LDUpdate(sw.getId(), null,
+                                       UpdateOperation.SWITCH_UPDATED);
+        updates.add(update);
+    }
+
+    /**
+     * When a switch disconnects we remove any links from our map and notify.
+     *
+     * @param The
+     *            id of the switch
+     */
+    @Override
+    public void removedSwitch(IOFSwitch iofSwitch) {
+        // Update event history
+        long sw = iofSwitch.getId();
+        evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None");
+        List<Link> eraseList = new ArrayList<Link>();
+        lock.writeLock().lock();
         try {
-            result = new HashMap<Link, LinkInfo>(links);
+            if (switchLinks.containsKey(sw)) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Handle switchRemoved. Switch {}; removing links {}",
+                              HexString.toHexString(sw), switchLinks.get(sw));
+                }
+
+                List<LDUpdate> updateList = new ArrayList<LDUpdate>();
+                updateList.add(new LDUpdate(sw, null,
+                                            UpdateOperation.SWITCH_REMOVED));
+                // add all tuples with an endpoint on this switch to erase list
+                eraseList.addAll(switchLinks.get(sw));
+
+                // Sending the updateList, will ensure the updates in this
+                // list will be added at the end of all the link updates.
+                // Thus, it is not necessary to explicitly add these updates
+                // to the queue.
+                deleteLinks(eraseList, "Switch Removed", updateList);
+            } else {
+                // Switch does not have any links.
+                updates.add(new LDUpdate(sw, null,
+                                         UpdateOperation.SWITCH_REMOVED));
+            }
         } finally {
-            lock.readLock().unlock();
+            lock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * We don't react the port changed notifications here. we listen for
+     * OFPortStatus messages directly. Might consider using this notifier
+     * instead
+     */
+    @Override
+    public void switchPortChanged(Long switchId) {
+        // no-op
+    }
+
+    //*********************
+    //   Storage Listener
+    //*********************
+    /**
+     * Sets the IStorageSource to use for ITology
+     *
+     * @param storageSource
+     *            the storage source to use
+     */
+    public void setStorageSource(IStorageSourceService storageSource) {
+        this.storageSource = storageSource;
+    }
+
+    /**
+     * Gets the storage source for this ITopology
+     *
+     * @return The IStorageSource ITopology is writing to
+     */
+    public IStorageSourceService getStorageSource() {
+        return storageSource;
+    }
+
+    @Override
+    public void rowsModified(String tableName, Set<Object> rowKeys) {
+
+        if (tableName.equals(TOPOLOGY_TABLE_NAME)) {
+            readTopologyConfigFromStorage();
+            return;
+        }
+
+        Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches();
+        ArrayList<IOFSwitch> updated_switches = new ArrayList<IOFSwitch>();
+        for (Object key : rowKeys) {
+            Long swId = new Long(HexString.toLong((String) key));
+            if (switches.containsKey(swId)) {
+                IOFSwitch sw = switches.get(swId);
+                boolean curr_status = sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
+                boolean new_status = false;
+                IResultSet resultSet = null;
+
+                try {
+                    resultSet = storageSource.getRow(tableName, key);
+                    for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) {
+                        // In case of multiple rows, use the status in last row?
+                        Map<String, Object> row = it.next().getRow();
+                        if (row.containsKey(SWITCH_CONFIG_CORE_SWITCH)) {
+                            new_status = ((String) row.get(SWITCH_CONFIG_CORE_SWITCH)).equals("true");
+                        }
+                    }
+                } finally {
+                    if (resultSet != null) resultSet.close();
+                }
+
+                if (curr_status != new_status) {
+                    updated_switches.add(sw);
+                }
+            } else {
+                if (log.isTraceEnabled()) {
+                    log.trace("Update for switch which has no entry in switch "
+                                      + "list (dpid={}), a delete action.",
+                              key);
+                }
+            }
+        }
+
+        for (IOFSwitch sw : updated_switches) {
+            // Set SWITCH_IS_CORE_SWITCH to it's inverse value
+            if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) {
+                sw.removeAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
+                if (log.isTraceEnabled()) {
+                    log.trace("SWITCH_IS_CORE_SWITCH set to False for {}",
+                              sw);
+                }
+                updates.add(new LDUpdate(sw.getId(),
+                                         SwitchType.BASIC_SWITCH,
+                                         UpdateOperation.SWITCH_UPDATED));
+            } else {
+                sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH,
+                                new Boolean(true));
+                if (log.isTraceEnabled()) {
+                    log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw);
+                }
+                updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH,
+                                         UpdateOperation.SWITCH_UPDATED));
+            }
         }
-        return result;
     }
 
-    protected void addLinkToBroadcastDomain(Link lt) {
-
-        NodePortTuple srcNpt, dstNpt;
-        srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
-        dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
-
-        if (!portBroadcastDomainLinks.containsKey(srcNpt))
-            portBroadcastDomainLinks.put(srcNpt,
-                                         new HashSet<Link>());
-        portBroadcastDomainLinks.get(srcNpt).add(lt);
-
-        if (!portBroadcastDomainLinks.containsKey(dstNpt))
-            portBroadcastDomainLinks.put(dstNpt,
-                                         new HashSet<Link>());
-        portBroadcastDomainLinks.get(dstNpt).add(lt);
+    @Override
+    public void rowsDeleted(String tableName, Set<Object> rowKeys) {
+        // Ignore delete events, the switch delete will do the
+        // right thing on it's own.
+        readTopologyConfigFromStorage();
     }
 
-    protected void removeLinkFromBroadcastDomain(Link lt) {
 
-        NodePortTuple srcNpt, dstNpt;
-        srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
-        dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort());
+    //******************************
+    // Internal methods - Config Related
+    //******************************
 
-        if (portBroadcastDomainLinks.containsKey(srcNpt)) {
-            portBroadcastDomainLinks.get(srcNpt).remove(lt);
-            if (portBroadcastDomainLinks.get(srcNpt).isEmpty())
-                                                               portBroadcastDomainLinks.remove(srcNpt);
-        }
+    protected void readTopologyConfigFromStorage() {
+        IResultSet topologyResult = storageSource.executeQuery(TOPOLOGY_TABLE_NAME,
+                                                               null, null,
+                                                               null);
 
-        if (portBroadcastDomainLinks.containsKey(dstNpt)) {
-            portBroadcastDomainLinks.get(dstNpt).remove(lt);
-            if (portBroadcastDomainLinks.get(dstNpt).isEmpty())
-                                                               portBroadcastDomainLinks.remove(dstNpt);
+        if (topologyResult.next()) {
+            boolean apf = topologyResult.getBoolean(TOPOLOGY_AUTOPORTFAST);
+            autoPortFastFeature = apf;
+        } else {
+            this.autoPortFastFeature = AUTOPORTFAST_DEFAULT;
         }
+
+        if (autoPortFastFeature)
+            log.info("Setting autoportfast feature to ON");
+        else
+            log.info("Setting autoportfast feature to OFF");
     }
 
-    // STORAGE METHODS
     /**
      * Deletes all links from storage
      */
@@ -1740,22 +1934,9 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         storageSource.deleteRowsAsync(LINK_TABLE_NAME, null);
     }
 
-    /**
-     * Gets the storage key for a LinkTuple
-     * 
-     * @param lt
-     *            The LinkTuple to get
-     * @return The storage key as a String
-     */
-    private String getLinkId(Link lt) {
-        return HexString.toHexString(lt.getSrc()) + "-" + lt.getSrcPort()
-               + "-" + HexString.toHexString(lt.getDst()) + "-"
-               + lt.getDstPort();
-    }
-
     /**
      * Writes a LinkTuple and corresponding LinkInfo to storage
-     * 
+     *
      * @param lt
      *            The LinkTuple to write
      * @param linkInfo
@@ -1823,6 +2004,17 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         storageSource.updateRowAsync(LINK_TABLE_NAME, rowValues);
     }
 
+    /**
+     * Removes a link from storage using an asynchronous call.
+     *
+     * @param lt
+     *            The LinkTuple to delete.
+     */
+    protected void removeLinkFromStorage(Link lt) {
+        String id = getLinkId(lt);
+        storageSource.deleteRowAsync(LINK_TABLE_NAME, id);
+    }
+
     public Long readLinkValidTime(Link lt) {
         // FIXME: We're not currently using this right now, but if we start
         // to use this again, we probably shouldn't use it in its current
@@ -1838,9 +2030,9 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             resultSet = storageSource.executeQuery(LINK_TABLE_NAME,
                                                    columns,
                                                    new OperatorPredicate(
-                                                                         LINK_ID,
-                                                                         OperatorPredicate.Operator.EQ,
-                                                                         id),
+                                                        LINK_ID,
+                                                        OperatorPredicate.Operator.EQ,
+                                                        id),
                                                    null);
             if (resultSet.next())
                                  validTime = resultSet.getLong(LINK_VALID_TIME);
@@ -1851,171 +2043,44 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     }
 
     /**
-     * Removes a link from storage using an asynchronous call.
-     * 
+     * Gets the storage key for a LinkTuple
+     *
      * @param lt
-     *            The LinkTuple to delete.
-     */
-    protected void removeLinkFromStorage(Link lt) {
-        String id = getLinkId(lt);
-        storageSource.deleteRowAsync(LINK_TABLE_NAME, id);
-    }
-
-    @Override
-    public void addListener(ILinkDiscoveryListener listener) {
-        linkDiscoveryAware.add(listener);
-    }
-
-    /**
-     * Register a link discovery aware component
-     * 
-     * @param linkDiscoveryAwareComponent
-     */
-    public
-            void
-            addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
-        // TODO make this a copy on write set or lock it somehow
-        this.linkDiscoveryAware.add(linkDiscoveryAwareComponent);
-    }
-
-    /**
-     * Deregister a link discovery aware component
-     * 
-     * @param linkDiscoveryAwareComponent
-     */
-    public
-            void
-            removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) {
-        // TODO make this a copy on write set or lock it somehow
-        this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent);
-    }
-
-    /**
-     * Sets the IStorageSource to use for ITology
-     * 
-     * @param storageSource
-     *            the storage source to use
-     */
-    public void setStorageSource(IStorageSourceService storageSource) {
-        this.storageSource = storageSource;
-    }
-
-    /**
-     * Gets the storage source for this ITopology
-     * 
-     * @return The IStorageSource ITopology is writing to
+     *            The LinkTuple to get
+     * @return The storage key as a String
      */
-    public IStorageSourceService getStorageSource() {
-        return storageSource;
-    }
-
-    @Override
-    public boolean isCallbackOrderingPrereq(OFType type, String name) {
-        return false;
-    }
-
-    @Override
-    public boolean isCallbackOrderingPostreq(OFType type, String name) {
-        return false;
-    }
-
-    @Override
-    public void rowsModified(String tableName, Set<Object> rowKeys) {
-
-        if (tableName.equals(TOPOLOGY_TABLE_NAME)) {
-            readTopologyConfigFromStorage();
-            return;
-        }
-
-        Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches();
-        ArrayList<IOFSwitch> updated_switches = new ArrayList<IOFSwitch>();
-        for (Object key : rowKeys) {
-            Long swId = new Long(HexString.toLong((String) key));
-            if (switches.containsKey(swId)) {
-                IOFSwitch sw = switches.get(swId);
-                boolean curr_status = sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
-                boolean new_status = false;
-                IResultSet resultSet = null;
-
-                try {
-                    resultSet = storageSource.getRow(tableName, key);
-                    for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) {
-                        // In case of multiple rows, use the status in last row?
-                        Map<String, Object> row = it.next().getRow();
-                        if (row.containsKey(SWITCH_CONFIG_CORE_SWITCH)) {
-                            new_status = ((String) row.get(SWITCH_CONFIG_CORE_SWITCH)).equals("true");
-                        }
-                    }
-                } finally {
-                    if (resultSet != null) resultSet.close();
-                }
-
-                if (curr_status != new_status) {
-                    updated_switches.add(sw);
-                }
-            } else {
-                if (log.isTraceEnabled()) {
-                    log.trace("Update for switch which has no entry in switch "
-                                      + "list (dpid={}), a delete action.",
-                              (String) key);
-                }
-            }
-        }
-
-        for (IOFSwitch sw : updated_switches) {
-            // Set SWITCH_IS_CORE_SWITCH to it's inverse value
-            if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) {
-                sw.removeAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH);
-                if (log.isTraceEnabled()) {
-                    log.trace("SWITCH_IS_CORE_SWITCH set to False for {}",
-                              sw);
-                }
-                updates.add(new LDUpdate(sw.getId(),
-                                         SwitchType.BASIC_SWITCH,
-                                         UpdateOperation.SWITCH_UPDATED));
-            } else {
-                sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH,
-                                new Boolean(true));
-                if (log.isTraceEnabled()) {
-                    log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw);
-                }
-                updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH,
-                                         UpdateOperation.SWITCH_UPDATED));
-            }
-        }
-    }
-
-    @Override
-    public void rowsDeleted(String tableName, Set<Object> rowKeys) {
-        // Ignore delete events, the switch delete will do the
-        // right thing on it's own.
-        readTopologyConfigFromStorage();
+    private String getLinkId(Link lt) {
+        return HexString.toHexString(lt.getSrc()) + "-" + lt.getSrcPort()
+               + "-" + HexString.toHexString(lt.getDst()) + "-"
+               + lt.getDstPort();
     }
 
-    // IFloodlightModule classes
+    //***************
+    // IFloodlightModule
+    //***************
 
     @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleServices() {
-        Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        Collection<Class<? extends IFloodlightService>> l =
+                new ArrayList<Class<? extends IFloodlightService>>();
         l.add(ILinkDiscoveryService.class);
         // l.add(ITopologyService.class);
         return l;
     }
 
     @Override
-    public Map<Class<? extends IFloodlightService>, IFloodlightService>
-            getServiceImpls() {
-        Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
+    public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
+        Map<Class<? extends IFloodlightService>, IFloodlightService> m =
+                new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
         // We are the class that implements the service
         m.put(ILinkDiscoveryService.class, this);
         return m;
     }
 
     @Override
-    public Collection<Class<? extends IFloodlightService>>
-            getModuleDependencies() {
-        Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>();
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        Collection<Class<? extends IFloodlightService>> l =
+                new ArrayList<Class<? extends IFloodlightService>>();
         l.add(IFloodlightProviderService.class);
         l.add(IStorageSourceService.class);
         l.add(IThreadPoolService.class);
@@ -2024,10 +2089,8 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     }
 
     @Override
-    public
-            void
-            init(FloodlightModuleContext context)
-                                                 throws FloodlightModuleException {
+    public void init(FloodlightModuleContext context)
+            throws FloodlightModuleException {
         floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
         storageSource = context.getServiceImpl(IStorageSourceService.class);
         threadPool = context.getServiceImpl(IThreadPoolService.class);
@@ -2044,7 +2107,7 @@ public class LinkDiscoveryManager implements IOFMessageListener,
             log.warn("Error event history size, using default of {} seconds", EVENT_HISTORY_SIZE);
         }
         log.debug("Event history size set to {}", EVENT_HISTORY_SIZE);
-        
+
         // Set the autoportfast feature to false.
         this.autoPortFastFeature = AUTOPORTFAST_DEFAULT;
 
@@ -2063,7 +2126,8 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         this.evHistTopologySwitch = new EventHistory<EventHistoryTopologySwitch>(EVENT_HISTORY_SIZE);
         this.evHistTopologyLink = new EventHistory<EventHistoryTopologyLink>(EVENT_HISTORY_SIZE);
         this.evHistTopologyCluster = new EventHistory<EventHistoryTopologyCluster>(EVENT_HISTORY_SIZE);
-        this.ignoreMACSet = new HashSet<MACRange>();
+        this.ignoreMACSet = Collections.newSetFromMap(
+                                new ConcurrentHashMap<MACRange,Boolean>());
     }
 
     @Override
@@ -2089,8 +2153,7 @@ public class LinkDiscoveryManager implements IOFMessageListener,
                                     explanation = "An unknown error occured while sending LLDP "
                                                   + "messages to switches.",
                                     recommendation = LogMessageDoc.CHECK_SWITCH) })
-    public
-            void startUp(FloodlightModuleContext context) {
+    public void startUp(FloodlightModuleContext context) {
         // Create our storage tables
         if (storageSource == null) {
             log.error("No storage source found.");
@@ -2191,7 +2254,9 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     // Topology Manager's Event History members and methods
     // ****************************************************
 
-    // Topology Manager event history
+    /**
+     *  Topology Manager event history
+     */
     public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch;
     public EventHistory<EventHistoryTopologyLink> evHistTopologyLink;
     public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster;
@@ -2199,9 +2264,10 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     public EventHistoryTopologyLink evTopoLink;
     public EventHistoryTopologyCluster evTopoCluster;
 
-    // Switch Added/Deleted
-    private void
-            evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
+    /**
+     *  Switch Added/Deleted Events
+     */
+    private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) {
         if (evTopoSwitch == null) {
             evTopoSwitch = new EventHistoryTopologySwitch();
         }
@@ -2264,6 +2330,10 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action);
     }
 
+    //*********************
+    //  IInfoProvider
+    //*********************
+
     @Override
     public Map<String, Object> getInfo(String type) {
         if (!"summary".equals(type)) return null;
@@ -2278,7 +2348,10 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         return info;
     }
 
-    // IHARoleListener
+    //***************
+    // IHAListener
+    //***************
+
     @Override
     public void roleChanged(Role oldRole, Role newRole) {
         switch (newRole) {
@@ -2311,60 +2384,10 @@ public class LinkDiscoveryManager implements IOFMessageListener,
     }
 
     @Override
-    public
-            void
-            controllerNodeIPsChanged(Map<String, String> curControllerNodeIPs,
-                                     Map<String, String> addedControllerNodeIPs,
-                                     Map<String, String> removedControllerNodeIPs) {
+    public void controllerNodeIPsChanged(Map<String, String> curControllerNodeIPs,
+                                         Map<String, String> addedControllerNodeIPs,
+                                         Map<String, String> removedControllerNodeIPs) {
         // ignore
     }
 
-    public boolean isAutoPortFastFeature() {
-        return autoPortFastFeature;
-    }
-
-    public void setAutoPortFastFeature(boolean autoPortFastFeature) {
-        this.autoPortFastFeature = autoPortFastFeature;
-    }
-
-    @Override
-    public void addMACToIgnoreList(long mac, int ignoreBits) {
-        MACRange range = new MACRange();
-        range.baseMAC = mac;
-        range.ignoreBits = ignoreBits;
-        ignoreMACSet.add(range);
-    }
-
-    private boolean ignorePacketInFromSource(long srcMAC) {
-        Iterator<MACRange> it = ignoreMACSet.iterator();
-        while (it.hasNext()) {
-            MACRange range = it.next();
-            long mask = ~0;
-            if (range.ignoreBits >= 0 && range.ignoreBits <= 48) {
-                mask = mask << range.ignoreBits;
-                if ((range.baseMAC & mask) == (srcMAC & mask)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public void readTopologyConfigFromStorage() {
-        IResultSet topologyResult = storageSource.executeQuery(TOPOLOGY_TABLE_NAME,
-                                                               null, null,
-                                                               null);
-
-        if (topologyResult.next()) {
-            boolean apf = topologyResult.getBoolean(TOPOLOGY_AUTOPORTFAST);
-            autoPortFastFeature = apf;
-        } else {
-            this.autoPortFastFeature = AUTOPORTFAST_DEFAULT;
-        }
-
-        if (autoPortFastFeature)
-            log.info("Setting autoportfast feature to ON");
-        else
-            log.info("Setting autoportfast feature to OFF");
-    }
 }
diff --git a/src/main/java/net/floodlightcontroller/packet/BasePacket.java b/src/main/java/net/floodlightcontroller/packet/BasePacket.java
index 4ecfdedddd813b1fbd42a3ad1a2dbc48eafc45ed..eed530c03d2e8047774697bc70032d58eee63981 100644
--- a/src/main/java/net/floodlightcontroller/packet/BasePacket.java
+++ b/src/main/java/net/floodlightcontroller/packet/BasePacket.java
@@ -17,12 +17,16 @@
 
 package net.floodlightcontroller.packet;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 
 /**
 *
 * @author David Erickson (daviderickson@cs.stanford.edu)
 */
 public abstract class BasePacket implements IPacket {
+    public static final Logger log = LoggerFactory.getLogger(BasePacket.class);
     protected IPacket parent;
     protected IPacket payload;
 
@@ -113,4 +117,4 @@ public abstract class BasePacket implements IPacket {
         pkt.setParent(this.parent);
         return pkt;
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
index 44a908118d9e649ec0421f5620684a9d967f6519..4fc2380762a5c71e019886cb6c0da82e6b84fc19 100644
--- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java
+++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java
@@ -230,7 +230,7 @@ public class Ethernet extends BasePacket {
 
     @Override
     public IPacket deserialize(byte[] data, int offset, int length) {
-        if (length <= 0)
+        if (length <= 16)  // Ethernet packet minium should be 60, this is reasonable
             return null;
         ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
         if (this.destinationMACAddress == null)
@@ -261,13 +261,23 @@ public class Ethernet extends BasePacket {
             Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap.get(this.etherType);
             try {
                 payload = clazz.newInstance();
+                this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
             } catch (Exception e) {
-                throw new RuntimeException("Error parsing payload for Ethernet packet", e);
+                if (log.isTraceEnabled()) {
+                    log.trace("Failed to parse ethernet packet {}->{}" +
+                            " payload as {}, treat as plain ethernet packet",
+                            new Object[] {this.sourceMACAddress,
+                                          this.destinationMACAddress,
+                                          clazz.getClass().getName()});
+                    log.trace("Exception from parsing {}", e);
+                }
+                payload = new Data();
+                this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
             }
         } else {
             payload = new Data();
+            this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
         }
-        this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
         this.payload.setParent(this);
         return this;
     }
diff --git a/src/main/java/net/floodlightcontroller/packet/ICMP.java b/src/main/java/net/floodlightcontroller/packet/ICMP.java
index 5431277f72061e47129d6b7a99ba757baae71a74..6b02b5bd502a5869e35a86579152417f39cd0698 100644
--- a/src/main/java/net/floodlightcontroller/packet/ICMP.java
+++ b/src/main/java/net/floodlightcontroller/packet/ICMP.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -18,6 +18,8 @@
 package net.floodlightcontroller.packet;
 
 import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Implements ICMP packet format
@@ -28,6 +30,24 @@ public class ICMP extends BasePacket {
     protected byte icmpCode;
     protected short checksum;
 
+    // The value is the number of bytes of padding
+    public static final Map<Byte, Short> paddingMap;
+
+    public static final byte ECHO_REPLY = 0x0;
+    public static final byte ECHO_REQUEST = 0x8;
+    public static final byte TIME_EXCEEDED = 0xB;
+    public static final byte DESTINATION_UNREACHABLE = 0x3;
+
+    public static final byte CODE_PORT_UNREACHABLE = 0x3;
+
+    static {
+        paddingMap = new HashMap<Byte, Short>();
+        ICMP.paddingMap.put(ICMP.ECHO_REPLY, (short) 0);
+        ICMP.paddingMap.put(ICMP.ECHO_REQUEST, (short) 0);
+        ICMP.paddingMap.put(ICMP.TIME_EXCEEDED, (short) 4);
+        ICMP.paddingMap.put(ICMP.DESTINATION_UNREACHABLE, (short) 4);
+    }
+
     /**
      * @return the icmpType
      */
@@ -79,8 +99,13 @@ public class ICMP extends BasePacket {
      *      -checksum : 0
      *      -length : 0
      */
+    @Override
     public byte[] serialize() {
-        int length = 4;
+        short padding = 0;
+        if (paddingMap.containsKey(this.icmpType))
+            padding = paddingMap.get(this.icmpType);
+
+        int length = 4 + padding;
         byte[] payloadData = null;
         if (payload != null) {
             payload.setParent(this);
@@ -94,6 +119,9 @@ public class ICMP extends BasePacket {
         bb.put(this.icmpType);
         bb.put(this.icmpCode);
         bb.putShort(this.checksum);
+        for (int i = 0; i < padding; i++)
+            bb.put((byte) 0);
+
         if (payloadData != null)
             bb.put(payloadData);
 
@@ -161,7 +189,13 @@ public class ICMP extends BasePacket {
         this.icmpType = bb.get();
         this.icmpCode = bb.get();
         this.checksum = bb.getShort();
-        
+
+        short padding = 0;
+        if (paddingMap.containsKey(this.icmpType))
+            padding = paddingMap.get(this.icmpType);
+
+        bb.position(bb.position() + padding);
+
         this.payload = new Data();
         this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position());
         this.payload.setParent(this);
diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java
index 76a140ed002bad4ab93db3488a771b2141223376..507cef88cb408c8af5c4301c3b5f925177061814 100644
--- a/src/main/java/net/floodlightcontroller/packet/IPv4.java
+++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java
@@ -58,6 +58,7 @@ public class IPv4 extends BasePacket {
     protected byte[] options;
 
     protected boolean isTruncated;
+    protected boolean isFragment;
 
     /**
      * Default constructor that sets the version to 4.
@@ -66,6 +67,7 @@ public class IPv4 extends BasePacket {
         super();
         this.version = 4;
         isTruncated = false;
+        isFragment = false;
     }
 
     /**
@@ -127,6 +129,14 @@ public class IPv4 extends BasePacket {
         this.isTruncated = isTruncated;
     }
 
+    public boolean isFragment() {
+        return isFragment;
+    }
+
+    public void setFragment(boolean isFrag) {
+        this.isFragment = isFrag;
+    }
+
     /**
      * @param identification the identification to set
      */
@@ -361,7 +371,8 @@ public class IPv4 extends BasePacket {
         }
 
         IPacket payload;
-        if (IPv4.protocolClassMap.containsKey(this.protocol)) {
+        isFragment = ((this.flags & 0x1) != 0) || (this.fragmentOffset != 0);
+        if (!isFragment && IPv4.protocolClassMap.containsKey(this.protocol)) {
             Class<? extends IPacket> clazz = IPv4.protocolClassMap.get(this.protocol);
             try {
                 payload = clazz.newInstance();
@@ -369,6 +380,11 @@ public class IPv4 extends BasePacket {
                 throw new RuntimeException("Error parsing payload for IPv4 packet", e);
             }
         } else {
+            if (log.isTraceEnabled() && isFragment) {
+                log.trace("IPv4 fragment detected {}->{}, forward using IP header only",
+                        fromIPv4Address(this.sourceAddress),
+                        fromIPv4Address(this.destinationAddress));
+            }
             payload = new Data();
         }
         int payloadLength = this.totalLength - this.headerLength * 4;
@@ -493,7 +509,7 @@ public class IPv4 extends BasePacket {
      * @return The IP address separated into bytes.
      */
     public static byte[] toIPv4AddressBytes(int ipAddress) {
-    	return new byte[] {
+        return new byte[] {
                 (byte)(ipAddress >>> 24),
                 (byte)(ipAddress >>> 16),
                 (byte)(ipAddress >>> 8),
diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java
index 5fbb38d748971197531c5046cbf913421916b1c8..ab09375486b9f61b2cfa98b83a5e9eeb483bd20b 100644
--- a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java
+++ b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -29,18 +29,23 @@ public interface IRoutingDecision {
         /*
          * NONE:                    NO-OP, continue with the packet processing chain
          * DROP:                    Drop this packet and this flow
-         * FORWARD:                 Forward this packet, and this flow, to the first (and only device) in getDestinationDevices(),
-         *                          if the destination is not known at this time, initiate a discovery action for it (e.g. ARP)
-         * FORWARD_OR_FLOOD:        Forward this packet, and this flow, to the first (and only device) in getDestinationDevices(),
-         *                          if the destination is not known at this time, flood this packet on the source switch
-         * MULTICAST:               Multicast this packet to all the interfaces and devices attached
+         * FORWARD:                 Forward this packet, and this flow, to the first
+         *                          (and only device) in getDestinationDevices(),
+         *                          if the destination is not known at this time,
+         *                          initiate a discovery action for it (e.g. ARP)
+         * FORWARD_OR_FLOOD:        Forward this packet, and this flow, to the first
+         *                          (and only device) in getDestinationDevices(),
+         *                          if the destination is not known at this time,
+         *                          flood this packet on the source switch
+         * MULTICAST:               Multicast this packet to all the interfaces
+         *                          and devices attached
          */
         NONE, DROP, FORWARD, FORWARD_OR_FLOOD, MULTICAST
     }
-    
+
     public static final FloodlightContextStore<IRoutingDecision> rtStore =
         new FloodlightContextStore<IRoutingDecision>();
-    public static final String CONTEXT_DECISION = 
+    public static final String CONTEXT_DECISION =
             "net.floodlightcontroller.routing.decision";
 
     public void addToContext(FloodlightContext cntx);
@@ -54,4 +59,6 @@ public interface IRoutingDecision {
     public void setMulticastInterfaces(List<SwitchPort> lspt);
     public Integer getWildcards();
     public void setWildcards(Integer wildcards);
+    public short getHardTimeout();
+    public void setHardTimeout(short hardTimeout);
 }
diff --git a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java
index 5fff3a2251333bd62e3125b661f3a8378bdccbca..aa4b3dde823ab75a38671e53767294d1964012f3 100644
--- a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java
+++ b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java
@@ -29,6 +29,7 @@ public class RoutingDecision implements IRoutingDecision {
 
     protected RoutingAction action;
     protected Integer wildcards;
+    protected short hardTimeout;
     protected SwitchPort srcPort;
     protected IDevice srcDevice;
     protected List<IDevice> destDevices;
@@ -46,6 +47,7 @@ public class RoutingDecision implements IRoutingDecision {
                 Collections.synchronizedList(new ArrayList<SwitchPort>());
         this.action = action;
         this.wildcards = null;
+        this.hardTimeout = ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT;
     }
     
     @Override
@@ -100,6 +102,16 @@ public class RoutingDecision implements IRoutingDecision {
         this.wildcards = wildcards;
     }
    
+    @Override
+    public short getHardTimeout() {
+        return hardTimeout;
+    }
+
+    @Override
+    public void setHardTimeout(short hardTimeout) {
+        this.hardTimeout = hardTimeout;
+    }
+
     @Override
     public void addToContext(FloodlightContext cntx) {
         rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION, this);
diff --git a/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java b/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java
index aae3962073e8b0aa6708b69d98e582bbd3cf5ae0..3033e99e4fd44eb799045b38f7b947ceddcf3c7b 100644
--- a/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java
+++ b/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java
@@ -46,6 +46,7 @@ import net.floodlightcontroller.storage.web.StorageWebRoutable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 @LogMessageCategory("System Database")
 public abstract class AbstractStorageSource 
     implements IStorageSourceService, IFloodlightModule {
@@ -460,6 +461,9 @@ public abstract class AbstractStorageSource
             		" storage listeners",
             recommendation=LogMessageDoc.GENERIC_ACTION)
     protected synchronized void notifyListeners(StorageSourceNotification notification) {
+        if (logger.isTraceEnabled()) {
+            logger.trace("Notifying storage listeneres: {}", notification);
+        }
         String tableName = notification.getTableName();
         Set<Object> keys = notification.getKeys();
         Set<IStorageSourceListener> tableListeners = listeners.get(tableName);
diff --git a/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java b/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java
index 8a69eca7f1920bd5995c451d90468314c8b45d20..ee4fac5ac0d49018b1bd7d4ecabd4366d4e621d4 100644
--- a/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java
+++ b/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java
@@ -70,7 +70,10 @@ public class MemoryStorageSource extends NoSqlStorageSource {
             Collection<Map<String,Object>> allRows = table.getAllRows();
             for (Map<String,Object> row : allRows) {
                 Object v = row.get(predicateColumnName);
-                if (value.equals(v)) {
+                if (value != null) {
+                    if ((v != null) && value.equals(v))
+                        result.add(row);
+                } else if (v == null) {
                     result.add(row);
                 }
             }
diff --git a/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java b/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java
index d7e5f956b4ddee76c68c99cc802a2fa447b19a00..8d19fdd95bf29256e4612f55f2cef4c04d7558b5 100644
--- a/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java
+++ b/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java
@@ -59,7 +59,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
     private Map<String, Map<String,ColumnIndexMode>> tableIndexedColumnMap =
         new HashMap<String,Map<String,ColumnIndexMode>>();
     
-    abstract class NoSqlPredicate {
+    abstract static class NoSqlPredicate {
 
         public boolean incorporateComparison(String columnName,
                 OperatorPredicate.Operator operator, Comparable<?> value,
@@ -80,7 +80,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
     }
     
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    class NoSqlRangePredicate extends NoSqlPredicate {
+    static class NoSqlRangePredicate extends NoSqlPredicate {
         NoSqlStorageSource storageSource;
         String tableName;
         String columnName;
@@ -320,7 +320,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
         }
     }
     
-    class NoSqlOperatorPredicate extends NoSqlPredicate {
+    static class NoSqlOperatorPredicate extends NoSqlPredicate {
         
         NoSqlStorageSource storageSource;
         String columnName;
@@ -354,16 +354,19 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
         }
     }
     
-    class NoSqlCompoundPredicate extends NoSqlPredicate {
+    static class NoSqlCompoundPredicate extends NoSqlPredicate {
         
         NoSqlStorageSource storageSource;
         CompoundPredicate.Operator operator;
         boolean negated;
+        String tableName;
         List<NoSqlPredicate> predicateList;
         
-        NoSqlCompoundPredicate(NoSqlStorageSource storageSource, CompoundPredicate.Operator operator,
-                boolean negated, List<NoSqlPredicate> predicateList) {
+        NoSqlCompoundPredicate(NoSqlStorageSource storageSource, String tableName,
+                CompoundPredicate.Operator operator, boolean negated,
+                List<NoSqlPredicate> predicateList) {
             this.storageSource = storageSource;
+            this.tableName = tableName;
             this.operator = operator;
             this.negated = negated;
             this.predicateList = predicateList;
@@ -404,7 +407,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
         }
 
         @SuppressWarnings({ "unchecked", "rawtypes" })
-        class RowComparator implements Comparator<Map<String,Object>> {
+        static class RowComparator implements Comparator<Map<String,Object>> {
             private String primaryKeyName;
             
             public RowComparator(String primaryKeyName) {
@@ -456,31 +459,48 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
                 }
                 if (update2) {
                     if (iterator2.hasNext()) {
-                        row2 = iterator1.next();
+                        row2 = iterator2.next();
                         key2 = (Comparable<?>)row2.get(primaryKeyName);
                     } else {
                         row2 = null;
                     }
                 }
+                update1 = update2 = false;
                 if (operator == CompoundPredicate.Operator.AND) {
                     if ((row1 == null) || (row2 == null))
                         break;
-                    if (key1.equals(key2))
+                    int result = ((Comparable)key1).compareTo(key2);
+                    if (result == 0) {
                         combinedRowList.add(row1);
+                        update1 = update2 = true;
+                    } else if (result < 0) {
+                        update1 = true;
+                    } else {
+                        update2 = true;
+                    }
                 } else {
-                    if (row1 == null) {
-                        if (row2 == null)
-                            break;
-                        combinedRowList.add(row2);
-                    } else if ((row2 == null) || (((Comparable)key1).compareTo(key2) <= 0)) {
+                    if (row1 != null) {
+                        if (row2 != null) {
+                            int result = ((Comparable)key1).compareTo(key2);
+                            if (result <= 0) {
+                                combinedRowList.add(row1);
+                                update1 = true;
+                                update2 = (result == 0);
+                            } else {
+                                combinedRowList.add(row2);
+                                update2 = true;
+                            }
+                        } else {
+                            combinedRowList.add(row1);
+                            update1 = true;
+                        }
+                    } else if (row2 != null) {
                         combinedRowList.add(row2);
+                        update2 = true;
                     } else {
-                        combinedRowList.add(row1);
+                        break;
                     }
                 }
-                
-                update1 = (key2 == null) || (((Comparable)key1).compareTo(key2) <= 0);
-                update2 = (key1 == null) || (((Comparable)key2).compareTo(key1) <= 0);
             }
             
             return combinedRowList;
@@ -488,15 +508,35 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
         
         public List<Map<String,Object>> execute(String columnNames[]) {
             List<Map<String,Object>> combinedRowList = null;
+            Set<NoSqlPredicate> inefficientPredicates = new HashSet<NoSqlPredicate>();
             for (NoSqlPredicate predicate: predicateList) {
-                List<Map<String,Object>> rowList = predicate.execute(columnNames);
-                if (combinedRowList != null) {
-                    combinedRowList = combineRowLists("id", combinedRowList, rowList, operator);
+                if (predicate.canExecuteEfficiently()) {
+                    List<Map<String,Object>> rowList = predicate.execute(columnNames);
+                    if (combinedRowList != null) {
+                        String primaryKeyName = storageSource.getTablePrimaryKeyName(tableName);
+                        combinedRowList = combineRowLists(primaryKeyName,
+                                combinedRowList, rowList, operator);
+                    } else {
+                        combinedRowList = rowList;
+                    }
                 } else {
-                    combinedRowList = rowList;
+                    inefficientPredicates.add(predicate);
                 }
             }
-            return combinedRowList;
+
+            if (inefficientPredicates.isEmpty())
+                return combinedRowList;
+
+            List<Map<String,Object>> filteredRowList = new ArrayList<Map<String,Object>>();
+            for (Map<String,Object> row: combinedRowList) {
+                for (NoSqlPredicate predicate: inefficientPredicates) {
+                    if (predicate.matchesRow(row)) {
+                        filteredRowList.add(row);
+                    }
+                }
+            }
+
+            return filteredRowList;
         }
 
         public boolean matchesRow(Map<String,Object> row) {
@@ -602,7 +642,8 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource {
                     noSqlPredicateList.add(noSqlPredicate);
                 }
             }
-            convertedPredicate = new NoSqlCompoundPredicate(this, compoundPredicate.getOperator(),
+            convertedPredicate = new NoSqlCompoundPredicate(this, tableName,
+                    compoundPredicate.getOperator(),
                     compoundPredicate.isNegated(), noSqlPredicateList);
         } else if (predicate instanceof OperatorPredicate) {
             OperatorPredicate operatorPredicate = (OperatorPredicate) predicate;
diff --git a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java
index 7bab984abdc3b6bd79dbc007d8ad0c537d6d17bd..8711551b82e321b8aa261845dae3e24fefba3552 100644
--- a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java
+++ b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java
@@ -29,7 +29,8 @@ import org.openflow.util.HexString;
  * to the actual objects.
  * @author srini
  */
-public class NodePortTuple {
+
+public class NodePortTuple implements Comparable<NodePortTuple> {
     protected long nodeId; // switch DPID
     protected short portId; // switch port id
 
@@ -93,7 +94,7 @@ public class NodePortTuple {
             return false;
         return true;
     }
-    
+
     /**
      * API to return a String value formed wtih NodeID and PortID
      * The portID is a 16-bit field, so mask it as an integer to get full
@@ -103,4 +104,23 @@ public class NodePortTuple {
     public String toKeyString() {
         return (HexString.toHexString(nodeId)+ "|" + (portId & 0xffff));
     }
+
+    @Override
+    public int compareTo(NodePortTuple obj) {
+        final int BEFORE = -1;
+        final int EQUAL = 0;
+        final int AFTER = 1;
+
+        if (this.getNodeId() < obj.getNodeId())
+            return BEFORE;
+        if (this.getNodeId() > obj.getNodeId())
+            return AFTER;
+
+        if (this.getPortId() < obj.getPortId())
+            return BEFORE;
+        if (this.getPortId() > obj.getPortId())
+            return AFTER;
+
+        return EQUAL;
+    }
 }
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
index 7bc878614d124540d3165d42f7fa35088caf78e8..af49de70bbb6ca8f29426bc370d5b343fdc30442 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
@@ -19,7 +19,6 @@ package net.floodlightcontroller.topology;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -37,10 +36,10 @@ import net.floodlightcontroller.routing.BroadcastTree;
 import net.floodlightcontroller.routing.Link;
 import net.floodlightcontroller.routing.Route;
 import net.floodlightcontroller.routing.RouteId;
-import net.floodlightcontroller.util.LRUHashMap;
+import com.google.common.cache.*;
 
 /**
- * A representation of a network topology.  Used internally by 
+ * A representation of a network topology.  Used internally by
  * {@link TopologyManager}
  */
 @LogMessageCategory("Network Topology")
@@ -48,7 +47,7 @@ public class TopologyInstance {
 
     public static final short LT_SH_LINK = 1;
     public static final short LT_BD_LINK = 2;
-    public static final short LT_TUNNEL  = 3; 
+    public static final short LT_TUNNEL  = 3;
 
     public static final int MAX_LINK_WEIGHT = 10000;
     public static final int MAX_PATH_WEIGHT = Integer.MAX_VALUE - MAX_LINK_WEIGHT - 1;
@@ -61,7 +60,7 @@ public class TopologyInstance {
      * switch ports may be provided at the time of instantiation. In addition,
      * we may add additional ports to this set.
      */
-    protected Set<NodePortTuple> blockedPorts;  
+    protected Set<NodePortTuple> blockedPorts;
     protected Map<NodePortTuple, Set<Link>> switchPortLinks; // Set of links organized by node port tuple
     /** Set of links that are blocked. */
     protected Set<Link> blockedLinks;
@@ -77,7 +76,23 @@ public class TopologyInstance {
     protected Map<Long, BroadcastTree> destinationRootedTrees;
     protected Map<Long, Set<NodePortTuple>> clusterBroadcastNodePorts;
     protected Map<Long, BroadcastTree> clusterBroadcastTrees;
-    protected LRUHashMap<RouteId, Route> pathcache;
+
+    protected class PathCacheLoader extends CacheLoader<RouteId, Route> {
+        TopologyInstance ti;
+        PathCacheLoader(TopologyInstance ti) {
+            this.ti = ti;
+        }
+
+        @Override
+        public Route load(RouteId rid) {
+            return ti.buildroute(rid);
+        }
+    }
+
+    // Path cache loader is defined for loading a path when it not present
+    // in the cache.
+    private final PathCacheLoader pathCacheLoader = new PathCacheLoader(this);
+    protected LoadingCache<RouteId, Route> pathcache;
 
     public TopologyInstance() {
         this.switches = new HashSet<Long>();
@@ -95,7 +110,7 @@ public class TopologyInstance {
     {
         this.switches = new HashSet<Long>(switchPorts.keySet());
         this.switchPorts = new HashMap<Long, Set<Short>>(switchPorts);
-        this.switchPortLinks = new HashMap<NodePortTuple, 
+        this.switchPortLinks = new HashMap<NodePortTuple,
                 Set<Link>>(switchPortLinks);
         this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts);
         this.tunnelPorts = new HashSet<NodePortTuple>();
@@ -121,7 +136,7 @@ public class TopologyInstance {
         this.blockedPorts = new HashSet<NodePortTuple>(blockedPorts);
         this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
         for(NodePortTuple npt: switchPortLinks.keySet()) {
-            this.switchPortLinks.put(npt, 
+            this.switchPortLinks.put(npt,
                                      new HashSet<Link>(switchPortLinks.get(npt)));
         }
         this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts);
@@ -133,7 +148,15 @@ public class TopologyInstance {
         destinationRootedTrees = new HashMap<Long, BroadcastTree>();
         clusterBroadcastTrees = new HashMap<Long, BroadcastTree>();
         clusterBroadcastNodePorts = new HashMap<Long, Set<NodePortTuple>>();
-        pathcache = new LRUHashMap<RouteId, Route>(PATH_CACHE_SIZE);
+
+        pathcache = CacheBuilder.newBuilder().concurrencyLevel(4)
+                    .maximumSize(1000L)
+                    .build(
+                            new CacheLoader<RouteId, Route>() {
+                                public Route load(RouteId rid) {
+                                    return pathCacheLoader.load(rid);
+                                }
+                            });
     }
 
     public void compute() {
@@ -143,23 +166,19 @@ public class TopologyInstance {
         // Must ignore blocked links.
         identifyOpenflowDomains();
 
-        // Step 0: Remove all links connected to blocked ports.
-        // removeLinksOnBlockedPorts();
-
-
         // Step 1.1: Add links to clusters
         // Avoid adding blocked links to clusters
         addLinksToOpenflowDomains();
 
-        // Step 2. Compute shortest path trees in each cluster for 
+        // Step 2. Compute shortest path trees in each cluster for
         // unicast routing.  The trees are rooted at the destination.
         // Cost for tunnel links and direct links are the same.
         calculateShortestPathTreeInClusters();
 
         // Step 3. Compute broadcast tree in each cluster.
-        // Cost for tunnel links are high to discourage use of 
+        // Cost for tunnel links are high to discourage use of
         // tunnel links.  The cost is set to the number of nodes
-        // in the cluster + 1, to use as minimum number of 
+        // in the cluster + 1, to use as minimum number of
         // clusters as possible.
         calculateBroadcastNodePortsInClusters();
 
@@ -268,7 +287,6 @@ public class TopologyInstance {
      * @param currSet: Set of nodes in the current cluster in formation
      * @return long: DSF index to be used when a new node is visited
      */
-
     private long dfsTraverse (long parentIndex, long currIndex, long currSw,
                               Map<Long, ClusterDFS> dfsList, Set <Long> currSet) {
 
@@ -296,7 +314,7 @@ public class TopologyInstance {
                     // ignore incoming links.
                     if (dstSw == currSw) continue;
 
-                    // ignore if the destination is already added to 
+                    // ignore if the destination is already added to
                     // another cluster
                     if (switchClusterMap.get(dstSw) != null) continue;
 
@@ -359,41 +377,6 @@ public class TopologyInstance {
         return currIndex;
     }
 
-    /**
-     *  Go through every link and identify it is a blocked link or not.
-     *  If blocked, remove it from the switchport links and put them in the
-     *  blocked link category.
-     *
-     *  Note that we do not update the tunnel ports and broadcast domain 
-     *  port structures.  We need those to still answer the question if the
-     *  ports are tunnel or broadcast domain ports.
-     *
-     *  If we add additional ports to blocked ports later on, we may simply
-     *  call this method again to remove the links on the newly blocked ports.
-     */
-    protected void removeLinksOnBlockedPorts() {
-        Iterator<NodePortTuple> nptIter;
-        Iterator<Link> linkIter;
-
-        // Iterate through all the links and all the switch ports
-        // and move the links on blocked switch ports to blocked links
-        nptIter = this.switchPortLinks.keySet().iterator();
-        while (nptIter.hasNext()) {
-            NodePortTuple npt = nptIter.next();
-            linkIter = switchPortLinks.get(npt).iterator();
-            while (linkIter.hasNext()) {
-                Link link = linkIter.next();
-                if (isBlockedLink(link)) {
-                    this.blockedLinks.add(link);
-                    linkIter.remove();
-                }
-            }
-            // Note that at this point, the switchport may have
-            // no links in it.  We could delete the switch port, 
-            // but we will leave it as is.
-        }
-    }
-
     public Set<NodePortTuple> getBlockedPorts() {
         return this.blockedPorts;
     }
@@ -402,7 +385,7 @@ public class TopologyInstance {
         return this.blockedLinks;
     }
 
-    /** Returns true if a link has either one of its switch ports 
+    /** Returns true if a link has either one of its switch ports
      * blocked.
      * @param l
      * @return
@@ -438,12 +421,12 @@ public class TopologyInstance {
     }
 
     protected class NodeDist implements Comparable<NodeDist> {
-        private Long node;
+        private final Long node;
         public Long getNode() {
             return node;
         }
 
-        private int dist; 
+        private final int dist;
         public int getDist() {
             return dist;
         }
@@ -453,6 +436,7 @@ public class TopologyInstance {
             this.dist = dist;
         }
 
+        @Override
         public int compareTo(NodeDist o) {
             if (o.dist == this.dist) {
                 return (int)(this.node - o.node);
@@ -490,7 +474,7 @@ public class TopologyInstance {
         }
     }
 
-    protected BroadcastTree dijkstra(Cluster c, Long root, 
+    protected BroadcastTree dijkstra(Cluster c, Long root,
                                      Map<Link, Integer> linkCost,
                                      boolean isDstRooted) {
         HashMap<Long, Link> nexthoplinks = new HashMap<Long, Link>();
@@ -551,7 +535,7 @@ public class TopologyInstance {
     }
 
     protected void calculateShortestPathTreeInClusters() {
-        pathcache.clear();
+        pathcache.invalidateAll();
         destinationRootedTrees.clear();
 
         Map<Link, Integer> linkCost = new HashMap<Link, Integer>();
@@ -607,8 +591,10 @@ public class TopologyInstance {
         }
     }
 
-    protected Route buildroute(RouteId id, long srcId, long dstId) {
+    protected Route buildroute(RouteId id) {
         NodePortTuple npt;
+        long srcId = id.getSrc();
+        long dstId = id.getDst();
 
         LinkedList<NodePortTuple> switchPorts =
                 new LinkedList<NodePortTuple>();
@@ -642,7 +628,7 @@ public class TopologyInstance {
         // else, no path exists, and path equals null
 
         Route result = null;
-        if (switchPorts != null && !switchPorts.isEmpty()) 
+        if (switchPorts != null && !switchPorts.isEmpty())
             result = new Route(id, switchPorts);
         if (log.isTraceEnabled()) {
             log.trace("buildroute: {}", result);
@@ -656,7 +642,7 @@ public class TopologyInstance {
         return (bt.getCost(srcId));
     }
 
-    /* 
+    /*
      * Getter Functions
      */
 
@@ -702,15 +688,23 @@ public class TopologyInstance {
         return r;
     }
 
+    // NOTE: Return a null route if srcId equals dstId.  The null route
+    // need not be stored in the cache.  Moreover, the LoadingCache will
+    // throw an exception if null route is returned.
     protected Route getRoute(long srcId, long dstId, long cookie) {
+        // Return null route if srcId equals dstId
+        if (srcId == dstId) return null;
+
+
         RouteId id = new RouteId(srcId, dstId);
         Route result = null;
-        if (pathcache.containsKey(id)) {
+
+        try {
             result = pathcache.get(id);
-        } else {
-            result = buildroute(id, srcId, dstId);
-            pathcache.put(id, result);
+        } catch (Exception e) {
+            log.error("{}", e);
         }
+
         if (log.isTraceEnabled()) {
             log.trace("getRoute: {} -> {}", id, result);
         }
@@ -723,9 +717,9 @@ public class TopologyInstance {
         return clusterBroadcastTrees.get(c.id);
     }
 
-    // 
+    //
     //  ITopologyService interface method helpers.
-    // 
+    //
 
     protected boolean isInternalToOpenflowDomain(long switchid, short port) {
         return !isAttachmentPointPort(switchid, port);
@@ -811,7 +805,7 @@ public class TopologyInstance {
 
     public NodePortTuple getIncomingSwitchPort(long src, short srcPort,
                                                long dst, short dstPort) {
-        // Use this function to reinject traffic from a 
+        // Use this function to reinject traffic from a
         // different port if needed.
         return new NodePortTuple(src, srcPort);
     }
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
index ed85b57acd205086ce948641d6b53cf09b8ae596..9bc0f030f89e5f94aa3bf56844683f58f102a912 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
@@ -68,21 +68,21 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Topology manager is responsible for maintaining the controller's notion
- * of the network graph, as well as implementing tools for finding routes 
+ * of the network graph, as well as implementing tools for finding routes
  * through the topology.
  */
 @LogMessageCategory("Network Topology")
-public class TopologyManager implements 
-        IFloodlightModule, ITopologyService, 
+public class TopologyManager implements
+        IFloodlightModule, ITopologyService,
         IRoutingService, ILinkDiscoveryListener,
         IOFMessageListener, IHAListener {
 
     protected static Logger log = LoggerFactory.getLogger(TopologyManager.class);
 
-    public static final String CONTEXT_TUNNEL_ENABLED = 
+    public static final String CONTEXT_TUNNEL_ENABLED =
             "com.bigswitch.floodlight.topologymanager.tunnelEnabled";
 
-    /** 
+    /**
      * Set of ports for each switch
      */
     protected Map<Long, Set<Short>> switchPorts;
@@ -171,7 +171,7 @@ public class TopologyManager implements
             		"discovery module.",
             recommendation=LogMessageDoc.CHECK_CONTROLLER)
     protected class UpdateTopologyWorker implements Runnable {
-        @Override 
+        @Override
         public void run() {
             try {
                 if (ldUpdates.peek() != null)
@@ -223,7 +223,7 @@ public class TopologyManager implements
         }
         ldUpdates.add(update);
     }
-    
+
     // ****************
     // ITopologyService
     // ****************
@@ -241,16 +241,16 @@ public class TopologyManager implements
         topologyAware.add(listener);
     }
 
-    @Override 
+    @Override
     public boolean isAttachmentPointPort(long switchid, short port) {
         return isAttachmentPointPort(switchid, port, true);
     }
 
     @Override
-    public boolean isAttachmentPointPort(long switchid, short port, 
+    public boolean isAttachmentPointPort(long switchid, short port,
                                          boolean tunnelEnabled) {
 
-        // If the switch port is a tunnel endpoint, it is not
+        // If the switch port is 'tun-bsn' port, it is not
         // an attachment point port, irrespective of whether
         // a link is found through it or not.
         if (linkDiscovery.isTunnelPort(switchid, port))
@@ -273,10 +273,12 @@ public class TopologyManager implements
         return (sw.portEnabled(port));
     }
 
+    @Override
     public long getOpenflowDomainId(long switchId) {
         return getOpenflowDomainId(switchId, true);
     }
 
+    @Override
     public long getOpenflowDomainId(long switchId, boolean tunnelEnabled) {
         TopologyInstance ti = getCurrentInstance(tunnelEnabled);
         return ti.getOpenflowDomainId(switchId);
@@ -323,6 +325,7 @@ public class TopologyManager implements
         return isIncomingBroadcastAllowed(sw, portId, true);
     }
 
+    @Override
     public boolean isIncomingBroadcastAllowed(long sw, short portId,
                                               boolean tunnelEnabled) {
         TopologyInstance ti = getCurrentInstance(tunnelEnabled);
@@ -346,20 +349,22 @@ public class TopologyManager implements
 
     ////////////////////////////////////////////////////////////////////////
     ////////////////////////////////////////////////////////////////////////
-    /** Get all the ports on the target switch (targetSw) on which a 
+    /** Get all the ports on the target switch (targetSw) on which a
      * broadcast packet must be sent from a host whose attachment point
      * is on switch port (src, srcPort).
      */
-    public Set<Short> getBroadcastPorts(long targetSw, 
+    @Override
+    public Set<Short> getBroadcastPorts(long targetSw,
                                         long src, short srcPort) {
         return getBroadcastPorts(targetSw, src, srcPort, true);
     }
 
-    /** Get all the ports on the target switch (targetSw) on which a 
+    /** Get all the ports on the target switch (targetSw) on which a
      * broadcast packet must be sent from a host whose attachment point
      * is on switch port (src, srcPort).
      */
-    public Set<Short> getBroadcastPorts(long targetSw, 
+    @Override
+    public Set<Short> getBroadcastPorts(long targetSw,
                                         long src, short srcPort,
                                         boolean tunnelEnabled) {
         TopologyInstance ti = getCurrentInstance(tunnelEnabled);
@@ -374,7 +379,7 @@ public class TopologyManager implements
         // Use this function to redirect traffic if needed.
         return getOutgoingSwitchPort(src, srcPort, dst, dstPort, true);
     }
-    
+
     @Override
     public NodePortTuple getOutgoingSwitchPort(long src, short srcPort,
                                                long dst, short dstPort,
@@ -502,13 +507,13 @@ public class TopologyManager implements
     ////////////////////////////////////////////////////////////////////////
     ////////////////////////////////////////////////////////////////////////
     @Override
-    public NodePortTuple 
+    public NodePortTuple
     getAllowedIncomingBroadcastPort(long src, short srcPort) {
         return getAllowedIncomingBroadcastPort(src,srcPort, true);
     }
 
     @Override
-    public NodePortTuple 
+    public NodePortTuple
     getAllowedIncomingBroadcastPort(long src, short srcPort,
                                     boolean tunnelEnabled) {
         TopologyInstance ti = getCurrentInstance(tunnelEnabled);
@@ -541,6 +546,7 @@ public class TopologyManager implements
         return tunnelPorts;
     }
 
+    @Override
     public Set<NodePortTuple> getBlockedPorts() {
         Set<NodePortTuple> bp;
         Set<NodePortTuple> blockedPorts =
@@ -587,12 +593,12 @@ public class TopologyManager implements
     }
 
     @Override
-    public Route getRoute(long src, short srcPort, long dst, short dstPort, long cookie, 
+    public Route getRoute(long src, short srcPort, long dst, short dstPort, long cookie,
                           boolean tunnelEnabled) {
         TopologyInstance ti = getCurrentInstance(tunnelEnabled);
         return ti.getRoute(src, srcPort, dst, dstPort, cookie);
     }
-    
+
     @Override
     public boolean routeExists(long src, long dst) {
         return routeExists(src, dst, true);
@@ -604,6 +610,16 @@ public class TopologyManager implements
         return ti.routeExists(src, dst);
     }
 
+    @Override
+    public ArrayList<Route> getRoutes(long srcDpid, long dstDpid,
+                                      boolean tunnelEnabled) {
+        // Floodlight supports single path routing now
+
+        // return single path now
+        ArrayList<Route> result=new ArrayList<Route>();
+        result.add(getRoute(srcDpid, dstDpid, 0, tunnelEnabled));
+        return result;
+    }
 
     // ******************
     // IOFMessageListener
@@ -629,10 +645,10 @@ public class TopologyManager implements
                            FloodlightContext cntx) {
         switch (msg.getType()) {
             case PACKET_IN:
-                return this.processPacketInMessage(sw, 
+                return this.processPacketInMessage(sw,
                                                    (OFPacketIn) msg, cntx);
             default:
-            	break;
+                break;
         }
 
         return Command.CONTINUE;
@@ -678,7 +694,7 @@ public class TopologyManager implements
 
     @Override
     public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-        Collection<Class<? extends IFloodlightService>> l = 
+        Collection<Class<? extends IFloodlightService>> l =
                 new ArrayList<Class<? extends IFloodlightService>>();
         l.add(ITopologyService.class);
         l.add(IRoutingService.class);
@@ -689,7 +705,7 @@ public class TopologyManager implements
     public Map<Class<? extends IFloodlightService>, IFloodlightService>
             getServiceImpls() {
         Map<Class<? extends IFloodlightService>,
-        IFloodlightService> m = 
+        IFloodlightService> m =
             new HashMap<Class<? extends IFloodlightService>,
                 IFloodlightService>();
         // We are the class that implements the service
@@ -699,9 +715,9 @@ public class TopologyManager implements
     }
 
     @Override
-    public Collection<Class<? extends IFloodlightService>> 
+    public Collection<Class<? extends IFloodlightService>>
             getModuleDependencies() {
-        Collection<Class<? extends IFloodlightService>> l = 
+        Collection<Class<? extends IFloodlightService>> l =
                 new ArrayList<Class<? extends IFloodlightService>>();
         l.add(ILinkDiscoveryService.class);
         l.add(IThreadPoolService.class);
@@ -716,7 +732,7 @@ public class TopologyManager implements
             throws FloodlightModuleException {
         linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
         threadPool = context.getServiceImpl(IThreadPoolService.class);
-        floodlightProvider = 
+        floodlightProvider =
                 context.getServiceImpl(IFloodlightProviderService.class);
         restApi = context.getServiceImpl(IRestApiService.class);
 
@@ -776,13 +792,10 @@ public class TopologyManager implements
                 result = Command.STOP;
             }
         }
-
-        // if sufficient information is available, then drop broadcast
-        // packets here as well.
         return result;
     }
 
-    /** 
+    /**
      * TODO This method must be moved to a layer below forwarding
      * so that anyone can use it.
      * @param packetData
@@ -795,14 +808,14 @@ public class TopologyManager implements
             explanation="An I/O error occured while trying send " +
             		"topology discovery packet",
             recommendation=LogMessageDoc.CHECK_SWITCH)
-    public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw, 
+    public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw,
                                        Set<Short> ports,
                                        FloodlightContext cntx) {
 
         if (ports == null) return;
         if (packetData == null || packetData.length <= 0) return;
 
-        OFPacketOut po = 
+        OFPacketOut po =
                 (OFPacketOut) floodlightProvider.getOFMessageFactory().
                 getMessage(OFType.PACKET_OUT);
 
@@ -814,7 +827,7 @@ public class TopologyManager implements
         // set actions
         po.setActions(actions);
         // set action length
-        po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * 
+        po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH *
                 ports.size()));
         // set buffer-id to BUFFER_ID_NONE
         po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
@@ -825,8 +838,8 @@ public class TopologyManager implements
         po.setPacketData(packetData);
 
         // compute and set packet length.
-        short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + 
-                po.getActionsLength() + 
+        short poLength = (short)(OFPacketOut.MINIMUM_LENGTH +
+                po.getActionsLength() +
                 packetData.length);
 
         po.setLength(poLength);
@@ -834,7 +847,7 @@ public class TopologyManager implements
         try {
             //counterStore.updatePktOutFMCounterStore(sw, po);
             if (log.isTraceEnabled()) {
-                log.trace("write broadcast packet on switch-id={} " + 
+                log.trace("write broadcast packet on switch-id={} " +
                         "interaces={} packet-data={} packet-out={}",
                         new Object[] {sw.getId(), ports, packetData, po});
             }
@@ -870,13 +883,13 @@ public class TopologyManager implements
     /**
      * The BDDP packets are forwarded out of all the ports out of an
      * openflowdomain.  Get all the switches in the same openflow
-     * domain as the sw (disabling tunnels).  Then get all the 
+     * domain as the sw (disabling tunnels).  Then get all the
      * external switch ports and send these packets out.
      * @param sw
      * @param pi
      * @param cntx
      */
-    protected void doFloodBDDP(long pinSwitch, OFPacketIn pi, 
+    protected void doFloodBDDP(long pinSwitch, OFPacketIn pi,
                                FloodlightContext cntx) {
 
         TopologyInstance ti = getCurrentInstance(false);
@@ -900,13 +913,13 @@ public class TopologyManager implements
             ports.addAll(enabledPorts);
 
             // all the ports known to topology // without tunnels.
-            // out of these, we need to choose only those that are 
+            // out of these, we need to choose only those that are
             // broadcast port, otherwise, we should eliminate.
             Set<Short> portsKnownToTopo = ti.getPortsWithLinks(sid);
 
             if (portsKnownToTopo != null) {
                 for(short p: portsKnownToTopo) {
-                    NodePortTuple npt = 
+                    NodePortTuple npt =
                             new NodePortTuple(sid, p);
                     if (ti.isBroadcastDomainPort(npt) == false) {
                         ports.remove(p);
@@ -930,11 +943,11 @@ public class TopologyManager implements
 
     }
 
-    protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, 
+    protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi,
                                              FloodlightContext cntx) {
 
         // get the packet-in switch.
-        Ethernet eth = 
+        Ethernet eth =
                 IFloodlightProviderService.bcStore.
                 get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
 
@@ -949,13 +962,12 @@ public class TopologyManager implements
                 return Command.CONTINUE;
 
             doFloodBDDP(sw.getId(), pi, cntx);
+            return Command.STOP;
         } else {
             return dropFilter(sw.getId(), pi, cntx);
         }
-        return Command.STOP;
     }
 
-
     /**
      * Updates concerning switch disconnect and port down are not processed.
      * LinkDiscoveryManager is expected to process those messages and send
@@ -983,7 +995,7 @@ public class TopologyManager implements
                                 update.getDst(), update.getDstPort(),
                                 update.getType());
             } else if (update.getOperation() == UpdateOperation.LINK_REMOVED){
-                removeLink(update.getSrc(), update.getSrcPort(), 
+                removeLink(update.getSrc(), update.getSrcPort(),
                            update.getDst(), update.getDstPort());
             } else if (update.getOperation() == UpdateOperation.TUNNEL_PORT_ADDED) {
                 addTunnelPort(update.getSrc(), update.getSrcPort());
@@ -1007,9 +1019,6 @@ public class TopologyManager implements
         tunnelPortsUpdated = true;
     }
 
-    /**
-     * This function computes a new topology.
-     */
     /**
      * This function computes a new topology instance.
      * It ignores links connected to all broadcast domain ports
@@ -1022,7 +1031,7 @@ public class TopologyManager implements
         if (!linksUpdated) return false;
 
         Map<NodePortTuple, Set<Link>> openflowLinks;
-        openflowLinks = 
+        openflowLinks =
                 new HashMap<NodePortTuple, Set<Link>>();
         Set<NodePortTuple> nptList = switchPortLinks.keySet();
 
@@ -1056,9 +1065,9 @@ public class TopologyManager implements
             }
         }
 
-        TopologyInstance nt = new TopologyInstance(switchPorts, 
+        TopologyInstance nt = new TopologyInstance(switchPorts,
                                                    blockedPorts,
-                                                   openflowLinks, 
+                                                   openflowLinks,
                                                    broadcastDomainPorts,
                                                    tunnelPorts);
         nt.compute();
@@ -1162,7 +1171,7 @@ public class TopologyManager implements
     }
 
     public boolean removeSwitch(long sid) {
-        // Delete all the links in the switch, switch and all 
+        // Delete all the links in the switch, switch and all
         // associated data should be deleted.
         if (switchPorts.containsKey(sid) == false) return false;
 
@@ -1187,9 +1196,9 @@ public class TopologyManager implements
      * @param l
      * @return
      */
-    private boolean addLinkToStructure(Map<NodePortTuple, 
+    private boolean addLinkToStructure(Map<NodePortTuple,
                                        Set<Link>> s, Link l) {
-        boolean result1 = false, result2 = false; 
+        boolean result1 = false, result2 = false;
 
         NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
         NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort());
@@ -1213,7 +1222,7 @@ public class TopologyManager implements
      * @param l
      * @return
      */
-    private boolean removeLinkFromStructure(Map<NodePortTuple, 
+    private boolean removeLinkFromStructure(Map<NodePortTuple,
                                             Set<Link>> s, Link l) {
 
         boolean result1 = false, result2 = false;
@@ -1236,7 +1245,7 @@ public class TopologyManager implements
         // If you need to handle tunnel links, this is a placeholder.
     }
 
-    public void addOrUpdateLink(long srcId, short srcPort, long dstId, 
+    public void addOrUpdateLink(long srcId, short srcPort, long dstId,
                                 short dstPort, LinkType type) {
         Link link = new Link(srcId, srcPort, dstId, dstPort);
 
@@ -1268,9 +1277,9 @@ public class TopologyManager implements
         removeLinkFromStructure(portBroadcastDomainLinks, link);
         removeLinkFromStructure(switchPortLinks, link);
 
-        NodePortTuple srcNpt = 
+        NodePortTuple srcNpt =
                 new NodePortTuple(link.getSrc(), link.getSrcPort());
-        NodePortTuple dstNpt = 
+        NodePortTuple dstNpt =
                 new NodePortTuple(link.getDst(), link.getDstPort());
 
         // Remove switch ports if there are no links through those switch ports
@@ -1284,11 +1293,11 @@ public class TopologyManager implements
         }
 
         // Remove the node if no ports are present
-        if (switchPorts.get(srcNpt.getNodeId())!=null && 
+        if (switchPorts.get(srcNpt.getNodeId())!=null &&
                 switchPorts.get(srcNpt.getNodeId()).isEmpty()) {
             switchPorts.remove(srcNpt.getNodeId());
         }
-        if (switchPorts.get(dstNpt.getNodeId())!=null && 
+        if (switchPorts.get(dstNpt.getNodeId())!=null &&
                 switchPorts.get(dstNpt.getNodeId()).isEmpty()) {
             switchPorts.remove(dstNpt.getNodeId());
         }
@@ -1350,6 +1359,7 @@ public class TopologyManager implements
     /**
      *  Switch methods
      */
+    @Override
     public Set<Short> getPorts(long sw) {
         Set<Short> ports = new HashSet<Short>();
         IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
@@ -1366,14 +1376,5 @@ public class TopologyManager implements
         return ports;
     }
 
-    @Override
-    public ArrayList<Route> getRoutes(long srcDpid, long dstDpid,
-                                      boolean tunnelEnabled) {
-        // Floodlight supports single path routing now
-        
-        // return single path now
-        ArrayList<Route> result=new ArrayList<Route>();
-        result.add(getRoute(srcDpid, dstDpid, 0, tunnelEnabled));
-        return result;
-    }
+
 }
diff --git a/src/main/java/org/openflow/protocol/OFFlowRemoved.java b/src/main/java/org/openflow/protocol/OFFlowRemoved.java
index 4f4e0c7ff92acbd9a716c5301bdcd93a06335ae4..33570063c73850cf995606890ac0c3e9e97f67f5 100644
--- a/src/main/java/org/openflow/protocol/OFFlowRemoved.java
+++ b/src/main/java/org/openflow/protocol/OFFlowRemoved.java
@@ -194,7 +194,11 @@ public class OFFlowRemoved extends OFMessage {
         this.match.readFrom(data);
         this.cookie = data.readLong();
         this.priority = data.readShort();
-        this.reason = OFFlowRemovedReason.values()[(0xff & data.readByte())];
+        int reasonIndex = (int)(0xff & data.readByte());
+        if (reasonIndex >= OFFlowRemovedReason.values().length) {
+            reasonIndex = OFFlowRemovedReason.values().length - 1;
+        }
+        this.reason = OFFlowRemovedReason.values()[reasonIndex];
         data.readByte(); // pad
         this.durationSeconds = data.readInt();
         this.durationNanoseconds = data.readInt();
diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
index f6f94f880fda7904abd0cc68580094c9713f4971..fd373279e85f151de765bd4c5cfc7af5de3fac68 100644
--- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
+++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java
@@ -1,7 +1,7 @@
 /**
-*    Copyright 2011, Big Switch Networks, Inc. 
+*    Copyright 2011, Big Switch Networks, Inc.
 *    Originally created by David Erickson, Stanford University
-* 
+*
 *    Licensed under the Apache License, Version 2.0 (the "License"); you may
 *    not use this file except in compliance with the License. You may obtain
 *    a copy of the License at
@@ -71,6 +71,7 @@ import org.easymock.EasyMock;
 import org.jboss.netty.channel.Channel;
 import org.junit.Test;
 import org.openflow.protocol.OFError;
+import org.openflow.protocol.OFError.OFBadRequestCode;
 import org.openflow.protocol.OFError.OFErrorType;
 import org.openflow.protocol.OFFeaturesReply;
 import org.openflow.protocol.OFMessage;
@@ -101,7 +102,7 @@ import org.openflow.vendor.nicira.OFRoleReplyVendorData;
  */
 public class ControllerTest extends FloodlightTestCase
         implements IOFSwitchDriver {
-   
+
     private Controller controller;
     private MockThreadPoolService tp;
     private boolean test_bind_order = false;
@@ -111,26 +112,26 @@ public class ControllerTest extends FloodlightTestCase
     public void setUp() throws Exception {
         super.setUp();
         FloodlightModuleContext fmc = new FloodlightModuleContext();
-        
+
         FloodlightProvider cm = new FloodlightProvider();
         controller = (Controller)cm.getServiceImpls().get(IFloodlightProviderService.class);
         fmc.addService(IFloodlightProviderService.class, controller);
-        
+
         MemoryStorageSource memstorage = new MemoryStorageSource();
         fmc.addService(IStorageSourceService.class, memstorage);
-        
+
         RestApiServer restApi = new RestApiServer();
         fmc.addService(IRestApiService.class, restApi);
-        
+
         CounterStore cs = new CounterStore();
         fmc.addService(ICounterStoreService.class, cs);
-        
+
         PktInProcessingTime ppt = new PktInProcessingTime();
         fmc.addService(IPktInProcessingTimeService.class, ppt);
-        
+
         tp = new MockThreadPoolService();
         fmc.addService(IThreadPoolService.class, tp);
-        
+
         ppt.init(fmc);
         restApi.init(fmc);
         memstorage.init(fmc);
@@ -161,15 +162,15 @@ public class ControllerTest extends FloodlightTestCase
             sr.setFlags((short) 1);
         return sr;
     }
-    
+
     /* Set the mock expectations for sw when sw is passed to addSwitch */
     protected void setupSwitchForAddSwitch(IOFSwitch sw, long dpid) {
         String dpidString = HexString.toHexString(dpid);
-                
+
         expect(sw.getId()).andReturn(dpid).anyTimes();
         expect(sw.getStringId()).andReturn(dpidString).anyTimes();
     }
-    
+
     /**
      * Run the controller's main loop so that updates are processed
      */
@@ -213,7 +214,7 @@ public class ControllerTest extends FloodlightTestCase
                 .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
         byte[] testPacketSerialized = testPacket.serialize();
 
-        // Build the PacketIn        
+        // Build the PacketIn
         OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN))
                 .setBufferId(-1)
                 .setInPort((short) 1)
@@ -244,7 +245,7 @@ public class ControllerTest extends FloodlightTestCase
 
         // verify STOP works
         reset(test1, test2, sw);
-        expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class))).andReturn(Command.STOP);       
+        expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class))).andReturn(Command.STOP);
         expect(sw.getId()).andReturn(0L).anyTimes();
         expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
         replay(test1, test2, sw);
@@ -285,7 +286,7 @@ public class ControllerTest extends FloodlightTestCase
     }
 
     /**
-     * 
+     *
      * @throws Exception
      */
     @Test
@@ -373,7 +374,7 @@ public class ControllerTest extends FloodlightTestCase
         fmCntx.addService(IFloodlightProviderService.class, mfp);
         fmCntx.addService(IThreadPoolService.class, mtp);
         String sid = null;
-        
+
         mfm.init(fmCntx);
         mfm.startUp(fmCntx);
 
@@ -396,14 +397,14 @@ public class ControllerTest extends FloodlightTestCase
         assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize());
 
         // Deleting the filter works.
-        mfm.setupFilter(sid, null, -1);        
+        mfm.setupFilter(sid, null, -1);
         assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize()-1);
 
-        // Creating mock switch to which we will send packet out and 
+        // Creating mock switch to which we will send packet out and
         IOFSwitch sw = createMock(IOFSwitch.class);
         expect(sw.getId()).andReturn(new Long(0));
 
-        // Mock Packet-in   
+        // Mock Packet-in
         IPacket testPacket = new Ethernet()
         .setSourceMACAddress("00:44:33:22:11:00")
         .setDestinationMACAddress("00:11:22:33:44:55")
@@ -421,7 +422,7 @@ public class ControllerTest extends FloodlightTestCase
                 .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
         byte[] testPacketSerialized = testPacket.serialize();
 
-        // Build the PacketIn        
+        // Build the PacketIn
         OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN))
                 .setBufferId(-1)
                 .setInPort((short) 1)
@@ -446,7 +447,7 @@ public class ControllerTest extends FloodlightTestCase
 
 
         // Let's check the listeners.
-        List <IOFMessageListener> lm; 
+        List <IOFMessageListener> lm;
 
         // Check to see if all the listeners are active.
         lm = mfp.getListeners().get(OFType.PACKET_OUT);
@@ -461,7 +462,7 @@ public class ControllerTest extends FloodlightTestCase
         assertTrue(lm.size() == 1);
         assertTrue(lm.get(0).equals(mfm));
 
-        HashSet<String> matchedFilters;        
+        HashSet<String> matchedFilters;
 
         // Send a packet in and check if it matches a filter.
         matchedFilters = mfm.getMatchedFilters(pi, cntx);
@@ -495,14 +496,14 @@ public class ControllerTest extends FloodlightTestCase
         expect(newsw.getId()).andReturn(0L).anyTimes();
         expect(newsw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
         controller.activeSwitches.put(0L, oldsw);
-        
+
         replay(newsw, channel);
 
         controller.addSwitch(newsw, false);
 
         verify(newsw, channel);
     }
-    
+
     @Test
     public void testAddSwitchClearFM() throws Exception {
         controller.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
@@ -524,14 +525,14 @@ public class ControllerTest extends FloodlightTestCase
         newsw.clearAllFlowMods();
         expectLastCall().once();
         controller.activeSwitches.put(0L, oldsw);
-        
+
         replay(newsw, channel);
 
         controller.addSwitch(newsw, true);
 
         verify(newsw, channel);
     }
-    
+
     @Test
     public void testUpdateQueue() throws Exception {
         class DummySwitchListener implements IOFSwitchListener {
@@ -567,29 +568,29 @@ public class ControllerTest extends FloodlightTestCase
         IOFSwitch sw = createMock(IOFSwitch.class);
         ControllerRunThread t = new ControllerRunThread();
         t.start();
-        
+
         controller.addOFSwitchListener(switchListener);
         synchronized(switchListener) {
             controller.updates.put(controller.new SwitchUpdate(sw,
                                       Controller.SwitchUpdateType.ADDED));
             switchListener.wait(500);
-            assertTrue("IOFSwitchListener.addedSwitch() was not called", 
+            assertTrue("IOFSwitchListener.addedSwitch() was not called",
                     switchListener.nAdded == 1);
-            controller.updates.put(controller.new SwitchUpdate(sw, 
+            controller.updates.put(controller.new SwitchUpdate(sw,
                                       Controller.SwitchUpdateType.REMOVED));
             switchListener.wait(500);
-            assertTrue("IOFSwitchListener.removedSwitch() was not called", 
+            assertTrue("IOFSwitchListener.removedSwitch() was not called",
                     switchListener.nRemoved == 1);
-            controller.updates.put(controller.new SwitchUpdate(sw, 
+            controller.updates.put(controller.new SwitchUpdate(sw,
                                       Controller.SwitchUpdateType.PORTCHANGED));
             switchListener.wait(500);
-            assertTrue("IOFSwitchListener.switchPortChanged() was not called", 
+            assertTrue("IOFSwitchListener.switchPortChanged() was not called",
                     switchListener.nPortChanged == 1);
         }
     }
-    
 
-    private Map<String,Object> getFakeControllerIPRow(String id, String controllerId, 
+
+    private Map<String,Object> getFakeControllerIPRow(String id, String controllerId,
             String type, int number, String discoveredIP ) {
         HashMap<String, Object> row = new HashMap<String,Object>();
         row.put(Controller.CONTROLLER_INTERFACE_ID, id);
@@ -602,14 +603,14 @@ public class ControllerTest extends FloodlightTestCase
 
     /**
      * Test notifications for controller node IP changes. This requires
-     * synchronization between the main test thread and another thread 
+     * synchronization between the main test thread and another thread
      * that runs Controller's main loop and takes / handles updates. We
-     * synchronize with wait(timeout) / notifyAll(). We check for the 
+     * synchronize with wait(timeout) / notifyAll(). We check for the
      * expected condition after the wait returns. However, if wait returns
      * due to the timeout (or due to spurious awaking) and the check fails we
      * might just not have waited long enough. Using a long enough timeout
-     * mitigates this but we cannot get rid of the fundamental "issue". 
-     * 
+     * mitigates this but we cannot get rid of the fundamental "issue".
+     *
      * @throws Exception
      */
     @Test
@@ -619,16 +620,16 @@ public class ControllerTest extends FloodlightTestCase
             public Map<String, String> addedControllerNodeIPs;
             public Map<String, String> removedControllerNodeIPs;
             public int nCalled;
-            
+
             public DummyHAListener() {
                 this.nCalled = 0;
             }
-                
+
             @Override
             public void roleChanged(Role oldRole, Role newRole) {
                 // ignore
             }
-    
+
             @Override
             public synchronized void controllerNodeIPsChanged(
                     Map<String, String> curControllerNodeIPs,
@@ -640,19 +641,19 @@ public class ControllerTest extends FloodlightTestCase
                 this.nCalled++;
                 notifyAll();
             }
-            
+
             public void do_assert(int nCalled,
                     Map<String, String> curControllerNodeIPs,
                     Map<String, String> addedControllerNodeIPs,
                     Map<String, String> removedControllerNodeIPs) {
                 assertEquals("nCalled is not as expected", nCalled, this.nCalled);
-                assertEquals("curControllerNodeIPs is not as expected", 
+                assertEquals("curControllerNodeIPs is not as expected",
                         curControllerNodeIPs, this.curControllerNodeIPs);
-                assertEquals("addedControllerNodeIPs is not as expected", 
+                assertEquals("addedControllerNodeIPs is not as expected",
                         addedControllerNodeIPs, this.addedControllerNodeIPs);
-                assertEquals("removedControllerNodeIPs is not as expected", 
+                assertEquals("removedControllerNodeIPs is not as expected",
                         removedControllerNodeIPs, this.removedControllerNodeIPs);
-                
+
             }
         }
         long waitTimeout = 250; // ms
@@ -660,11 +661,11 @@ public class ControllerTest extends FloodlightTestCase
         HashMap<String,String> expectedCurMap = new HashMap<String, String>();
         HashMap<String,String> expectedAddedMap = new HashMap<String, String>();
         HashMap<String,String> expectedRemovedMap = new HashMap<String, String>();
-        
+
         controller.addHAListener(listener);
         ControllerRunThread t = new ControllerRunThread();
         t.start();
-        
+
         synchronized(listener) {
             // Insert a first entry
             controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
@@ -676,12 +677,12 @@ public class ControllerTest extends FloodlightTestCase
             expectedAddedMap.put("c1", "1.1.1.1");
             listener.wait(waitTimeout);
             listener.do_assert(1, expectedCurMap, expectedAddedMap, expectedRemovedMap);
-            
-            // Add an interface that we want to ignore. 
+
+            // Add an interface that we want to ignore.
             controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                     getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2"));
             listener.wait(waitTimeout); // TODO: do a different check. This call will have to wait for the timeout
-            assertTrue("controllerNodeIPsChanged() should not have been called here", 
+            assertTrue("controllerNodeIPsChanged() should not have been called here",
                     listener.nCalled == 1);
 
             // Add another entry
@@ -711,7 +712,7 @@ public class ControllerTest extends FloodlightTestCase
             listener.do_assert(3, expectedCurMap, expectedAddedMap, expectedRemovedMap);
 
             // Delete an entry
-            controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, 
+            controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                     "row3");
             expectedCurMap.clear();
             expectedAddedMap.clear();
@@ -722,11 +723,11 @@ public class ControllerTest extends FloodlightTestCase
             listener.do_assert(4, expectedCurMap, expectedAddedMap, expectedRemovedMap);
         }
     }
-    
+
     @Test
     public void testGetControllerNodeIPs() {
         HashMap<String,String> expectedCurMap = new HashMap<String, String>();
-        
+
         controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                 getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1"));
         controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
@@ -734,46 +735,46 @@ public class ControllerTest extends FloodlightTestCase
         controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                 getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2"));
         expectedCurMap.put("c1", "1.1.1.1");
-        expectedCurMap.put("c2", "2.2.2.2");    
-        assertEquals("expectedControllerNodeIPs is not as expected", 
+        expectedCurMap.put("c2", "2.2.2.2");
+        assertEquals("expectedControllerNodeIPs is not as expected",
                 expectedCurMap, controller.getControllerNodeIPs());
     }
-    
+
     @Test
     public void testSetRoleNull() {
         try {
-            controller.setRole(null);
+            controller.setRole(null, null);
             fail("Should have thrown an Exception");
         }
         catch (NullPointerException e) {
             //exptected
         }
     }
-    
-    @Test 
+
+    @Test
     public void testSetRole() {
         controller.connectedSwitches.add(new OFSwitchImpl());
-        RoleChanger roleChanger = createMock(RoleChanger.class); 
+        RoleChanger roleChanger = createMock(RoleChanger.class);
         roleChanger.submitRequest(controller.connectedSwitches, Role.SLAVE);
         controller.roleChanger = roleChanger;
-        
-        assertEquals("Check that update queue is empty", 0, 
+
+        assertEquals("Check that update queue is empty", 0,
                     controller.updates.size());
-        
+
         replay(roleChanger);
-        controller.setRole(Role.SLAVE);
+        controller.setRole(Role.SLAVE, "Testing");
         controller.doSetRole(); // avoid wait
         verify(roleChanger);
-        
+
         Controller.IUpdate upd = controller.updates.poll();
         assertNotNull("Check that update queue has an update", upd);
-        assertTrue("Check that update is HARoleUpdate", 
+        assertTrue("Check that update is HARoleUpdate",
                    upd instanceof Controller.HARoleUpdate);
         Controller.HARoleUpdate roleUpd = (Controller.HARoleUpdate)upd;
         assertSame(Role.MASTER, roleUpd.oldRole);
         assertSame(Role.SLAVE, roleUpd.newRole);
     }
-    
+
     @SuppressWarnings("unchecked")
     @Test
     public void testCheckSwitchReady() {
@@ -784,8 +785,8 @@ public class ControllerTest extends FloodlightTestCase
         OFDescriptionStatistics desc = new OFDescriptionStatistics();
         OFFeaturesReply featuresReply = new OFFeaturesReply();
         featuresReply.setPorts(new ArrayList<OFPhysicalPort>());
-        
-        // Wrong current state 
+
+        // Wrong current state
         // Should not go to READY
         state.hsState = OFChannelState.HandshakeState.HELLO;
         state.hasDescription = false;
@@ -796,7 +797,7 @@ public class ControllerTest extends FloodlightTestCase
         replay(channel);
         chdlr.checkSwitchReady();
         assertSame(OFChannelState.HandshakeState.HELLO, state.hsState);
-        
+
         // Have only config reply
         state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY;
         state.hasDescription = false;
@@ -807,7 +808,7 @@ public class ControllerTest extends FloodlightTestCase
         assertSame(OFChannelState.HandshakeState.FEATURES_REPLY, state.hsState);
         assertTrue(controller.connectedSwitches.isEmpty());
         assertTrue(controller.activeSwitches.isEmpty());
-        
+
         // Have only desc reply
         state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY;
         state.hasDescription = true;
@@ -818,7 +819,7 @@ public class ControllerTest extends FloodlightTestCase
         assertSame(OFChannelState.HandshakeState.FEATURES_REPLY, state.hsState);
         assertTrue(controller.connectedSwitches.isEmpty());
         assertTrue(controller.activeSwitches.isEmpty());
-        
+
         //////////////////////////////////////////
         // Finally, everything is right. Should advance to READY
         //////////////////////////////////////////
@@ -830,7 +831,7 @@ public class ControllerTest extends FloodlightTestCase
         state.featuresReply = featuresReply;
         state.switchBindingDone = false;
         // Role support disabled. Switch should be promoted to active switch
-        // list. 
+        // list.
         // setupSwitchForAddSwitch(chdlr.sw, 0L);
         // chdlr.sw.clearAllFlowMods();
         desc.setManufacturerDescription("test vendor");
@@ -844,14 +845,14 @@ public class ControllerTest extends FloodlightTestCase
         reset(controller.roleChanger);
         controller.connectedSwitches.clear();
         controller.activeSwitches.clear();
-        
-        
-        // Role support enabled. 
+
+
+        // Role support enabled.
         state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY;
         controller.role = Role.MASTER;
-        Capture<Collection<IOFSwitch>> swListCapture = 
+        Capture<Collection<IOFSwitch>> swListCapture =
                     new Capture<Collection<IOFSwitch>>();
-        controller.roleChanger.submitRequest(capture(swListCapture), 
+        controller.roleChanger.submitRequest(capture(swListCapture),
                     same(Role.MASTER));
         replay(controller.roleChanger);
         chdlr.checkSwitchReady();
@@ -862,7 +863,7 @@ public class ControllerTest extends FloodlightTestCase
         Collection<IOFSwitch> swList = swListCapture.getValue();
         assertEquals(1, swList.size());
     }
-    
+
     public class TestSwitchClass extends OFSwitchImpl {
     }
 
@@ -872,11 +873,11 @@ public class ControllerTest extends FloodlightTestCase
     @Test
     public void testBindSwitchToDriver() {
         controller.addOFSwitchDriver("test", this);
-        
+
         OFChannelState state = new OFChannelState();
         Controller.OFChannelHandler chdlr =
                 controller.new OFChannelHandler(state);
-        
+
         // Swith should be bound of OFSwitchImpl (default)
         state.hsState = OFChannelState.HandshakeState.HELLO;
         state.hasDescription = true;
@@ -893,7 +894,7 @@ public class ControllerTest extends FloodlightTestCase
         chdlr.bindSwitchToDriver();
         assertTrue(chdlr.sw instanceof OFSwitchImpl);
         assertTrue(!(chdlr.sw instanceof TestSwitchClass));
-        
+
         // Switch should be bound to TestSwitchImpl
         state.switchBindingDone = false;
         desc.setManufacturerDescription("test1 switch");
@@ -914,7 +915,7 @@ public class ControllerTest extends FloodlightTestCase
         chdlr.bindSwitchToDriver();
         assertTrue(chdlr.sw instanceof Test11SwitchClass);
     }
-    
+
     @Test
     public void testBindSwitchOrder() {
         List<String> order = new ArrayList<String>(3);
@@ -925,12 +926,12 @@ public class ControllerTest extends FloodlightTestCase
         order.add("test");
         order.add("");
         test_bind_order = true;
-        
+
         OFChannelState state = new OFChannelState();
         Controller.OFChannelHandler chdlr =
                 controller.new OFChannelHandler(state);
         chdlr.sw = null;
-        
+
         // Swith should be bound of OFSwitchImpl (default)
         state.hsState = OFChannelState.HandshakeState.HELLO;
         state.hasDescription = true;
@@ -952,29 +953,29 @@ public class ControllerTest extends FloodlightTestCase
         test_bind_order = false;
         bind_order = null;
    }
-    
+
     @Test
     public void testChannelDisconnected() throws Exception {
         OFChannelState state = new OFChannelState();
         state.hsState = OFChannelState.HandshakeState.READY;
         Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state);
         chdlr.sw = createMock(IOFSwitch.class);
-        
-        // Switch is active 
+
+        // Switch is active
         expect(chdlr.sw.getId()).andReturn(0L).anyTimes();
         expect(chdlr.sw.getStringId()).andReturn("00:00:00:00:00:00:00:00")
                     .anyTimes();
         chdlr.sw.cancelAllStatisticsReplies();
         chdlr.sw.setConnected(false);
         expect(chdlr.sw.isConnected()).andReturn(true);
-        
+
         controller.connectedSwitches.add(chdlr.sw);
         controller.activeSwitches.put(0L, chdlr.sw);
-        
+
         replay(chdlr.sw);
         chdlr.channelDisconnected(null, null);
         verify(chdlr.sw);
-        
+
         // Switch is connected but not active
         reset(chdlr.sw);
         expect(chdlr.sw.getId()).andReturn(0L).anyTimes();
@@ -982,20 +983,20 @@ public class ControllerTest extends FloodlightTestCase
         replay(chdlr.sw);
         chdlr.channelDisconnected(null, null);
         verify(chdlr.sw);
-        
+
         // Not in ready state
         state.hsState = HandshakeState.START;
         reset(chdlr.sw);
         replay(chdlr.sw);
         chdlr.channelDisconnected(null, null);
         verify(chdlr.sw);
-        
+
         // Switch is null
         state.hsState = HandshakeState.READY;
         chdlr.sw = null;
         chdlr.channelDisconnected(null, null);
     }
-    
+
     /*
     @Test
     public void testRoleChangeForSerialFailoverSwitch() throws Exception {
@@ -1004,12 +1005,12 @@ public class ControllerTest extends FloodlightTestCase
         expect(newsw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes();
         Channel channel2 = createMock(Channel.class);
         expect(newsw.getChannel()).andReturn(channel2);
-        
+
         // newsw.role is null because the switch does not support
         // role request messages
         expect(newsw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE))
                         .andReturn(false);
-        // switch is connected 
+        // switch is connected
         controller.connectedSwitches.add(newsw);
 
         // the switch should get disconnected when role is changed to SLAVE
@@ -1029,40 +1030,40 @@ public class ControllerTest extends FloodlightTestCase
         Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state);
         chdlr.sw = createMock(IOFSwitch.class);
         Channel ch = createMock(Channel.class);
-        
+
         // the error returned when role request message is not supported by sw
         OFError msg = new OFError();
         msg.setType(OFType.ERROR);
         msg.setXid(xid);
         msg.setErrorType(OFErrorType.OFPET_BAD_REQUEST);
-        
+
         // the switch connection should get disconnected when the controller is
         // in SLAVE mode and the switch does not support role-request messages
         controller.role = Role.SLAVE;
-        setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456);                
+        setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456);
         expect(chdlr.sw.getHARole()).andReturn(null);
         chdlr.sw.setHARole(Role.SLAVE, false);
         expect(chdlr.sw.getHARole()).andReturn(Role.SLAVE);
         chdlr.sw.disconnectOutputStream();
-        
+
         replay(ch, chdlr.sw);
         chdlr.processOFMessage(msg);
         verify(ch, chdlr.sw);
         assertTrue("activeSwitches must be empty",
                    controller.activeSwitches.isEmpty());
         reset(ch, chdlr.sw);
-              
+
         // We are MASTER, the switch should be added to the list of active
         // switches.
         controller.role = Role.MASTER;
-        setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456);                
+        setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456);
         expect(chdlr.sw.getHARole()).andReturn(null);
         chdlr.sw.setHARole(controller.role, false);
         setupSwitchForAddSwitch(chdlr.sw, 0L);
         chdlr.sw.clearAllFlowMods();
         expect(chdlr.sw.getHARole()).andReturn(null).anyTimes();
         replay(ch, chdlr.sw);
-        
+
         chdlr.processOFMessage(msg);
         verify(ch, chdlr.sw);
         assertSame("activeSwitches must contain this switch",
@@ -1070,12 +1071,12 @@ public class ControllerTest extends FloodlightTestCase
         reset(ch, chdlr.sw);
 
     }
-    
-    
-    @Test 
+
+
+    @Test
     public void testVendorMessageUnknown() throws Exception {
         // Check behavior with an unknown vendor id
-        // Ensure that vendor message listeners get called, even for Vendors 
+        // Ensure that vendor message listeners get called, even for Vendors
         // unknown to floodlight. It is the responsibility of the listener to
         // discard unknown vendors.
         OFChannelState state = new OFChannelState();
@@ -1086,15 +1087,15 @@ public class ControllerTest extends FloodlightTestCase
         IOFSwitch sw = createMock(IOFSwitch.class);
         chdlr.sw = sw;
         controller.activeSwitches.put(1L, sw);
-        
+
         // prepare the Vendor Message Listener expectations
-        ListenerDispatcher<OFType, IOFMessageListener> ld = 
+        ListenerDispatcher<OFType, IOFMessageListener> ld =
                 new ListenerDispatcher<OFType, IOFMessageListener>();
         IOFMessageListener ml = createMock(IOFMessageListener.class);
         expect(ml.getName()).andReturn("Dummy").anyTimes();
-        expect(ml.isCallbackOrderingPrereq((OFType)anyObject(), 
+        expect(ml.isCallbackOrderingPrereq((OFType)anyObject(),
                 (String)anyObject())).andReturn(false).anyTimes();
-        expect(ml.isCallbackOrderingPostreq((OFType)anyObject(), 
+        expect(ml.isCallbackOrderingPostreq((OFType)anyObject(),
                 (String)anyObject())).andReturn(false).anyTimes();
         expect(ml.receive(eq(sw), eq(msg), isA(FloodlightContext.class))).
                 andReturn(Command.CONTINUE).once();
@@ -1106,14 +1107,47 @@ public class ControllerTest extends FloodlightTestCase
         expect(sw.isConnected()).andReturn(true).anyTimes();
         expect(sw.getHARole()).andReturn(Role.MASTER).anyTimes();
         expect(sw.getId()).andReturn(1L).anyTimes();
-        
+
         // test
         replay(chdlr.sw, lock, ml);
         ld.addListener(OFType.VENDOR, ml);
         chdlr.processOFMessage(msg);
     }
-    
-    
+
+    @Test
+    public void testErrorEPERM() throws Exception {
+        // Check behavior with a BAD_REQUEST/EPERM error
+        // Ensure controller attempts to reset switch role.
+        OFChannelState state = new OFChannelState();
+        state.hsState = HandshakeState.READY;
+        Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state);
+        OFError error = new OFError();
+        error.setErrorType(OFErrorType.OFPET_BAD_REQUEST);
+        error.setErrorCode(OFBadRequestCode.OFPBRC_EPERM);
+        IOFSwitch sw = createMock(IOFSwitch.class);
+        chdlr.sw = sw;
+        controller.activeSwitches.put(1L, sw);
+
+        // prepare the switch and lock expectations
+        Lock lock = createNiceMock(Lock.class);
+        expect(sw.getListenerReadLock()).andReturn(lock).anyTimes();
+        expect(sw.isConnected()).andReturn(true).anyTimes();
+        expect(sw.getHARole()).andReturn(Role.MASTER).anyTimes();
+        expect(sw.getId()).andReturn(1L).anyTimes();
+
+        // Make sure controller attempts to reset switch master
+        expect(sw.getAttribute("supportsNxRole")).andReturn(true).anyTimes();
+        expect(sw.getNextTransactionId()).andReturn(0).anyTimes();
+        sw.write(EasyMock.<List<OFMessage>> anyObject(),
+                 (FloodlightContext)anyObject());
+
+        // test
+        replay(sw, lock);
+        chdlr.processOFMessage(error);
+        // Verify there is a pending role change request
+        assertTrue(controller.roleChanger.pendingTasks.peek() != null);
+   }
+
     // Helper function.
     protected Controller.OFChannelHandler getChannelHandlerForRoleReplyTest() {
         OFChannelState state = new OFChannelState();
@@ -1122,19 +1156,19 @@ public class ControllerTest extends FloodlightTestCase
         chdlr.sw = createMock(IOFSwitch.class);
         return chdlr;
     }
-    
+
     // Helper function
     protected OFVendor getRoleReplyMsgForRoleReplyTest(int xid, int nicira_role) {
         OFVendor msg = new OFVendor();
         msg.setXid(xid);
         msg.setVendor(OFNiciraVendorData.NX_VENDOR_ID);
-        OFRoleReplyVendorData roleReplyVendorData = 
+        OFRoleReplyVendorData roleReplyVendorData =
                 new OFRoleReplyVendorData(OFRoleReplyVendorData.NXT_ROLE_REPLY);
         msg.setVendorData(roleReplyVendorData);
         roleReplyVendorData.setRole(nicira_role);
         return msg;
     }
-    
+
     // Helper function
     protected void setupPendingRoleRequest(IOFSwitch sw, int xid, Role role,
             long cookie) {
@@ -1145,10 +1179,10 @@ public class ControllerTest extends FloodlightTestCase
                 new PendingRoleRequestEntry(xid, role, cookie);
         pendingList.add(entry);
     }
-    
+
     /** invalid role in role reply */
-    @Test 
-    public void testNiciraRoleReplyInvalidRole() 
+    @Test
+    public void testNiciraRoleReplyInvalidRole()
                     throws Exception {
         int xid = 424242;
         Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest();
@@ -1159,17 +1193,17 @@ public class ControllerTest extends FloodlightTestCase
         chdlr.processOFMessage(msg);
         verify(chdlr.sw, ch);
     }
-    
+
     /** First role reply message received: transition from slave to master */
-    @Test 
-    public void testNiciraRoleReplySlave2MasterFristTime() 
+    @Test
+    public void testNiciraRoleReplySlave2MasterFristTime()
                     throws Exception {
         int xid = 424242;
         Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest();
         OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid,
                                        OFRoleReplyVendorData.NX_ROLE_MASTER);
 
-        setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456);                
+        setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456);
         expect(chdlr.sw.getHARole()).andReturn(null);
         chdlr.sw.setHARole(Role.MASTER, true);
         setupSwitchForAddSwitch(chdlr.sw, 1L);
@@ -1180,18 +1214,18 @@ public class ControllerTest extends FloodlightTestCase
         assertSame("activeSwitches must contain this switch",
                    chdlr.sw, controller.activeSwitches.get(1L));
     }
-    
-    
+
+
     /** Not first role reply message received: transition from slave to master */
-    @Test 
-    public void testNiciraRoleReplySlave2MasterNotFristTime() 
+    @Test
+    public void testNiciraRoleReplySlave2MasterNotFristTime()
                     throws Exception {
         int xid = 424242;
         Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest();
         OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid,
                                        OFRoleReplyVendorData.NX_ROLE_MASTER);
 
-        setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456);        
+        setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456);
         expect(chdlr.sw.getHARole()).andReturn(Role.SLAVE);
         chdlr.sw.setHARole(Role.MASTER, true);
         setupSwitchForAddSwitch(chdlr.sw, 1L);
@@ -1202,17 +1236,17 @@ public class ControllerTest extends FloodlightTestCase
         assertSame("activeSwitches must contain this switch",
                    chdlr.sw, controller.activeSwitches.get(1L));
     }
-    
+
     /** transition from slave to equal */
-    @Test 
-    public void testNiciraRoleReplySlave2Equal() 
+    @Test
+    public void testNiciraRoleReplySlave2Equal()
                     throws Exception {
         int xid = 424242;
         Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest();
         OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid,
                                        OFRoleReplyVendorData.NX_ROLE_OTHER);
-        
-        setupPendingRoleRequest(chdlr.sw, xid, Role.EQUAL, 123456);                
+
+        setupPendingRoleRequest(chdlr.sw, xid, Role.EQUAL, 123456);
         expect(chdlr.sw.getHARole()).andReturn(null);
         chdlr.sw.setHARole(Role.EQUAL, true);
         setupSwitchForAddSwitch(chdlr.sw, 1L);
@@ -1223,16 +1257,16 @@ public class ControllerTest extends FloodlightTestCase
         assertSame("activeSwitches must contain this switch",
                    chdlr.sw, controller.activeSwitches.get(1L));
     };
-    
+
     @Test
     /** Slave2Slave transition ==> no change */
     public void testNiciraRoleReplySlave2Slave() throws Exception{
         int xid = 424242;
         Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest();
-        OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, 
+        OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid,
                                        OFRoleReplyVendorData.NX_ROLE_SLAVE);
-        
-        setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456);                
+
+        setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456);
         expect(chdlr.sw.getHARole()).andReturn(null);
         chdlr.sw.setHARole(Role.SLAVE, true);
         expect(chdlr.sw.getId()).andReturn(1L).anyTimes();
@@ -1242,19 +1276,19 @@ public class ControllerTest extends FloodlightTestCase
         replay(chdlr.sw);
         chdlr.processOFMessage(msg);
         verify(chdlr.sw);
-        assertTrue("activeSwitches must be empty", 
+        assertTrue("activeSwitches must be empty",
                    controller.activeSwitches.isEmpty());
     }
-    
+
     @Test
     /** Equal2Master transition ==> no change */
     public void testNiciraRoleReplyEqual2Master() throws Exception{
         int xid = 424242;
         Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest();
-        OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, 
+        OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid,
                                        OFRoleReplyVendorData.NX_ROLE_MASTER);
-        
-        setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456);                
+
+        setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456);
         expect(chdlr.sw.getHARole()).andReturn(null);
         chdlr.sw.setHARole(Role.MASTER, true);
         expect(chdlr.sw.getId()).andReturn(1L).anyTimes();
@@ -1268,16 +1302,16 @@ public class ControllerTest extends FloodlightTestCase
         assertSame("activeSwitches must contain this switch",
                    chdlr.sw, controller.activeSwitches.get(1L));
     }
-    
-    @Test 
-    public void testNiciraRoleReplyMaster2Slave() 
+
+    @Test
+    public void testNiciraRoleReplyMaster2Slave()
                     throws Exception {
         int xid = 424242;
         Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest();
-        OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, 
+        OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid,
                                        OFRoleReplyVendorData.NX_ROLE_SLAVE);
-        
-        setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456);                
+
+        setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456);
         expect(chdlr.sw.getHARole()).andReturn(null);
         chdlr.sw.setHARole(Role.SLAVE, true);
         expect(chdlr.sw.getId()).andReturn(1L).anyTimes();
@@ -1290,10 +1324,10 @@ public class ControllerTest extends FloodlightTestCase
         replay(chdlr.sw);
         chdlr.processOFMessage(msg);
         verify(chdlr.sw);
-        assertTrue("activeSwitches must be empty", 
+        assertTrue("activeSwitches must be empty",
                    controller.activeSwitches.isEmpty());
     }
-    
+
     /**
      * Tests that you can't remove a switch from the active
      * switch list.
@@ -1314,7 +1348,7 @@ public class ControllerTest extends FloodlightTestCase
         assertTrue(exceptionThrown);
         verify(sw);
     }
-    
+
     public void verifyPortChangedUpdateInQueue(IOFSwitch sw) throws Exception {
         assertEquals(1, controller.updates.size());
         IUpdate update = controller.updates.take();
@@ -1323,21 +1357,21 @@ public class ControllerTest extends FloodlightTestCase
         assertEquals(sw, swUpdate.sw);
         assertEquals(SwitchUpdateType.PORTCHANGED, swUpdate.switchUpdateType);
     }
-    
+
     /*
      * Test handlePortStatus()
      * TODO: test correct updateStorage behavior!
      */
-    @Test 
+    @Test
     public void testHandlePortStatus() throws Exception {
         IOFSwitch sw = createMock(IOFSwitch.class);
         OFPhysicalPort port = new OFPhysicalPort();
         port.setName("myPortName1");
         port.setPortNumber((short)42);
-        
+
         OFPortStatus ofps = new OFPortStatus();
         ofps.setDesc(port);
-        
+
         ofps.setReason((byte)OFPortReason.OFPPR_ADD.ordinal());
         sw.setPort(port);
         expectLastCall().once();
@@ -1346,7 +1380,7 @@ public class ControllerTest extends FloodlightTestCase
         verify(sw);
         verifyPortChangedUpdateInQueue(sw);
         reset(sw);
-        
+
         ofps.setReason((byte)OFPortReason.OFPPR_MODIFY.ordinal());
         sw.setPort(port);
         expectLastCall().once();
@@ -1355,7 +1389,7 @@ public class ControllerTest extends FloodlightTestCase
         verify(sw);
         verifyPortChangedUpdateInQueue(sw);
         reset(sw);
-        
+
         ofps.setReason((byte)OFPortReason.OFPPR_DELETE.ordinal());
         sw.deletePort(port.getPortNumber());
         expectLastCall().once();
@@ -1386,8 +1420,8 @@ public class ControllerTest extends FloodlightTestCase
         }
         return null;
     }
-    
-    private void setupSwitchForDispatchTest(IOFSwitch sw, 
+
+    private void setupSwitchForDispatchTest(IOFSwitch sw,
                                             boolean isConnected,
                                             Role role) {
         Lock lock = createNiceMock(Lock.class);
@@ -1397,10 +1431,10 @@ public class ControllerTest extends FloodlightTestCase
         expect(sw.isConnected()).andReturn(isConnected).anyTimes();
         expect(sw.getHARole()).andReturn(role).anyTimes();
         replay(lock);
-        
+
     }
-    
-    @Test 
+
+    @Test
     public void testMessageDispatch() throws Exception {
         // Mock a dummy packet in
         // Build our test packet
@@ -1421,7 +1455,7 @@ public class ControllerTest extends FloodlightTestCase
                 .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
         byte[] testPacketSerialized = testPacket.serialize();
 
-        // Build the PacketIn        
+        // Build the PacketIn
         OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN))
                 .setBufferId(-1)
                 .setInPort((short) 1)
@@ -1429,60 +1463,60 @@ public class ControllerTest extends FloodlightTestCase
                 .setReason(OFPacketInReason.NO_MATCH)
                 .setTotalLength((short) testPacketSerialized.length);
 
-        
-        // Mock switch and add to data structures 
+
+        // Mock switch and add to data structures
         IOFSwitch sw = createMock(IOFSwitch.class);
-        
+
         controller.connectedSwitches.add(sw);
-        
-        // create a channel handler 
+
+        // create a channel handler
         OFChannelState state = new OFChannelState();
         state.hsState = HandshakeState.READY;
         Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state);
         chdlr.sw = sw;
-        
-        // mock role changer 
-        RoleChanger roleChanger = createMock(RoleChanger.class); 
-        roleChanger.submitRequest(eq(controller.connectedSwitches), 
+
+        // mock role changer
+        RoleChanger roleChanger = createMock(RoleChanger.class);
+        roleChanger.submitRequest(eq(controller.connectedSwitches),
                                   anyObject(Role.class));
         expectLastCall().anyTimes();
         controller.roleChanger = roleChanger;
-        
-        
-        // Mock message listener and add 
+
+
+        // Mock message listener and add
         IOFMessageListener listener = createNiceMock(IOFMessageListener.class);
         expect(listener.getName()).andReturn("foobar").anyTimes();
         replay(listener);
         controller.addOFMessageListener(OFType.PACKET_IN, listener);
         resetToStrict(listener);
-        
-        
-        assertEquals("Check that update queue is empty", 0, 
+
+
+        assertEquals("Check that update queue is empty", 0,
                     controller.updates.size());
-        
-        
+
+
         replay(roleChanger);
-        
+
         //-------------------
         // Test 1: role is master, switch is master and in activeMap
-        // we expect the msg to be dispatched 
+        // we expect the msg to be dispatched
         reset(sw);
         resetToDefault(listener);
         controller.activeSwitches.put(1L, sw);
         setupSwitchForDispatchTest(sw, true, Role.MASTER);
-        listener.receive(same(sw), same(pi), 
+        listener.receive(same(sw), same(pi),
                          anyObject(FloodlightContext.class));
         expectLastCall().andReturn(Command.STOP).once();
         replay(sw, listener);
         chdlr.processOFMessage(pi);
         verify(sw, listener);
         assertEquals(0, controller.updates.size());
-        
-        
+
+
         //-------------------
         // Test 1b: role is master, switch is master and in activeMap
-        // but switch is not connected 
-        // no message dispatched 
+        // but switch is not connected
+        // no message dispatched
         reset(sw);
         resetToDefault(listener);
         controller.activeSwitches.put(1L, sw);
@@ -1491,11 +1525,11 @@ public class ControllerTest extends FloodlightTestCase
         chdlr.processOFMessage(pi);
         verify(sw, listener);
         assertEquals(0, controller.updates.size());
-        
-        
+
+
         //-------------------
         // Test 1c: role is master, switch is slave and in activeMap
-        // no message dispatched 
+        // no message dispatched
         reset(sw);
         resetToDefault(listener);
         controller.activeSwitches.put(1L, sw);
@@ -1504,11 +1538,11 @@ public class ControllerTest extends FloodlightTestCase
         chdlr.processOFMessage(pi);
         verify(sw, listener);
         assertEquals(0, controller.updates.size());
-        
-        
+
+
         //-------------------
         // Test 1d: role is master, switch is master but not in activeMap
-        // we expect the msg to be dispatched 
+        // we expect the msg to be dispatched
         reset(sw);
         resetToDefault(listener);
         controller.activeSwitches.remove(1L);
@@ -1516,30 +1550,30 @@ public class ControllerTest extends FloodlightTestCase
         replay(sw, listener);
         chdlr.processOFMessage(pi);
         verify(sw, listener);
-        assertEquals(0, controller.updates.size());        
-        
-        
-        
+        assertEquals(0, controller.updates.size());
+
+
+
         //-------------------
         // Test 2: check correct dispatch and HA notification behavior
-        // We set the role to slave but do not notify the clients 
+        // We set the role to slave but do not notify the clients
         reset(sw);
         resetToDefault(listener);
         controller.activeSwitches.put(1L, sw);
         setupSwitchForDispatchTest(sw, true, Role.MASTER);
-        listener.receive(same(sw), same(pi), 
+        listener.receive(same(sw), same(pi),
                          anyObject(FloodlightContext.class));
         expectLastCall().andReturn(Command.STOP).once();
         replay(sw, listener);
-        controller.setRole(Role.SLAVE);
+        controller.setRole(Role.SLAVE, "Testing");
         controller.doSetRole();  // avoid the wait
         chdlr.processOFMessage(pi);
         verify(sw, listener);
         assertEquals(1, controller.updates.size());
-        
-        // Now notify listeners 
+
+        // Now notify listeners
         Controller.IUpdate upd = controller.updates.poll(1, TimeUnit.NANOSECONDS);
-        assertTrue("Check that update is HARoleUpdate", 
+        assertTrue("Check that update is HARoleUpdate",
                    upd instanceof Controller.HARoleUpdate);
         upd.dispatch();
         resetToDefault(listener);
@@ -1547,49 +1581,49 @@ public class ControllerTest extends FloodlightTestCase
         chdlr.processOFMessage(pi);
         verify(listener);
         assertEquals(0, controller.updates.size());
-        
+
         // transition back to master but don't notify yet
         resetToDefault(listener);
         replay(listener);
-        controller.setRole(Role.MASTER);
+        controller.setRole(Role.MASTER, "Testing");
         controller.doSetRole(); // avoid the wait
         chdlr.processOFMessage(pi);
         verify(listener);
         assertEquals(1, controller.updates.size());
-        
-        // now notify listeners 
+
+        // now notify listeners
         upd = controller.updates.poll(1, TimeUnit.NANOSECONDS);
-        assertTrue("Check that update is HARoleUpdate", 
+        assertTrue("Check that update is HARoleUpdate",
                    upd instanceof Controller.HARoleUpdate);
         upd.dispatch();
         resetToDefault(listener);
-        listener.receive(same(sw), same(pi), 
+        listener.receive(same(sw), same(pi),
                          anyObject(FloodlightContext.class));
         expectLastCall().andReturn(Command.STOP).once();
         replay(listener);
         chdlr.processOFMessage(pi);
         verify(listener);
         assertEquals(0, controller.updates.size());
-        
+
         verify(sw);
     }
-    
-    
+
+
     /*
      * Test correct timing behavior between HA Role notification and dispatching
-     * OFMessages to listeners. 
+     * OFMessages to listeners.
      * When transitioning to SLAVE: stop dispatching message before sending
      *    notifications
      * When transitioning to MASTER: start dispatching messages after sending
      *     notifications
-     * (This implies that messages should not be dispatched while the 
-     * notifications are being sent). 
-     * 
+     * (This implies that messages should not be dispatched while the
+     * notifications are being sent).
+     *
      * We encapsulate the logic for this in a class that implements both
      * IHAListener and IOFMessageListener. Then we inject an OFMessage fom
-     * the IHAListener and check that it gets dropped correctly. 
+     * the IHAListener and check that it gets dropped correctly.
      */
-    @Test 
+    @Test
     public void testRoleNotifcationAndMessageDispatch() throws Exception {
         class TestRoleNotificationsAndDispatch implements IHAListener, IOFMessageListener {
             OFPacketIn pi;
@@ -1597,7 +1631,7 @@ public class ControllerTest extends FloodlightTestCase
             IOFSwitch sw;
             private boolean haveReceived;
             private boolean doInjectMessageFromHAListener;
-            
+
             public TestRoleNotificationsAndDispatch() {
                 IPacket testPacket = new Ethernet()
                 .setSourceMACAddress("00:44:33:22:11:00")
@@ -1615,78 +1649,78 @@ public class ControllerTest extends FloodlightTestCase
                         .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
                         .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
                 byte[] testPacketSerialized = testPacket.serialize();
-        
-                // Build the PacketIn        
+
+                // Build the PacketIn
                 pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN))
                         .setBufferId(-1)
                         .setInPort((short) 1)
                         .setPacketData(testPacketSerialized)
                         .setReason(OFPacketInReason.NO_MATCH)
                         .setTotalLength((short) testPacketSerialized.length);
-                
-                // Mock switch and add to data structures 
+
+                // Mock switch and add to data structures
                 sw = createMock(IOFSwitch.class);
                 controller.connectedSwitches.add(sw);
                 controller.activeSwitches.put(1L, sw);
                 setupSwitchForDispatchTest(sw, true, Role.MASTER);
                 replay(sw);
-                
-                // create a channel handler 
+
+                // create a channel handler
                 OFChannelState state = new OFChannelState();
                 state.hsState = HandshakeState.READY;
                 chdlr = controller.new OFChannelHandler(state);
                 chdlr.sw = this.sw;
-                
+
                 // add ourself as listeners
                 controller.addOFMessageListener(OFType.PACKET_IN, this);
                 controller.addHAListener(this);
             }
-            
-            
+
+
             private void injectMessage(boolean shouldReceive) throws Exception {
                 haveReceived = false;
                 chdlr.processOFMessage(pi);
                 assertEquals(shouldReceive, haveReceived);
             }
-            
+
             public void transitionToSlave() throws Exception {
                 IUpdate update;
-                
+
                 // Bring controller into well defined state for MASTER
-                doInjectMessageFromHAListener = false; 
+                doInjectMessageFromHAListener = false;
                 update = controller.new HARoleUpdate(Role.MASTER, Role.SLAVE);
                 update.dispatch();
                 doInjectMessageFromHAListener = true;
-                
-                
-                // inject message. Listener called 
+
+
+                // inject message. Listener called
                 injectMessage(true);
-                // Dispatch update 
+                // Dispatch update
                 update = controller.new HARoleUpdate(Role.SLAVE, Role.MASTER);
                 update.dispatch();
                 // inject message. Listener not called
                 injectMessage(false);
             }
-            
+
             public void transitionToMaster() throws Exception {
                 IUpdate update;
-                
+
                 // Bring controller into well defined state for SLAVE
-                doInjectMessageFromHAListener = false; 
+                doInjectMessageFromHAListener = false;
                 update = controller.new HARoleUpdate(Role.SLAVE, Role.MASTER);
                 update.dispatch();
                 doInjectMessageFromHAListener = true;
-                
-                
-                // inject message. Listener not called 
+
+
+                // inject message. Listener not called
                 injectMessage(false);
-                // Dispatch update 
+                // Dispatch update
                 update = controller.new HARoleUpdate(Role.MASTER, Role.SLAVE);
                 update.dispatch();
                 // inject message. Listener called
                 injectMessage(true);
             }
-            
+
             //---------------
             // IHAListener
             //---------------
@@ -1700,7 +1734,7 @@ public class ControllerTest extends FloodlightTestCase
                     e.printStackTrace();
                 }
             }
-            
+
             @Override
             public
                     void
@@ -1709,7 +1743,7 @@ public class ControllerTest extends FloodlightTestCase
                                              Map<String, String> removedControllerNodeIPs) {
                 // TODO Auto-generated method stub
             }
-            
+
             //-------------------------
             // IOFMessageListener
             //-------------------------
@@ -1726,18 +1760,18 @@ public class ControllerTest extends FloodlightTestCase
                 return false;
             }
             @Override
-            public Command receive(IOFSwitch sw, 
-                                   OFMessage msg, 
+            public Command receive(IOFSwitch sw,
+                                   OFMessage msg,
                                    FloodlightContext cntx) {
                 haveReceived = true;
                 return Command.STOP;
             }
         }
-        
+
         TestRoleNotificationsAndDispatch x = new TestRoleNotificationsAndDispatch();
         x.transitionToSlave();
         x.transitionToMaster();
-        
+
     }
 
 }
diff --git a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
index 301832f6a9400f29f28a0d7c6fa9c871e2ffc449..7b5cd6293c1888250299c6352918c610df70733c 100644
--- a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
+++ b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java
@@ -17,6 +17,8 @@
 
 package net.floodlightcontroller.core.test;
 
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -39,6 +41,7 @@ import net.floodlightcontroller.core.IOFSwitchDriver;
 import net.floodlightcontroller.core.IOFSwitchFilter;
 import net.floodlightcontroller.core.IOFSwitchListener;
 import net.floodlightcontroller.core.IListener.Command;
+import net.floodlightcontroller.core.RoleInfo;
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
 import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.floodlightcontroller.core.module.IFloodlightModule;
@@ -102,6 +105,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
     /**
      * @return the listeners
      */
+    @Override
     public Map<OFType, List<IOFMessageListener>> getListeners() {
         Map<OFType, List<IOFMessageListener>> lers = 
                 new HashMap<OFType, List<IOFMessageListener>>();
@@ -158,6 +162,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
         }
     }
     
+    @Override
     public void handleOutgoingMessage(IOFSwitch sw, OFMessage m, FloodlightContext bc) {
         List<IOFMessageListener> msgListeners = null;
         if (listeners.containsKey(m.getType())) {
@@ -191,6 +196,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
         return switchListeners;
     }
     
+    @Override
     public void terminate() {
     }
 
@@ -289,7 +295,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
     }
     
     @Override
-    public void setRole(Role role) {
+    public void setRole(Role role, String roleChangeDescription) {
         
     }
     
@@ -304,10 +310,6 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
         }
     }
 
-    @Override
-    public String getControllerId() {
-        return "localhost";
-    }
 
     @Override
     public Map<String, String> getControllerNodeIPs() {
@@ -355,4 +357,24 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro
         
     }
 
+    @Override
+    public RoleInfo getRoleInfo() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Map<String, Long> getMemory() {
+        Map<String, Long> m = new HashMap<String, Long>();
+        Runtime runtime = Runtime.getRuntime();
+        m.put("total", runtime.totalMemory());
+        m.put("free", runtime.freeMemory());
+        return m;
+    }
+
+    @Override
+    public Long getUptime() {
+        RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
+        return rb.getUptime();
+    }
 }
diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
index 671ef50609cd3022dfbb5b31f199f377cbb750c9..40986e9f2577af119523534ac3762404bb66c6f0 100644
--- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
+++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java
@@ -95,7 +95,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
     testARPReplyPacket_3;
     protected IPacket testARPReqPacket_1, testARPReqPacket_2;
     protected byte[] testARPReplyPacket_1_Srld, testARPReplyPacket_2_Srld;
-    private byte[] testARPReplyPacket_3_Serialized;
     DeviceManagerImpl deviceManager;
     MemoryStorageSource storageSource;
     FlowReconcileManager flowReconcileMgr;
@@ -185,9 +184,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         // Another test packet with a different source IP
         this.testARPReplyPacket_2 = new Ethernet()
-        .setSourceMACAddress("00:44:33:22:11:01")
+        .setSourceMACAddress("00:99:88:77:66:55")
         .setDestinationMACAddress("00:11:22:33:44:55")
         .setEtherType(Ethernet.TYPE_ARP)
+        .setVlanID((short)5)
         .setPayload(
                     new ARP()
                     .setHardwareType(ARP.HW_TYPE_ETHERNET)
@@ -201,23 +201,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                     .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
         this.testARPReplyPacket_2_Srld = testARPReplyPacket_2.serialize();
 
-        this.testARPReplyPacket_3 = new Ethernet()
-        .setSourceMACAddress("00:44:33:22:11:01")
-        .setDestinationMACAddress("00:11:22:33:44:55")
-        .setEtherType(Ethernet.TYPE_ARP)
-        .setPayload(
-                    new ARP()
-                    .setHardwareType(ARP.HW_TYPE_ETHERNET)
-                    .setProtocolType(ARP.PROTO_TYPE_IP)
-                    .setHardwareAddressLength((byte) 6)
-                    .setProtocolAddressLength((byte) 4)
-                    .setOpCode(ARP.OP_REPLY)
-                    .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01"))
-                    .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.3"))
-                    .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55"))
-                    .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2")));
-        this.testARPReplyPacket_3_Serialized = testARPReplyPacket_3.serialize();
-        
         // Build the PacketIn
         this.packetIn_1 = ((OFPacketIn) mockFloodlightProvider.
                 getOFMessageFactory().getMessage(OFType.PACKET_IN))
@@ -235,21 +218,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                 .setPacketData(this.testARPReplyPacket_2_Srld)
                 .setReason(OFPacketInReason.NO_MATCH)
                 .setTotalLength((short) this.testARPReplyPacket_2_Srld.length);
-
-        // Build the PacketIn
-        this.packetIn_3 = ((OFPacketIn) mockFloodlightProvider.
-                getOFMessageFactory().getMessage(OFType.PACKET_IN))
-                .setBufferId(-1)
-                .setInPort((short) 1)
-                .setPacketData(this.testARPReplyPacket_3_Serialized)
-                .setReason(OFPacketInReason.NO_MATCH)
-                .setTotalLength((short) this.testARPReplyPacket_3_Serialized.length);
     }
 
 
 
 
-    
+
     @Test
     public void testLastSeen() throws Exception {
         Calendar c = Calendar.getInstance();
@@ -423,11 +397,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         assertEquals(4, deviceManager.getAllDevices().size());
         verify(mockListener);
-        
-        
+
+
         reset(mockListener);
         replay(mockListener);
-        
+
         reset(deviceManager.topology);
         deviceManager.topology.addListener(deviceManager);
         expectLastCall().times(1);
@@ -437,16 +411,16 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         deviceManager.startUp(null);
         Entity entityNoClass = new Entity(5L, (short)1, 5, -1L, 1, new Date());
         assertEquals(null, deviceManager.learnDeviceByEntity(entityNoClass));
-        
+
         verify(mockListener);
     }
-    
-    
+
+
     private void doTestEntityOrdering(boolean computeInsertionPoint) throws Exception {
         Entity e = new Entity(10L, null, null, null, null, null);
         IEntityClass ec = createNiceMock(IEntityClass.class);
         Device d = new Device(deviceManager, 1L, e, ec);
-        
+
         int expectedLength = 1;
         Long[] macs = new Long[] {  5L,  // new first element
                                    15L,  // new last element
@@ -457,7 +431,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                                     1L,
                                    20L
                                   };
-        
+
         for (Long mac: macs) {
             e = new Entity(mac, null, null, null, null, null);
             int insertionPoint;
@@ -473,12 +447,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                 assertEquals(-1, d.entities[i].compareTo(d.entities[i+1]));
         }
     }
-    
+
     @Test
     public void testEntityOrderingExternal() throws Exception {
         doTestEntityOrdering(true);
     }
-    
+
     @Test
     public void testEntityOrderingInternal() throws Exception {
         doTestEntityOrdering(false);
@@ -594,12 +568,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         assertArrayEquals(new Integer[] { 1 }, ips);
         verify(mockListener);
     }
-    
+
     private void verifyEntityArray(Entity[] expected, Device d) {
         Arrays.sort(expected);
         assertArrayEquals(expected, d.entities);
     }
-    
+
     @Test
     public void testNoLearningOnInternalPorts() throws Exception {
         IDeviceListener mockListener =
@@ -636,7 +610,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         // Switches 2 and 4 have only internal ports
         expect(mockTopology.isAttachmentPointPort(or(eq(2L), eq(4L)), anyShort()))
                 .andReturn(false).anyTimes();
-        
+
         expect(mockTopology.isConsistent(1L, (short)1, 3L, (short)1))
                 .andReturn(false).once();
 
@@ -664,13 +638,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         mockListener.deviceAdded(isA(IDevice.class));
         expectLastCall().once();
         replay(mockListener);
-        
+
         // cannot learn device internal ports
         d = deviceManager.learnDeviceByEntity(entity2);
         assertNull(d);
         d = deviceManager.learnDeviceByEntity(entity4);
         assertNull(d);
-        
+
         d = deviceManager.learnDeviceByEntity(entity1);
         assertEquals(1, deviceManager.getAllDevices().size());
         aps = d.getAttachmentPoints();
@@ -679,10 +653,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         ips = d.getIPv4Addresses();
         assertArrayEquals(new Integer[] { 1 }, ips);
         verify(mockListener);
-        
+
         reset(mockListener);
         replay(mockListener);
-        
+
         // don't learn
         d = deviceManager.learnDeviceByEntity(entity2);
         assertEquals(1, deviceManager.getAllDevices().size());
@@ -692,12 +666,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         ips = d.getIPv4Addresses();
         assertArrayEquals(new Integer[] { 1 }, ips);
         verify(mockListener);
-        
+
         reset(mockListener);
         mockListener.deviceMoved(isA(IDevice.class));
         mockListener.deviceIPV4AddrChanged(isA(IDevice.class));
         replay(mockListener);
-        
+
         // learn
         d = deviceManager.learnDeviceByEntity(entity3);
         assertEquals(1, deviceManager.getAllDevices().size());
@@ -708,10 +682,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Arrays.sort(ips);
         assertArrayEquals(new Integer[] { 1, 3 }, ips);
         verify(mockListener);
-        
+
         reset(mockListener);
         replay(mockListener);
-        
+
         // don't learn
         d = deviceManager.learnDeviceByEntity(entity4);
         assertEquals(1, deviceManager.getAllDevices().size());
@@ -723,7 +697,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         assertArrayEquals(new Integer[] { 1, 3 }, ips);
         verify(mockListener);
     }
-    
+
     @Test
     public void testAttachmentPointSuppression() throws Exception {
         IDeviceListener mockListener =
@@ -773,6 +747,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         Calendar c = Calendar.getInstance();
         Entity entity0 = new Entity(1L, null, null, null, null, c.getTime());
+        // No attachment point should be learnt on 1L, 1
         Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
         c.add(Calendar.SECOND, 1);
         Entity entity2 = new Entity(1L, null, 1, 5L, 1, c.getTime());
@@ -788,7 +763,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         mockListener.deviceAdded(isA(IDevice.class));
         mockListener.deviceIPV4AddrChanged((isA(IDevice.class)));
         replay(mockListener);
-        
+
         // TODO: we currently do learn entities on suppressed APs
         // // cannot learn device on suppressed AP
         // d = deviceManager.learnDeviceByEntity(entity1);
@@ -805,10 +780,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         verify(mockListener);
 
         reset(mockListener);
-        mockListener.deviceMoved((isA(IDevice.class)));
+        //mockListener.deviceMoved((isA(IDevice.class)));
         //mockListener.deviceIPV4AddrChanged((isA(IDevice.class)));
         replay(mockListener);
-
+        // there is no device moved because entity 1 was not learned due to suppression
         d = deviceManager.learnDeviceByEntity(entity2);
         assertEquals(1, deviceManager.getAllDevices().size());
         aps = d.getAttachmentPoints();
@@ -968,23 +943,19 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, OFPort.OFPP_LOCAL.getValue()) }, aps);
     }
 
-
     @Test
-    public void testPacketIn() throws Exception {
-        byte[] dataLayerSource =
-                ((Ethernet)this.testARPReplyPacket_1).getSourceMACAddress();
-
+    public void testPacketInBasic(byte[] deviceMac, OFPacketIn packetIn) {
         // Mock up our expected behavior
         ITopologyService mockTopology = createMock(ITopologyService.class);
         deviceManager.topology = mockTopology;
         expect(mockTopology.isAttachmentPointPort(EasyMock.anyLong(),
-                                                  EasyMock.anyShort())).
-                                                  andReturn(true).anyTimes();
+                EasyMock.anyShort())).
+                andReturn(true).anyTimes();
         expect(mockTopology.isConsistent(EasyMock.anyLong(),
-                                         EasyMock.anyShort(),
-                                         EasyMock.anyLong(),
-                                         EasyMock.anyShort())).andReturn(false).
-                                         anyTimes();
+                EasyMock.anyShort(),
+                EasyMock.anyLong(),
+                EasyMock.anyShort())).andReturn(false).
+                anyTimes();
         expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
         replay(mockTopology);
 
@@ -994,29 +965,26 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Integer ipaddr = IPv4.toIPv4Address("192.168.1.1");
         Device device =
                 new Device(deviceManager,
-                           new Long(deviceManager.deviceKeyCounter),
-                           new Entity(Ethernet.toLong(dataLayerSource),
-                                      (short)5,
-                                      ipaddr,
-                                      1L,
-                                      1,
-                                      currentDate),
-                                      DefaultEntityClassifier.entityClass);
-
-
-
+                        new Long(deviceManager.deviceKeyCounter),
+                        new Entity(Ethernet.toLong(deviceMac),
+                                (short)5,
+                                ipaddr,
+                                1L,
+                                1,
+                                currentDate),
+                                DefaultEntityClassifier.entityClass);
 
         // Get the listener and trigger the packet in
         IOFSwitch switch1 = mockFloodlightProvider.getSwitches().get(1L);
-        mockFloodlightProvider.dispatchMessage(switch1, this.packetIn_1);
+        mockFloodlightProvider.dispatchMessage(switch1, packetIn);
 
         // Verify the replay matched our expectations
         // verify(mockTopology);
 
         // Verify the device
         Device rdevice = (Device)
-                deviceManager.findDevice(Ethernet.toLong(dataLayerSource),
-                                         (short)5, null, null, null);
+                deviceManager.findDevice(Ethernet.toLong(deviceMac),
+                        (short)5, null, null, null);
 
         assertEquals(device, rdevice);
         assertEquals(new Short((short)5), rdevice.getVlanId()[0]);
@@ -1024,8 +992,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Device result = null;
         Iterator<? extends IDevice> dstiter =
                 deviceManager.queryClassDevices(device.getEntityClass(),
-                                                null, null, ipaddr,
-                                                null, null);
+                        null, null, ipaddr,
+                        null, null);
         if (dstiter.hasNext()) {
             result = (Device)dstiter.next();
         }
@@ -1034,32 +1002,32 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         device =
                 new Device(device,
-                           new Entity(Ethernet.toLong(dataLayerSource),
-                                      (short)5,
-                                      ipaddr,
-                                      5L,
-                                      2,
-                                      currentDate),
-                                      -1);
+                        new Entity(Ethernet.toLong(deviceMac),
+                                (short)5,
+                                ipaddr,
+                                5L,
+                                2,
+                                currentDate),
+                                -1);
 
         reset(mockTopology);
         expect(mockTopology.isAttachmentPointPort(anyLong(),
-                                                  anyShort())).
-                                                  andReturn(true).
-                                                  anyTimes();
+                anyShort())).
+                andReturn(true).
+                anyTimes();
         expect(mockTopology.isConsistent(EasyMock.anyLong(),
-                                         EasyMock.anyShort(),
-                                         EasyMock.anyLong(),
-                                         EasyMock.anyShort())).andReturn(false).
-                                         anyTimes();
+                EasyMock.anyShort(),
+                EasyMock.anyLong(),
+                EasyMock.anyShort())).andReturn(false).
+                anyTimes();
         expect(mockTopology.isBroadcastDomainPort(EasyMock.anyLong(),
-                                                  EasyMock.anyShort()))
-                                                  .andReturn(false)
-                                                  .anyTimes();
+                EasyMock.anyShort()))
+                .andReturn(false)
+                .anyTimes();
         expect(mockTopology.getL2DomainId(1L)).andReturn(1L).anyTimes();
         expect(mockTopology.getL2DomainId(5L)).andReturn(1L).anyTimes();
         expect(mockTopology.isInSameBroadcastDomain(1L, (short)1, 5L, (short)2)).
-               andReturn(false).anyTimes();
+        andReturn(false).anyTimes();
 
         // Start recording the replay on the mocks
         replay(mockTopology);
@@ -1073,12 +1041,36 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         // Verify the device
         rdevice = (Device)
-                deviceManager.findDevice(Ethernet.toLong(dataLayerSource),
-                                         (short)5, null, null, null);
+                deviceManager.findDevice(Ethernet.toLong(deviceMac),
+                        (short)5, null, null, null);
         assertEquals(device, rdevice);
     }
 
-    
+    @Test
+    public void testPacketIn() throws Exception {
+        byte[] deviceMac1 =
+                ((Ethernet)this.testARPReplyPacket_1).getSourceMACAddress();
+        testPacketInBasic(deviceMac1, packetIn_1);
+    }
+
+    /**
+     * This test ensures the device manager learns the source device
+     * corresponding to the senderHardwareAddress and senderProtocolAddress
+     * in an ARP response whenever the senderHardwareAddress is different
+     * from the source MAC address of the Ethernet frame.
+     *
+     * This test is the same as testPacketIn method, except for the
+     * packet-in that's used.
+     * @throws Exception
+     */
+    @Test
+    public void testDeviceLearningFromArpResponseData() throws Exception {
+        ARP arp = (ARP)((Ethernet)this.testARPReplyPacket_2).getPayload();
+        byte[] deviceMac2 = arp.getSenderHardwareAddress();
+
+        testPacketInBasic(deviceMac2, packetIn_2);
+    }
+
     /**
      * Note: Entity expiration does not result in device moved notification.
      * @throws Exception
@@ -1175,12 +1167,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         .andReturn(false).atLeastOnce();
         expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject()))
         .andReturn(false).atLeastOnce();
-        
+
         Calendar c = Calendar.getInstance();
         c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1);
         Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime());
         Entity entity2 = new Entity(1L, null, 2, 5L, 1, c.getTime());
-        
+
         ITopologyService mockTopology = createMock(ITopologyService.class);
         deviceManager.topology = mockTopology;
 
@@ -1222,16 +1214,16 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
 
         r = deviceManager.findDevice(1L, null, null, null, null);
         assertNull(r);
-        
+
         verify(mockListener);
     }
-    
+
     /*
      * A ConcurrentHashMap for devices (deviceMap) that can be used to test
      * code that specially handles concurrent modification situations. In
      * particular, we overwrite values() and will replace / remove all the
      * elements returned by values.
-     * 
+     *
      * The remove flag in the constructor specifies if devices returned by
      * values() should be removed or replaced.
      */
@@ -1243,7 +1235,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
             super();
             this.remove = remove;
         }
-        
+
         @Override
         public Collection<Device> values() {
             // Get the values from the real map and copy them since
@@ -1285,17 +1277,17 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
             return devs;
         }
     }
-   
+
     @Test
     public void testEntityExpiration() throws Exception {
         doTestEntityExpiration();
     }
-    
+
     @Test
     public void testDeviceExpiration() throws Exception {
         doTestDeviceExpiration();
     }
-    
+
     /* Test correct entity cleanup behavior when a concurrent modification
      * occurs.
      */
@@ -1304,7 +1296,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false);
         doTestEntityExpiration();
     }
-    
+
     /* Test correct entity cleanup behavior when a concurrent remove
      * occurs.
      */
@@ -1313,7 +1305,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(true);
         doTestDeviceExpiration();
     }
-    
+
     /* Test correct entity cleanup behavior when a concurrent modification
      * occurs.
      */
@@ -1322,7 +1314,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false);
         doTestDeviceExpiration();
     }
-    
+
 
     @Test
     public void testAttachmentPointFlapping() throws Exception {
@@ -1591,7 +1583,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                                                   andReturn(true).anyTimes();
         expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes();
         replay(mockTopology);
-        
+
         doTestDeviceQuery();
     }
 
@@ -1695,19 +1687,19 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date());
         Entity entity2 = new Entity(2L, (short)2, 2, 1L, 2, new Date());
         Entity entity2b = new Entity(22L, (short)2, 2, 1L, 2, new Date());
-        
+
         Entity entity3 = new Entity(3L, (short)1, 3, 2L, 1, new Date());
         Entity entity4 = new Entity(4L, (short)2, 4, 2L, 2, new Date());
-        
+
         Entity entity5 = new Entity(5L, (short)1, 5, 3L, 1, new Date());
-        
+
 
         IDevice d1 = deviceManager.learnDeviceByEntity(entity1);
         IDevice d2 = deviceManager.learnDeviceByEntity(entity2);
         IDevice d3 = deviceManager.learnDeviceByEntity(entity3);
         IDevice d4 = deviceManager.learnDeviceByEntity(entity4);
         IDevice d5 = deviceManager.learnDeviceByEntity(entity5);
-        
+
         // Make sure the entity classifier worked as expected
         assertEquals(MockEntityClassifierMac.testECMac1, d1.getEntityClass());
         assertEquals(MockEntityClassifierMac.testECMac1, d2.getEntityClass());
@@ -1715,7 +1707,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         assertEquals(MockEntityClassifierMac.testECMac2, d4.getEntityClass());
         assertEquals(DefaultEntityClassifier.entityClass,
                      d5.getEntityClass());
-        
+
         // Look up the device using findDevice() which uses only the primary
         // index
         assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(),
@@ -1782,12 +1774,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         }
         if (!exceptionCaught)
             fail("findDevice() did not throw IllegalArgumentException");
-        
-        
+
+
         Entity entityNoClass = new Entity(5L, (short)1, 5, -1L, 1, new Date());
         assertEquals(null, deviceManager.findDeviceByEntity(entityNoClass));
-        
-        
+
+
         // Now look up destination devices
         assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(),
                                                   entity1.getMacAddress(),
@@ -1802,11 +1794,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                                                   (short) -1,
                                                   0));
     }
-    
+
+
+
     @Test
     public void testGetIPv4Addresses() {
         // Looks like Date is only 1s granularity
-        
+
         ITopologyService mockTopology = createMock(ITopologyService.class);
         deviceManager.topology = mockTopology;
         expect(mockTopology.isAttachmentPointPort(anyLong(),
@@ -1833,8 +1827,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Entity e1 = new Entity(1L, (short)1, null, null, null, new Date(2000));
         Device d1 = deviceManager.learnDeviceByEntity(e1);
         assertArrayEquals(new Integer[0], d1.getIPv4Addresses());
-        
-        
+
+
         Entity e2 = new Entity(2L, (short)2, 2, null, null, new Date(2000));
         Device d2 = deviceManager.learnDeviceByEntity(e2);
         d2 = deviceManager.learnDeviceByEntity(e2);
@@ -1849,7 +1843,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         d2 = deviceManager.learnDeviceByEntity(e2c);
         assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses());
         assertEquals(3, d2.entities.length);
-        
+
         // Other devices with different IPs shouldn't interfere
         Entity e3 = new Entity(3L, (short)3, 3, null, null, new Date(4000));
         Entity e3b = new Entity(3L, (short)3, 3, 3L, 3, new Date(4400));
@@ -1857,14 +1851,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         d3 = deviceManager.learnDeviceByEntity(e3b);
         assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses());
         assertArrayEquals(new Integer[] { 3 }, d3.getIPv4Addresses());
-        
+
         // Add another IP to d3
         Entity e3c = new Entity(3L, (short)3, 33, 3L, 3, new Date(4400));
         d3 = deviceManager.learnDeviceByEntity(e3c);
         Integer[] ips = d3.getIPv4Addresses();
         Arrays.sort(ips);
         assertArrayEquals(new Integer[] { 3, 33 }, ips);
-        
+
         // Add another device that also claims IP2 but is older than e2
         Entity e4 = new Entity(4L, (short)4, 2, null, null, new Date(1000));
         Entity e4b = new Entity(4L, (short)4, null, 4L, 4, new Date(1000));
@@ -1874,7 +1868,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         // add another entity to d4
         d4 = deviceManager.learnDeviceByEntity(e4b);
         assertArrayEquals(new Integer[0], d4.getIPv4Addresses());
-        
+
         // Make e4 and e4a newer
         Entity e4c = new Entity(4L, (short)4, 2, null, null, new Date(5000));
         Entity e4d = new Entity(4L, (short)4, null, 4L, 5, new Date(5000));
@@ -1883,7 +1877,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         assertArrayEquals(new Integer[0], d2.getIPv4Addresses());
         // FIXME: d4 should not return IP4
         assertArrayEquals(new Integer[] { 2 }, d4.getIPv4Addresses());
-        
+
         // Add another newer entity to d2 but with different IP
         Entity e2d = new Entity(2L, (short)2, 22, 4L, 6, new Date(6000));
         d2 = deviceManager.learnDeviceByEntity(e2d);
@@ -1901,7 +1895,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         ips= d4.getIPv4Addresses();
         Arrays.sort(ips);
         assertArrayEquals(new Integer[] { 2, 42 }, ips);
-        
+
         // add a couple more IPs
         Entity e2f = new Entity(2L, (short)2, 4242, 2L, 5, new Date(8000));
         d2 = deviceManager.learnDeviceByEntity(e2f);
@@ -1914,7 +1908,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         Arrays.sort(ips);
         assertArrayEquals(new Integer[] { 2, 42, 4242 }, ips);
     }
-    
+
     // TODO: this test should really go into a separate class that collects
     // unit tests for Device
     @Test
@@ -1941,7 +1935,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
             assertArrayEquals(new Short[0],
                               d.getSwitchPortVlanIds(swp2x1));
     }
-    
+
     @Test
     public void testReclassifyDevice() {
         MockFlexEntityClassifier flexClassifier =
@@ -1966,20 +1960,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                                                   .andReturn(false)
                                                   .anyTimes();
         replay(mockTopology);
-        
+
         //flexClassifier.createTestEntityClass("Class1");
-        
+
         Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date());
         Entity entity1b = new Entity(1L, (short)2, 1, 1L, 1, new Date());
         Entity entity2 = new Entity(2L, (short)1, 2, 2L, 2, new Date());
         Entity entity2b = new Entity(2L, (short)2, 2, 2L, 2, new Date());
-        
-        
+
+
         Device d1 = deviceManager.learnDeviceByEntity(entity1);
         Device d2 = deviceManager.learnDeviceByEntity(entity2);
         Device d1b = deviceManager.learnDeviceByEntity(entity1b);
         Device d2b = deviceManager.learnDeviceByEntity(entity2b);
-        
+
         d1 = deviceManager.getDeviceIteratorForQuery(entity1.getMacAddress(),
                         entity1.getVlan(), entity1.getIpv4Address(),
                         entity1.getSwitchDPID(), entity1.getSwitchPort())
@@ -1987,9 +1981,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
         d1b = deviceManager.getDeviceIteratorForQuery(entity1b.getMacAddress(),
                 entity1b.getVlan(), entity1b.getIpv4Address(),
                 entity1b.getSwitchDPID(), entity1b.getSwitchPort()).next();
-        
+
         assertEquals(d1, d1b);
-        
+
         d2 = deviceManager.getDeviceIteratorForQuery(entity2.getMacAddress(),
                 entity2.getVlan(), entity2.getIpv4Address(),
                 entity2.getSwitchDPID(), entity2.getSwitchPort()).next();
@@ -1997,32 +1991,32 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                 entity2b.getVlan(), entity2b.getIpv4Address(),
                 entity2b.getSwitchDPID(), entity2b.getSwitchPort()).next();
         assertEquals(d2, d2b);
-        
+
         IEntityClass eC1 = flexClassifier.createTestEntityClass("C1");
         IEntityClass eC2 = flexClassifier.createTestEntityClass("C2");
-        
+
         flexClassifier.addVlanEntities((short)1, eC1);
         flexClassifier.addVlanEntities((short)2, eC1);
-        
+
         deviceManager.reclassifyDevice(d1);
         deviceManager.reclassifyDevice(d2);
-        
+
         d1 = deviceManager.deviceMap.get(
                 deviceManager.primaryIndex.findByEntity(entity1));
         d1b = deviceManager.deviceMap.get(
                 deviceManager.primaryIndex.findByEntity(entity1b));
-        
+
         assertEquals(d1, d1b);
-        
+
         d2 = deviceManager.deviceMap.get(
                 deviceManager.primaryIndex.findByEntity(entity2));
         d2b = deviceManager.deviceMap.get(
                 deviceManager.primaryIndex.findByEntity(entity2b));
-        
+
         assertEquals(d2, d2b);
-                        
+
         flexClassifier.addVlanEntities((short)1, eC2);
-        
+
         deviceManager.reclassifyDevice(d1);
         deviceManager.reclassifyDevice(d2);
         d1 = deviceManager.deviceMap.get(
@@ -2033,11 +2027,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                 deviceManager.primaryIndex.findByEntity(entity2));
         d2b = deviceManager.deviceMap.get(
                 deviceManager.primaryIndex.findByEntity(entity2b));
-        
+
         assertNotSame(d1, d1b);
-       
+
         assertNotSame(d2, d2b);
-        
+
         flexClassifier.addVlanEntities((short)1, eC1);
         deviceManager.reclassifyDevice(d1);
         deviceManager.reclassifyDevice(d2);
@@ -2058,7 +2052,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase {
                 classState.classIndex.findByEntity(entity2b);
 
         assertEquals(deviceKey1, deviceKey1b);
-        
+
         assertEquals(deviceKey2, deviceKey2b);
 
 
diff --git a/src/test/java/net/floodlightcontroller/packet/IPv4Test.java b/src/test/java/net/floodlightcontroller/packet/IPv4Test.java
index acea62d25f195b35f012b8714e70983adf84e8af..c62b77602b2124eb9305162ba9e0e7ccd48df6cf 100644
--- a/src/test/java/net/floodlightcontroller/packet/IPv4Test.java
+++ b/src/test/java/net/floodlightcontroller/packet/IPv4Test.java
@@ -107,6 +107,40 @@ public class IPv4Test {
         IPv4 packet = new IPv4();
         packet.deserialize(pktSerializedPadded, 0, pktSerializedPadded.length);
         byte[] pktSerialized1 = packet.serialize();
+        // Padding is ignored
         assertTrue(Arrays.equals(pktSerialized, pktSerialized1));
     }
+
+    @Test
+    public void testDeserializeFragment() {
+        // IP packet with more frag bit set
+        byte[] pktSerializedFirstFrag = new byte[] { 0x45, 0x00,
+                0x00, 0x2e, 0x41, (byte) 0xbe, 0x20, 0x00, 0x40, 0x06,
+                (byte) 0xd4, (byte) 0xf0, (byte) 0xc0, (byte) 0xa8, 0x02, (byte) 0xdb, (byte) 0xd0, 0x55,
+                (byte) 0x90, 0x42, (byte) 0xd5, 0x48, 0x01, (byte) 0xbb, (byte) 0xe3, 0x50,
+                (byte) 0xb2, 0x2f, (byte) 0xfc, (byte) 0xf8, (byte) 0xa8, 0x2c, 0x50, 0x18,
+                (byte) 0xff, (byte) 0xff, 0x24, 0x3c, 0x00, 0x00, 0x14, 0x03,
+                0x01, 0x00, 0x01, 0x01,
+        };
+        IPv4 packet1 = new IPv4();
+        packet1.deserialize(pktSerializedFirstFrag, 0, pktSerializedFirstFrag.length);
+        byte[] pktSerialized1 = packet1.serialize();
+        assertTrue(packet1.isFragment());
+        assertTrue(Arrays.equals(pktSerializedFirstFrag, pktSerialized1));
+
+        // IP packet with offset
+        byte[] pktSerializedLastFrag = new byte[] { 0x45, 0x00,
+                0x00, 0x2e, 0x41, (byte) 0xbe, 0x01, 0x00, 0x40, 0x06,
+                (byte) 0xd4, (byte) 0xf0, (byte) 0xc0, (byte) 0xa8, 0x02, (byte) 0xdb, (byte) 0xd0, 0x55,
+                (byte) 0x90, 0x42, (byte) 0xd5, 0x48, 0x01, (byte) 0xbb, (byte) 0xe3, 0x50,
+                (byte) 0xb2, 0x2f, (byte) 0xfc, (byte) 0xf8, (byte) 0xa8, 0x2c, 0x50, 0x18,
+                (byte) 0xff, (byte) 0xff, 0x24, 0x3c, 0x00, 0x00, 0x14, 0x03,
+                0x01, 0x00, 0x01, 0x01,
+        };
+        IPv4 packet2 = new IPv4();
+        packet2.deserialize(pktSerializedLastFrag, 0, pktSerializedLastFrag.length);
+        byte[] pktSerialized2 = packet2.serialize();
+        assertTrue(packet2.isFragment());
+        assertTrue(Arrays.equals(pktSerializedLastFrag, pktSerialized2));
+    }
 }
diff --git a/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java b/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java
index 578cd9d1ae7fac34c1337529da23f9d5764e0301..d127181907f0ca7036132f039006fd89b5eb3653 100644
--- a/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java
+++ b/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java
@@ -142,6 +142,7 @@ public abstract class StorageTest extends FloodlightTestCase {
     public void setUp() throws Exception {
         super.setUp();
         Set<String> indexedColumnNames = new HashSet<String>();
+        indexedColumnNames.add(PERSON_FIRST_NAME);
         indexedColumnNames.add(PERSON_LAST_NAME);
         storageSource.setExceptionHandler(null);
         storageSource.createTable(PERSON_TABLE_NAME, indexedColumnNames);
@@ -223,6 +224,38 @@ public abstract class StorageTest extends FloodlightTestCase {
         checkExpectedResults(resultSet, columnList, expectedResults);
     }
     
+    @Test
+    public void testEfficientOrQuery() {
+        String[] columnList = {PERSON_FIRST_NAME,PERSON_LAST_NAME};
+        Object[][] expectedResults = {
+                {"John", "Smith"},
+                {"Lisa", "Jones"},
+                {"Susan", "Jones"}
+        };
+        IResultSet resultSet = storageSource.executeQuery(PERSON_TABLE_NAME, columnList,
+                new CompoundPredicate(CompoundPredicate.Operator.OR, false,
+                        new OperatorPredicate(PERSON_LAST_NAME, OperatorPredicate.Operator.EQ, "Jones"),
+                        new OperatorPredicate(PERSON_LAST_NAME, OperatorPredicate.Operator.EQ, "Smith")
+                ),
+                new RowOrdering(PERSON_SSN));
+        checkExpectedResults(resultSet, columnList, expectedResults);
+    }
+
+    @Test
+    public void testEfficientAndQuery() {
+        String[] columnList = {PERSON_FIRST_NAME,PERSON_LAST_NAME};
+        Object[][] expectedResults = {
+                {"Lisa", "Jones"}
+        };
+        IResultSet resultSet = storageSource.executeQuery(PERSON_TABLE_NAME, columnList,
+                new CompoundPredicate(CompoundPredicate.Operator.AND, false,
+                        new OperatorPredicate(PERSON_LAST_NAME, OperatorPredicate.Operator.EQ, "Jones"),
+                        new OperatorPredicate(PERSON_FIRST_NAME, OperatorPredicate.Operator.EQ, "Lisa")
+                ),
+                new RowOrdering(PERSON_SSN));
+        checkExpectedResults(resultSet, columnList, expectedResults);
+    }
+
     @Test
     public void testOrQuery() {
         String[] columnList = {PERSON_FIRST_NAME,PERSON_LAST_NAME, PERSON_AGE};