diff --git a/src/main/java/net/floodlightcontroller/core/IOFConnection.java b/src/main/java/net/floodlightcontroller/core/IOFConnection.java index a14021fbd47b087495b57bdd3b1736966df0a798..21855fd33aeb96714451ae38d5dee1b88dc16383 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFConnection.java +++ b/src/main/java/net/floodlightcontroller/core/IOFConnection.java @@ -1,11 +1,12 @@ package net.floodlightcontroller.core; import java.net.SocketAddress; - import java.util.Date; + import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.U64; /** Contract for an openflow connection to a switch. @@ -58,5 +59,9 @@ public interface IOFConnection extends IOFMessageWriter { */ boolean isConnected(); - + /** + * Get the one-way latency from the switch to the controller. + * @return milliseconds + */ + public U64 getLatency(); } diff --git a/src/main/java/net/floodlightcontroller/core/IOFConnectionBackend.java b/src/main/java/net/floodlightcontroller/core/IOFConnectionBackend.java index 0ec7a9e1b2b081b34c813d5d9dd959028e1e49cc..aef346b86449910e59ddf0bd4938ba92cc34a8ea 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFConnectionBackend.java +++ b/src/main/java/net/floodlightcontroller/core/IOFConnectionBackend.java @@ -1,5 +1,7 @@ package net.floodlightcontroller.core; +import org.projectfloodlight.openflow.types.U64; + import net.floodlightcontroller.core.internal.IOFConnectionListener; public interface IOFConnectionBackend extends IOFConnection { @@ -20,4 +22,21 @@ public interface IOFConnectionBackend extends IOFConnection { /** set the message/closing listener for this connection */ void setListener(IOFConnectionListener listener); + + /** + * Update the present latency between the switch and + * the controller. The latency should be in milliseconds + * and should be one-way. The caller must convert all + * round-trip values to one-way prior to invoking this + * function. + * + * The old link latency being updated will retain X% + * of the value, while the new link latency will attribute + * (100-X)%. This should allow new network configurations to + * quickly overtake old ones but will still allow + * outlier values to be absorbed. + * + * @param latency + */ + public void updateLatency(U64 latency); } diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index df2c58a743918cc6c27aa89ec7e94dd064b65ed6..4cc47498d8534a5cce3f24e78243f4cac7313394 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -35,6 +35,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U64; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; @@ -362,5 +363,10 @@ public interface IOFSwitch extends IOFMessageWriter { * @return */ short getNumTables(); - -} + + /** + * Get the one-way latency from the switch to the controller. + * @return milliseconds + */ + public U64 getLatency(); +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/NullConnection.java b/src/main/java/net/floodlightcontroller/core/NullConnection.java index 41a5a1591bf631e2bd419b2ed996e1294dc3748a..34f225e1343a1f32953f786a61555cf053438f8d 100644 --- a/src/main/java/net/floodlightcontroller/core/NullConnection.java +++ b/src/main/java/net/floodlightcontroller/core/NullConnection.java @@ -2,9 +2,10 @@ package net.floodlightcontroller.core; import java.net.SocketAddress; import java.util.List; - import java.util.Date; + import net.floodlightcontroller.core.internal.IOFConnectionListener; + import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFMessage; @@ -14,6 +15,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.U64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -111,4 +113,13 @@ public class NullConnection implements IOFConnectionBackend, IOFMessageWriter { public void setListener(IOFConnectionListener listener) { } + @Override + public U64 getLatency() { + return U64.ZERO; + } + + @Override + public void updateLatency(U64 latency) { + // noop + } } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/OFConnection.java b/src/main/java/net/floodlightcontroller/core/OFConnection.java index 36efb215975f38dbe22b41433485c0ba2a49d9b1..009b31bb9310b0d4110634fabbbf2b271b5b0b3c 100644 --- a/src/main/java/net/floodlightcontroller/core/OFConnection.java +++ b/src/main/java/net/floodlightcontroller/core/OFConnection.java @@ -51,6 +51,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.U64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,9 +86,10 @@ public class OFConnection implements IOFConnection, IOFConnectionBackend{ private static final long DELIVERABLE_TIME_OUT = 60; private static final TimeUnit DELIVERABLE_TIME_OUT_UNIT = TimeUnit.SECONDS; - private final OFConnectionCounters counters; private IOFConnectionListener listener; + + private volatile U64 latency; public OFConnection(@Nonnull DatapathId dpid, @Nonnull OFFactory factory, @@ -110,6 +112,7 @@ public class OFConnection implements IOFConnection, IOFConnectionBackend{ this.xidDeliverableMap = new ConcurrentHashMap<>(); this.counters = new OFConnectionCounters(debugCounters, dpid, this.auxId); this.timer = timer; + this.latency = U64.ZERO; } @Override @@ -380,6 +383,27 @@ public class OFConnection implements IOFConnection, IOFConnectionBackend{ listener.messageReceived(this, m); } } + + @Override + public U64 getLatency() { + return this.latency; + } + + @Override + public void updateLatency(U64 latency) { + if (latency == null) { + logger.error("Latency must be non-null. Ignoring null latency value."); + return; + } else if (this.latency.equals(U64.ZERO)) { + logger.debug("Recording previously 0ms switch {} latency as {}ms", this.getDatapathId(), latency.getValue()); + this.latency = latency; + return; + } else { + double oldWeight = 0.30; + this.latency = U64.of((long) (this.latency.getValue() * oldWeight + latency.getValue() * (1 - oldWeight))); + logger.debug("Switch {} latency updated to {}ms", this.getDatapathId(), this.latency.getValue()); + } + } /** A dummy connection listener that just logs warn messages. Saves us a few null checks * @author Andreas Wundsam <andreas.wundsam@bigswitch.com> @@ -409,6 +433,5 @@ public class OFConnection implements IOFConnection, IOFConnectionBackend{ // TODO Auto-generated method stub } - } } diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitch.java b/src/main/java/net/floodlightcontroller/core/OFSwitch.java index 146bfe19179531501d1f36c66a0404575f70c7ce..16684795ec488d7b0cdc71ba32218d462a8f1ad3 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitch.java @@ -69,6 +69,7 @@ import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U64; import net.floodlightcontroller.util.LinkedHashSetWrapper; import net.floodlightcontroller.util.OrderedCollection; @@ -124,7 +125,7 @@ public class OFSwitch implements IOFSwitchBackend { private SwitchStatus status; public static final int OFSWITCH_APP_ID = ident(5); - + private TableId maxTableToGetTableMissFlow = TableId.of(4); /* this should cover most HW switches that have a couple SW flow tables */ static { @@ -759,7 +760,7 @@ public class OFSwitch implements IOFSwitchBackend { public void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category) { if (isActive()) { this.getConnection(category).write(msglist); - + for(OFMessage m : msglist) { switchManager.handleOutgoingMessage(this, m); } @@ -793,7 +794,7 @@ public class OFSwitch implements IOFSwitchBackend { public void write(Iterable<OFMessage> msglist) { if (isActive()) { connections.get(OFAuxId.MAIN).write(msglist); - + for(OFMessage m : msglist) { switchManager.handleOutgoingMessage(this, m); } @@ -831,7 +832,7 @@ public class OFSwitch implements IOFSwitchBackend { /* OF1.3+ Per-table actions are set later in the OFTableFeaturesRequest/Reply */ this.actions = featuresReply.getActions(); } - + this.nTables = featuresReply.getNTables(); } @@ -986,7 +987,7 @@ public class OFSwitch implements IOFSwitchBackend { private <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> addInternalStatsReplyListener(final ListenableFuture<List<REPLY>> future, OFStatsRequest<REPLY> request) { switch (request.getStatsType()) { case TABLE_FEATURES: - /* case YOUR_CASE_HERE */ + /* case YOUR_CASE_HERE */ future.addListener(new Runnable() { /* * We know the reply will be a list of OFStatsReply. @@ -1009,7 +1010,7 @@ public class OFSwitch implements IOFSwitchBackend { case TABLE_FEATURES: processOFTableFeatures((List<OFTableFeaturesStatsReply>) future.get()); break; - /* case YOUR_CASE_HERE */ + /* case YOUR_CASE_HERE */ default: throw new Exception("Received an invalid OFStatsReply of " + replies.get(0).getStatsType().toString() + ". Expected TABLE_FEATURES."); @@ -1097,7 +1098,7 @@ public class OFSwitch implements IOFSwitchBackend { public Collection<TableId> getTables() { return new ArrayList<TableId>(tables); } - + @Override public short getNumTables() { return this.nTables; @@ -1236,7 +1237,7 @@ public class OFSwitch implements IOFSwitchBackend { public TableId getMaxTableForTableMissFlow() { return maxTableToGetTableMissFlow; } - + @Override public TableId setMaxTableForTableMissFlow(TableId max) { if (max.getValue() >= nTables) { @@ -1246,4 +1247,9 @@ public class OFSwitch implements IOFSwitchBackend { } return maxTableToGetTableMissFlow; } -} + + @Override + public U64 getLatency() { + return this.connections.get(OFAuxId.MAIN).getLatency(); + } +} \ 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 a6702b74f6b014a498c41c0f9263e5cba8742eaa..8f3cbedb6d168729a0582c41e0630d95fb4c599b 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java @@ -53,6 +53,7 @@ import org.projectfloodlight.openflow.protocol.ver13.OFHelloElemTypeSerializerVe import org.projectfloodlight.openflow.protocol.ver14.OFHelloElemTypeSerializerVer14; import org.projectfloodlight.openflow.types.OFAuxId; import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -84,6 +85,8 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { * We will count down */ private long handshakeTransactionIds = 0x00FFFFFFFFL; + + private volatile long echoSendTime; /** @@ -107,11 +110,12 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { void processOFEchoReply(OFEchoReply m) throws IOException { - // do nothing + /* Update the latency -- halve it for one-way time */ + updateLatency(U64.of( (System.currentTimeMillis() - echoSendTime) / 2) ); } void processOFError(OFErrorMsg m) { - logErrorDisconnect(m); + logErrorDisconnect(m); } void processOFExperimenter(OFExperimenter m) { @@ -880,13 +884,15 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { .build(); channel.write(Collections.singletonList(m)); - log.debug("Send hello: {}", m); + log.debug("Send hello: {}", m); } private void sendEchoRequest() { OFEchoRequest request = factory.buildEchoRequest() .setXid(handshakeTransactionIds--) .build(); + /* Record for latency calculation */ + echoSendTime = System.currentTimeMillis(); channel.write(Collections.singletonList(request)); } @@ -910,4 +916,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { return this.pipeline; } + private void updateLatency(U64 latency) { + if (connection != null) { + connection.updateLatency(latency); + } + } } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index e8f073aa33118a3f71e347e4669f66878c5a9083..26dd12e59f4bbe46364e9e669d0fb941d86a9656 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -378,7 +378,7 @@ IFloodlightModule, IInfoProvider { .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()) + .putLong(System.currentTimeMillis() + iofSwitch.getLatency().getValue() /* account for our switch's one-way latency */) .array(); LLDPTLV timestampTLV = new LLDPTLV() @@ -691,8 +691,8 @@ IFloodlightModule, IInfoProvider { && lldptlv.getValue()[1] == 0x26 && lldptlv.getValue()[2] == (byte) 0xe1 && lldptlv.getValue()[3] == 0x01) { /* 0x01 for timestamp */ - ByteBuffer tsBB = ByteBuffer.wrap(lldptlv.getValue()); - timestamp = tsBB.getLong(4); /* skip OpenFlow OUI (4 bytes above) */ + 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 */ } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8) { otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong(); if (myId == otherId) myLLDP = true; diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml index 487bbc58a2303978334014d2b55dabd86d83ac52..b5e3bfd8093df991c049e5f86e873a9834d12747 100644 --- a/src/main/resources/logback-test.xml +++ b/src/main/resources/logback-test.xml @@ -17,7 +17,7 @@ <logger name="net.floodlightcontroller.devicemanager" level="INFO"></logger> <logger name="net.floodlightcontroller.linkdiscovery" level="INFO"></logger> <logger name="net.floodlightcontroller.forwarding" level="INFO"></logger> - <logger name="net.floodlightcontroller.core.internal" level="INFO"></logger> + <logger name="net.floodlightcontroller.core" level="INFO"></logger> <logger name="net.floodlightcontroller.topology" level="INFO" ></logger> <logger name="org.projectfloodlight.openflow" level="INFO" ></logger> </configuration> diff --git a/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java b/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java index 64631b78afd5cfe93e065ddac678717aec507579..c1eab8ba8baedd748aaddeb089504befb4cad6a0 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java +++ b/src/test/java/net/floodlightcontroller/core/internal/MockOFConnection.java @@ -7,9 +7,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import java.util.Date; + import net.floodlightcontroller.core.IOFConnectionBackend; + import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFRequest; @@ -17,6 +18,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsReply; import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.U64; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; @@ -214,4 +216,16 @@ public class MockOFConnection implements IOFConnectionBackend { public OFMessage retrieveMessage() { return this.messages.remove(0); } + + @Override + public U64 getLatency() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void updateLatency(U64 latency) { + // TODO Auto-generated method stub + + } } diff --git a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java index 845f8bec08124cdce5aaaf6bf2e3eff93121744d..cd7fe7ae38caec6fa499d4fcec08155cf2631ce3 100644 --- a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java +++ b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java @@ -468,6 +468,7 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { expect(sw1.getEnabledPortNumbers()).andReturn(ports).anyTimes(); expect(sw1.getPort(OFPort.of(EasyMock.anyInt()))).andReturn(ofpp).anyTimes(); expect(sw1.getOFFactory()).andReturn(OFFactories.getFactory(OFVersion.OF_13)).anyTimes(); + expect(sw1.getLatency()).andReturn(U64.ZERO).anyTimes(); sw1.write(capture(wc)); expectLastCall().anyTimes(); replay(sw1); diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java index 1416e8353b59fb7b82bf42a5ad95a8d37c2eb10e..c4005b32aaec719cfd0f4f43732bacc941f75bb3 100644 --- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java +++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java @@ -44,6 +44,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsRequest; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U64; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; @@ -348,4 +349,10 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { // TODO Auto-generated method stub return null; } + + @Override + public U64 getLatency() { + // TODO Auto-generated method stub + return null; + } }