Make a link depends on packet-in LLDP packet.

The ONOS does not process the incoming LLDP packet from switches.
The ONOS only process LLDP packets if LLDP packet has "ONOS Discovery" optional TLV.

So, this changes process incoming LLDP packet from switch and make a link information and onos can utilize it.

Also, this patch changes the requested appId of DefaultForwardingObjective generated by PacketManager.
So, AppId in result of flows cli will be printed to actual requested appId.

ONOS-7748

Change-Id: I2611b34655bec2369b8817ce0cd29fb2edbed845
diff --git a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
index 5af7f7a..ce3c094 100644
--- a/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
+++ b/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java
@@ -216,6 +216,11 @@
     public static final String SSHKEY = "sshkey";
 
     /**
+     * Annotation key for the protocol layer.
+     */
+    public static final String LAYER = "layer";
+
+    /**
      * Returns the value annotated object for the specified annotation key.
      * The annotated value is expected to be String that can be parsed as double.
      * If parsing fails, the returned value will be {@value #DEFAULT_VALUE}.
diff --git a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
index ac5c732..b7d1cd8 100644
--- a/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
+++ b/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
@@ -347,6 +347,7 @@
     }
 
     private DefaultForwardingObjective.Builder createBuilder(PacketRequest request) {
+        ApplicationId requestedAppId = coreService.getAppId(request.appId().name()); // Validate app id
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .punt()
                 .wipeDeferred()
@@ -355,7 +356,7 @@
         return DefaultForwardingObjective.builder()
                 .withPriority(request.priority().priorityValue())
                 .withSelector(request.selector())
-                .fromApp(appId)
+                .fromApp(requestedAppId == null ? appId : requestedAppId)
                 .withFlag(ForwardingObjective.Flag.VERSATILE)
                 .withTreatment(treatment)
                 .makePermanent();
diff --git a/drivers/arista/src/main/java/org/onosproject/drivers/arista/LinkDiscoveryAristaImpl.java b/drivers/arista/src/main/java/org/onosproject/drivers/arista/LinkDiscoveryAristaImpl.java
index 49ed615..a32c6b7 100644
--- a/drivers/arista/src/main/java/org/onosproject/drivers/arista/LinkDiscoveryAristaImpl.java
+++ b/drivers/arista/src/main/java/org/onosproject/drivers/arista/LinkDiscoveryAristaImpl.java
@@ -261,7 +261,7 @@
         ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
         ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
         DefaultAnnotations annotations = DefaultAnnotations.builder()
-                .set("layer", "ETHERNET")
+                .set(AnnotationKeys.LAYER, "ETHERNET")
                 .build();
 
         linkDescriptions.add(new DefaultLinkDescription(
diff --git a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java
index cb07843..3f4adb6 100644
--- a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java
+++ b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/FlowRuleJuniperImpl.java
@@ -381,7 +381,7 @@
         DeviceService deviceService = this.handler().get(DeviceService.class);
         //Using only links with adjacency discovered by the LLDP protocol (see LinkDiscoveryJuniperImpl)
         Map<DeviceId, Port> dstPorts = links.stream().filter(l ->
-                IP_STRING.toUpperCase().equals(l.annotations().value("layer")))
+                IP_STRING.toUpperCase().equals(l.annotations().value(AnnotationKeys.LAYER)))
                 .collect(Collectors.toMap(
                         l -> l.dst().deviceId(),
                         l -> deviceService.getPort(l.dst().deviceId(), l.dst().port())));
diff --git a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java
index fca4697..d6a6f65 100644
--- a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java
+++ b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/JuniperUtils.java
@@ -502,12 +502,37 @@
         ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
         ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
         DefaultAnnotations annotations = DefaultAnnotations.builder()
-                .set("layer", "IP")
+                .set(AnnotationKeys.LAYER, "ETHERNET")
                 .build();
         descs.add(new DefaultLinkDescription(
-                local, remote, Link.Type.INDIRECT, false, annotations));
+                local, remote, Link.Type.DIRECT, true, annotations));
         descs.add(new DefaultLinkDescription(
-                remote, local, Link.Type.INDIRECT, false, annotations));
+                remote, local, Link.Type.DIRECT, true, annotations));
+    }
+
+    /**
+     * Create one way LinkDescriptions.
+     *
+     * @param localDevId  the identity of the local device
+     * @param localPort   the port of the local device
+     * @param remoteDevId the identity of the remote device
+     * @param remotePort  the port of the remote device
+     * @param descs       the collection to which the link descriptions
+     *                    should be added
+     */
+    public static void createOneWayLinkDescription(DeviceId localDevId,
+                                                   Port localPort,
+                                                   DeviceId remoteDevId,
+                                                   Port remotePort,
+                                                   Set<LinkDescription> descs) {
+
+        ConnectPoint local = new ConnectPoint(localDevId, localPort.number());
+        ConnectPoint remote = new ConnectPoint(remoteDevId, remotePort.number());
+        DefaultAnnotations annotations = DefaultAnnotations.builder()
+                .set(AnnotationKeys.LAYER, "ETHERNET")
+                .build();
+        descs.add(new DefaultLinkDescription(
+                remote, local, Link.Type.DIRECT, true, annotations));
     }
 
     /**
diff --git a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/LinkDiscoveryJuniperImpl.java b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/LinkDiscoveryJuniperImpl.java
index 37e655a..e78779e 100644
--- a/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/LinkDiscoveryJuniperImpl.java
+++ b/drivers/juniper/src/main/java/org/onosproject/drivers/juniper/LinkDiscoveryJuniperImpl.java
@@ -136,12 +136,11 @@
                 continue;
             }
 
-            JuniperUtils.createBiDirLinkDescription(localDeviceId,
-                                                    localPort.get(),
-                                                    remoteDevice.id(),
-                                                    remotePort.get(),
-                                                    descriptions);
-
+            JuniperUtils.createOneWayLinkDescription(localDeviceId,
+                                                     localPort.get(),
+                                                     remoteDevice.id(),
+                                                     remotePort.get(),
+                                                     descriptions);
         }
         return descriptions;
     }
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 3ea56af..884f3ed 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
@@ -798,6 +798,11 @@
         }
 
         @Override
+        public void setTtl(LinkKey key, short ttl) {
+            linkTimes.put(key, System.currentTimeMillis() - staleLinkAge + SECONDS.toMillis(ttl));
+        }
+
+        @Override
         public DeviceService deviceService() {
             return deviceService;
         }
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 33e5f4d..bea59ee 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
@@ -25,13 +25,18 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.ONOSLLDP;
 import org.onlab.util.Timer;
+import org.onlab.util.Tools;
+import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultPort;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link.Type;
 import org.onosproject.net.LinkKey;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.link.DefaultLinkDescription;
 import org.onosproject.net.link.LinkDescription;
 import org.onosproject.net.link.ProbedLinkProvider;
@@ -42,6 +47,10 @@
 
 import java.nio.ByteBuffer;
 import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import static com.google.common.base.Strings.isNullOrEmpty;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -59,6 +68,9 @@
  */
 public class LinkDiscovery implements TimerTask {
 
+    private static final String SCHEME_NAME = "linkdiscovery";
+    private static final String ETHERNET = "ETHERNET";
+
     private final Logger log = getLogger(getClass());
 
     private final Device device;
@@ -166,6 +178,27 @@
             return false;
         }
 
