Merge branch 'master' into merge

Change-Id: I35af23202e94a114f129f2f000ab237165b26737
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/PurgeInstancePortsCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/PurgeInstancePortsCommand.java
index 431472c..6109a77 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/PurgeInstancePortsCommand.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/PurgeInstancePortsCommand.java
@@ -24,6 +24,7 @@
 import org.onosproject.openstacknetworking.api.InstancePortAdminService;
 
 import static org.onosproject.openstacknetworking.api.InstancePort.State.INACTIVE;
+import static org.onosproject.openstacknetworking.api.InstancePort.State.REMOVE_PENDING;
 
 /**
  * Purges existing instance ports.
@@ -42,6 +43,11 @@
             required = false, multiValued = false)
     private boolean isInactive = false;
 
+    @Option(name = "-p", aliases = "--pending",
+            description = "Instance ports in pending removal state",
+            required = false, multiValued = false)
+    private boolean isPending = false;
+
     @Argument(index = 0, name = "portIds", description = "Instance Port IDs",
             required = false, multiValued = true)
     private String[] portIds = null;
@@ -50,11 +56,12 @@
     protected void doExecute() {
         InstancePortAdminService service = get(InstancePortAdminService.class);
 
-        if ((!isAll && !isInactive && portIds == null) ||
-                (isAll && isInactive) ||
-                (isInactive && portIds != null) ||
-                (portIds != null && isAll)) {
-            print("Please specify one of portIds, --all, and --inactive options.");
+        if ((!isAll && !isInactive && !isPending && portIds == null) ||
+                (isAll && isInactive && isPending) ||
+                (isInactive && isPending && portIds != null) ||
+                (portIds != null && isAll && isPending) ||
+                (isAll && isInactive && portIds != null)) {
+            print("Please specify one of portIds, --all or --inactive or --pending options.");
             return;
         }
 
@@ -65,6 +72,10 @@
             portIds = service.instancePorts().stream()
                              .filter(p -> p.state() == INACTIVE)
                              .map(InstancePort::portId).toArray(String[]::new);
+        } else if (isPending) {
+            portIds = service.instancePorts().stream()
+                             .filter(p -> p.state() == REMOVE_PENDING)
+                             .map(InstancePort::portId).toArray(String[]::new);
         }
 
         for (String portId : portIds) {
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 8c45d4c..731933f 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -476,6 +476,7 @@
     private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
                             OpenstackNode gateway, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(gateway.uplinkPortNum())
                 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
                 .matchArpOp(ARP.OP_REQUEST)
                 .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
@@ -583,7 +584,8 @@
             // do not allow to proceed without leadership
             NodeId leader = leadershipService.getLeader(appId.name());
             return Objects.equals(localNodeId, leader) &&
-                    DEVICE_OWNER_ROUTER_GW.equals(osPort.getDeviceOwner());
+                    DEVICE_OWNER_ROUTER_GW.equals(osPort.getDeviceOwner()) &&
+                        ARP_BROADCAST_MODE.equals(getArpMode());
         }
 
         @Override
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
index 4423bd3..bf3285f 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -894,8 +894,7 @@
                     if (fip != null) {
                         instancePortService.updateInstancePort(
                                             instPort.updateState(REMOVE_PENDING));
-                        eventExecutor.execute(() ->
-                                updateFipStore(instancePortService.instancePort(event.port().getId())));
+                        eventExecutor.execute(() -> updateFipStore(event.port().getId()));
                     } else {
                         // FIXME: we have dependency with security group, need to
                         // find a better way to remove this dependency
@@ -909,9 +908,9 @@
             }
         }
 
-        private void updateFipStore(InstancePort port) {
+        private void updateFipStore(String portId) {
 
-            if (port == null) {
+            if (portId == null) {
                 return;
             }
 
@@ -923,7 +922,7 @@
                 if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
                     continue;
                 }
-                if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
+                if (fip.getPortId().equals(portId)) {
                     NeutronFloatingIP neutronFip = (NeutronFloatingIP) fip;
                     // invalidate bound fixed IP and port
                     neutronFip.setFixedIpAddress(null);
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
index 550fc37..b759bff 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupHandler.java
@@ -25,6 +25,7 @@
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
 import org.onlab.util.Tools;
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.cluster.ClusterService;
@@ -170,6 +171,9 @@
     private static final String INGRESS = "INGRESS";
     private static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
 
+    private static final String VXLAN = "VXLAN";
+    private static final String VLAN = "VLAN";
+
     // We expose pipeline structure to SONA application considering removing pipeline soon.
     private static final int GOTO_CONNTRACK_TABLE = CT_TABLE;
     private static final int GOTO_JUMP_TABLE = JUMP_TABLE;
@@ -283,9 +287,9 @@
         if (sgRule.getRemoteGroupId() != null && !sgRule.getRemoteGroupId().isEmpty()) {
             getRemoteInstPorts(port.getTenantId(), sgRule.getRemoteGroupId())
                     .forEach(rInstPort -> {
-                        populateSecurityGroupRule(sgRule, instPort,
+                        populateSecurityGroupRule(sgRule, instPort, port,
                                 rInstPort.ipAddress().toIpPrefix(), install);
-                        populateSecurityGroupRule(sgRule, rInstPort,
+                        populateSecurityGroupRule(sgRule, rInstPort, port,
                                 instPort.ipAddress().toIpPrefix(), install);
 
                         SecurityGroupRule rSgRule =
@@ -295,13 +299,13 @@
                                 .direction(sgRule.getDirection().toUpperCase()
                                             .equals(EGRESS) ? INGRESS : EGRESS)
                                 .build();
-                        populateSecurityGroupRule(rSgRule, instPort,
+                        populateSecurityGroupRule(rSgRule, instPort, port,
                                 rInstPort.ipAddress().toIpPrefix(), install);
-                        populateSecurityGroupRule(rSgRule, rInstPort,
+                        populateSecurityGroupRule(rSgRule, rInstPort, port,
                                 instPort.ipAddress().toIpPrefix(), install);
                     });
         } else {
-            populateSecurityGroupRule(sgRule, instPort,
+            populateSecurityGroupRule(sgRule, instPort, port,
                     sgRule.getRemoteIpPrefix() == null ? IP_PREFIX_ANY :
                     IpPrefix.valueOf(sgRule.getRemoteIpPrefix()), install);
         }
@@ -309,9 +313,11 @@
 
     private void populateSecurityGroupRule(SecurityGroupRule sgRule,
                                            InstancePort instPort,
-                                           IpPrefix remoteIp, boolean install) {
+                                           Port port,
+                                           IpPrefix remoteIp,
+                                           boolean install) {
         Set<TrafficSelector> selectors = buildSelectors(sgRule,
-                Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp);
+                Ip4Address.valueOf(instPort.ipAddress().toInetAddress()), remoteIp, port);
         if (selectors == null || selectors.isEmpty()) {
             return;
         }
@@ -417,7 +423,8 @@
 
     private Set<TrafficSelector> buildSelectors(SecurityGroupRule sgRule,
                                                 Ip4Address vmIp,
-                                                IpPrefix remoteIp) {
+                                                IpPrefix remoteIp,
+                                                Port port) {
         if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
             // do nothing if the remote IP is my IP
             return null;
@@ -426,7 +433,7 @@
         Set<TrafficSelector> selectorSet = Sets.newHashSet();
 
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        buildMatches(sBuilder, sgRule, vmIp, remoteIp);
+        buildMatches(sBuilder, sgRule, vmIp, remoteIp, port);
 
         if (sgRule.getPortRangeMax() != null && sgRule.getPortRangeMin() != null &&
                 sgRule.getPortRangeMin() < sgRule.getPortRangeMax()) {
@@ -461,7 +468,8 @@
 
     private void buildMatches(TrafficSelector.Builder sBuilder,
                               SecurityGroupRule sgRule,
-                              Ip4Address vmIp, IpPrefix remoteIp) {
+                              Ip4Address vmIp, IpPrefix remoteIp, Port port) {
+        buildTunnelId(sBuilder, port);
         buildMatchEthType(sBuilder, sgRule.getEtherType());
         buildMatchDirection(sBuilder, sgRule.getDirection(), vmIp);
         buildMatchProto(sBuilder, sgRule.getProtocol());
@@ -474,6 +482,17 @@
         }
     }
 
+    private void buildTunnelId(TrafficSelector.Builder sBuilder, Port port) {
+        String segId = osNetService.segmentId(port.getNetworkId());
+        String netType = osNetService.networkType(port.getNetworkId());
+
+        if (VLAN.equals(netType)) {
+            sBuilder.matchVlanId(VlanId.vlanId(segId));
+        } else if (VXLAN.equals(netType)) {
+            sBuilder.matchTunnelId(Long.valueOf(segId));
+        }
+    }
+
     private void buildMatchDirection(TrafficSelector.Builder sBuilder,
                                      String direction,
                                      Ip4Address vmIp) {
@@ -689,8 +708,11 @@
         public void event(InstancePortEvent event) {
             InstancePort instPort = event.subject();
             switch (event.type()) {
-                case OPENSTACK_INSTANCE_PORT_DETECTED:
                 case OPENSTACK_INSTANCE_PORT_UPDATED:
+                    if (instPort.state() == REMOVE_PENDING) {
+                        break;
+                    }
+                case OPENSTACK_INSTANCE_PORT_DETECTED:
                     log.debug("Instance port detected MAC:{} IP:{}",
                             instPort.macAddress(),
                             instPort.ipAddress());
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index 008f484..6d1e038 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -284,6 +284,7 @@
             if (!install) {
                 long numOfDupGws = osNetworkService.subnets().stream()
                         .filter(s -> !s.getId().equals(osSubnet.getId()))
+                        .filter(s -> s.getGateway() != null)
                         .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
                         .count();
                 if (numOfDupGws > 0) {
diff --git a/apps/rabbitmq/src/test/java/org/onosproject/rabbitmq/listener/MQEventHandlerTest.java b/apps/rabbitmq/src/test/java/org/onosproject/rabbitmq/listener/MQEventHandlerTest.java
index d8a2b8f..c1b35c3 100644
--- a/apps/rabbitmq/src/test/java/org/onosproject/rabbitmq/listener/MQEventHandlerTest.java
+++ b/apps/rabbitmq/src/test/java/org/onosproject/rabbitmq/listener/MQEventHandlerTest.java
@@ -327,10 +327,10 @@
 
         @Override
         public InboundPacket inPacket() {
-            ONOSLLDP lldp = ONOSLLDP.onosLLDP(deviceService.getDevice(DID1)
+            ONOSLLDP lldp = ONOSLLDP.onosSecureLLDP(deviceService.getDevice(DID1)
                                               .id().toString(),
-                                              device.chassisId(),
-                                              (int) pd1.number().toLong());
+                                                    device.chassisId(),
+                                                    (int) pd1.number().toLong(), "", "test");
 
             Ethernet ethPacket = new Ethernet();
             ethPacket.setEtherType(Ethernet.TYPE_LLDP);
diff --git a/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java b/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java
index e1f664e..bf3e4a0 100644
--- a/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java
+++ b/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java
@@ -46,6 +46,8 @@
     private final ControllerNode localNode;
     private final Set<ControllerNode> controllerNodes;
     private final Set<Node> storageNodes;
+    private final String clusterSecret;
+
 
     public static final Funnel<ClusterMetadata> HASH_FUNNEL = new Funnel<ClusterMetadata>() {
         @Override
@@ -61,6 +63,30 @@
         localNode = null;
         controllerNodes = null;
         storageNodes = null;
+        clusterSecret = null;
+    }
+
+    /**
+     * @deprecated since 1.15.
+     * @param providerId the provider Id
+     * @param name The cluster Name
+     * @param localNode The local node
+     * @param controllerNodes Set of nodes in cluster
+     * @param storageNodes Set of storage nodes
+     */
+    @Deprecated
+    public ClusterMetadata(
+            ProviderId providerId,
+            String name,
+            ControllerNode localNode,
+            Set<ControllerNode> controllerNodes,
+            Set<Node> storageNodes) {
+        this.providerId = checkNotNull(providerId);
+        this.name = checkNotNull(name);
+        this.localNode = localNode;
+        this.controllerNodes = ImmutableSet.copyOf(checkNotNull(controllerNodes));
+        this.storageNodes = ImmutableSet.copyOf(checkNotNull(storageNodes));
+        this.clusterSecret = "INSECURE!";
     }
 
     public ClusterMetadata(
@@ -68,17 +94,33 @@
         String name,
         ControllerNode localNode,
         Set<ControllerNode> controllerNodes,
-        Set<Node> storageNodes) {
+        Set<Node> storageNodes,
+        String clusterSecret) {
         this.providerId = checkNotNull(providerId);
         this.name = checkNotNull(name);
         this.localNode = localNode;
         this.controllerNodes = ImmutableSet.copyOf(checkNotNull(controllerNodes));
         this.storageNodes = ImmutableSet.copyOf(checkNotNull(storageNodes));
+        this.clusterSecret = clusterSecret;
+    }
+
+    /**
+     * @deprecated since 1.15.
+     * @param name The cluster Name
+     * @param localNode The local node
+     * @param controllerNodes Set of nodes in cluster
+     * @param storageNodes Set of storage nodes
+     */
+    @Deprecated
+    public ClusterMetadata(
+            String name, ControllerNode localNode, Set<ControllerNode> controllerNodes, Set<Node> storageNodes) {
+        this(new ProviderId("none", "none"), name, localNode, controllerNodes, storageNodes, "INSECURE!");
     }
 
     public ClusterMetadata(
-            String name, ControllerNode localNode, Set<ControllerNode> controllerNodes, Set<Node> storageNodes) {
-        this(new ProviderId("none", "none"), name, localNode, controllerNodes, storageNodes);
+            String name, ControllerNode localNode, Set<ControllerNode> controllerNodes, Set<Node> storageNodes,
+            String clusterSecret) {
+        this(new ProviderId("none", "none"), name, localNode, controllerNodes, storageNodes, clusterSecret);
     }
 
     @Override
