lldp discovery independent of OF

Change-Id: I720f727f6628e30e5d732e6d7bf742d1b7050812
diff --git a/utils/misc/src/main/java/org/onlab/packet/ChassisId.java b/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
new file mode 100644
index 0000000..3029647
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ChassisId.java
@@ -0,0 +1,74 @@
+package org.onlab.packet;
+
+/**
+ * The class representing a network device chassisId.
+ * This class is immutable.
+ */
+// TODO: Move this to a reasonable place.
+public final class ChassisId {
+
+    private static final long UNKNOWN = 0;
+    private final long value;
+
+    /**
+     * Default constructor.
+     */
+    public ChassisId() {
+        this.value = ChassisId.UNKNOWN;
+    }
+
+    /**
+     * Constructor from a long value.
+     *
+     * @param value the value to use.
+     */
+    public ChassisId(long value) {
+        this.value = value;
+    }
+
+    /**
+     * Constructor from a string.
+     *
+     * @param value the value to use.
+     */
+    public ChassisId(String value) {
+        this.value = Long.valueOf(value);
+    }
+
+    /**
+     * Get the value of the chassis id.
+     *
+     * @return the value of the chassis id.
+     */
+    public long value() {
+        return value;
+    }
+
+    /**
+     * Convert the Chassis Id value to a ':' separated hexadecimal string.
+     *
+     * @return the Chassis Id value as a ':' separated hexadecimal string.
+     */
+    @Override
+    public String toString() {
+        return Long.toHexString(this.value);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof ChassisId)) {
+            return false;
+        }
+
+        ChassisId otherChassisId = (ChassisId) other;
+
+        return value == otherChassisId.value;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 17;
+        hash += 31 * hash + (int) (value ^ value >>> 32);
+        return hash;
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
index 7587a54..eecdb53 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ethernet.java
@@ -58,6 +58,7 @@
         Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
         Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
         Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
