From a3447938501adeb5774ed1868e1b862166941b5c Mon Sep 17 00:00:00 2001
From: Ryan Izard <rizard@g.clemson.edu>
Date: Mon, 5 Oct 2015 14:27:46 -0400
Subject: [PATCH] Integrated new topology code and updated unit tests to a
 passing state. I'd be more comfortable adding more unit tests to the
 topology. Also made some changes to the link discovery manager's latency
 subsystem. We start with a baseline latency based on the switch's features
 reply turnaround time. This should be relatively quick -- just like an echo.
 Next step for latency is to keep a rolling list of past latencies for more
 defined topology updates based on latency updates. We don't want to update
 latencies too frequently, since tiny changes (or outliers) shouldn't require
 a complete topology recomputation.

---
 .../core/OFConnection.java                    |   2 +-
 .../core/internal/OFChannelHandler.java       |  15 +-
 .../internal/OFSwitchHandshakeHandler.java    |   7 +-
 .../forwarding/Forwarding.java                | 172 +++-----
 .../linkdiscovery/ILinkDiscovery.java         |   2 +
 .../linkdiscovery/LinkInfo.java               |   2 +-
 .../internal/LinkDiscoveryManager.java        |  73 +++-
 .../floodlightcontroller/routing/Link.java    |   5 +-
 .../topology/ITopologyService.java            |  21 +-
 .../topology/TopologyInstance.java            | 395 +++++++++++++-----
 .../topology/TopologyManager.java             |  82 +++-
 src/main/resources/logback-test.xml           |   2 +-
 12 files changed, 532 insertions(+), 246 deletions(-)

diff --git a/src/main/java/net/floodlightcontroller/core/OFConnection.java b/src/main/java/net/floodlightcontroller/core/OFConnection.java
index 009b31bb9..eb59851d1 100644
--- a/src/main/java/net/floodlightcontroller/core/OFConnection.java
+++ b/src/main/java/net/floodlightcontroller/core/OFConnection.java
@@ -434,4 +434,4 @@ public class OFConnection implements IOFConnection, IOFConnectionBackend{
 			
 		}
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
index 8f3cbedb6..437d97b4f 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java
@@ -87,8 +87,8 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	private long handshakeTransactionIds = 0x00FFFFFFFFL;
 	
     private volatile long echoSendTime;
-
-
+    private volatile long featuresLatency;
+    
 	/**
 	 * Default implementation for message handlers in any OFChannelState.
 	 *
@@ -385,8 +385,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 	 * we send capture the features reply.
 	 * Next state is CompleteState
 	 */
-	class WaitFeaturesReplyState extends OFChannelState{
-
+	class WaitFeaturesReplyState extends OFChannelState {
 		WaitFeaturesReplyState() {
 			super(false);
 		}
@@ -394,6 +393,8 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		void processOFFeaturesReply(OFFeaturesReply  m)
 				throws IOException {
 			featuresReply = m;
+			
+			featuresLatency = (System.currentTimeMillis() - featuresLatency) / 2;
 
 			// Mark handshake as completed
 			setState(new CompleteState());
@@ -433,6 +434,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 		@Override
 		void enterState() throws IOException {
 			sendFeaturesRequest();
+			featuresLatency = System.currentTimeMillis();
 		}
 
 		@Override
@@ -474,9 +476,12 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
 					setAuxChannelIdle();
 				}
 			}
+			
+			connection.updateLatency(U64.of(featuresLatency));
+			echoSendTime = 0;
+			
 			// Notify the connection broker
 			notifyConnectionOpened(connection);
-
 		}
 	};
 
diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
index 6ce132a0a..1a1993e21 100644
--- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
+++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java
@@ -1098,8 +1098,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 	 * we send a DescriptionStatsRequest to the switch.
 	 * Next state: WAIT_DESCRIPTION_STAT_REPLY
 	 */