@@ -140,6 +182,14 @@
         return Collections.emptySet();
     }
 
+    /**
+     * Returns the cluster's shared secret.
+     * @return key.
+     */
+    public String getClusterSecret() {
+        return clusterSecret;
+    }
+
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(ClusterMetadata.class)
diff --git a/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataDiff.java b/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataDiff.java
index acad25f..e9f73b4 100644
--- a/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataDiff.java
+++ b/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataDiff.java
@@ -37,6 +37,7 @@
     private final ClusterMetadata newValue;
     private final Set<ControllerNode> nodesAdded;
     private final Set<NodeId> nodesRemoved;
+    private final boolean secretChanged;
 
      public ClusterMetadataDiff(ClusterMetadata oldValue, ClusterMetadata newValue) {
          this.oldValue = oldValue;
@@ -51,6 +52,20 @@
                             .stream()
                             .map(ControllerNode::id)
                             .collect(Collectors.toSet());
+
+         boolean haveOldSecret = (oldValue != null && oldValue.getClusterSecret() != null);
+         boolean haveNewSecret = (newValue != null && newValue.getClusterSecret() != null);
+
+         if (!haveOldSecret && haveNewSecret) {
+             secretChanged = true;
+         } else if (haveOldSecret && haveNewSecret &&
+                 !oldValue.getClusterSecret().equals(newValue.getClusterSecret())) {
+             secretChanged = true;
+         } else if (haveOldSecret && !haveNewSecret) {
+             secretChanged = true;
+         } else {
+             secretChanged = false;
+         }
      }
 
     /**
@@ -70,6 +85,14 @@
     }
 
     /**
+     * Returns whether the cluster-wide shared secret changed.
+     * @return  whether the cluster secret changed
+     */
+    public boolean clusterSecretChanged() {
+        return secretChanged;
+    }
+
+    /**
      * Returns a mapping of all partition diffs.
      * @return partition diffs.
      */
