[ONOS-4424] Tag LLDP/BDDP source address with fingerprint

Link probes incorporate cluster fingerprint in source
MAC address. This removes the need for an additional TLV and the
complexity associated with it, and also adds fingerprinting to
BDDPs for free.

 - fingerprint in Ethernet source address. The old default MAC
   value is only used when the CusterMetadata service isn't ready.
 - remove support for TLV fingerprint field and associated config
   knobs.
 - links at control domain boundary are classified as EDGE type links.

Change-Id: Idb07dd06fbeee25e9fcee3fbdddec7a7dbb2c397
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
deleted file mode 100644
index 92a1feb..0000000
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/FingerprintProbeFromDevice.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package 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/LldpLinkProvider.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java
index 6ff437a..cd7e2d2 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
@@ -29,7 +29,6 @@
 import org.onlab.util.SharedExecutors;
 import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.cluster.ClusterMetadata;
 import org.onosproject.cluster.ClusterMetadataService;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.core.ApplicationId;
@@ -53,7 +52,7 @@
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.link.DefaultLinkDescription;
-import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.ProbedLinkProvider;
 import org.onosproject.net.link.LinkProviderRegistry;
 import org.onosproject.net.link.LinkProviderService;
 import org.onosproject.net.link.LinkService;
@@ -85,7 +84,6 @@
 import static org.onlab.packet.Ethernet.TYPE_LLDP;
 import static org.onlab.util.Tools.get;
 import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.cluster.ClusterMetadata.NO_NAME;
 import static org.onosproject.net.Link.Type.DIRECT;
 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
 import static org.onosproject.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY;
@@ -96,7 +94,7 @@
  * Provider which uses LLDP and BDDP packets to detect network infrastructure links.
  */
 @Component(immediate = true)
-public class LldpLinkProvider extends AbstractProvider implements LinkProvider {
+public class LldpLinkProvider extends AbstractProvider implements ProbedLinkProvider {
 
     private static final String PROVIDER_NAME = "org.onosproject.provider.lldp";
 
@@ -194,7 +192,6 @@
 
     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,
@@ -218,19 +215,11 @@
                 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();
-                }
             }
     );
 
     private final InternalConfigListener cfgListener = new InternalConfigListener();
 
-
     /**
      * Creates an OpenFlow link provider.
      */
@@ -238,6 +227,17 @@
         super(new ProviderId("lldp", PROVIDER_NAME));
     }
 
+    private final String buildSrcMac() {
+        String srcMac = ProbedLinkProvider.fingerprintMac(clusterMetadataService.getClusterMetadata());
+        String defMac = ProbedLinkProvider.defaultMac();
+        if (srcMac.equals(defMac)) {
+            log.warn("Couldn't generate fingerprint. Using default value {}", defMac);
+            return defMac;
+        }
+        log.trace("Generated MAC address {}", srcMac);
+        return srcMac;
+    }
+
     @Activate
     public void activate(ComponentContext context) {
         cfgService.registerProperties(getClass());
@@ -404,14 +404,6 @@
         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.
      *
@@ -433,11 +425,6 @@
 
         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();
         }
@@ -753,14 +740,13 @@
         }
 
         @Override
-        public String fingerprint() {
-            ClusterMetadata mdata = clusterMetadataService.getClusterMetadata();
-            return mdata == null ? NO_NAME : mdata.getName();
+        public DeviceService deviceService() {
+            return deviceService;
         }
 
         @Override
-        public DeviceService deviceService() {
-            return deviceService;
+        public String fingerprint() {
+            return buildSrcMac();
         }
     }
 
@@ -808,15 +794,6 @@
                         }
                     }
 
-                } 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/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscovery.java b/providers/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscovery.java
index a15a0a2..9d021e4 100644
--- a/providers/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscovery.java
+++ b/providers/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscovery.java
@@ -34,6 +34,7 @@
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.link.ProbedLinkProvider;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
@@ -43,7 +44,6 @@
 import static org.onosproject.net.PortNumber.portNumber;
 import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
 import static org.slf4j.LoggerFactory.getLogger;