+        if (processOnosLldp(packetContext, eth)) {
+            return true;
+        }
+
+        if (processLldp(packetContext, eth)) {
+            return true;
+        }
+
+        ONOSLLDP lldp = ONOSLLDP.parseLLDP(eth);
+
+        if (lldp == null) {
+            log.debug("Cannot parse the packet. It seems that it is not the lldp or bsn packet.");
+        } else {
+            log.debug("LLDP packet is dropped due to there are no handlers that properly handle this packet: {}",
+                    lldp.toString());
+        }
+
+        return false;
+    }
+
+    private boolean processOnosLldp(PacketContext packetContext, Ethernet eth) {
         ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
         if (onoslldp != null) {
             Type lt;
@@ -198,6 +231,7 @@
                     context.providerService().linkDetected(ld);
                     context.touchLink(LinkKey.linkKey(src, dst));
                 } catch (IllegalStateException e) {
+                    log.debug("There is a exception during link creation: {}", e);
                     return true;
                 }
                 return true;
@@ -206,6 +240,114 @@
         return false;
     }
 
+    private boolean processLldp(PacketContext packetContext, Ethernet eth) {
+        ONOSLLDP onoslldp = ONOSLLDP.parseLLDP(eth);
+        if (onoslldp != null) {
+            Type lt = eth.getEtherType() == Ethernet.TYPE_LLDP ?
+                    Type.DIRECT : Type.INDIRECT;
+
+            DeviceService deviceService = context.deviceService();
+            MacAddress srcChassisId = onoslldp.getChassisIdByMac();
+            String srcPortName = onoslldp.getPortNameString();
+            String srcPortDesc = onoslldp.getPortDescString();
+
+            log.debug("srcChassisId:{}, srcPortName:{}, srcPortDesc:{}", srcChassisId, srcPortName, srcPortDesc);
+
+            if (srcChassisId == null && srcPortDesc == null) {
+                log.warn("there are no valid port id");
+                return false;
+            }
+
+            Optional<Device> srcDevice = findSourceDeviceByChassisId(deviceService, srcChassisId);
+
+            if (!srcDevice.isPresent()) {
+                log.warn("source device not found. srcChassisId value: {}", srcChassisId);
+                return false;
+            }
+            Optional<Port> sourcePort = findSourcePortByName(
+                    srcPortName == null ? srcPortDesc : srcPortName,
+                    deviceService,
+                    srcDevice.get());
+
+            if (!sourcePort.isPresent()) {
+                log.warn("source port not found. sourcePort value: {}", sourcePort);
+                return false;
+            }
+
+            PortNumber srcPort = sourcePort.get().number();
+            PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
+
+            DeviceId srcDeviceId = srcDevice.get().id();
+            DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();
+
+            ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
+            ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
+
+            DefaultAnnotations annotations = DefaultAnnotations.builder()
+                    .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
+                    .set(AnnotationKeys.LAYER, ETHERNET)
+                    .build();
+
+            LinkDescription ld = new DefaultLinkDescription(src, dst, lt, true, annotations);
+            try {
+                context.providerService().linkDetected(ld);
+                context.setTtl(LinkKey.linkKey(src, dst), onoslldp.getTtlBySeconds());
+            } catch (IllegalStateException e) {
+                log.debug("There is a exception during link creation: {}", e);
+                return true;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private Optional<Device> findSourceDeviceByChassisId(DeviceService deviceService, MacAddress srcChassisId) {
+        Supplier<Stream<Device>> deviceStream = () ->
+                StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false);
+        Optional<Device> remoteDeviceOptional = deviceStream.get()
+                .filter(device -> device.chassisId() != null
+                        && MacAddress.valueOf(device.chassisId().value()).equals(srcChassisId))
+                .findAny();
+
+        if (remoteDeviceOptional.isPresent()) {
+            log.debug("sourceDevice found by chassis id: {}", srcChassisId);
+            return remoteDeviceOptional;
+        } else {
+            remoteDeviceOptional = deviceStream.get().filter(device ->
+                    Tools.stream(deviceService.getPorts(device.id()))
+                            .anyMatch(port -> port.annotations().keys().contains(AnnotationKeys.PORT_MAC)
+                                    && MacAddress.valueOf(port.annotations().value(AnnotationKeys.PORT_MAC))
+                                    .equals(srcChassisId)))
+                    .findAny();
+            if (remoteDeviceOptional.isPresent()) {
+                log.debug("sourceDevice found by port mac: {}", srcChassisId);
+                return remoteDeviceOptional;
+            } else {
+                return Optional.empty();
+            }
+        }
+    }
+
+    private Optional<Port> findSourcePortByName(String remotePortName,
+                                                DeviceService deviceService,
+                                                Device remoteDevice) {
+        Optional<Port> remotePort = deviceService.getPorts(remoteDevice.id())
+                .stream().filter(port -> remotePortName.equals(port.annotations().value(AnnotationKeys.PORT_NAME)))
+                .findAny();
+
+        if (remotePort.isPresent()) {
+            return remotePort;
+        } else {
+            int portNumber = Integer.parseInt(remotePortName.replaceAll("\\D+", ""));
+            DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+                    .set(AnnotationKeys.PORT_NAME, remotePortName);
+
+            return Optional.of(new DefaultPort(remoteDevice, PortNumber.portNumber(portNumber),
+                    true,
+                    annotations.build()));
+        }
+    }
+
     // true if *NOT* this cluster's own probe.
     private boolean notMy(String mac) {
         // if we are using DEFAULT_MAC, clustering hadn't initialized, so conservative 'yes'
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 a325b95..056e3e7 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
@@ -76,6 +76,14 @@
     void touchLink(LinkKey key);
 
     /**
+     * Set the TTL to the link identified by the given key to indicate that it's active.
+     *
+     * @param key link key
+     * @param ttl ttl value(seconds)
+     */
+    void setTtl(LinkKey key, short ttl);
+
+    /**
      * Returns the cluster-wide unique identifier.
      *
      * @return the cluster identifier
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 e811f96..a2e91ad 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
@@ -263,6 +263,10 @@
         }
 
         @Override
+        public void setTtl(LinkKey key, short ttl) {
+        }
+
+        @Override
         public String fingerprint() {
             return buildSrcMac();
         }
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDP.java b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
index 33b1f85..e555f1f 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
@@ -37,8 +37,16 @@
 
     public static final byte PORT_TLV_TYPE = 2;
     public static final short PORT_TLV_SIZE = 5;
+
+    /**
+     * @deprecated since 1.15. Use the PORT_TLV_COMPONENT_SUBTYPE instead of PORT_TLV_SUBTYPE.
+     */
+    @Deprecated
     public static final byte PORT_TLV_SUBTYPE = 2;
 
+    public static final byte PORT_TLV_COMPONENT_SUBTYPE = PORT_TLV_SUBTYPE;
+    public static final byte PORT_TLV_INTERFACE_NAME_SUBTYPE = 5;
+
     public static final byte TTL_TLV_TYPE = 3;
     public static final short TTL_TLV_SIZE = 2;
 
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 f0b981c..7405bbf 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import org.apache.commons.lang.ArrayUtils;
+import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
@@ -31,16 +32,18 @@
 
 import static org.onlab.packet.LLDPOrganizationalTLV.OUI_LENGTH;
 import static org.onlab.packet.LLDPOrganizationalTLV.SUBTYPE_LENGTH;
+import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  *  ONOS LLDP containing organizational TLV for ONOS device discovery.
  */
 public class ONOSLLDP extends LLDP {
 
+    private static final Logger log = getLogger(ONOSLLDP.class);
+
     public static final String DEFAULT_DEVICE = "INVALID";
     public static final String DEFAULT_NAME = "ONOS Discovery";
 
-
     protected static final byte NAME_SUBTYPE = 1;
     protected static final byte DEVICE_SUBTYPE = 2;
     protected static final byte DOMAIN_SUBTYPE = 3;
@@ -62,11 +65,7 @@
     private static final byte CHASSIS_TLV_SIZE = 7;
     private static final byte CHASSIS_TLV_SUBTYPE = 4;
 
-    private static final byte PORT_TLV_TYPE = 2;
-    private static final byte PORT_TLV_SUBTYPE = 2;
-
     private static final byte TTL_TLV_TYPE = 3;
-
     private static final byte PORT_DESC_TLV_TYPE = 4;
 
     private final byte[] ttlValue = new byte[] {0, 0x78};
@@ -137,7 +136,7 @@
     }
 
     public void setPortId(final int portNumber) {
-        byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_SUBTYPE},
+        byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_COMPONENT_SUBTYPE},
                 String.valueOf(portNumber).getBytes(StandardCharsets.UTF_8));
 
         LLDPTLV portTLV = new LLDPTLV();
@@ -147,6 +146,17 @@
         this.setPortId(portTLV);
     }
 