diff --git a/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataEventTest.java b/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataEventTest.java
index 49ff25f..f0cb6bc 100644
--- a/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataEventTest.java
+++ b/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataEventTest.java
@@ -38,11 +38,11 @@
     private final ControllerNode n2 =
             new DefaultControllerNode(nid2, IpAddress.valueOf("10.0.0.2"), 9876);
     private final ClusterMetadata metadata1 =
-            new ClusterMetadata("foo", n1, ImmutableSet.of(), ImmutableSet.of(n1));
+            new ClusterMetadata("foo", n1, ImmutableSet.of(), ImmutableSet.of(n1), "test");
     private final ClusterMetadata metadata2 =
-            new ClusterMetadata("bar", n1, ImmutableSet.of(), ImmutableSet.of(n1, n2));
+            new ClusterMetadata("bar", n1, ImmutableSet.of(), ImmutableSet.of(n1, n2), "test");
     private final ClusterMetadata metadata3 =
-            new ClusterMetadata("baz", n1, ImmutableSet.of(), ImmutableSet.of(n2));
+            new ClusterMetadata("baz", n1, ImmutableSet.of(), ImmutableSet.of(n2), "test");
 
     private final ClusterMetadataEvent event1 =
             new ClusterMetadataEvent(ClusterMetadataEvent.Type.METADATA_CHANGED, metadata1, time1);
