[Falcon] link discovery -
- Support for TLV containing cluster fingerprint info
- Config for enabling extra TLV at device level
- Refactored ONOSLLDP constructor for ease of use
Change-Id: I93abe6c0ed8b7e37c80af5920649272faad8856e
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java
index a9da92a..04729e4 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/DiscoveryContext.java
@@ -66,4 +66,11 @@
* @param key link key
*/
void touchLink(LinkKey key);
+
+ /**
+ * Returns the cluster-wide unique identifier.
+ *
+ * @return the cluster identifier
+ */
+ String fingerprint();
}
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/FingerprintProbeFromDevice.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/FingerprintProbeFromDevice.java
new file mode 100644
index 0000000..f6e3c2d
--- /dev/null
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/FingerprintProbeFromDevice.java
@@ -0,0 +1,18 @@
+package org.onosproject.provider.lldp.impl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.basics.BasicFeatureConfig;
+
+/**
+ * A feature to send and receive probes carrying a cluster-unique fingerprint.
+ * Note that, as it leverages LinkDiscovery, disabling linkDiscovery will disable
+ * this function.
+ */
+public class FingerprintProbeFromDevice extends BasicFeatureConfig<DeviceId> {
+
+ protected FingerprintProbeFromDevice() {
+ // default:disabled
+ super(false);
+ }
+
+}
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
index 4b962ae..11b8cd3 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
@@ -16,6 +16,7 @@
package org.onosproject.provider.lldp.impl;
import com.google.common.collect.Sets;
+
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.onlab.packet.Ethernet;
@@ -59,13 +60,13 @@
private final Device device;
private final DiscoveryContext context;
- private final ONOSLLDP lldpPacket;
private final Ethernet ethPacket;
private final Ethernet bddpEth;
private Timeout timeout;
private volatile boolean isStopped;
-
+ // This LinkDiscovery can handle remote link probes (default false).
+ private volatile boolean fingerprinted;
// Set of ports to be probed
private final Set<Long> ports = Sets.newConcurrentHashSet();
@@ -81,22 +82,17 @@
this.device = device;
this.context = context;
- lldpPacket = new ONOSLLDP();
- lldpPacket.setChassisId(device.chassisId());
- lldpPacket.setDevice(device.id().toString());
-
ethPacket = new Ethernet();
ethPacket.setEtherType(Ethernet.TYPE_LLDP);
ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
- ethPacket.setPayload(this.lldpPacket);
ethPacket.setPad(true);
bddpEth = new Ethernet();
- bddpEth.setPayload(lldpPacket);
bddpEth.setEtherType(Ethernet.TYPE_BSN);
bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
bddpEth.setPad(true);
+ fingerprinted = false;
isStopped = true;
start();
log.debug("Started discovery manager for switch {}", device.id());
@@ -220,8 +216,8 @@
if (port == null) {
return null;
}
- lldpPacket.setPortId(port.intValue());
- ethPacket.setSourceMACAddress(SRC_MAC);
+ ONOSLLDP lldp = getLinkProbe(port);
+ ethPacket.setSourceMACAddress(SRC_MAC).setPayload(lldp);
return new DefaultOutboundPacket(device.id(),
builder().setOutput(portNumber(port)).build(),
ByteBuffer.wrap(ethPacket.serialize()));
@@ -237,13 +233,21 @@
if (port == null) {
return null;
}
- lldpPacket.setPortId(port.intValue());
- bddpEth.setSourceMACAddress(SRC_MAC);
+ ONOSLLDP lldp = getLinkProbe(port);
+ bddpEth.setSourceMACAddress(SRC_MAC).setPayload(lldp);
return new DefaultOutboundPacket(device.id(),
builder().setOutput(portNumber(port)).build(),
ByteBuffer.wrap(bddpEth.serialize()));
}
+ private ONOSLLDP getLinkProbe(Long port) {
+ return fingerprinted
+ ? ONOSLLDP.fingerprintedLLDP(device.id().toString(), device.chassisId(),
+ port.intValue(), context.fingerprint())
+ : ONOSLLDP.onosLLDP(device.id().toString(), device.chassisId(),
+ port.intValue());
+ }
+
private void sendProbes(Long portNumber) {
log.trace("Sending probes out to {}@{}", portNumber, device.id());
OutboundPacket pkt = createOutBoundLldp(portNumber);
@@ -258,4 +262,11 @@
return ports.contains(portNumber);
}
+ protected void enableFingerprint() {
+ fingerprinted = true;
+ }
+
+ protected void disableFingerprint() {
+ fingerprinted = false;
+ }
}
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
index ce2826c..a87b6a7 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
@@ -47,6 +47,7 @@
import org.onlab.packet.Ethernet;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterMetadataService;
import org.onosproject.cluster.ClusterService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
@@ -133,6 +134,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry cfgRegistry;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterMetadataService clusterMetadataService;
+
private LinkProviderService providerService;
private ScheduledExecutorService executor;
@@ -185,6 +189,7 @@
public static final String CONFIG_KEY = "suppression";
public static final String FEATURE_NAME = "linkDiscovery";
+ public static final String FINGERPRINT_FEATURE_NAME = "fingerprint";
private final Set<ConfigFactory<?, ?>> factories = ImmutableSet.of(
new ConfigFactory<ApplicationId, SuppressionConfig>(APP_SUBJECT_FACTORY,
@@ -208,6 +213,13 @@
public LinkDiscoveryFromPort createConfig() {
return new LinkDiscoveryFromPort();
}
+ },
+ new ConfigFactory<DeviceId, FingerprintProbeFromDevice>(DEVICE_SUBJECT_FACTORY,
+ FingerprintProbeFromDevice.class, FINGERPRINT_FEATURE_NAME) {
+ @Override
+ public FingerprintProbeFromDevice createConfig() {
+ return new FingerprintProbeFromDevice();
+ }
}
);
@@ -385,6 +397,14 @@
return isBlacklisted(new ConnectPoint(port.element().id(), port.number()));
}
+ private boolean isFingerprinted(DeviceId did) {
+ FingerprintProbeFromDevice cfg = cfgRegistry.getConfig(did, FingerprintProbeFromDevice.class);
+ if (cfg == null) {
+ return false;
+ }
+ return cfg.enabled();
+ }
+
/**
* Updates discovery helper for specified device.
*
@@ -405,6 +425,11 @@
}
LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
did -> new LinkDiscovery(device, context));
+ if (isFingerprinted(device.id())) {
+ ld.enableFingerprint();
+ } else {
+ ld.disableFingerprint();
+ }
if (ld.isStopped()) {
ld.start();
}
@@ -715,6 +740,11 @@
public void touchLink(LinkKey key) {
linkTimes.put(key, System.currentTimeMillis());
}
+
+ @Override
+ public String fingerprint() {
+ return clusterMetadataService.getClusterMetadata().getName();
+ }
}
static final EnumSet<NetworkConfigEvent.Type> CONFIG_CHANGED
@@ -760,6 +790,15 @@
}
}
+ } else if (event.configClass() == FingerprintProbeFromDevice.class &&
+ CONFIG_CHANGED.contains(event.type())) {
+
+ if (event.subject() instanceof DeviceId) {
+ final DeviceId did = (DeviceId) event.subject();
+ Device device = deviceService.getDevice(did);
+ updateDevice(device);
+ }
+
} else if (event.configClass().equals(SuppressionConfig.class) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) {
diff --git a/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java b/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java
index 758c34e..28b08ea 100644
--- a/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java
+++ b/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java
@@ -21,14 +21,22 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.ChassisId;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
import org.onlab.packet.ONOSLLDP;
import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.cluster.ClusterMetadata;
+import org.onosproject.cluster.ClusterMetadataService;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.DefaultControllerNode;
import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.Partition;
import org.onosproject.cluster.RoleInfo;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
@@ -80,7 +88,6 @@
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
-
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
@@ -143,7 +150,7 @@
provider.packetService = packetService;
provider.providerRegistry = linkRegistry;
provider.masterService = masterService;
-
+ provider.clusterMetadataService = new TestMetadataService();
provider.activate(null);
}
@@ -696,11 +703,9 @@
@Override
public InboundPacket inPacket() {
- ONOSLLDP lldp = new ONOSLLDP();
- lldp.setChassisId(device.chassisId());
- lldp.setPortId((int) pd1.number().toLong());
- lldp.setDevice(deviceService.getDevice(DID1).id().toString());
-
+ ONOSLLDP lldp = ONOSLLDP.onosLLDP(deviceService.getDevice(DID1).id().toString(),
+ device.chassisId(),
+ (int) pd1.number().toLong());
Ethernet ethPacket = new Ethernet();
ethPacket.setEtherType(Ethernet.TYPE_LLDP);
@@ -708,8 +713,6 @@
ethPacket.setPayload(lldp);
ethPacket.setPad(true);
-
-
ethPacket.setSourceMACAddress("DE:AD:BE:EF:BA:11");
ConnectPoint cp = new ConnectPoint(device.id(), pd3.number());
@@ -941,4 +944,26 @@
return this;
}
}
+
+ private final class TestMetadataService implements ClusterMetadataService {
+ @Override
+ public ClusterMetadata getClusterMetadata() {
+ final NodeId nid = new NodeId("test-node");
+ final IpAddress addr = IpAddress.valueOf(0);
+ final Partition p = new Partition("test-pt", Sets.newHashSet(nid));
+ return ClusterMetadata.builder()
+ .withName("test-cluster")
+ .withControllerNodes(Sets.newHashSet(new DefaultControllerNode(nid, addr)))
+ .withPartitions(Sets.newHashSet(p)).build();
+ }
+
+ @Override
+ public void setClusterMetadata(ClusterMetadata metadata) {
+ }
+
+ @Override
+ public ControllerNode getLocalNode() {
+ return null;
+ }
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java b/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
index 4d5d58b..78a6ad3 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
@@ -15,14 +15,21 @@
*/
package org.onlab.packet;
+import java.util.HashMap;
+
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
import org.apache.commons.lang.ArrayUtils;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import static org.onlab.packet.LLDPOrganizationalTLV.OUI_LENGTH;
+import static org.onlab.packet.LLDPOrganizationalTLV.SUBTYPE_LENGTH;
+
/**
- * ONOS LLDP containing organizational TLV for ONOS device dicovery.
+ * ONOS LLDP containing organizational TLV for ONOS device discovery.
*/
public class ONOSLLDP extends LLDP {
@@ -37,12 +44,16 @@
public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
- private static final byte NAME_SUBTYPE = 1;
- private static final byte DEVICE_SUBTYPE = 2;
- private static final short NAME_LENGTH = 4; //1 for subtype + 3 for OUI
- private static final short DEVICE_LENGTH = 4; //1 for subtype + 3 for OUI
- private final LLDPOrganizationalTLV nameTLV = new LLDPOrganizationalTLV();
- private final LLDPOrganizationalTLV deviceTLV = new LLDPOrganizationalTLV();
+ protected static final byte NAME_SUBTYPE = 1;
+ protected static final byte DEVICE_SUBTYPE = 2;
+ protected static final byte DOMAIN_SUBTYPE = 3;
+
+ private static final short NAME_LENGTH = OUI_LENGTH + SUBTYPE_LENGTH;
+ private static final short DEVICE_LENGTH = OUI_LENGTH + SUBTYPE_LENGTH;
+ private static final short DOMAIN_LENGTH = OUI_LENGTH + SUBTYPE_LENGTH;
+
+ private final HashMap<Byte, LLDPOrganizationalTLV> opttlvs =
+ Maps.<Byte, LLDPOrganizationalTLV>newHashMap();
// TLV constants: type, size and subtype
// Organizationally specific TLV also have packet offset and contents of TLV
@@ -57,18 +68,24 @@
private static final byte TTL_TLV_TYPE = 3;
-
private final byte[] ttlValue = new byte[] {0, 0x78};
- public ONOSLLDP() {
+ // Only needs to be accessed from LinkProbeFactory.
+ protected ONOSLLDP(byte ... subtype) {
super();
+ for (byte st : subtype) {
+ opttlvs.put(st, new LLDPOrganizationalTLV());
+ }
+ // guarantee the following (name and device) TLVs exist
+ opttlvs.putIfAbsent(NAME_SUBTYPE, new LLDPOrganizationalTLV());
+ opttlvs.putIfAbsent(DEVICE_SUBTYPE, new LLDPOrganizationalTLV());
setName(DEFAULT_NAME);
setDevice(DEFAULT_DEVICE);
- setOptionalTLVList(Lists.<LLDPTLV>newArrayList(nameTLV, deviceTLV));
+
+ setOptionalTLVList(Lists.<LLDPTLV>newArrayList(opttlvs.values()));
setTtl(new LLDPTLV().setType(TTL_TLV_TYPE)
.setLength((short) ttlValue.length)
.setValue(ttlValue));
-
}
private ONOSLLDP(LLDP lldp) {
@@ -79,17 +96,31 @@
}
public void setName(String name) {
- nameTLV.setLength((short) (name.length() + NAME_LENGTH));
- nameTLV.setInfoString(name);
- nameTLV.setSubType(NAME_SUBTYPE);
- nameTLV.setOUI(ONLAB_OUI);
+ LLDPOrganizationalTLV nametlv = opttlvs.get(NAME_SUBTYPE);
+ nametlv.setLength((short) (name.length() + NAME_LENGTH));
+ nametlv.setInfoString(name);
+ nametlv.setSubType(NAME_SUBTYPE);
+ nametlv.setOUI(ONLAB_OUI);
}
public void setDevice(String device) {
- deviceTLV.setInfoString(device);
- deviceTLV.setLength((short) (device.length() + DEVICE_LENGTH));
- deviceTLV.setSubType(DEVICE_SUBTYPE);
- deviceTLV.setOUI(ONLAB_OUI);
+ LLDPOrganizationalTLV devicetlv = opttlvs.get(DEVICE_SUBTYPE);
+ devicetlv.setInfoString(device);
+ devicetlv.setLength((short) (device.length() + DEVICE_LENGTH));
+ devicetlv.setSubType(DEVICE_SUBTYPE);
+ devicetlv.setOUI(ONLAB_OUI);
+ }
+
+ public void setDomainInfo(String domainId) {
+ LLDPOrganizationalTLV domaintlv = opttlvs.get(DOMAIN_SUBTYPE);
+ if (domaintlv == null) {
+ // maybe warn people not to set this if remote probes aren't.
+ return;
+ }
+ domaintlv.setInfoString(domainId);
+ domaintlv.setLength((short) (domainId.length() + DOMAIN_LENGTH));
+ domaintlv.setSubType(DOMAIN_SUBTYPE);
+ domaintlv.setOUI(ONLAB_OUI);
}
public void setChassisId(final ChassisId chassisId) {
@@ -139,6 +170,24 @@
return null;
}
+ /**
+ * Gets the TLV associated with remote probing. This TLV will be null if
+ * remote probing is disabled.
+ *
+ * @return A TLV containing domain ID, or null.
+ */
+ public LLDPOrganizationalTLV getDomainTLV() {
+ for (LLDPTLV tlv : this.getOptionalTLVList()) {
+ if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+ LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
+ if (orgTLV.getSubType() == DOMAIN_SUBTYPE) {
+ return orgTLV;
+ }
+ }
+ }
+ return null;
+ }
+
public String getNameString() {
LLDPOrganizationalTLV tlv = getNameTLV();
if (tlv != null) {
@@ -155,6 +204,14 @@
return null;
}
+ public String getDomainString() {
+ LLDPOrganizationalTLV tlv = getDomainTLV();
+ if (tlv != null) {
+ return new String(tlv.getInfoString(), StandardCharsets.UTF_8);
+ }
+ return null;
+ }
+
public Integer getPort() {
ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
portBB.position(1);
@@ -177,4 +234,40 @@
}
return null;
}
+
+ /**
+ * Creates a link probe for link discovery/verification.
+ *
+ * @param deviceId The device ID as a String
+ * @param chassisId The chassis ID of the device
+ * @param portNum Port number of port to send probe out of
+ * @return ONOSLLDP probe message
+ */
+ public static ONOSLLDP onosLLDP(String deviceId, ChassisId chassisId, int portNum) {
+ ONOSLLDP probe = new ONOSLLDP(NAME_SUBTYPE, DEVICE_SUBTYPE);
+ probe.setPortId(portNum);
+ probe.setDevice(deviceId);
+ probe.setChassisId(chassisId);
+ return probe;
+ }
+
+ /**
+ * Creates a link probe carrying a fingerprint unique to the ONOS cluster managing
+ * link discovery/verification.
+ *
+ * @param deviceId The device ID as a String
+ * @param chassisId The chassis ID of the device
+ * @param portNum Port number of port to send probe out of
+ * @param domainId The cluster's fingerprint
+ * @return ONOSLLDP probe message
+ */
+ public static ONOSLLDP fingerprintedLLDP(
+ String deviceId, ChassisId chassisId, int portNum, String domainId) {
+ ONOSLLDP probe = new ONOSLLDP(NAME_SUBTYPE, DEVICE_SUBTYPE, DOMAIN_SUBTYPE);
+ probe.setPortId(portNum);
+ probe.setDevice(deviceId);
+ probe.setChassisId(chassisId);
+ probe.setDomainInfo(domainId);
+ return probe;
+ }
}