+    public void setPortName(final String portName) {
+        byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_INTERFACE_NAME_SUBTYPE},
+                portName.getBytes(StandardCharsets.UTF_8));
+
+        LLDPTLV portTLV = new LLDPTLV();
+        portTLV.setLength((short) port.length);
+        portTLV.setType(PORT_TLV_TYPE);
+        portTLV.setValue(port);
+        this.setPortId(portTLV);
+    }
+
     public void setTimestamp(long timestamp) {
         LLDPOrganizationalTLV tmtlv = opttlvs.get(TIMESTAMP_SUBTYPE);
         if (tmtlv == null) {
@@ -235,6 +245,17 @@
         return null;
     }
 
+    public LLDPTLV getPortDescTLV() {
+        for (LLDPTLV tlv : this.getOptionalTLVList()) {
+            if (tlv.getType() == PORT_DESC_TLV_TYPE) {
+                return tlv;
+            }
+        }
+
+        log.error("Cannot find the port description tlv type.");
+        return null;
+    }
+
     public String getNameString() {
         LLDPOrganizationalTLV tlv = getNameTLV();
         if (tlv != null) {
@@ -259,12 +280,57 @@
         return null;
     }
 
+    public String getPortDescString() {
+        LLDPTLV tlv = getPortDescTLV();
+        if (tlv != null) {
+            return new String(tlv.getValue(), StandardCharsets.UTF_8);
+        }
+        return null;
+    }
+
     public Integer getPort() {
         ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
-        portBB.position(1);
+        byte type = portBB.get();
 
-        return Integer.parseInt(new String(portBB.array(),
-                portBB.position(), portBB.remaining(), StandardCharsets.UTF_8));
+        if (type == PORT_TLV_COMPONENT_SUBTYPE) {
+            return Integer.parseInt(new String(portBB.array(),
+                    portBB.position(), portBB.remaining(), StandardCharsets.UTF_8));
+        } else {
+            return -1;
+        }
+    }
+
+    public String getPortNameString() {
+        ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
+        byte type = portBB.get();
+
+        if (type == PORT_TLV_INTERFACE_NAME_SUBTYPE) {
+            return new String(portBB.array(), portBB.position(), portBB.remaining(), StandardCharsets.UTF_8);
+        } else {
+            log.error("Cannot find the port name tlv type.");
+            return null;
+        }
+    }
+
+    public MacAddress getChassisIdByMac() {
+        ByteBuffer portBB = ByteBuffer.wrap(this.getChassisId().getValue());
+        byte type = portBB.get();
+
+        if (type == CHASSIS_TLV_SUBTYPE) {
+            byte[] bytes = new byte[portBB.remaining()];
+
+            System.arraycopy(portBB.array(), portBB.position(), bytes, 0, MacAddress.MAC_ADDRESS_LENGTH);
+
+            return new MacAddress(bytes);
+        } else {
+            return MacAddress.NONE;
+        }
+    }
+
+    public short getTtlBySeconds() {
+        ByteBuffer portBB = ByteBuffer.wrap(this.getTtl().getValue());
+
+        return portBB.getShort();
     }
 
     public long getTimestamp() {
@@ -303,6 +369,22 @@
     }
 
     /**
+     * Given an ethernet packet, returns the device the LLDP came from.
+     * @param eth an ethernet packet
+     * @return a the lldp packet or null
+     */
+    public static ONOSLLDP parseLLDP(Ethernet eth) {
+        if (eth.getEtherType() == Ethernet.TYPE_LLDP ||
+                eth.getEtherType() == Ethernet.TYPE_BSN) {
+
+            return new ONOSLLDP((LLDP) eth.getPayload());
+        }
+
+        log.error("Packet is not the LLDP or BSN.");
+        return null;
+    }
+
+    /**
      * Creates a link probe for link discovery/verification.
      * @deprecated since 1.15. Insecure, do not use.
      *
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 b2494b5..2405998 100644
--- a/utils/misc/src/test/java/org/onlab/packet/ONOSLLDPTest.java
+++ b/utils/misc/src/test/java/org/onlab/packet/ONOSLLDPTest.java
@@ -30,6 +30,7 @@
     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 PORT_NAME = "Ethernet2";
     private static final String TEST_SECRET = "test";
 
     private ONOSLLDP onoslldp = ONOSLLDP.onosSecureLLDP(DEVICE_ID, CHASSIS_ID, PORT_NUMBER, PORT_DESC, TEST_SECRET);
@@ -39,11 +40,21 @@
      */
     @Test
     public void testPortNumber() throws Exception {
-        assertEquals("the value from constructor with getPort value is miss matched",
+        assertEquals("the value from constructor with getPort value is mismatched",
                 PORT_NUMBER, onoslldp.getPort());
 
         onoslldp.setPortId(PORT_NUMBER_2);
-        assertEquals("the value from setPortId with getPort value is miss matched",
+        assertEquals("the value from setPortId with getPort value is mismatched",
                 PORT_NUMBER_2, onoslldp.getPort());
     }
+
+    /**
+     * Tests port name and getters.
+     */
+    @Test
+    public void testPortName() throws Exception {
+        onoslldp.setPortName(PORT_NAME);
+        assertEquals("the value from setPortName with getPortNameString value is mismatched",
+                PORT_NAME, onoslldp.getPortNameString());
+    }
 }
\ No newline at end of file