diff --git a/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataServiceAdapter.java b/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataServiceAdapter.java
index f4f6100..8715475 100644
--- a/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataServiceAdapter.java
+++ b/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataServiceAdapter.java
@@ -31,7 +31,8 @@
                 "test-cluster",
                 new DefaultControllerNode(nid, addr),
                 Sets.newHashSet(),
-                Sets.newHashSet());
+                Sets.newHashSet(),
+                "test-secret");
     }
 
     @Override
diff --git a/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataTest.java b/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataTest.java
index 1b32363..04077ea 100644
--- a/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataTest.java
+++ b/core/api/src/test/java/org/onosproject/cluster/ClusterMetadataTest.java
@@ -39,11 +39,11 @@
             new DefaultControllerNode(nid2, IpAddress.valueOf("10.0.0.2"), 9876);
 
     private final ClusterMetadata metadata1 =
-            new ClusterMetadata("foo", n1, ImmutableSet.of(), ImmutableSet.of(n1));
+            new ClusterMetadata("foo", n1, ImmutableSet.of(), ImmutableSet.of(n1), "");
     private final ClusterMetadata sameAsMetadata1 =
-            new ClusterMetadata("foo", n1, ImmutableSet.of(), ImmutableSet.of(n1));
+            new ClusterMetadata("foo", n1, ImmutableSet.of(), ImmutableSet.of(n1), "");
     private final ClusterMetadata metadata2 =