+        Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
     }
 
     protected MacAddress destinationMACAddress;
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 105a9f3..7277cda 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDP.java
@@ -150,7 +150,7 @@
         final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
         LLDPTLV tlv;
         do {
-            tlv = new LLDPTLV().deserialize(bb);
+            tlv = new LLDPOrganizationalTLV().deserialize(bb);
 
             // if there was a failure to deserialize stop processing TLVs
             if (tlv == null) {
@@ -169,6 +169,7 @@
             case 0x3:
                 this.ttl = tlv;
                 break;
+
             default:
                 this.optionalTLVList.add(tlv);
                 break;
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java b/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
index fb359a4..4d4e0a4 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDPOrganizationalTLV.java
@@ -140,6 +140,9 @@
 
     @Override
     public byte[] serialize() {
+        if (this.type != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+            return super.serialize();
+        }
         final int valueLength = LLDPOrganizationalTLV.OUI_LENGTH
                 + LLDPOrganizationalTLV.SUBTYPE_LENGTH + this.infoString.length;
         this.value = new byte[valueLength];
@@ -152,7 +155,11 @@
 
     @Override
     public LLDPTLV deserialize(final ByteBuffer bb) {
-        super.deserialize(bb);
+        LLDPTLV tlv = super.deserialize(bb);
+        if (tlv.getType() != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+            return tlv;
+        }
+
         final ByteBuffer optionalField = ByteBuffer.wrap(this.value);
 
         final byte[] oui = new byte[LLDPOrganizationalTLV.OUI_LENGTH];
diff --git a/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
index 04f89a0..16c9e31 100644
--- a/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
+++ b/utils/misc/src/main/java/org/onlab/packet/LLDPTLV.java
@@ -111,6 +111,7 @@
         sscratch = bb.getShort();
         this.type = (byte) (sscratch >> 9 & 0x7f);
         this.length = (short) (sscratch & 0x1ff);
+
         if (this.length > 0) {
             this.value = new byte[this.length];
 
@@ -120,6 +121,7 @@
             }
             bb.get(this.value);
         }
+
         return this;
     }
 
diff --git a/utils/misc/src/main/java/org/onlab/packet/ONLabLddp.java b/utils/misc/src/main/java/org/onlab/packet/ONLabLddp.java
index 37213d0..ecfcbd8 100644
--- a/utils/misc/src/main/java/org/onlab/packet/ONLabLddp.java
+++ b/utils/misc/src/main/java/org/onlab/packet/ONLabLddp.java
@@ -30,6 +30,7 @@
  * Refer to IEEE Std 802.1ABTM-2009 for more information.
  *
  */
+@Deprecated
 public class ONLabLddp extends LLDP {
 
     private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class);
diff --git a/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java b/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
new file mode 100644
index 0000000..ec35de8
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/ONOSLLDP.java
@@ -0,0 +1,169 @@
+package org.onlab.packet;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.ArrayUtils;
+
+import java.nio.ByteBuffer;
+
+/**
+ *  ONOS LLDP containing organizational TLV for ONOS device dicovery.
+ */
+public class ONOSLLDP extends LLDP {
+
+    public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
+    public static final String DEFAULT_DEVICE = "INVALID";
+    public static final String DEFAULT_NAME = "ONOS Discovery";
+
+    public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
+            0x01};
+    public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
+            (byte) 0xc2, 0x00, 0x00, 0x0e};
+    public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
+            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+
+    private static final byte NAME_SUBTYPE = 1;
+    private static final byte DEVICE_SUBTYPE = 2;
+    private static final short NAME_LENGTH = 4; //1 for subtype + 3 for OUI
+    private static final short DEVICE_LENGTH = 4; //1 for subtype + 3 for OUI
+    private final LLDPOrganizationalTLV nameTLV = new LLDPOrganizationalTLV();
+    private final LLDPOrganizationalTLV deviceTLV =  new LLDPOrganizationalTLV();
+
+    // TLV constants: type, size and subtype
+    // Organizationally specific TLV also have packet offset and contents of TLV
+    // header
+    private static final byte CHASSIS_TLV_TYPE = 1;
+    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_SIZE = 5;
+    private static final byte PORT_TLV_SUBTYPE = 2;
+
+    private static final byte TTL_TLV_TYPE = 3;
+
+
+    private final byte[] ttlValue = new byte[] {0, 0x78};
+
+    public ONOSLLDP() {
+        super();
+        setName(DEFAULT_NAME);
+        setDevice(DEFAULT_DEVICE);
+        setOptionalTLVList(Lists.<LLDPTLV>newArrayList(nameTLV, deviceTLV));
+        setTtl(new LLDPTLV().setType((byte) TTL_TLV_TYPE)
+                       .setLength((short) ttlValue.length)
+                       .setValue(ttlValue));
+
+    }
+
+    private ONOSLLDP(LLDP lldp) {
+        this.portId = lldp.getPortId();
+        this.chassisId = lldp.getChassisId();
+        this.ttl = lldp.getTtl();
+        this.optionalTLVList = lldp.getOptionalTLVList();
+    }
+
+    public void setName(String name) {
+        nameTLV.setLength((short) (name.length() + NAME_LENGTH));
+        nameTLV.setInfoString(name);
+        nameTLV.setSubType(NAME_SUBTYPE);
+        nameTLV.setOUI(ONLAB_OUI);
+    }
+
+    public void setDevice(String device) {
+        deviceTLV.setInfoString(device);
+        deviceTLV.setLength((short) (device.length() + DEVICE_LENGTH));
+        deviceTLV.setSubType(DEVICE_SUBTYPE);
+        deviceTLV.setOUI(ONLAB_OUI);
+    }
+
+    public void setChassisId(final ChassisId chassisId) {
+        MacAddress chassisMac = MacAddress.valueOf(chassisId.value());
+        byte[] chassis = ArrayUtils.addAll(new byte[] {CHASSIS_TLV_SUBTYPE},
+                                           chassisMac.getAddress());
+
+        LLDPTLV chassisTLV = new LLDPTLV();
+        chassisTLV.setLength(CHASSIS_TLV_SIZE);
+        chassisTLV.setType(CHASSIS_TLV_TYPE);
+        chassisTLV.setValue(chassis);
+        this.setChassisId(chassisTLV);
+    }
+
+    public void setPortId(final int portNumber) {
+        byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_SUBTYPE},
+                                        ByteBuffer.allocate(4).putInt(portNumber).array());
+
+        LLDPTLV portTLV = new LLDPTLV();
+        portTLV.setLength(PORT_TLV_SIZE);
+        portTLV.setType(PORT_TLV_TYPE);
+        portTLV.setValue(port);
+        this.setPortId(portTLV);
+    }
+
+    public LLDPOrganizationalTLV getNameTLV() {
+        for (LLDPTLV tlv : this.getOptionalTLVList()) {
+            if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+                LLDPOrganizationalTLV orgTLV =  (LLDPOrganizationalTLV) tlv;
+                if (orgTLV.getSubType() == NAME_SUBTYPE) {
+                    return orgTLV;
+                }
+            }
+        }
+        return null;
+    }
+
+    public LLDPOrganizationalTLV getDeviceTLV() {
+        for (LLDPTLV tlv : this.getOptionalTLVList()) {
+            if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
+                LLDPOrganizationalTLV orgTLV =  (LLDPOrganizationalTLV) tlv;
+                if (orgTLV.getSubType() == DEVICE_SUBTYPE) {
+                    return orgTLV;
+                }
+            }
+        }
+        return null;
+    }
+
+    public String getNameString() {
+        LLDPOrganizationalTLV tlv = getNameTLV();
+        if (tlv != null) {
+            return new String(tlv.getInfoString());
+        }
+        return null;
+    }
+
+    public String getDeviceString() {
+        LLDPOrganizationalTLV tlv = getDeviceTLV();
+        if (tlv != null) {
+            return new String(tlv.getInfoString());
+        }
+        return null;
+    }
+
+    public Integer getPort() {
+        ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
+        portBB.position(1);
+        return portBB.getInt();
+    }
+
+    /**
+     * Given an ethernet packet, determines if this is an LLDP from
+     * ONOS and returns the device the LLDP came from.
+     * @param eth an ethernet packet
+     * @return a the lldp packet or null
+     */
+    public static ONOSLLDP parseONOSLLDP(Ethernet eth) {
+        if (eth.getEtherType() == Ethernet.TYPE_LLDP ||
+                eth.getEtherType() == Ethernet.TYPE_BSN) {
+           ONOSLLDP onosLldp = new ONOSLLDP((LLDP) eth.getPayload()); //(ONOSLLDP) eth.getPayload();
+           if (ONOSLLDP.DEFAULT_NAME.equals(onosLldp.getNameString())) {
+               return onosLldp;
+           }
+        }
+        return null;
+    }
+
+
+
+
+
+}