-	public class WaitConfigReplyState extends OFSwitchHandshakeState {
-
+	public class WaitConfigReplyState extends OFSwitchHandshakeState {		
 		WaitConfigReplyState() {
 			super(false);
 		}
@@ -1179,6 +1178,8 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 	 */
 	public class WaitDescriptionStatReplyState extends OFSwitchHandshakeState{
 
+		long timestamp;
+		
 		WaitDescriptionStatReplyState() {
 			super(false);
 		}
@@ -1207,12 +1208,12 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener {
 			if (portDescStats != null) {
 				sw.setPortDescStats(portDescStats);
 			}
+			
 			/*
 			 * Need to add after setting the features.
 			 */
 			switchManager.switchAdded(sw);
 
-
 			// Handle pending messages now that we have a sw object
 			handlePendingPortStatusMessages(description);
 
diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
index 7f07b1a59..32f0ff09c 100644
--- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
@@ -19,17 +19,14 @@ package net.floodlightcontroller.forwarding;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import net.floodlightcontroller.core.FloodlightContext;
 import net.floodlightcontroller.core.IFloodlightProviderService;
 import net.floodlightcontroller.core.IOFSwitch;
-import net.floodlightcontroller.devicemanager.IDevice;
-import net.floodlightcontroller.devicemanager.IDeviceService;
-import net.floodlightcontroller.devicemanager.SwitchPort;
 import net.floodlightcontroller.core.annotations.LogMessageCategory;
 import net.floodlightcontroller.core.annotations.LogMessageDoc;
 import net.floodlightcontroller.core.annotations.LogMessageDocs;
@@ -40,6 +37,9 @@ import net.floodlightcontroller.core.module.IFloodlightModule;
 import net.floodlightcontroller.core.module.IFloodlightService;
 import net.floodlightcontroller.core.util.AppCookie;
 import net.floodlightcontroller.debugcounter.IDebugCounterService;
+import net.floodlightcontroller.devicemanager.IDevice;
+import net.floodlightcontroller.devicemanager.IDeviceService;
+import net.floodlightcontroller.devicemanager.SwitchPort;
 import net.floodlightcontroller.packet.Ethernet;
 import net.floodlightcontroller.packet.IPv4;
 import net.floodlightcontroller.packet.IPv6;
@@ -52,12 +52,13 @@ import net.floodlightcontroller.routing.Route;
 import net.floodlightcontroller.topology.ITopologyService;
 
 import org.projectfloodlight.openflow.protocol.OFFlowMod;
-import org.projectfloodlight.openflow.protocol.match.Match;
-import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.protocol.OFFlowModCommand;
 import org.projectfloodlight.openflow.protocol.OFPacketIn;
 import org.projectfloodlight.openflow.protocol.OFPacketOut;
 import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.EthType;
 import org.projectfloodlight.openflow.types.IPv4Address;
@@ -69,7 +70,6 @@ import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
 import org.projectfloodlight.openflow.types.U64;
 import org.projectfloodlight.openflow.types.VlanVid;
-import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -136,7 +136,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 		OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); // this will be a drop-flow; a flow that will not output to any ports
 		List<OFAction> actions = new ArrayList<OFAction>(); // set no action to drop
 		U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-
+		log.info("Droppingggg");
 		fmb.setCookie(cookie)
 		.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
 		.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
@@ -162,111 +162,82 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 		// Check if we have the location of the destination
 		IDevice dstDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
 
+		DatapathId source=sw.getId();
+		log.info("Packet arrived on switch {}",source);
+		log.info("Port number {}", inPort);
 		if (dstDevice != null) {
 			IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);
-			DatapathId srcIsland = topologyService.getL2DomainId(sw.getId());
+			DatapathId srcIsland = topologyService.getL2DomainId(source);
 
 			if (srcDevice == null) {
-				log.debug("No device entry found for source device");
+				log.info("No device entry found for source device");
 				return;
 			}
 			if (srcIsland == null) {
-				log.debug("No openflow island found for source {}/{}",
+				log.info("No openflow island found for source {}/{}",
 						sw.getId().toString(), inPort);
 				return;
 			}
 
-			// Validate that we have a destination known on the same island
 			// Validate that the source and destination are not on the same switchport
-			boolean on_same_island = false;
+
 			boolean on_same_if = false;
 			for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
-				DatapathId dstSwDpid = dstDap.getSwitchDPID();
-				DatapathId dstIsland = topologyService.getL2DomainId(dstSwDpid);
-				log.trace("Source Island: {}, Destination Island: {}", srcIsland, dstIsland);
-				log.trace("Source Device: {}, Destination Device: {}", srcDevice, dstDevice);
-				if ((dstIsland != null) && dstIsland.equals(srcIsland)) {
-					on_same_island = true;
-					if (sw.getId().equals(dstSwDpid) && inPort.equals(dstDap.getPort())) {
-						on_same_if = true;
-					}
-					break;
+				if (sw.getId().equals(dstDap.getSwitchDPID()) && inPort.equals(dstDap.getPort())) {
+					on_same_if = true;
 				}
-			}
-
-			if (!on_same_island) {
-				// Flood since we don't know the dst device
-				if (log.isTraceEnabled()) {
-					log.trace("No first hop island found for destination " +
-							"device {}, Action = flooding", dstDevice);
-				}
-				doFlood(sw, pi, cntx);
-				return;
+				break;
 			}
 
 			if (on_same_if) {
-				if (log.isTraceEnabled()) {
-					log.trace("Both source and destination are on the same " +
-							"switch/port {}/{}, Action = NOP",
-							sw.toString(), inPort);
-				}
+				log.info("Both source and destination are on the same " +
+						"switch/port {}/{}, Action = NOP",
+						sw.toString(), inPort);
 				return;
 			}
 
-			// Install all the routes where both src and dst have attachment
-			// points.  Since the lists are stored in sorted order we can
-			// traverse the attachment points in O(m+n) time
-			SwitchPort[] srcDaps = srcDevice.getAttachmentPoints();
-			Arrays.sort(srcDaps, clusterIdComparator);
 			SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
-			Arrays.sort(dstDaps, clusterIdComparator);
-
-			int iSrcDaps = 0, iDstDaps = 0;
-
-			while ((iSrcDaps < srcDaps.length) && (iDstDaps < dstDaps.length)) {
-				SwitchPort srcDap = srcDaps[iSrcDaps];
-				SwitchPort dstDap = dstDaps[iDstDaps];
-
-				// srcCluster and dstCluster here cannot be null as
-				// every switch will be at least in its own L2 domain.
-				DatapathId srcCluster = topologyService.getL2DomainId(srcDap.getSwitchDPID());
-				DatapathId dstCluster = topologyService.getL2DomainId(dstDap.getSwitchDPID());
-
-				int srcVsDest = srcCluster.compareTo(dstCluster);
-				if (srcVsDest == 0) {
-					if (!srcDap.equals(dstDap)) {
-						Route route =
-								routingEngineService.getRoute(srcDap.getSwitchDPID(), 
-										srcDap.getPort(),
-										dstDap.getSwitchDPID(),
-										dstDap.getPort(), U64.of(0)); //cookie = 0, i.e., default route
-						if (route != null) {
-							if (log.isTraceEnabled()) {
-								log.trace("pushRoute inPort={} route={} " +
-										"destination={}:{}",
-										new Object[] { inPort, route,
-										dstDap.getSwitchDPID(),
-										dstDap.getPort()});
-							}
-
-							U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-
-							Match m = createMatchFromPacket(sw, inPort, cntx);
-
-							pushRoute(route, m, pi, sw.getId(), cookie,
-									cntx, requestFlowRemovedNotifn, false,
-									OFFlowModCommand.ADD);
-						}
-					}
-					iSrcDaps++;
-					iDstDaps++;
-				} else if (srcVsDest < 0) {
-					iSrcDaps++;
-				} else {
-					iDstDaps++;
+
+			SwitchPort dstDap = null;
+
+			// looking for the last attachment (at the network edge)
+			for (SwitchPort ap : dstDaps){
+				if (topologyService.isEdge(ap.getSwitchDPID(),ap.getPort())){
+					dstDap = ap;
+					break;
 				}
+			}	
+
+			if (!topologyService.isEdge(source, inPort)) {
+				// It's possible that we learned packed destination while it was in flight
+				log.info("Packet destination is known, but packet was not received on the edge port. Flooding... {}");
+				doFlood(sw, pi, cntx);
+				return; 
+			}				
+			Route route = routingEngineService.getRoute(source, 
+					inPort,
+					dstDap.getSwitchDPID(),
+					dstDap.getPort(), U64.of(0)); //cookie = 0, i.e., default route
+
+			if (route != null) {
+
+				log.info("pushRoute inPort={} route={} " +
+						"destination={}:{}",
+						new Object[] { inPort, route,
+						dstDap.getSwitchDPID(),
+						dstDap.getPort()});
+
+
+				U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
+
+				Match m = createMatchFromPacket(sw, inPort, cntx);
+				log.info("Cretaing flow rules on the route, match rule: {}", m);
+				pushRoute(route, m, pi, sw.getId(), cookie,
+						cntx, requestFlowRemovedNotifn, false,
+						OFFlowModCommand.ADD);	
 			}
 		} else {
+			log.info("Destination unknown, flooding");
 			// Flood since we don't know the dst device
 			doFlood(sw, pi, cntx);
 		}
@@ -306,7 +277,6 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 		}
 
 		// TODO Detect switch type and match to create hardware-implemented flow
-		// TODO Allow for IPv6 matches
 		if (eth.getEtherType() == EthType.IPv4) { /* shallow check for equality is okay for EthType */
 			IPv4 ip = (IPv4) eth.getPayload();
 			IPv4Address srcIp = ip.getSourceAddress();
@@ -393,25 +363,17 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 							recommendation=LogMessageDoc.CHECK_SWITCH)
 	protected void doFlood(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
 		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-		if (topologyService.isIncomingBroadcastAllowed(sw.getId(), inPort) == false) {
-			if (log.isTraceEnabled()) {
-				log.trace("doFlood, drop broadcast packet, pi={}, " +
-						"from a blocked port, srcSwitch=[{},{}], linkInfo={}",
-						new Object[] {pi, sw.getId(), inPort});
-			}
-			return;
-		}
-
 		// Set Action to flood
 		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
 		List<OFAction> actions = new ArrayList<OFAction>();
-		if (sw.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) {
-			actions.add(sw.getOFFactory().actions().output(OFPort.FLOOD, Integer.MAX_VALUE)); // FLOOD is a more selective/efficient version of ALL
-		} else {
-			actions.add(sw.getOFFactory().actions().output(OFPort.ALL, Integer.MAX_VALUE));
+		Set<OFPort> broadcastPorts = this.topologyService.getSwitchBroadcastPorts(sw.getId());
+
+		for (OFPort p : broadcastPorts) {
+			if (p.equals(inPort)) continue;
+			actions.add(sw.getOFFactory().actions().output(p, Integer.MAX_VALUE));
 		}
 		pob.setActions(actions);
-
+		// log.info("actions {}",actions);
 		// set buffer-id, in-port and packet-data based on packet-in
 		pob.setBufferId(OFBufferId.NO_BUFFER);
 		pob.setInPort(inPort);
@@ -501,7 +463,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 		}
 		tmp = configParameters.get("priority");
 		if (tmp != null) {
-			FLOWMOD_DEFAULT_PRIORITY = Integer.parseInt(tmp);
+			FLOWMOD_DEFAULT_PRIORITY = 35777;
 			log.info("Default priority set to {}.", FLOWMOD_DEFAULT_PRIORITY);
 		} else {
 			log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY);
@@ -537,4 +499,4 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule {
 	public void startUp(FloodlightModuleContext context) {
 		super.startUp();
 	}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java
index 051f2d412..20ebbc2eb 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java
@@ -86,6 +86,7 @@ public interface ILinkDiscovery {
             this.operation = oper;
             this.src = switchId;
             this.srcType = stype;
+            this.latency = U64.ZERO;
         }
 
         // For port up or port down; and tunnel port added and removed.
@@ -93,6 +94,7 @@ public interface ILinkDiscovery {
             this.src = sw;
             this.srcPort = port;
             this.operation = operation;
+            this.latency = U64.ZERO;
         }
 
         public DatapathId getSrc() {
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java
index 6c2d7334e..ec3f7180b 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java
@@ -49,7 +49,7 @@ public class LinkInfo {
     }
 
     protected Date firstSeenTime;
-    protected Date lastLldpReceivedTime; /* Standard LLLDP received time */
+    protected Date lastLldpReceivedTime; /* Standard LLDP received time */
     protected Date lastBddpReceivedTime; /* Modified LLDP received time  */
 
     /** The port states stored here are topology's last knowledge of
diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
index 26dd12e59..159f14f37 100644
--- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -209,6 +209,12 @@ IFloodlightModule, IInfoProvider {
 	protected LLDPTLV controllerTLV;
 	protected ReentrantReadWriteLock lock;
 	int lldpTimeCount = 0;
+	
+	/*
+	 * Latency tracking
+	 */
+	protected static int LATENCY_HISTORY_SIZE = 4;
+	protected static double LATENCY_UPDATE_THRESHOLD = 0.25;
 
 	/**
 	 * Flag to indicate if automatic port fast is enabled or not. Default is set
@@ -362,7 +368,7 @@ IFloodlightModule, IInfoProvider {
 		} else {
 			lldp.getOptionalTLVList().add(forwardTLV);
 		}
-		
+
 		/* 
 		 * Introduce a new TLV for med-granularity link latency detection.
 		 * If same controller, can assume system clock is the same, but
@@ -373,19 +379,24 @@ IFloodlightModule, IInfoProvider {
 		 * 
 		 * Note Long.SIZE is in bits (64).
 		 */
+		long time = System.currentTimeMillis();
+		long swLatency = iofSwitch.getLatency().getValue();
+		if (log.isDebugEnabled()) {
+			log.debug("SETTING LLDP LATENCY TLV: Current Time {}; {} control plane latency {}; sum {}", new Object[] { time, iofSwitch.getId(), swLatency, time + swLatency });
+		}
 		byte[] timestampTLVValue = ByteBuffer.allocate(Long.SIZE / 8 + 4)
 				.put((byte) 0x00)
 				.put((byte) 0x26)
 				.put((byte) 0xe1)
 				.put((byte) 0x01) /* 0x01 is what we'll use to differentiate DPID (0x00) from time (0x01) */
-				.putLong(System.currentTimeMillis() + iofSwitch.getLatency().getValue() /* account for our switch's one-way latency */)
+				.putLong(time + swLatency /* account for our switch's one-way latency */)
 				.array();
 
 		LLDPTLV timestampTLV = new LLDPTLV()
 		.setType((byte) 127)
 		.setLength((short) timestampTLVValue.length)
 		.setValue(timestampTLVValue);
-		
+
 		/* Now add TLV to our LLDP packet */
 		lldp.getOptionalTLVList().add(timestampTLV);
 
@@ -676,7 +687,7 @@ IFloodlightModule, IInfoProvider {
 		OFPort remotePort = OFPort.of(portBB.getShort());
 		IOFSwitch remoteSwitch = null;
 		long timestamp = 0;
-		
+
 		// Verify this LLDP packet matches what we're looking for
 		for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
 			if (lldptlv.getType() == 127 && lldptlv.getLength() == 12
@@ -692,7 +703,12 @@ IFloodlightModule, IInfoProvider {
 					&& lldptlv.getValue()[2] == (byte) 0xe1
 					&& lldptlv.getValue()[3] == 0x01) { /* 0x01 for timestamp */
 				ByteBuffer tsBB = ByteBuffer.wrap(lldptlv.getValue()); /* skip OpenFlow OUI (4 bytes above) */
-				timestamp = tsBB.getLong(4) + iofSwitch.getLatency().getValue(); /* include the RX switch latency to "subtract" it */
+				long swLatency = iofSwitch.getLatency().getValue();
+				timestamp = tsBB.getLong(4); /* include the RX switch latency to "subtract" it */
+				if (log.isDebugEnabled()) {
+					log.debug("RECEIVED LLDP LATENCY TLV: Got timestamp of {}; Switch {} latency of {}", new Object[] { timestamp, iofSwitch.getId(), iofSwitch.getLatency().getValue() }); 
+				}
+				timestamp = timestamp + swLatency;
 			} else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8) {
 				otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong();
 				if (myId == otherId) myLLDP = true;
@@ -768,7 +784,12 @@ IFloodlightModule, IInfoProvider {
 
 		// Store the time of update to this link, and push it out to
 		// routingEngine
-		U64 latency = timestamp != 0 ? U64.of(System.currentTimeMillis() - timestamp) : U64.ZERO;
+		long time = System.currentTimeMillis();
+		U64 latency = (timestamp != 0 && (time - timestamp) > 0) ? U64.of(time - timestamp) : U64.ZERO;
+		if (log.isDebugEnabled()) {
+			log.debug("COMPUTING FINAL DATAPLANE LATENCY: Current time {}; Dataplane+{} latency {}; Overall latency from {} to {} is {}", 
+					new Object[] { time, iofSwitch.getId(), timestamp, remoteSwitch.getId(), iofSwitch.getId(), String.valueOf(latency.getValue()) });
+		}
 		Link lt = new Link(remoteSwitch.getId(), remotePort,
 				iofSwitch.getId(), inPort, latency); /* we assume 0 latency is undefined */
 
@@ -1303,6 +1324,13 @@ IFloodlightModule, IInfoProvider {
 		return true;
 	}
 
+	/**
+	 * Determine if a link should be updated. This includes 
+	 * @param lt
+	 * @param oldInfo
+	 * @param newInfo
+	 * @return
+	 */
 	protected boolean updateLink(Link lt, LinkInfo oldInfo, LinkInfo newInfo) {
 		boolean linkChanged = false;
 		// Since the link info is already there, we need to
@@ -1331,7 +1359,12 @@ IFloodlightModule, IInfoProvider {
 		} else if (oldTime == null & newTime != null) {
 			linkChanged = true;
 		}
-
+		
+		/*
+		 * TODO Update Link latency
+		 */
+		
+		
 		return linkChanged;
 	}
 
@@ -1351,9 +1384,9 @@ IFloodlightModule, IInfoProvider {
 		try {
 			// put the new info. if an old info exists, it will be returned.
 			LinkInfo oldInfo = links.put(lt, newInfo);
-			if (oldInfo != null
-					&& oldInfo.getFirstSeenTime().getTime() < newInfo.getFirstSeenTime().getTime())
+			if (oldInfo != null && oldInfo.getFirstSeenTime().getTime() < newInfo.getFirstSeenTime().getTime()) {
 				newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime());
+			}
 
 			if (log.isTraceEnabled()) {
 				log.trace("addOrUpdateLink: {} {}",
@@ -1951,9 +1984,29 @@ IFloodlightModule, IInfoProvider {
 				EVENT_HISTORY_SIZE = Short.parseShort(histSize);
 			}
 		} catch (NumberFormatException e) {
-			log.warn("Error event history size, using default of {} seconds", EVENT_HISTORY_SIZE);
+			log.warn("Error event history size. Using default of {} seconds", EVENT_HISTORY_SIZE);
 		}
 		log.debug("Event history size set to {}", EVENT_HISTORY_SIZE);
+		
+		try {
+			String latencyHistorySize = configOptions.get("latency-history-size");
+			if (latencyHistorySize != null) {
+				LATENCY_HISTORY_SIZE = Short.parseShort(latencyHistorySize);
+			}
+		} catch (NumberFormatException e) {
+			log.warn("Error in latency history size. Using default of {} LLDP intervals", LATENCY_HISTORY_SIZE);
+		}
+		log.info("Latency history size set to {}. Link latency updates will happen no sooner than LLDP update intervals of {}", LATENCY_HISTORY_SIZE, LATENCY_HISTORY_SIZE);
+		
+		try {
+			String latencyUpdateThreshold = configOptions.get("latency-update-threshold");
+			if (latencyUpdateThreshold != null) {
+				LATENCY_UPDATE_THRESHOLD = Short.parseShort(latencyUpdateThreshold);
+			}
+		} catch (NumberFormatException e) {
+			log.warn("Error in latency update threshold. Can be from 0 to 1.", LATENCY_UPDATE_THRESHOLD);
+		}
+		log.info("Latency update threshold set to +/-{} ({}%) of last averaged link latency value", LATENCY_UPDATE_THRESHOLD, LATENCY_UPDATE_THRESHOLD * 100);
 
 		// Set the autoportfast feature to false.
 		this.autoPortFastFeature = AUTOPORTFAST_DEFAULT;
diff --git a/src/main/java/net/floodlightcontroller/routing/Link.java b/src/main/java/net/floodlightcontroller/routing/Link.java
index a4017836c..349fb3c81 100755
--- a/src/main/java/net/floodlightcontroller/routing/Link.java
+++ b/src/main/java/net/floodlightcontroller/routing/Link.java
@@ -131,6 +131,8 @@ public class Link implements Comparable<Link> {
                 + ", dst=" + this.dst.toString()
                 + ", inPort="
                 + dstPort.toString()
+                + ", latency="
+                + String.valueOf(latency.getValue())
                 + "]";
     }
     
@@ -158,5 +160,4 @@ public class Link implements Comparable<Link> {
         
         return this.getDstPort().compareTo(a.getDstPort());
     }
-}
-
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java
index e1330bc70..77246df3f 100644
--- a/src/main/java/net/floodlightcontroller/topology/ITopologyService.java
+++ b/src/main/java/net/floodlightcontroller/topology/ITopologyService.java
@@ -17,12 +17,14 @@
 package net.floodlightcontroller.topology;
 
 import java.util.Date;
+import java.util.Map;
 import java.util.Set;
 
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFPort;
 
 import net.floodlightcontroller.core.module.IFloodlightService;
+import net.floodlightcontroller.routing.Link;
 
 public interface ITopologyService extends IFloodlightService  {
 
@@ -39,7 +41,22 @@ public interface ITopologyService extends IFloodlightService  {
 
 	public DatapathId getOpenflowDomainId(DatapathId switchId);
 	public DatapathId getOpenflowDomainId(DatapathId switchId, boolean tunnelEnabled);
-
+	
+	
+	/**
+	 * Returns a complete list of the network links, including intra-cluster and inter-cluster links. Links are grouped for each DatapathId separately.
+	 */
+	public Map<DatapathId, Set<Link>> getAllLinks();
+	
+	/**
+	 * Checks if port "p" is edge port or belongs to one of the detected network links.
+	 */
+   	public boolean isEdge(DatapathId sw, OFPort p);
+	
+	/**
+	 * Returns list of switch ports allowed for broadcast
+	 */
+	public Set<OFPort> getSwitchBroadcastPorts(DatapathId sw);
 	/**
 	 * Returns the identifier of the L2 domain of a given switch.
 	 * @param switchId The DPID of the switch in long form
@@ -210,4 +227,4 @@ public interface ITopologyService extends IFloodlightService  {
 	 * has only quarantined ports. Will never return null.
 	 */
 	public Set<OFPort> getPorts(DatapathId sw);
-}
\ No newline at end of file
+}
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
index 4122bfb4c..5174531f9 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java
@@ -17,6 +17,7 @@
 package net.floodlightcontroller.topology;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -25,6 +26,15 @@ import java.util.Map;
 import java.util.PriorityQueue;
 import java.util.Set;
 
+import net.floodlightcontroller.core.annotations.LogMessageCategory;
+import net.floodlightcontroller.core.annotations.LogMessageDoc;
+import net.floodlightcontroller.routing.BroadcastTree;
+import net.floodlightcontroller.routing.Link;
+import net.floodlightcontroller.routing.Route;
+import net.floodlightcontroller.routing.RouteId;
+import net.floodlightcontroller.servicechaining.ServiceChain;
+import net.floodlightcontroller.util.ClusterDFS;
+
 import org.projectfloodlight.openflow.types.DatapathId;
 import org.projectfloodlight.openflow.types.OFPort;
 import org.projectfloodlight.openflow.types.U64;
@@ -35,15 +45,6 @@ import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 
-import net.floodlightcontroller.util.ClusterDFS;
-import net.floodlightcontroller.core.annotations.LogMessageCategory;
-import net.floodlightcontroller.core.annotations.LogMessageDoc;
-import net.floodlightcontroller.routing.BroadcastTree;
-import net.floodlightcontroller.routing.Link;
-import net.floodlightcontroller.routing.Route;
-import net.floodlightcontroller.routing.RouteId;
-import net.floodlightcontroller.servicechaining.ServiceChain;
-
 /**
  * A representation of a network topology.  Used internally by
  * {@link TopologyManager}
@@ -70,7 +71,7 @@ public class TopologyInstance {
     protected Map<NodePortTuple, Set<Link>> switchPortLinks; // Set of links organized by node port tuple
     /** Set of links that are blocked. */
     protected Set<Link> blockedLinks;
-
+  
     protected Set<DatapathId> switches;
     protected Set<NodePortTuple> broadcastDomainPorts;
     protected Set<NodePortTuple> tunnelPorts;
@@ -80,8 +81,24 @@ public class TopologyInstance {
 
     // States for routing
     protected Map<DatapathId, BroadcastTree> destinationRootedTrees;
-    protected Map<DatapathId, Set<NodePortTuple>> clusterBroadcastNodePorts;
+  
+    protected Map<DatapathId, Set<NodePortTuple>> clusterPorts;
     protected Map<DatapathId, BroadcastTree> clusterBroadcastTrees;
+ 
+    protected Map<DatapathId, Set<NodePortTuple>> clusterBroadcastNodePorts;
+	//Broadcast tree over whole topology which may be consisted of multiple clusters
+    protected BroadcastTree finiteBroadcastTree;
+	//Set of NodePortTuples of the finiteBroadcastTree
+    protected Set<NodePortTuple> broadcastNodePorts;  
+	//destinationRootedTrees over whole topology (not only intra-cluster tree)
+    protected Map<DatapathId, BroadcastTree> destinationRootedFullTrees;
+	//Set of all links organized by node port tuple. Note that switchPortLinks does not contain all links of multi-cluster topology.
+    protected Map<NodePortTuple, Set<Link>> allLinks;
+	//Set of all ports organized by DatapathId. Note that switchPorts map contains only ports with links.
+	protected Map<DatapathId, Set<OFPort>> allPorts;
+	// Maps broadcast ports to DatapathId
+    protected Map<DatapathId, Set<OFPort>> broadcastPortMap;
+    
 
     protected class PathCacheLoader extends CacheLoader<RouteId, Route> {
         TopologyInstance ti;
@@ -91,7 +108,7 @@ public class TopologyInstance {
 
         @Override
         public Route load(RouteId rid) {
-        	return ti.buildroute(rid);
+            return ti.buildroute(rid);
         }
     }
 
@@ -99,61 +116,50 @@ public class TopologyInstance {
     // in the cache.
     private final PathCacheLoader pathCacheLoader = new PathCacheLoader(this);
     protected LoadingCache<RouteId, Route> pathcache;
-
-    public TopologyInstance() {
-        this.switches = new HashSet<DatapathId>();
-        this.switchPorts = new HashMap<DatapathId, Set<OFPort>>();
-        this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
-        this.broadcastDomainPorts = new HashSet<NodePortTuple>();
-        this.tunnelPorts = new HashSet<NodePortTuple>();
-        this.blockedPorts = new HashSet<NodePortTuple>();
-        this.blockedLinks = new HashSet<Link>();
-    }
-
-    public TopologyInstance(Map<DatapathId, Set<OFPort>> switchPorts,
-                            Map<NodePortTuple, Set<Link>> switchPortLinks,
-                            Set<NodePortTuple> broadcastDomainPorts)
-    {
-        this.switches = new HashSet<DatapathId>(switchPorts.keySet());
-        this.switchPorts = new HashMap<DatapathId, Set<OFPort>>(switchPorts);
-        this.switchPortLinks = new HashMap<NodePortTuple,
-                Set<Link>>(switchPortLinks);
-        this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts);
-        this.tunnelPorts = new HashSet<NodePortTuple>();
-        this.blockedPorts = new HashSet<NodePortTuple>();
-        this.blockedLinks = new HashSet<Link>();
-
-        clusters = new HashSet<Cluster>();
-        switchClusterMap = new HashMap<DatapathId, Cluster>();
-    }
+	
     public TopologyInstance(Map<DatapathId, Set<OFPort>> switchPorts,
                             Set<NodePortTuple> blockedPorts,
                             Map<NodePortTuple, Set<Link>> switchPortLinks,
                             Set<NodePortTuple> broadcastDomainPorts,
-                            Set<NodePortTuple> tunnelPorts){
+                            Set<NodePortTuple> tunnelPorts, Map<NodePortTuple, Set<Link>> allLinks, Map<DatapathId, Set<OFPort>> allPorts){
 
         // copy these structures
+	
         this.switches = new HashSet<DatapathId>(switchPorts.keySet());
         this.switchPorts = new HashMap<DatapathId, Set<OFPort>>();
         for(DatapathId sw: switchPorts.keySet()) {
             this.switchPorts.put(sw, new HashSet<OFPort>(switchPorts.get(sw)));
         }
+		
+		this.allPorts = new HashMap<DatapathId, Set<OFPort>>();
+		for(DatapathId sw: allPorts.keySet()) {
+            this.allPorts.put(sw, new HashSet<OFPort>(allPorts.get(sw)));
+        }
 
         this.blockedPorts = new HashSet<NodePortTuple>(blockedPorts);
         this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>();
         for(NodePortTuple npt: switchPortLinks.keySet()) {
             this.switchPortLinks.put(npt,
                                      new HashSet<Link>(switchPortLinks.get(npt)));
+        }
+		this.allLinks = new HashMap<NodePortTuple, Set<Link>>();
+        for(NodePortTuple npt: allLinks.keySet()) {
+            this.allLinks.put(npt,
+                                     new HashSet<Link>(allLinks.get(npt)));
         }
         this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts);
         this.tunnelPorts = new HashSet<NodePortTuple>(tunnelPorts);
 
-        blockedLinks = new HashSet<Link>();
-        clusters = new HashSet<Cluster>();
-        switchClusterMap = new HashMap<DatapathId, Cluster>();
-        destinationRootedTrees = new HashMap<DatapathId, BroadcastTree>();
-        clusterBroadcastTrees = new HashMap<DatapathId, BroadcastTree>();
-        clusterBroadcastNodePorts = new HashMap<DatapathId, Set<NodePortTuple>>();
+        this.blockedLinks = new HashSet<Link>();
+       
+        this.clusters = new HashSet<Cluster>();
+        this.switchClusterMap = new HashMap<DatapathId, Cluster>();
+        this.destinationRootedTrees = new HashMap<DatapathId, BroadcastTree>();
+        this.destinationRootedFullTrees= new HashMap<DatapathId, BroadcastTree>();
+		this.broadcastNodePorts= new HashSet<NodePortTuple>();
+		this.broadcastPortMap = new HashMap<DatapathId,Set<OFPort>>();
+        this.clusterBroadcastTrees = new HashMap<DatapathId, BroadcastTree>();
+        this.clusterBroadcastNodePorts = new HashMap<DatapathId, Set<NodePortTuple>>();
 
         pathcache = CacheBuilder.newBuilder().concurrencyLevel(4)
                     .maximumSize(1000L)
@@ -164,9 +170,8 @@ public class TopologyInstance {
                                 }
                             });
     }
-
+	
     public void compute() {
-
         // Step 1: Compute clusters ignoring broadcast domain links
         // Create nodes for clusters in the higher level topology
         // Must ignore blocked links.
@@ -180,29 +185,64 @@ public class TopologyInstance {
         // unicast routing.  The trees are rooted at the destination.
         // Cost for tunnel links and direct links are the same.
         calculateShortestPathTreeInClusters();
-
-        // Step 3. Compute broadcast tree in each cluster.
+		
+		// Step 3. Compute broadcast tree in each cluster.
         // Cost for tunnel links are high to discourage use of
         // tunnel links.  The cost is set to the number of nodes
         // in the cluster + 1, to use as minimum number of
         // clusters as possible.
         calculateBroadcastNodePortsInClusters();
+        
+        // Step 4. Compute e2e shortest path trees on entire topology for unicast routing.
+		// The trees are rooted at the destination.
+        // Cost for tunnel links and direct links are the same.
+		calculateAllShortestPaths();
+		
+		// Step 5. Compute broadcast tree for the whole topology (needed to avoid loops).
+        // Cost for tunnel links are high to discourage use of
+        // tunnel links.  The cost is set to the number of nodes
+        // in the cluster + 1, to use as minimum number of
+        // clusters as possible.
+        calculateAllBroadcastNodePorts();
 
-        // Step 4. print topology.
+		// Step 6. Compute set of ports for broadcasting. Edge ports are included.
+       	calculateBroadcastPortMap();
+       	
+        // Step 7. print topology.
         printTopology();
     }
 
+	/*
+	 * Checks if OF port is edge port
+	 */
+    public boolean isEdge(DatapathId sw, OFPort portId){ 
+		NodePortTuple np = new NodePortTuple(sw, portId);
+		if (allLinks.get(np) == null){
+			return true;
+		}
+		else {
+			return false;
+		}
+    }   
+
+	/*
+	 * Returns broadcast ports for the given DatapathId
+	 */
+    public Set<OFPort> swBroadcastPorts(DatapathId sw){
+	
+		return this.broadcastPortMap.get(sw);
+    }
+
     public void printTopology() {
-        if (log.isTraceEnabled()) {
-            log.trace("-----------------------------------------------");
-            log.trace("Links: {}",this.switchPortLinks);
-            log.trace("broadcastDomainPorts: {}", broadcastDomainPorts);
-            log.trace("tunnelPorts: {}", tunnelPorts);
-            log.trace("clusters: {}", clusters);
-            log.trace("destinationRootedTrees: {}", destinationRootedTrees);
-            log.trace("clusterBroadcastNodePorts: {}", clusterBroadcastNodePorts);
-            log.trace("-----------------------------------------------");
-        }
+        log.info("-----------------Topology-----------------------");
+        log.info("All Links: {}", allLinks);
+		log.info("Broadcast Tree: {}", finiteBroadcastTree);
+        log.info("Broadcast Domain Ports: {}", broadcastDomainPorts);
+        log.info("Tunnel Ports: {}", tunnelPorts);
+        log.info("Clusters: {}", clusters);
+        log.info("Destination Rooted Full Trees: {}", destinationRootedFullTrees);
+        log.info("Broadcast Node Ports: {}", broadcastNodePorts);
+        log.info("-----------------------------------------------");  
     }
 
     protected void addLinksToOpenflowDomains() {
@@ -480,7 +520,8 @@ public class TopologyInstance {
         }
     }
 
-    protected BroadcastTree dijkstra(Cluster c, DatapathId root,
+	//calculates the broadcast tree in cluster. Old version of code.
+    protected BroadcastTree clusterDijkstra(Cluster c, DatapathId root,
                                      Map<Link, Integer> linkCost,
                                      boolean isDstRooted) {
         HashMap<DatapathId, Link> nexthoplinks = new HashMap<DatapathId, Link>();
@@ -539,12 +580,120 @@ public class TopologyInstance {
         BroadcastTree ret = new BroadcastTree(nexthoplinks, cost);
         return ret;
     }
+    
+	
+	/*
+	 * Dijkstra that calculates destination rooted trees over the entire topology.
+	*/
+    
+    protected BroadcastTree dijkstra(Map<DatapathId, Set<Link>> links, DatapathId root,
+            Map<Link, Integer> linkCost,
+            boolean isDstRooted) {
+    	HashMap<DatapathId, Link> nexthoplinks = new HashMap<DatapathId, Link>();
+    	//HashMap<Long, Long> nexthopnodes = new HashMap<Long, Long>();
+    	HashMap<DatapathId, Integer> cost = new HashMap<DatapathId, Integer>();
+    	int w;
+    	
+    	for (DatapathId node: links.keySet()) {
+    		nexthoplinks.put(node, null);
+    		//nexthopnodes.put(node, null);
+    		cost.put(node, MAX_PATH_WEIGHT);
+    	}
+		
+    	HashMap<DatapathId, Boolean> seen = new HashMap<DatapathId, Boolean>();
+    	PriorityQueue<NodeDist> nodeq = new PriorityQueue<NodeDist>();
+    	nodeq.add(new NodeDist(root, 0));
+    	cost.put(root, 0);
+    	while (nodeq.peek() != null) {
+    		NodeDist n = nodeq.poll();
+    		DatapathId cnode = n.getNode();
+    		int cdist = n.getDist();
+    		if (cdist >= MAX_PATH_WEIGHT) break;
+    		if (seen.containsKey(cnode)) continue;
+    		seen.put(cnode, true);
+
+    		for (Link link: links.get(cnode)) {
+    			DatapathId neighbor;
+
+    			if (isDstRooted == true) neighbor = link.getSrc();
+    			else neighbor = link.getDst();
+        
+    			// links directed toward cnode will result in this condition
+    			if (neighbor.equals(cnode)) continue;
+
+    			if (seen.containsKey(neighbor)) continue;
+
+    			if (linkCost == null || linkCost.get(link)==null) w = 1;
+    			else w = linkCost.get(link);
+    			int ndist = cdist + w; // the weight of the link, always 1 in current version of floodlight.
+    			if (ndist < cost.get(neighbor)) {
+    				cost.put(neighbor, ndist);
+    				nexthoplinks.put(neighbor, link);
+    				NodeDist ndTemp = new NodeDist(neighbor, ndist);
+    				// Remove an object that's already in there.
+    				// Note that the comparison is based on only the node id,
+    				// and not node id and distance.
+    				nodeq.remove(ndTemp);
+    				// add the current object to the queue.
+    				nodeq.add(ndTemp);
+    			}
+    		}
+    	}
+
+    	BroadcastTree ret = new BroadcastTree(nexthoplinks, cost);
+
+		return ret;
+	}
+	
+    /*
+	 * Modification of the calculateShortestPathTreeInClusters (dealing with whole topology, not individual clusters)
+	 */
+    public void calculateAllShortestPaths() {
+    	this.broadcastNodePorts.clear();
+    	this.destinationRootedFullTrees.clear();
+    	Map<Link, Integer> linkCost = new HashMap<Link, Integer>();
+        int tunnel_weight = switchPorts.size() + 1;
+		
+        for(NodePortTuple npt : tunnelPorts) {
+            if (allLinks.get(npt) == null) continue;
+            for(Link link : allLinks.get(npt)) {
+                if (link == null) continue;
+                linkCost.put(link, tunnel_weight);
+            }
+        }
+        
+        Map<DatapathId, Set<Link>> linkDpidMap = new HashMap<DatapathId, Set<Link>>();
+        for(DatapathId s : switches) {
+            if (switchPorts.get(s) == null) continue;
+            for (OFPort p: switchPorts.get(s)) {
+                NodePortTuple np = new NodePortTuple(s, p);
+                if (allLinks.get(np) == null) continue;
+                for(Link l: allLinks.get(np)) {
+                	if(linkDpidMap.containsKey(s)) {
+                		linkDpidMap.get(s).add(l);
+                	}
+                	else {
+                		linkDpidMap.put(s, new HashSet<Link>(Arrays.asList(l)));
+                	}
+                }
+            }
+        }   
+        
+        for (DatapathId node : linkDpidMap.keySet()) {
+        	BroadcastTree tree = dijkstra(linkDpidMap, node, linkCost, true);
+            destinationRootedFullTrees.put(node, tree);
+        }
+		//finiteBroadcastTree is randomly chosen in this implementation
+        if (this.destinationRootedFullTrees.size() > 0) {
+			this.finiteBroadcastTree =  destinationRootedFullTrees.values().iterator().next();
+        }         	
+    }
 
     protected void calculateShortestPathTreeInClusters() {
         pathcache.invalidateAll();
         destinationRootedTrees.clear();
 
-        Map<Link, Integer> linkCost = new HashMap<Link, Integer>();
+        Map <Link, Integer> linkCost = new HashMap<Link, Integer>();
         int tunnel_weight = switchPorts.size() + 1;
 
         for(NodePortTuple npt: tunnelPorts) {
@@ -557,7 +706,7 @@ public class TopologyInstance {
 
         for(Cluster c: clusters) {
             for (DatapathId node : c.links.keySet()) {
-                BroadcastTree tree = dijkstra(c, node, linkCost, true);
+                BroadcastTree tree = clusterDijkstra(c, node, linkCost, true);
                 destinationRootedTrees.put(node, tree);
             }
         }
@@ -570,11 +719,49 @@ public class TopologyInstance {
             clusterBroadcastTrees.put(c.id, tree);
         }
     }
+    
+	protected Set<NodePortTuple> getAllBroadcastNodePorts() {
+		return this.broadcastNodePorts;
+	}
+	
+    protected void calculateAllBroadcastNodePorts() {
+		if (this.destinationRootedFullTrees.size() > 0) {
+			this.finiteBroadcastTree=destinationRootedFullTrees.values().iterator().next();
+			Map<DatapathId, Link> links = finiteBroadcastTree.getLinks();
+			if (links == null) return;
+			for(DatapathId nodeId: links.keySet()) {
+				Link l = links.get(nodeId);
+				if (l == null) continue;
+				NodePortTuple npt1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
+				NodePortTuple npt2 = new NodePortTuple(l.getDst(), l.getDstPort());
+				this.broadcastNodePorts.add(npt1);
+				this.broadcastNodePorts.add(npt2);
+			}    
+		}		
+    }
 
+    protected void calculateBroadcastPortMap(){
+		this.broadcastPortMap.clear();
+		if (destinationRootedFullTrees.size() == 0) return;
+
+		for (DatapathId sw : this.switches) {
+			for (OFPort p : this.allPorts.get(sw)){
+				NodePortTuple npt = new NodePortTuple(sw, p);
+				if (isEdge(sw, p) || broadcastNodePorts.contains(npt)) { 
+					if (broadcastPortMap.containsKey(sw)) {
+                		broadcastPortMap.get(sw).add(p);
+                	}
+                	else {
+                		broadcastPortMap.put(sw,new HashSet<OFPort>(Arrays.asList(p)));
+                	}
+				}      		
+			}
+		}
+    }
+	
     protected void calculateBroadcastNodePortsInClusters() {
-
         clusterBroadcastTrees.clear();
-
+        
         calculateBroadcastTreeInClusters();
 
         for(Cluster c: clusters) {
@@ -585,7 +772,7 @@ public class TopologyInstance {
             Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>();
             Map<DatapathId, Link> links = tree.getLinks();
             if (links == null) continue;
-            for(DatapathId nodeId: links.keySet()) {
+            for (DatapathId nodeId: links.keySet()) {
                 Link l = links.get(nodeId);
                 if (l == null) continue;
                 NodePortTuple npt1 = new NodePortTuple(l.getSrc(), l.getSrcPort());
@@ -601,19 +788,19 @@ public class TopologyInstance {
         NodePortTuple npt;
         DatapathId srcId = id.getSrc();
         DatapathId dstId = id.getDst();
+		//set of NodePortTuples on the route
+        LinkedList<NodePortTuple> sPorts = new LinkedList<NodePortTuple>();
 
-        LinkedList<NodePortTuple> switchPorts = new LinkedList<NodePortTuple>();
+        if (destinationRootedFullTrees == null) return null;
+        if (destinationRootedFullTrees.get(dstId) == null) return null;
 
-        if (destinationRootedTrees == null) return null;
-        if (destinationRootedTrees.get(dstId) == null) return null;
-
-        Map<DatapathId, Link> nexthoplinks = destinationRootedTrees.get(dstId).getLinks();
+        Map<DatapathId, Link> nexthoplinks = destinationRootedFullTrees.get(dstId).getLinks();
 
         if (!switches.contains(srcId) || !switches.contains(dstId)) {
             // This is a switch that is not connected to any other switch
             // hence there was no update for links (and hence it is not
             // in the network)
-            log.debug("buildroute: Standalone switch: {}", srcId);
+            log.info("buildroute: Standalone switch: {}", srcId);
 
             // The only possible non-null path for this case is
             // if srcId equals dstId --- and that too is an 'empty' path []
@@ -621,41 +808,39 @@ public class TopologyInstance {
         } else if ((nexthoplinks!=null) && (nexthoplinks.get(srcId) != null)) {
             while (!srcId.equals(dstId)) {
                 Link l = nexthoplinks.get(srcId);
-
                 npt = new NodePortTuple(l.getSrc(), l.getSrcPort());
-                switchPorts.addLast(npt);
+                sPorts.addLast(npt);
                 npt = new NodePortTuple(l.getDst(), l.getDstPort());
-                switchPorts.addLast(npt);
+                sPorts.addLast(npt);
                 srcId = nexthoplinks.get(srcId).getDst();
             }
         }
         // else, no path exists, and path equals null
 
         Route result = null;
-        if (switchPorts != null && !switchPorts.isEmpty()) {
-            result = new Route(id, switchPorts);
+        if (sPorts != null && !sPorts.isEmpty()) {
+            result = new Route(id, sPorts);
         }
         if (log.isTraceEnabled()) {
             log.trace("buildroute: {}", result);
         }
-        return result; 
+        return result;
     }
 
+    /*
+     * Getter Functions
+     */
+
     protected int getCost(DatapathId srcId, DatapathId dstId) {
         BroadcastTree bt = destinationRootedTrees.get(dstId);
         if (bt == null) return -1;
         return (bt.getCost(srcId));
     }
-
-    /*
-     * Getter Functions
-     */
-
+    
     protected Set<Cluster> getClusters() {
         return clusters;
     }
 
-    // IRoutingEngineService interfaces
     protected boolean routeExists(DatapathId srcId, DatapathId dstId) {
         BroadcastTree bt = destinationRootedTrees.get(dstId);
         if (bt == null) return false;
@@ -664,12 +849,13 @@ public class TopologyInstance {
         return true;
     }
 
+	/*
+	* Calculates E2E route
+	*/
     protected Route getRoute(ServiceChain sc, DatapathId srcId, OFPort srcPort,
             DatapathId dstId, OFPort dstPort, U64 cookie) {
-
-
-        // Return null the route source and desitnation are the
-        // same switchports.
+        // Return null if the route source and destination are the
+        // same switch ports.
         if (srcId.equals(dstId) && srcPort.equals(dstPort))
             return null;
 
@@ -692,6 +878,7 @@ public class TopologyInstance {
         r = new Route(id, nptList);
         return r;
     }
+    
 
     // NOTE: Return a null route if srcId equals dstId.  The null route
     // need not be stored in the cache.  Moreover, the LoadingCache will
@@ -700,7 +887,6 @@ public class TopologyInstance {
         // Return null route if srcId equals dstId
         if (srcId.equals(dstId)) return null;
 
-
         RouteId id = new RouteId(srcId, dstId);
         Route result = null;
 
@@ -770,26 +956,26 @@ public class TopologyInstance {
         return true;
     }
 
-    protected boolean
-    isIncomingBroadcastAllowedOnSwitchPort(DatapathId sw, OFPort portId) {
-        if (isInternalToOpenflowDomain(sw, portId)) {
-            DatapathId clusterId = getOpenflowDomainId(sw);
+    /*
+	 * Takes finiteBroadcastTree into account to prevent loops in the network
+	 */
+    protected boolean isIncomingBroadcastAllowedOnSwitchPort(DatapathId sw, OFPort portId) {
+        if (!isEdge(sw, portId)){       
             NodePortTuple npt = new NodePortTuple(sw, portId);
-            if (clusterBroadcastNodePorts.get(clusterId).contains(npt))
+            if (broadcastNodePorts.contains(npt))
                 return true;
             else return false;
         }
         return true;
     }
 
-    public boolean isConsistent(DatapathId oldSw, OFPort oldPort, DatapathId newSw,
-                                OFPort newPort) {
+
+    public boolean isConsistent(DatapathId oldSw, OFPort oldPort, DatapathId newSw, OFPort newPort) {
         if (isInternalToOpenflowDomain(newSw, newPort)) return true;
         return (oldSw.equals(newSw) && oldPort.equals(newPort));
     }
 
-    protected Set<NodePortTuple>
-    getBroadcastNodePortsInCluster(DatapathId sw) {
+    protected Set<NodePortTuple> getBroadcastNodePortsInCluster(DatapathId sw) {
         DatapathId clusterId = getOpenflowDomainId(sw);
         return clusterBroadcastNodePorts.get(clusterId);
     }
@@ -826,7 +1012,7 @@ public class TopologyInstance {
     public Set<OFPort> getBroadcastPorts(DatapathId targetSw, DatapathId src, OFPort srcPort) {
         Set<OFPort> result = new HashSet<OFPort>();
         DatapathId clusterId = getOpenflowDomainId(targetSw);
-        for(NodePortTuple npt: clusterBroadcastNodePorts.get(clusterId)) {
+        for(NodePortTuple npt: clusterPorts.get(clusterId)) {
             if (npt.getNodeId().equals(targetSw)) {
                 result.add(npt.getPortId());
             }
@@ -834,14 +1020,11 @@ public class TopologyInstance {
         return result;
     }
 
-    public NodePortTuple
-    getAllowedOutgoingBroadcastPort(DatapathId src, OFPort srcPort, DatapathId dst,
-                                    OFPort dstPort) {
+    public NodePortTuple getAllowedOutgoingBroadcastPort(DatapathId src, OFPort srcPort, DatapathId dst, OFPort dstPort) {
         return null;
     }
 
-    public NodePortTuple
-    getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort) {
+    public NodePortTuple getAllowedIncomingBroadcastPort(DatapathId src, OFPort srcPort) {
         return null;
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
index 4d23837bd..2741d39b1 100644
--- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
+++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java
@@ -17,6 +17,7 @@
 package net.floodlightcontroller.topology;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -311,6 +312,8 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 	// **********************
 	// ILinkDiscoveryListener
 	// **********************
+        
+    
 	@Override
 	public void linkDiscoveryUpdate(List<LDUpdate> updateList) {
 		if (log.isTraceEnabled()) {
@@ -326,7 +329,9 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 		}
 		ldUpdates.add(update);
 	}
-
+	
+	
+	
 	// ****************
 	// ITopologyService
 	// ****************
@@ -334,6 +339,46 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 	//
 	// ITopologyService interface methods
 	//
+	
+	@Override
+	public Map<DatapathId, Set<Link>> getAllLinks(){
+		
+		Map<DatapathId, Set<Link>> dpidLinks = new HashMap<DatapathId, Set<Link>>();
+       	TopologyInstance ti = getCurrentInstance(true);
+		Set<DatapathId> switches = ti.getSwitches();
+		
+		for(DatapathId s: switches) {
+            if (this.switchPorts.get(s) == null) continue;
+            for (OFPort p: switchPorts.get(s)) {
+                NodePortTuple np = new NodePortTuple(s, p);
+                if (this.switchPortLinks.get(np) == null) continue;
+                for(Link l: this.switchPortLinks.get(np)) {
+                	if(dpidLinks.containsKey(s)) {
+                		dpidLinks.get(s).add(l);
+                	}
+                	else {
+                		dpidLinks.put(s,new HashSet<Link>(Arrays.asList(l)));
+                	}
+            	
+                }
+            }
+        }
+		
+		return dpidLinks;
+	}
+	
+	@Override
+    	public boolean isEdge(DatapathId sw, OFPort p){
+        TopologyInstance ti = getCurrentInstance(true);
+		return ti.isEdge(sw,p);
+   	}
+
+	@Override
+	public Set<OFPort> getSwitchBroadcastPorts(DatapathId sw){
+		TopologyInstance ti = getCurrentInstance(true);
+		return ti.swBroadcastPorts(sw);
+	}
+	 
 	@Override
 	public Date getLastUpdateTime() {
 		return lastUpdateTime;
@@ -1125,8 +1170,8 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 			switch (update.getOperation()) {
 			case LINK_UPDATED:
 				addOrUpdateLink(update.getSrc(), update.getSrcPort(),
-						update.getDst(), update.getDstPort(), update.getLatency(),
-						update.getType());
+						update.getDst(), update.getDstPort(),
+						update.getLatency(), update.getType());
 				break;
 			case LINK_REMOVED:
 				removeLink(update.getSrc(), update.getSrcPort(),
@@ -1173,6 +1218,8 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 	public boolean createNewInstance() {
 		return createNewInstance("internal");
 	}
+	
+	
 
 	/**
 	 * This function computes a new topology instance.
@@ -1180,12 +1227,17 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 	 * and tunnel ports. The method returns if a new instance of
 	 * topology was created or not.
 	 */
+	 
+	
+
+	
+
 	protected boolean createNewInstance(String reason) {
 		Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>();
-
+        
 		if (!linksUpdated) return false;
-
-		Map<NodePortTuple, Set<Link>> openflowLinks;
+		
+        Map<NodePortTuple, Set<Link>> openflowLinks;
 		openflowLinks =
 				new HashMap<NodePortTuple, Set<Link>>();
 		Set<NodePortTuple> nptList = switchPortLinks.keySet();
@@ -1219,23 +1271,33 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 				removeLinkFromStructure(openflowLinks, link);
 			}
 		}
-
+		//switchPorts contains only ports that are part of links. Calculation of broadcast ports needs set of all ports. 
+		Map<DatapathId, Set<OFPort>> allPorts = new HashMap<DatapathId, Set<OFPort>>();;
+		for (DatapathId sw : switchPorts.keySet()){
+			allPorts.put(sw, this.getPorts(sw));
+		}
+		
 		TopologyInstance nt = new TopologyInstance(switchPorts,
 				blockedPorts,
 				openflowLinks,
 				broadcastDomainPorts,
-				tunnelPorts);
+				tunnelPorts,switchPortLinks,allPorts);
+				
+        log.info("-----------creating Topology instance-------------");
 		nt.compute();
+		
 		// We set the instances with and without tunnels to be identical.
 		// If needed, we may compute them differently.
 		currentInstance = nt;
 		currentInstanceWithoutTunnels = nt;
 
+
 		TopologyEventInfo topologyInfo =
 				new TopologyEventInfo(0, nt.getClusters().size(),
 						new HashMap<DatapathId, List<NodePortTuple>>(),
 						0);
 		eventCategory.newEventWithFlush(new TopologyEvent(reason, topologyInfo));
+	
 		return true;
 	}
 
@@ -1463,7 +1525,7 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 
 	public void removeLink(DatapathId srcId, OFPort srcPort,
 			DatapathId dstId, OFPort dstPort) {
-		Link link = new Link(srcId, srcPort, dstId, dstPort, U64.ZERO /* not needed for lookup */);
+		Link link = new Link(srcId, srcPort, dstId, dstPort, U64.ZERO /* does not matter for remove (not included in .equals() of Link) */);
 		removeLink(link);
 	}
 
@@ -1531,4 +1593,4 @@ public class TopologyManager implements IFloodlightModule, ITopologyService, IRo
 
 		return ports;
 	}
-}
\ No newline at end of file
+}
diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml
index b5e3bfd80..560b32256 100644
--- a/src/main/resources/logback-test.xml
+++ b/src/main/resources/logback-test.xml
@@ -15,7 +15,7 @@
   <logger name="net.floodlightcontroller" level="INFO"/>
   <logger name="org.sdnplatform" level="INFO"></logger>
   <logger name="net.floodlightcontroller.devicemanager" level="INFO"></logger>
-  <logger name="net.floodlightcontroller.linkdiscovery" level="INFO"></logger>
+  <logger name="net.floodlightcontroller.linkdiscovery" level="DEBUG"></logger>
   <logger name="net.floodlightcontroller.forwarding" level="INFO"></logger>
   <logger name="net.floodlightcontroller.core" level="INFO"></logger>
   <logger name="net.floodlightcontroller.topology" level="INFO" ></logger>
-- 
GitLab