-            new ClusterMetadata("bar", n1, ImmutableSet.of(n1), ImmutableSet.of(n1, n2));
+            new ClusterMetadata("bar", n1, ImmutableSet.of(n1), ImmutableSet.of(n1, n2), "");
     private final ProviderId defaultProvider =
             new ProviderId("none", "none");
     /**
diff --git a/core/net/src/main/java/org/onosproject/cluster/impl/ConfigFileBasedClusterMetadataProvider.java b/core/net/src/main/java/org/onosproject/cluster/impl/ConfigFileBasedClusterMetadataProvider.java
index e07cb259..807a143 100644
--- a/core/net/src/main/java/org/onosproject/cluster/impl/ConfigFileBasedClusterMetadataProvider.java
+++ b/core/net/src/main/java/org/onosproject/cluster/impl/ConfigFileBasedClusterMetadataProvider.java
@@ -121,6 +121,7 @@
                 .stream()
                 .map(this::toPrototype)
                 .collect(Collectors.toSet()));
+        prototype.setClusterSecret(metadata.getClusterSecret());
         return prototype;
     }
 
@@ -276,7 +277,8 @@
                     metadata.getStorage()
                         .stream()
                         .map(node -> new DefaultControllerNode(getNodeId(node), getNodeHost(node), getNodePort(node)))
-                        .collect(Collectors.toSet())),
+                        .collect(Collectors.toSet()),
+                    metadata.getClusterSecret()),
                 version);
         } catch (IOException e) {
             throw new IllegalArgumentException(e);
@@ -309,6 +311,7 @@
         private NodePrototype node;
         private Set<NodePrototype> controller = Sets.newHashSet();
         private Set<NodePrototype> storage = Sets.newHashSet();
+        private String clusterSecret;
 
         public String getName() {
             return name;
@@ -341,6 +344,14 @@
         public void setStorage(Set<NodePrototype> storage) {
             this.storage = storage;
         }
+
+        public void setClusterSecret(String clusterSecret) {
+            this.clusterSecret = clusterSecret;
+        }
+
+        public String getClusterSecret() {
+            return clusterSecret;
+        }
     }
 
     private static class NodePrototype {
@@ -381,4 +392,4 @@
             this.port = port;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/net/src/main/java/org/onosproject/cluster/impl/DefaultClusterMetadataProvider.java b/core/net/src/main/java/org/onosproject/cluster/impl/DefaultClusterMetadataProvider.java
index 153bd65..2facc1c 100644
--- a/core/net/src/main/java/org/onosproject/cluster/impl/DefaultClusterMetadataProvider.java
+++ b/core/net/src/main/java/org/onosproject/cluster/impl/DefaultClusterMetadataProvider.java
@@ -20,6 +20,7 @@
 import java.net.NetworkInterface;
 import java.util.Collections;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 
@@ -73,7 +74,8 @@
         ControllerNode localNode =
                 new DefaultControllerNode(new NodeId(localIp), IpAddress.valueOf(localIp), DEFAULT_ONOS_PORT);
         ClusterMetadata metadata = new ClusterMetadata(
-            PROVIDER_ID, "default", localNode, ImmutableSet.of(), ImmutableSet.of());
+            PROVIDER_ID, "default", localNode, ImmutableSet.of(), ImmutableSet.of(),
+            UUID.randomUUID().toString());
         long version = System.currentTimeMillis();
         cachedMetadata.set(new Versioned<>(metadata, version));
         providerRegistry.register(this);
diff --git a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java
index 843231e..e935920 100644
--- a/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java
+++ b/drivers/gnmi/src/main/java/org/onosproject/drivers/gnmi/GnmiDeviceDescriptionDiscovery.java
@@ -290,7 +290,7 @@
             case "SPEED_10MB":
                 return 10;
             case "SPEED_100MB":
-                return 10;
+                return 100;
             case "SPEED_1GB":
                 return 1000;
             case "SPEED_10GB":
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 0ede2f3..963bac6 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
@@ -99,6 +99,7 @@
                 PROP_USE_BDDP + ":Boolean=" + USE_BDDP_DEFAULT,
                 PROP_PROBE_RATE + ":Integer=" + PROBE_RATE_DEFAULT,
                 PROP_STALE_LINK_AGE + ":Integer=" + STALE_LINK_AGE_DEFAULT,
+                PROP_DISCOVERY_DELAY + ":Integer=" + DISCOVERY_DELAY_DEFAULT,
         })
 public class LldpLinkProvider extends AbstractProvider implements ProbedLinkProvider {
 
@@ -106,7 +107,7 @@
 
     private static final String FORMAT =
             "Settings: enabled={}, useBDDP={}, probeRate={}, " +
-                    "staleLinkAge={}";
+                    "staleLinkAge={}, maxLLDPage={}";
 
     // When a Device/Port has this annotation, do not send out LLDP/BDDP
     public static final String NO_LLDP = "no-lldp";
@@ -170,6 +171,10 @@
     //        label = "Number of millis beyond which links will be considered stale")
     protected int staleLinkAge = STALE_LINK_AGE_DEFAULT;
 
+    //@Property(name = PROP_DISCOVERY_DELAY, intValue = DEFAULT_DISCOVERY_DELAY,
+    //        label = "Number of millis beyond which an LLDP packet will not be accepted")
+    private int maxDiscoveryDelayMs = DISCOVERY_DELAY_DEFAULT;
+
     private final LinkDiscoveryContext context = new InternalDiscoveryContext();
     private final InternalRoleListener roleListener = new InternalRoleListener();
     private final InternalDeviceListener deviceListener = new InternalDeviceListener();
@@ -293,7 +298,7 @@
         Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
 
         boolean newEnabled, newUseBddp;
-        int newProbeRate, newStaleLinkAge;
+        int newProbeRate, newStaleLinkAge, newDiscoveryDelay;
         try {
             String s = get(properties, PROP_ENABLED);
             newEnabled = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim());
@@ -307,12 +312,16 @@
             s = get(properties, PROP_STALE_LINK_AGE);
             newStaleLinkAge = isNullOrEmpty(s) ? staleLinkAge : Integer.parseInt(s.trim());
 
+            s = get(properties, PROP_DISCOVERY_DELAY);
+            newDiscoveryDelay = isNullOrEmpty(s) ? maxDiscoveryDelayMs : Integer.parseInt(s.trim());
+
         } catch (NumberFormatException e) {
             log.warn("Component configuration had invalid values", e);
             newEnabled = enabled;
             newUseBddp = useBddp;
             newProbeRate = probeRate;
             newStaleLinkAge = staleLinkAge;
+            newDiscoveryDelay = maxDiscoveryDelayMs;
         }
 
         boolean wasEnabled = enabled;
@@ -321,6 +330,7 @@
         useBddp = newUseBddp;
         probeRate = newProbeRate;
         staleLinkAge = newStaleLinkAge;
+        maxDiscoveryDelayMs = newDiscoveryDelay;
 
         if (!wasEnabled && enabled) {
             enable();
@@ -333,7 +343,7 @@
             }
         }
 
-        log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge);
+        log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge, maxDiscoveryDelayMs);
     }
 
     /**
@@ -791,6 +801,16 @@
         public String fingerprint() {
             return buildSrcMac();
         }
+
+        @Override
+        public String lldpSecret() {
+            return clusterMetadataService.getClusterMetadata().getClusterSecret();
+        }
+
+        @Override
+        public long maxDiscoveryDelay() {
+            return maxDiscoveryDelayMs;
+        }
     }
 
     static final EnumSet<NetworkConfigEvent.Type> CONFIG_CHANGED
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/OsgiPropertyConstants.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/OsgiPropertyConstants.java
index 95fb99c0..e67f3d8 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/OsgiPropertyConstants.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/OsgiPropertyConstants.java
@@ -35,4 +35,7 @@
     public static final String PROP_STALE_LINK_AGE = "staleLinkAge";
     public static final int STALE_LINK_AGE_DEFAULT = 10000;
 
+    public static final String PROP_DISCOVERY_DELAY = "maxLLDPAge";
+    public static final int DISCOVERY_DELAY_DEFAULT = 1000;
+
 }
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 77341c2..8e53c9a 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
@@ -652,9 +652,9 @@
 
         @Override
         public InboundPacket inPacket() {
-            ONOSLLDP lldp = ONOSLLDP.onosLLDP(deviceService.getDevice(DID1).id().toString(),
-                                              device.chassisId(),
-                                              (int) pd1.number().toLong());
+            ONOSLLDP lldp = ONOSLLDP.onosSecureLLDP(deviceService.getDevice(DID1).id().toString(),
+                                                    device.chassisId(),
+                                                    (int) pd1.number().toLong(), "", "test");
 
             Ethernet ethPacket = new Ethernet();
             ethPacket.setEtherType(Ethernet.TYPE_LLDP);
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 317fda8..3d69235 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
@@ -173,6 +173,12 @@
             } else {
                 lt = eth.getEtherType() == Ethernet.TYPE_LLDP ?
                         Type.DIRECT : Type.INDIRECT;
+
+                /* Verify MAC in LLDP packets */
+                if (!ONOSLLDP.verify(onoslldp, context.lldpSecret(), context.maxDiscoveryDelay())) {
+                    log.warn("LLDP Packet failed to validate!");
+                    return true;
+                }
             }
 
             PortNumber srcPort = portNumber(onoslldp.getPort());
