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 d2060e726443d0f9cd495cd175c3ab8bf7aeecd3..b0bdba0356210b0e49e758186e4c6bce58e722c3 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -143,7 +143,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 +160,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 +253,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 +378,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 +399,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 +410,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 +421,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,807 +436,892 @@ 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, or not from us, exit
+        if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) {
+            return Command.CONTINUE;
         }
-    }
-
-    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.
+            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;
+            }
         }
 
-        if (port == OFPort.OFPP_LOCAL.getValue()) return false;
+        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;
+        }
 
-        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;
         }
 
-        return true;
-    }
+        OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
+        int srcPortState = (physicalPort != null) ? physicalPort.getState()
+                                                 : 0;
+        physicalPort = iofSwitch.getPort(inPort);
+        int dstPortState = (physicalPort != null) ? physicalPort.getState()
+                                                 : 0;
 
-    /**
-     * 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;
-        }
-
-        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;
-        }
+        // Store the time of update to this link, and push it out to
+        // routingEngine
+        Link lt = new Link(remoteSwitch.getId(), remotePort,
+                           iofSwitch.getId(), inPort);
 
-        // For fast ports, do not send forward LLDPs or BDDPs.
-        if (!isReverse && autoPortFastFeature && iofSwitch.isFastPort(port))
-            return false;
-        return true;
-    }
+        if (!isLinkAllowed(lt.getSrc(), lt.getSrcPort(),
+                           lt.getDst(), lt.getDstPort()))
+            return Command.STOP;
 
-    /**
-     * 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;
-    }
+        // Continue only if link is allowed.
+        Long lastLldpTime = null;
+        Long lastBddpTime = null;
 
-    public OFPacketOut generateLLDPMessage(long sw, short port,
-                                       boolean isStandard, boolean isReverse) {
+        Long firstSeenTime = System.currentTimeMillis();
 
-        IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw);
-        OFPhysicalPort ofpPort = iofSwitch.getPort(port);
+        if (isStandard)
+            lastLldpTime = System.currentTimeMillis();
+        else
+            lastBddpTime = System.currentTimeMillis();
 
-        if (log.isTraceEnabled()) {
-            log.trace("Sending LLDP packet out of swich: {}, port: {}",
-                      HexString.toHexString(sw), port);
-        }
+        LinkInfo newLinkInfo = new LinkInfo(firstSeenTime, lastLldpTime,
+                                            lastBddpTime, srcPortState,
+                                            dstPortState);
 
-        // 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);
+        addOrUpdateLink(lt, newLinkInfo);
 
-        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);
+        // Continue only if addOrUpdateLink was successful.
 
-        // 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);
-        }
+        // 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.
 
-        // set the portId to the outgoing port
-        portBB.putShort(port);
-        if (log.isTraceEnabled()) {
-            log.trace("Sending LLDP out of interface: {}/{}",
-                      HexString.toHexString(sw), port);
+        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);
-        }
-        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);
 
-        if (ignorePacketInFromSource(eth.getSourceMAC().toLong())) {
-            return Command.STOP;
+        // 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);
+
+        // 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;
@@ -1130,6 +1336,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;
@@ -1271,13 +1488,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.
      */
@@ -1287,7 +1500,7 @@ public class LinkDiscoveryManager implements IOFMessageListener,
 
     /**
      * Removes links from memory and storage.
-     * 
+     *
      * @param links
      *            The List of @LinkTuple to delete.
      */
@@ -1341,243 +1554,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
      */
@@ -1665,72 +1657,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
      */
@@ -1738,22 +1940,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
@@ -1821,6 +2010,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
@@ -1836,9 +2036,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);
@@ -1849,171 +2049,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);
@@ -2022,10 +2095,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);
@@ -2042,7 +2113,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;
 
@@ -2087,8 +2158,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.");
@@ -2189,7 +2259,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;
@@ -2197,9 +2269,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();
         }
@@ -2262,6 +2335,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;
@@ -2276,7 +2353,10 @@ public class LinkDiscoveryManager implements IOFMessageListener,
         return info;
     }
 
-    // IHARoleListener
+    //***************
+    // IHAListener
+    //***************
+
     @Override
     public void roleChanged(Role oldRole, Role newRole) {
         switch (newRole) {
@@ -2309,60 +2389,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");
-    }
 }