-import static org.onosproject.cluster.ClusterMetadata.NO_NAME;
 
 /**
  * Run discovery process from a physical switch. Ports are initially labeled as
@@ -56,8 +56,6 @@
 
     private final Logger log = getLogger(getClass());
 
-    private static final String SRC_MAC = "DE:AD:BE:EF:BA:11";
-
     private final Device device;
     private final LinkDiscoveryContext context;
 
@@ -66,8 +64,6 @@
 
     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();
 
@@ -93,7 +89,6 @@
         bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
         bddpEth.setPad(true);
 
-        fingerprinted = false;
         isStopped = true;
         start();
         log.debug("Started discovery manager for switch {}", device.id());
@@ -160,8 +155,12 @@
 
         ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
         if (onoslldp != null) {
-            if (notMy(onoslldp)) {
-                return true;
+            Type lt;
+            if (notMy(eth.getSourceMAC().toString())) {
+                lt = Type.EDGE;
+            } else {
+                lt = eth.getEtherType() == Ethernet.TYPE_LLDP ?
+                        Type.DIRECT : Type.INDIRECT;
             }
 
             PortNumber srcPort = portNumber(onoslldp.getPort());
@@ -172,10 +171,7 @@
             ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
             ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
 
-            LinkDescription ld = eth.getEtherType() == Ethernet.TYPE_LLDP ?
-                    new DefaultLinkDescription(src, dst, Type.DIRECT) :
-                    new DefaultLinkDescription(src, dst, Type.INDIRECT);
-
+            LinkDescription ld = new DefaultLinkDescription(src, dst, lt);
             try {
                 context.providerService().linkDetected(ld);
                 context.touchLink(LinkKey.linkKey(src, dst));
@@ -188,24 +184,13 @@
     }
 
     // true if *NOT* this cluster's own probe.
-    private boolean notMy(ONOSLLDP onoslldp) {
-        if (onoslldp.getDomainTLV() == null) {
-            // not finger-printed - but we can check the source
-            DeviceId src = DeviceId.deviceId(onoslldp.getDeviceString());
-            if (context.deviceService().getDevice(src) == null) {
-                return true;
-            }
-            return false;
-        }
-
-        String us = context.fingerprint();
-        String them = onoslldp.getDomainString();
-        // if: Our and/or their MetadataService in poorly state, conservative 'yes'
-        if (NO_NAME.equals(us) || NO_NAME.equals(them)) {
+    private boolean notMy(String mac) {
+        // if we are using DEFAULT_MAC, clustering hadn't initialized, so conservative 'yes'
+        String ourMac = context.fingerprint();
+        if (ProbedLinkProvider.defaultMac().equalsIgnoreCase(ourMac)) {
             return true;
-        } else {
-            return !us.equals(them);
         }
+        return !mac.equalsIgnoreCase(ourMac);
     }
 
     /**
@@ -242,7 +227,7 @@
             return null;
         }
         ONOSLLDP lldp = getLinkProbe(port);
-        ethPacket.setSourceMACAddress(SRC_MAC).setPayload(lldp);
+        ethPacket.setSourceMACAddress(context.fingerprint()).setPayload(lldp);
         return new DefaultOutboundPacket(device.id(),
                                          builder().setOutput(portNumber(port)).build(),
                                          ByteBuffer.wrap(ethPacket.serialize()));
@@ -259,18 +244,14 @@
             return null;
         }
         ONOSLLDP lldp = getLinkProbe(port);
-        bddpEth.setSourceMACAddress(SRC_MAC).setPayload(lldp);
+        bddpEth.setSourceMACAddress(context.fingerprint()).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());
+        return ONOSLLDP.onosLLDP(device.id().toString(), device.chassisId(), port.intValue());
     }
 
     private void sendProbes(Long portNumber) {
@@ -286,12 +267,4 @@
     public boolean containsPort(long portNumber) {
         return ports.contains(portNumber);
     }
-
-    public void enableFingerprint() {
-        fingerprinted = true;
-    }
-
-    public void disableFingerprint() {
-        fingerprinted = false;
-    }
 }
diff --git a/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProvider.java b/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProvider.java
index 6851e7e..186024b 100644
--- a/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProvider.java
+++ b/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProvider.java
@@ -29,6 +29,7 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.ONOSLLDP;
+import org.onosproject.cluster.ClusterMetadataService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.mastership.MastershipService;
@@ -50,7 +51,7 @@
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.link.DefaultLinkDescription;
-import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.ProbedLinkProvider;
 import org.onosproject.net.link.LinkProviderRegistry;
 import org.onosproject.net.link.LinkProviderService;
 import org.onosproject.net.packet.InboundPacket;
@@ -77,7 +78,7 @@
 @Component(immediate = true)
 public class NetworkConfigLinksProvider
         extends AbstractProvider
-        implements LinkProvider {
+        implements ProbedLinkProvider {
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected LinkProviderRegistry providerRegistry;
@@ -97,6 +98,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterMetadataService metadataService;
+
     private static final String PROP_PROBE_RATE = "probeRate";
     private static final int DEFAULT_PROBE_RATE = 3000;
     @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE,
@@ -125,6 +129,17 @@
         super(new ProviderId("lldp", PROVIDER_NAME));
     }
 
+    private final String buildSrcMac() {
+        String srcMac = ProbedLinkProvider.fingerprintMac(metadataService.getClusterMetadata());
+        String defMac = ProbedLinkProvider.defaultMac();
+        if (srcMac.equals(defMac)) {
+            log.warn("Couldn't generate fingerprint. Using default value {}", defMac);
+            return defMac;
+        }
+        log.trace("Generated MAC address {}", srcMac);
+        return srcMac;
+    }
+
     private void createLinks() {
         netCfgService.getSubjects(LinkKey.class)
                 .forEach(linkKey -> configuredLinks.add(linkKey));
@@ -166,7 +181,7 @@
         }
 
         LinkDiscovery ld = discoverers.computeIfAbsent(device.id(),
-                                                       did -> new LinkDiscovery(device, context));
+                did -> new LinkDiscovery(device, context));
         if (ld.isStopped()) {
             ld.start();
         }
@@ -240,7 +255,7 @@
 
         @Override
         public String fingerprint() {
-            return "";
+            return buildSrcMac();
         }
 
         @Override
diff --git a/providers/netcfglinks/src/test/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProviderTest.java b/providers/netcfglinks/src/test/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProviderTest.java
index b19b917..7356ebe 100644
--- a/providers/netcfglinks/src/test/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProviderTest.java
+++ b/providers/netcfglinks/src/test/java/org/onosproject/provider/netcfglinks/NetworkConfigLinksProviderTest.java
@@ -24,6 +24,7 @@
 import org.onlab.packet.ChassisId;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.ONOSLLDP;
+import org.onosproject.cluster.ClusterMetadataServiceAdapter;
 import org.onosproject.core.CoreServiceAdapter;
 import org.onosproject.mastership.MastershipServiceAdapter;
 import org.onosproject.net.ConnectPoint;
@@ -232,6 +233,7 @@
         provider.deviceService = new TestDeviceManager();
         provider.masterService = new TestMastershipService();
         provider.packetService = new TestPacketService();
+        provider.metadataService = new ClusterMetadataServiceAdapter();
         provider.netCfgService = configRegistry;
 
         provider.activate();