@@ -269,7 +275,8 @@
     }
 
     private ONOSLLDP getLinkProbe(Long portNumber, String portDesc) {
-        return ONOSLLDP.onosLLDP(device.id().toString(), device.chassisId(), portNumber.intValue(), portDesc);
+        return ONOSLLDP.onosSecureLLDP(device.id().toString(), device.chassisId(), portNumber.intValue(), portDesc,
+                                       context.lldpSecret());
     }
 
     private void sendProbes(Long portNumber, String portDesc) {
diff --git a/providers/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscoveryContext.java b/providers/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscoveryContext.java
index e4a025e..a325b95 100644
--- a/providers/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscoveryContext.java
+++ b/providers/lldpcommon/src/main/java/org/onosproject/provider/lldpcommon/LinkDiscoveryContext.java
@@ -81,4 +81,18 @@
      * @return the cluster identifier
      */
     String fingerprint();
+
+    /**
+     * Returns the cluster-wide MAC secret used to secure LLDP packets.
+     *
+     * @return the secret
+     */
+    String lldpSecret();
+
+    /**
+     * Returns the maximum delay in milliseconds between sending an LLDP packet and receiving it elsewhere.
+     *
+     * @return delay in ms
+     */
+    long maxDiscoveryDelay();
 }
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 6092cec..06de0f9 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
@@ -68,6 +68,8 @@
 import static org.onlab.packet.Ethernet.TYPE_BSN;
 import static org.onlab.packet.Ethernet.TYPE_LLDP;
 import static org.onosproject.net.PortNumber.portNumber;
+import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.DISCOVERY_DELAY_DEFAULT;
+import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROP_DISCOVERY_DELAY;
 import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROP_PROBE_RATE;
 import static org.onosproject.provider.netcfglinks.OsgiPropertyConstants.PROBE_RATE_DEFAULT;
 
@@ -79,6 +81,7 @@
 @Component(immediate = true,
         property = {
             PROP_PROBE_RATE + ":Integer=" + PROBE_RATE_DEFAULT,
+            PROP_DISCOVERY_DELAY + ":Integer=" + DISCOVERY_DELAY_DEFAULT,
         })
 public class NetworkConfigLinksProvider
         extends AbstractProvider
@@ -109,6 +112,10 @@
     //        label = "LLDP and BDDP probe rate specified in millis")
     private int probeRate = PROBE_RATE_DEFAULT;
 
+    //@Property(name = PROP_DISCOVERY_DELAY, intValue = DEFAULT_DISCOVERY_DELAY,
+    //        label = "Number of millis beyond which an LLDP packet will not be accepted")
+    private int maxDiscoveryDelayMs = DISCOVERY_DELAY_DEFAULT;
+
     // Device link discovery helpers.
     protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
 
@@ -267,8 +274,29 @@
         public DeviceService deviceService() {
             return deviceService;
         }
+
+        @Override
+        public String lldpSecret() {
+            return metadataService.getClusterMetadata().getClusterSecret();
+        }
+
+        @Override
+        public long maxDiscoveryDelay() {
+            return maxDiscoveryDelayMs;
+        }
     }
 
+    // true if *NOT* this cluster's own probe.
+    private boolean isOthercluster(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;
+        }
+        return !mac.equalsIgnoreCase(ourMac);
+    }
+
+    //doesn't validate. Used just to decide if this is expected link.
     LinkKey extractLinkKey(PacketContext packetContext) {
         Ethernet eth = packetContext.inPacket().parsed();
         if (eth == null) {
@@ -289,6 +317,27 @@
         return null;
     }
 
+    private boolean verify(PacketContext packetContext) {
+        Ethernet eth = packetContext.inPacket().parsed();
+        if (eth == null) {
+            return false;
+        }
+
+        ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
+        if (onoslldp != null) {
+            if (!isOthercluster(eth.getSourceMAC().toString())) {
+                return false;
+            }
+
+            if (!ONOSLLDP.verify(onoslldp, context.lldpSecret(), context.maxDiscoveryDelay())) {
+                log.warn("LLDP Packet failed to validate!");
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Removes after stopping discovery helper for specified device.
      * @param deviceId device to remove
@@ -346,13 +395,15 @@
                         context.block();
                     }
                 } else {
-                    log.debug("Found link that was not in the configuration {}", linkKey);
-                    providerService.linkDetected(
-                            new DefaultLinkDescription(linkKey.src(),
-                                                       linkKey.dst(),
-                                                       Link.Type.DIRECT,
-                                                       DefaultLinkDescription.NOT_EXPECTED,
-                                                       DefaultAnnotations.EMPTY));
+                    if (verify(context)) {
+                        log.debug("Found link that was not in the configuration {}", linkKey);
+                        providerService.linkDetected(
+                                new DefaultLinkDescription(linkKey.src(),
+                                                           linkKey.dst(),
+                                                           Link.Type.DIRECT,
+                                                           DefaultLinkDescription.NOT_EXPECTED,
+                                                           DefaultAnnotations.EMPTY));
+                    }
                 }
             }
         }
diff --git a/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/OsgiPropertyConstants.java b/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/OsgiPropertyConstants.java
index c04dedb..5d718ed 100644
--- a/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/OsgiPropertyConstants.java
+++ b/providers/netcfglinks/src/main/java/org/onosproject/provider/netcfglinks/OsgiPropertyConstants.java
@@ -26,4 +26,7 @@
     public static final String PROP_PROBE_RATE = "probeRate";
     public static final int PROBE_RATE_DEFAULT = 3000;
 
+    public static final String PROP_DISCOVERY_DELAY = "maxLLDPAge";
+    public static final int DISCOVERY_DELAY_DEFAULT = 1000;
+
 }
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 117201a..13081f0 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
@@ -147,9 +147,9 @@
 
         @Override
         public InboundPacket inPacket() {
-            ONOSLLDP lldp = ONOSLLDP.onosLLDP(src.deviceId().toString(),
-                                              new ChassisId(),
-                                              (int) src.port().toLong());
+            ONOSLLDP lldp = ONOSLLDP.onosSecureLLDP(src.deviceId().toString(),
+                                                    new ChassisId(),
+                                                    (int) src.port().toLong(), "", "test-secret");
 
             Ethernet ethPacket = new Ethernet();
             ethPacket.setEtherType(Ethernet.TYPE_LLDP);
diff --git a/tools/package/onos-run-karaf b/tools/package/onos-run-karaf
index 7068802..3500f5b 100755
--- a/tools/package/onos-run-karaf
+++ b/tools/package/onos-run-karaf
@@ -60,12 +60,13 @@
     [ -d $ONOS_DIR/config ] || mkdir -p $ONOS_DIR/config
     cat > $ONOS_DIR/config/cluster.json <<-EOF
     {
-      "name": "default",
+      "name": "default-$RANDOM",
       "node": {
         "id": "$IP",
         "ip": "$IP",
         "port": 9876
-      }
+      },
+      "clusterSecret": "$RANDOM"
     }
 EOF
 
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 b1e44c5..f0b981c 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
@@ -21,9 +21,14 @@
 
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
 import java.util.HashMap;
 
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
 import static org.onlab.packet.LLDPOrganizationalTLV.OUI_LENGTH;
 import static org.onlab.packet.LLDPOrganizationalTLV.SUBTYPE_LENGTH;
 
@@ -39,10 +44,14 @@
     protected static final byte NAME_SUBTYPE = 1;
     protected static final byte DEVICE_SUBTYPE = 2;
     protected static final byte DOMAIN_SUBTYPE = 3;
+    protected static final byte TIMESTAMP_SUBTYPE = 4;
+    protected static final byte SIG_SUBTYPE = 5;
 
     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 static final short TIMESTAMP_LENGTH = OUI_LENGTH + SUBTYPE_LENGTH;
+    private static final short SIG_LENGTH = OUI_LENGTH + SUBTYPE_LENGTH;
 
     private final HashMap<Byte, LLDPOrganizationalTLV> opttlvs = Maps.newHashMap();
 
@@ -138,6 +147,28 @@
         this.setPortId(portTLV);
     }
 
+    public void setTimestamp(long timestamp) {
+        LLDPOrganizationalTLV tmtlv = opttlvs.get(TIMESTAMP_SUBTYPE);
+        if (tmtlv == null) {
+            return;
+        }
+        tmtlv.setInfoString(ByteBuffer.allocate(8).putLong(timestamp).array());
+        tmtlv.setLength((short) (8 + TIMESTAMP_LENGTH));
+        tmtlv.setSubType(TIMESTAMP_SUBTYPE);
+        tmtlv.setOUI(MacAddress.ONOS.oui());
+    }
+
+    public void setSig(byte[] sig) {
+        LLDPOrganizationalTLV sigtlv = opttlvs.get(SIG_SUBTYPE);
+        if (sigtlv == null) {
+            return;
+        }
+        sigtlv.setInfoString(sig);
+        sigtlv.setLength((short) (sig.length + SIG_LENGTH));
+        sigtlv.setSubType(SIG_SUBTYPE);
+        sigtlv.setOUI(MacAddress.ONOS.oui());
+    }
+
     public LLDPOrganizationalTLV getNameTLV() {
         for (LLDPTLV tlv : this.getOptionalTLVList()) {
             if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
@@ -153,7 +184,7 @@
     public LLDPOrganizationalTLV getDeviceTLV() {
         for (LLDPTLV tlv : this.getOptionalTLVList()) {
             if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
-                LLDPOrganizationalTLV orgTLV =  (LLDPOrganizationalTLV) tlv;
+                LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
                 if (orgTLV.getSubType() == DEVICE_SUBTYPE) {
                     return orgTLV;
                 }
@@ -162,6 +193,30 @@
         return null;
     }
 
+    public LLDPOrganizationalTLV getTimestampTLV() {
+        for (LLDPTLV tlv : this.getOptionalTLVList()) {
+            if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+                LLDPOrganizationalTLV orgTLV =  (LLDPOrganizationalTLV) tlv;
+                if (orgTLV.getSubType() == TIMESTAMP_SUBTYPE) {
+                    return orgTLV;
+                }
+            }
+        }
+        return null;
+    }
+
+    public LLDPOrganizationalTLV getSigTLV() {
+        for (LLDPTLV tlv : this.getOptionalTLVList()) {
+            if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+                LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
+                if (orgTLV.getSubType() == SIG_SUBTYPE) {
+                    return orgTLV;
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * Gets the TLV associated with remote probing. This TLV will be null if
      * remote probing is disabled.
@@ -212,6 +267,24 @@
                 portBB.position(), portBB.remaining(), StandardCharsets.UTF_8));
     }
 
+    public long getTimestamp() {
+        LLDPOrganizationalTLV tlv = getTimestampTLV();
+        if (tlv != null) {
+            ByteBuffer b = ByteBuffer.allocate(8).put(tlv.getInfoString());
+            b.flip();
+            return b.getLong();
+        }
+        return 0;
+    }
+
+    public byte[] getSig() {
+        LLDPOrganizationalTLV tlv = getSigTLV();
+        if (tlv != null) {
+            return tlv.getInfoString();
+        }
+        return null;
+    }
+
     /**
      * Given an ethernet packet, determines if this is an LLDP from
      * ONOS and returns the device the LLDP came from.
@@ -231,12 +304,14 @@
 
     /**
      * Creates a link probe for link discovery/verification.
+     * @deprecated since 1.15. Insecure, do not use.
      *
      * @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
      */
+   @Deprecated
     public static ONOSLLDP onosLLDP(String deviceId, ChassisId chassisId, int portNum) {
         ONOSLLDP probe = new ONOSLLDP(NAME_SUBTYPE, DEVICE_SUBTYPE);
         probe.setPortId(portNum);
@@ -251,13 +326,69 @@
      * @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 secret LLDP secret
+     * @return ONOSLLDP probe message
+     */
+    public static ONOSLLDP onosSecureLLDP(String deviceId, ChassisId chassisId, int portNum, String secret) {
+        ONOSLLDP probe = null;
+        if (secret == null) {
+            probe = new ONOSLLDP(NAME_SUBTYPE, DEVICE_SUBTYPE);
+        } else {
+            probe = new ONOSLLDP(NAME_SUBTYPE, DEVICE_SUBTYPE, TIMESTAMP_SUBTYPE, SIG_SUBTYPE);
+        }
+        probe.setPortId(portNum);
+        probe.setDevice(deviceId);
+        probe.setChassisId(chassisId);
+
+        if (secret != null) {
+            /* Secure Mode */
+            long ts = System.currentTimeMillis();
+            probe.setTimestamp(ts);
+            byte[] sig = createSig(deviceId, portNum, ts, secret);
+            if (sig == null) {
+                return null;
+            }
+            probe.setSig(sig);
+            sig = null;
+        }
+        return probe;
+    }
+
+    /**
+     * Creates a link probe for link discovery/verification.
+     * @deprecated since 1.15. Insecure, do not use.
+     *
+     * @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 portDesc Port description of port to send probe out of
      * @return ONOSLLDP probe message
      */
+    @Deprecated
     public static ONOSLLDP onosLLDP(String deviceId, ChassisId chassisId, int portNum, String portDesc) {
-
         ONOSLLDP probe = onosLLDP(deviceId, chassisId, portNum);
+        addPortDesc(probe, portDesc);
+        return probe;
+    }
 
+    /**
+     * 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
+     * @param portDesc  Port description of port to send probe out of
+     * @param secret    LLDP secret
+     * @return ONOSLLDP probe message
+     */
+    public static ONOSLLDP onosSecureLLDP(String deviceId, ChassisId chassisId, int portNum, String portDesc,
+                                          String secret) {
+        ONOSLLDP probe = onosSecureLLDP(deviceId, chassisId, portNum, secret);
+        addPortDesc(probe, portDesc);
+        return probe;
+    }
+
+    private static void addPortDesc(ONOSLLDP probe, String portDesc) {
         if (portDesc != null && !portDesc.isEmpty()) {
             byte[] bPortDesc = portDesc.getBytes(StandardCharsets.UTF_8);
 
@@ -270,7 +401,70 @@
                     .setValue(bPortDesc);
             probe.addOptionalTLV(portDescTlv);
         }
-        return probe;
+    }
+
+    private static byte[] createSig(String deviceId, int portNum, long timestamp, String secret) {
+        byte[] pnb = ByteBuffer.allocate(8).putLong(portNum).array();
+        byte[] tmb = ByteBuffer.allocate(8).putLong(timestamp).array();
+
+        try {
+            SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+            Mac mac = Mac.getInstance("HmacSHA256");
+            mac.init(signingKey);
+            mac.update(deviceId.getBytes());
+            mac.update(pnb);
+            mac.update(tmb);
+            byte[] sig = mac.doFinal();
+            return sig;
+        } catch (NoSuchAlgorithmException e) {
+            return null;
+        } catch (InvalidKeyException e) {
+            return null;
+        }
+    }
+
+    private static boolean verifySig(byte[] sig, String deviceId, int portNum, long timestamp, String secret) {
+        byte[] nsig = createSig(deviceId, portNum, timestamp, secret);
+        if (nsig == null) {
+            return false;
+        }
+
+        if (!ArrayUtils.isSameLength(nsig, sig)) {
+            return false;
+        }
+
+        boolean fail = false;
+        for (int i = 0; i < nsig.length; i++) {
+            if (sig[i] != nsig[i]) {
+                fail = true;
+            }
+        }
+        if (fail) {
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean verify(ONOSLLDP probe, String secret, long maxDelay) {
+        if (secret == null) {
+            return true;
+        }
+
+        String deviceId = probe.getDeviceString();
+        int portNum = probe.getPort();
+        long timestamp = probe.getTimestamp();
+        byte[] sig = probe.getSig();
+
+        if (deviceId == null || sig == null) {
+            return false;
+        }
+
+        if (timestamp + maxDelay <= System.currentTimeMillis() ||
+                timestamp > System.currentTimeMillis()) {
+            return false;
+        }
+
+        return verifySig(sig, deviceId, portNum, timestamp, secret);
     }
 
 }
diff --git a/utils/misc/src/test/java/org/onlab/packet/ONOSLLDPTest.java b/utils/misc/src/test/java/org/onlab/packet/ONOSLLDPTest.java
index 268b5f5..b2494b5 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ONOSLLDPTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ONOSLLDPTest.java
@@ -30,8 +30,9 @@
     private static final Integer PORT_NUMBER = 2;
     private static final Integer PORT_NUMBER_2 = 98761234;
     private static final String PORT_DESC = "Ethernet1";
+    private static final String TEST_SECRET = "test";
 
-    private ONOSLLDP onoslldp = ONOSLLDP.onosLLDP(DEVICE_ID, CHASSIS_ID, PORT_NUMBER, PORT_DESC);
+    private ONOSLLDP onoslldp = ONOSLLDP.onosSecureLLDP(DEVICE_ID, CHASSIS_ID, PORT_NUMBER, PORT_DESC, TEST_SECRET);
 
     /**
      * Tests port number and getters.