BUG 2820 - LLDP refactor 89/16689/5
authorJozef Gloncak <jgloncak@cisco.com>
Tue, 17 Mar 2015 12:35:51 +0000 (13:35 +0100)
committerJozef Gloncak <jgloncak@cisco.com>
Fri, 20 Mar 2015 07:40:46 +0000 (08:40 +0100)
LLDP class field changed from tlvList to mandatoryTLVs, optionalTLVs, customTLVs
Added new create custom TLV value in order to hold security hint for LLDP topology discovery.
Fixed guava bundle import
Fixed apache-lang3 bundle import

Change-Id: I5d0c6b9a9e29213d3f25aa99ff7edd5b30e6c7a8
Signed-off-by: Jozef Gloncak <jgloncak@cisco.com>
Signed-off-by: Michal Rehak <mirehak@cisco.com>
(cherry picked from commit 26dfd63f953982f661e4e72f306c6a6b8e02cfd4)

opendaylight/commons/liblldp/pom.xml
opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/CustomTLVKey.java [new file with mode: 0644]
opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDP.java
opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/LLDPTLV.java
opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/liblldp/LLDPTest.java

index 5e14c92ee78de72c3a3e1b13a2c2ca53159a4bc8..286c68e677e664383ec70cd81bcd9cc898ee37b6 100644 (file)
@@ -27,6 +27,7 @@
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>com.google.guava</groupId>
diff --git a/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/CustomTLVKey.java b/opendaylight/commons/liblldp/src/main/java/org/opendaylight/controller/liblldp/CustomTLVKey.java
new file mode 100644 (file)
index 0000000..7bc631e
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ *
+ */
+package org.opendaylight.controller.liblldp;
+
+public class CustomTLVKey {
+
+    private int oui;
+    private byte subtype;
+
+    /**
+     * @param oui
+     * @param subtype
+     */
+    public CustomTLVKey(int oui, byte subtype) {
+        this.oui = oui;
+        this.subtype = subtype;
+    }
+
+    /**
+     * @return the oui
+     */
+    public int getOui() {
+        return oui;
+    }
+    /**
+     * @return the subtype
+     */
+    public byte getSubtype() {
+        return subtype;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + oui;
+        result = prime * result + subtype;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        CustomTLVKey other = (CustomTLVKey) obj;
+        if (oui != other.oui) {
+            return false;
+        }
+
+        if (subtype != other.subtype) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+}
index c7dc6915ad32b432054c9a3d2c08e4b046003aec..85b2cbf82ecbf88123b0a351caf55328a4007d50 100644 (file)
@@ -9,7 +9,6 @@
 package org.opendaylight.controller.liblldp;
 
 import com.google.common.collect.Iterables;
-import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -23,32 +22,34 @@ public class LLDP extends Packet {
     private static final String SYSTEMNAMEID = "SystemNameID";
     private static final String PORTID = "PortId";
     private static final String TTL = "TTL";
-    private static final int LLDPDefaultTlvs = 4;
-    private static LLDPTLV emptyTLV = new LLDPTLV().setLength((short) 0)
-            .setType((byte) 0);
-    public static final byte[] LLDPMulticastMac = { 1, (byte) 0x80,
-            (byte) 0xc2, 0, 0, (byte) 0xe };
-    private Map<Byte, LLDPTLV> tlvList;
+    private static final int LLDPDefaultTlvs = 3;
+    private static LLDPTLV emptyTLV = new LLDPTLV().setLength((short) 0).setType((byte) 0);
+    public static final byte[] LLDPMulticastMac = { 1, (byte) 0x80, (byte) 0xc2, 0, 0, (byte) 0xe };
 
-    private List<LLDPTLV> customTlvList;
+    private Map<Byte, LLDPTLV> mandatoryTLVs;
+    private Map<Byte, LLDPTLV> optionalTLVs;
+    private Map<CustomTLVKey, LLDPTLV> customTLVs;
 
     /**
      * Default constructor that creates the tlvList LinkedHashMap
      */
     public LLDP() {
         super();
-        tlvList = new LinkedHashMap<>(LLDPDefaultTlvs);
-        customTlvList = new ArrayList<>();
+        init();
     }
 
     /**
-     * Constructor that creates the tlvList LinkedHashMap and sets the write
-     * access for the same
+     * Constructor that creates the tlvList LinkedHashMap and sets the write access for the same
      */
     public LLDP(boolean writeAccess) {
         super(writeAccess);
-        tlvList = new LinkedHashMap<Byte, LLDPTLV>(LLDPDefaultTlvs); // Mandatory
-                                                                     // TLVs
+        init();
+    }
+
+    private void init() {
+        mandatoryTLVs = new LinkedHashMap<>(LLDPDefaultTlvs);
+        optionalTLVs = new LinkedHashMap<>();
+        customTLVs = new LinkedHashMap<>();
     }
 
     /**
@@ -70,13 +71,35 @@ public class LLDP extends Packet {
         }
     }
 
+    private LLDPTLV getFromTLVs(Byte type) {
+        LLDPTLV tlv = null;
+        tlv = mandatoryTLVs.get(type);
+        if (tlv == null) {
+            tlv = optionalTLVs.get(type);
+        }
+        return tlv;
+    }
+
+    private void putToTLVs(final Byte type, final LLDPTLV tlv) {
+        if (type == LLDPTLV.TLVType.ChassisID.getValue() || type == LLDPTLV.TLVType.PortID.getValue()
+                || type == LLDPTLV.TLVType.TTL.getValue()) {
+            mandatoryTLVs.put(type, tlv);
+        } else if (type != LLDPTLV.TLVType.Custom.getValue()) {
+            optionalTLVs.put(type, tlv);
+        }
+    }
+
     /**
      * @param String
      *            - description of the type of TLV
      * @return LLDPTLV - full TLV
      */
     public LLDPTLV getTLV(String type) {
-        return tlvList.get(getType(type));
+        return getFromTLVs(getType(type));
+    }
+
+    public LLDPTLV getCustomTLV(CustomTLVKey key) {
+        return customTLVs.get(key);
     }
 
     /**
@@ -87,7 +110,7 @@ public class LLDP extends Packet {
      * @return void
      */
     public void setTLV(String type, LLDPTLV tlv) {
-        tlvList.put(getType(type), tlv);
+        putToTLVs(getType(type), tlv);
     }
 
     /**
@@ -102,7 +125,7 @@ public class LLDP extends Packet {
      *            - the chassisId to set
      */
     public LLDP setChassisId(LLDPTLV chassisId) {
-        tlvList.put(getType(CHASSISID), chassisId);
+        setTLV(CHASSISID, chassisId);
         return this;
     }
 
@@ -118,7 +141,7 @@ public class LLDP extends Packet {
      *            - the chassisId to set
      */
     public LLDP setSystemNameId(LLDPTLV systemNameId) {
-        tlvList.put(getType(SYSTEMNAMEID), systemNameId);
+        setTLV(SYSTEMNAMEID, systemNameId);
         return this;
     }
 
@@ -126,7 +149,7 @@ public class LLDP extends Packet {
      * @return LLDPTLV - the portId TLV
      */
     public LLDPTLV getPortId() {
-        return tlvList.get(getType(PORTID));
+        return getTLV(PORTID);
     }
 
     /**
@@ -135,7 +158,7 @@ public class LLDP extends Packet {
      * @return LLDP
      */
     public LLDP setPortId(LLDPTLV portId) {
-        tlvList.put(getType(PORTID), portId);
+        setTLV(PORTID, portId);
         return this;
     }
 
@@ -143,7 +166,7 @@ public class LLDP extends Packet {
      * @return LLDPTLV - the ttl TLV
      */
     public LLDPTLV getTtl() {
-        return tlvList.get(getType(TTL));
+        return getTLV(TTL);
     }
 
     /**
@@ -152,34 +175,22 @@ public class LLDP extends Packet {
      * @return LLDP
      */
     public LLDP setTtl(LLDPTLV ttl) {
-        tlvList.put(getType(TTL), ttl);
+        setTLV(TTL, ttl);
         return this;
     }
 
     /**
      * @return the optionalTLVList
      */
-    public List<LLDPTLV> getOptionalTLVList() {
-        List<LLDPTLV> list = new ArrayList<LLDPTLV>();
-        for (Map.Entry<Byte, LLDPTLV> entry : tlvList.entrySet()) {
-            byte type = entry.getKey();
-            if ((type == LLDPTLV.TLVType.ChassisID.getValue())
-                    || (type == LLDPTLV.TLVType.PortID.getValue())
-                    || (type == LLDPTLV.TLVType.TTL.getValue())
-                    || (type == LLDPTLV.TLVType.SystemName.getValue())) {
-                continue;
-            } else {
-                list.add(entry.getValue());
-            }
-        }
-        return list;
+    public Iterable<LLDPTLV> getOptionalTLVList() {
+        return optionalTLVs.values();
     }
 
     /**
      * @return the customTlvList
      */
-    public List<LLDPTLV> getCustomTlvList() {
-        return customTlvList;
+    public Iterable<LLDPTLV> getCustomTlvList() {
+        return customTLVs.values();
     }
 
     /**
@@ -189,7 +200,7 @@ public class LLDP extends Packet {
      */
     public LLDP setOptionalTLVList(List<LLDPTLV> optionalTLVList) {
         for (LLDPTLV tlv : optionalTLVList) {
-            tlvList.put(tlv.getType(), tlv);
+            optionalTLVs.put(tlv.getType(), tlv);
         }
         return this;
     }
@@ -199,20 +210,22 @@ public class LLDP extends Packet {
      *            the list of custom TLVs to set
      * @return this LLDP
      */
-    public LLDP setCustomTLVList(final List<LLDPTLV> customTLVList) {
-        this.customTlvList = new ArrayList<>(customTLVList);
+    public LLDP addCustomTLV(final LLDPTLV customTLV) {
+        CustomTLVKey key = new CustomTLVKey(LLDPTLV.extractCustomOUI(customTLV),
+                LLDPTLV.extractCustomSubtype(customTLV));
+        customTLVs.put(key, customTLV);
+
         return this;
     }
 
     @Override
-    public Packet deserialize(byte[] data, int bitOffset, int size)
-            throws PacketException {
+    public Packet deserialize(byte[] data, int bitOffset, int size) throws PacketException {
         int lldpOffset = bitOffset; // LLDP start
         int lldpSize = size; // LLDP size
 
         if (logger.isTraceEnabled()) {
-          logger.trace("LLDP: {} (offset {} bitsize {})", new Object[] {
-                  HexEncode.bytesToHexString(data), lldpOffset, lldpSize });
+            logger.trace("LLDP: {} (offset {} bitsize {})", new Object[] { HexEncode.bytesToHexString(data),
+                    lldpOffset, lldpSize });
         }
         /*
          * Deserialize the TLVs until we reach the end of the packet
@@ -221,15 +234,15 @@ public class LLDP extends Packet {
             LLDPTLV tlv = new LLDPTLV();
             tlv.deserialize(data, lldpOffset, lldpSize);
             if (tlv.getType() == 0 && tlv.getLength() == 0) {
-               break;
+                break;
             }
             int tlvSize = tlv.getTLVSize(); // Size of current TLV in bits
             lldpOffset += tlvSize;
             lldpSize -= tlvSize;
             if (tlv.getType() == LLDPTLV.TLVType.Custom.getValue()) {
-                customTlvList.add(tlv);
+                addCustomTLV(tlv);
             } else {
-                this.tlvList.put(tlv.getType(), tlv);
+                this.putToTLVs(tlv.getType(), tlv);
             }
         }
         return this;
@@ -240,12 +253,11 @@ public class LLDP extends Packet {
         int startOffset = 0;
         byte[] serializedBytes = new byte[getLLDPPacketLength()];
 
-        final Iterable<LLDPTLV> allTlvs = Iterables.concat(tlvList.values(), customTlvList);
+        final Iterable<LLDPTLV> allTlvs = Iterables.concat(mandatoryTLVs.values(), optionalTLVs.values(), customTLVs.values());
         for (LLDPTLV tlv : allTlvs) {
             int numBits = tlv.getTLVSize();
             try {
-                BitBufferHelper.setBytes(serializedBytes, tlv.serialize(),
-                        startOffset, numBits);
+                BitBufferHelper.setBytes(serializedBytes, tlv.serialize(), startOffset, numBits);
             } catch (BufferException e) {
                 throw new PacketException(e.getMessage());
             }
@@ -253,16 +265,14 @@ public class LLDP extends Packet {
         }
         // Now add the empty LLDPTLV at the end
         try {
-            BitBufferHelper.setBytes(serializedBytes,
-                    LLDP.emptyTLV.serialize(), startOffset,
+            BitBufferHelper.setBytes(serializedBytes, LLDP.emptyTLV.serialize(), startOffset,
                     LLDP.emptyTLV.getTLVSize());
         } catch (BufferException e) {
             throw new PacketException(e.getMessage());
         }
 
         if (logger.isTraceEnabled()) {
-          logger.trace("LLDP: serialized: {}",
-                  HexEncode.bytesToHexString(serializedBytes));
+            logger.trace("LLDP: serialized: {}", HexEncode.bytesToHexString(serializedBytes));
         }
         return serializedBytes;
     }
@@ -274,15 +284,9 @@ public class LLDP extends Packet {
      */
     private int getLLDPPacketLength() {
         int len = 0;
-        LLDPTLV tlv;
-
-        for (Map.Entry<Byte, LLDPTLV> entry : this.tlvList.entrySet()) {
-            tlv = entry.getValue();
-            len += tlv.getTLVSize();
-        }
 
-        for (LLDPTLV customTlv : this.customTlvList) {
-            len += customTlv.getTLVSize();
+        for (LLDPTLV lldptlv : Iterables.concat(mandatoryTLVs.values(), optionalTLVs.values(), customTLVs.values())) {
+            len += lldptlv.getTLVSize();
         }
 
         len += LLDP.emptyTLV.getTLVSize();
index 22bd4626d196c11a9c4b2a4bc00929e27e79ff33..7b93d5f19cc3f9dcdbdb85454bb128b64b84a7c6 100644 (file)
@@ -8,13 +8,16 @@
 
 package org.opendaylight.controller.liblldp;
 
+import org.apache.commons.lang3.ArrayUtils;
+import org.slf4j.LoggerFactory;
+
+import org.slf4j.Logger;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
-
 import org.apache.commons.lang3.tuple.MutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 
@@ -27,14 +30,26 @@ public class LLDPTLV extends Packet {
     private static final String LENGTH = "Length";
     private static final String VALUE = "Value";
     private static final int LLDPTLVFields = 3;
+
+    /** OpenFlow OUI */
     public static final byte[] OFOUI = new byte[] { (byte) 0x00, (byte) 0x26,
-        (byte) 0xe1 }; // OpenFlow OUI
-    public static final byte[] customTlvSubType = new byte[] { 0 };
-    public static final int customTlvOffset = OFOUI.length
-            + customTlvSubType.length;
+        (byte) 0xe1 };
+
+    /** Length of Organizationally defined subtype field of TLV in bytes   */
+    private static final byte customTlvSubTypeLength = (byte)1;
+
+    /** OpenFlow subtype: nodeConnectorId of source */
+    public static final byte[] CUSTOM_TLV_SUB_TYPE_NODE_CONNECTOR_ID = new byte[] { 0 };
+
+    /** OpenFlow subtype: custom sec = hash code of verification of origin of LLDP */
+    public static final byte[] CUSTOM_TLV_SUB_TYPE_CUSTOM_SEC = new byte[] { 1 };
+
+    public static final int customTlvOffset = OFOUI.length + customTlvSubTypeLength;
     public static final byte chassisIDSubType[] = new byte[] { 4 }; // MAC address for the system
     public static final byte portIDSubType[] = new byte[] { 7 }; // locally assigned
 
+    private static final Logger LOG = LoggerFactory.getLogger(LLDPTLV.class);
+
     public enum TLVType {
         Unknown((byte) 0), ChassisID((byte) 1), PortID((byte) 2), TTL((byte) 3), PortDesc(
                 (byte) 4), SystemName((byte) 5), SystemDesc((byte) 6), Custom(
@@ -254,16 +269,27 @@ public class LLDPTLV extends Packet {
      * @param portId
      *            port identifier string
      * @return the custom TLV value in byte array
+     * @see {@link #createCustomTLVValue(byte,String)}
      */
     static public byte[] createCustomTLVValue(String customString) {
-        byte[] customArray = customString.getBytes(Charset.defaultCharset());
-        byte[] customValue = new byte[customTlvOffset + customArray.length];
+        byte[] customByteArray = customString.getBytes(Charset.defaultCharset());
+        return createCustomTLVValue(CUSTOM_TLV_SUB_TYPE_NODE_CONNECTOR_ID, customByteArray);
+    }
+
+    /**
+     * Creates the custom TLV value including OUI, subtype and custom string
+     * @param subtype openflow subtype
+     * @param portId
+     *            port identifier string
+     * @return the custom TLV value in byte array
+     */
+    static public byte[] createCustomTLVValue(byte[] subtype, byte[] customByteArray) {
+        byte[] customValue = new byte[customTlvOffset + customByteArray.length];
 
         System.arraycopy(OFOUI, 0, customValue, 0, OFOUI.length);
-        System.arraycopy(customTlvSubType, 0, customValue, OFOUI.length,
-                customTlvSubType.length);
-        System.arraycopy(customArray, 0, customValue, customTlvOffset,
-                customArray.length);
+        System.arraycopy(subtype, 0, customValue, OFOUI.length, 1);
+        System.arraycopy(customByteArray, 0, customValue, customTlvOffset,
+                customByteArray.length);
 
         return customValue;
     }
@@ -334,4 +360,14 @@ public class LLDPTLV extends Packet {
 
         return customString;
     }
+
+    public static int extractCustomOUI(final LLDPTLV lldptlv) {
+        byte[] value = lldptlv.getValue();
+        return BitBufferHelper.getInt(ArrayUtils.subarray(value, 0, 3));
+    }
+
+    public static byte extractCustomSubtype(final LLDPTLV lldptlv) {
+        byte[] value = lldptlv.getValue();
+        return BitBufferHelper.getByte(ArrayUtils.subarray(value, 3, 4));
+    }
 }
index 1a14e0b650e9d041b9e862301a6ffb35f420768f..8bd700875a2aff65e8eec5758bab26d0c7deaa59 100644 (file)
@@ -8,7 +8,13 @@
 package org.opendaylight.controller.liblldp;
 
 import static org.junit.Assert.assertArrayEquals;
-
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import java.util.Iterator;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -44,7 +50,7 @@ public class LLDPTest {
     private static final byte[] SYSTEM_CAPABILITIES_VALUE = "dummy system capabilities".getBytes();
     private static final short SYSTEM_CAPABILITIES_LENGTH = (short) SYSTEM_CAPABILITIES_VALUE.length;
 
-    private static final byte[] OUI = new byte[] { (byte) 0X11, (byte) 0X22, (byte) 0X33 };
+    private static final byte[] OUI = LLDPTLV.OFOUI;
 
     private static final byte[] OUI_SUBTYPE_A = new byte[] { (byte) 0 };
     private static final byte[] CUSTOM_SUBTYPE_A_VALUE = "first custom value A".getBytes();
@@ -54,6 +60,15 @@ public class LLDPTest {
     private static final byte[] CUSTOM_SUBTYPE_B_VALUE = "second custom value B".getBytes();
     private static final short CUSTOM_SUBTYPE_B_LENGTH = (short) (OUI.length + OUI_SUBTYPE_B.length + CUSTOM_SUBTYPE_B_VALUE.length);
 
+    private static final byte[] BYTES_BEFORE_CUSTOM_A = new byte[] { 0x00, 0x26, (byte) 0xe1, OUI_SUBTYPE_A[0] };
+    private static final byte[] BYTES_BEFORE_CUSTOM_B = new byte[] { 0x00, 0x26, (byte) 0xe1, OUI_SUBTYPE_B[0] };
+    private LLDP lldpBuilder;
+
+    @Before
+    public void setup() {
+        lldpBuilder = new LLDP();
+    }
+
     /**
      * Tests whether serialization of LLDP packet is correct
      *
@@ -62,8 +77,6 @@ public class LLDPTest {
      */
     @Test
     public void testSerialize() throws PacketException {
-        LLDP lldpBuilder = new LLDP();
-
         lldpBuilder.setChassisId(dummyTlv(LLDPTLV.TLVType.ChassisID.getValue(), CHASSIS_ID_LENGTH, CHASSIS_ID_VALUE));
         lldpBuilder.setTtl(dummyTlv(LLDPTLV.TLVType.TTL.getValue(), TTL_LENGTH, TTL_VALUE));
         lldpBuilder.setPortId(dummyTlv(LLDPTLV.TLVType.PortID.getValue(), PORT_LENGTH, PORT_VALUE));
@@ -76,13 +89,11 @@ public class LLDPTest {
         optionalTLVs.add(dummyTlv(SYSTEM_CAPABILITIES_TLV, SYSTEM_CAPABILITIES_LENGTH, SYSTEM_CAPABILITIES_VALUE));
         lldpBuilder.setOptionalTLVList(optionalTLVs);
 
-        // // adding custom TLVs
-         final List<LLDPTLV> customTLVs = new ArrayList<>();
-         customTLVs.add(dummyCustomTlv(LLDPTLV.TLVType.Custom.getValue(), OUI, OUI_SUBTYPE_A,
-         CUSTOM_SUBTYPE_A_LENGTH, CUSTOM_SUBTYPE_A_VALUE));
-         customTLVs.add(dummyCustomTlv(LLDPTLV.TLVType.Custom.getValue(), OUI, OUI_SUBTYPE_B,
-         CUSTOM_SUBTYPE_B_LENGTH, CUSTOM_SUBTYPE_B_VALUE));
-         lldpBuilder.setCustomTLVList(customTLVs);
+        // adding custom TLVs
+        lldpBuilder.addCustomTLV(dummyCustomTlv(LLDPTLV.TLVType.Custom.getValue(), OUI, OUI_SUBTYPE_A,
+                CUSTOM_SUBTYPE_A_LENGTH, CUSTOM_SUBTYPE_A_VALUE));
+        lldpBuilder.addCustomTLV(dummyCustomTlv(LLDPTLV.TLVType.Custom.getValue(), OUI, OUI_SUBTYPE_B,
+                CUSTOM_SUBTYPE_B_LENGTH, CUSTOM_SUBTYPE_B_VALUE));
 
         byte[] serialized = lldpBuilder.serialize();
 
@@ -111,9 +122,6 @@ public class LLDPTest {
      */
     @Test
     public void testDeserialize() throws Exception {
-        LLDP lldp = new LLDP();
-        byte[] bytesBeforeCustomA = new byte[] { 0x00, 0x26, (byte) 0xe1, OUI_SUBTYPE_A[0] };
-        byte[] bytesBeforeCustomB = new byte[] { 0x00, 0x26, (byte) 0xe1, OUI_SUBTYPE_B[0] };
 
         byte[] rawLldpTlv = Bytes.concat(
                 awaitedBytes((byte) 0b00000010, CHASSIS_ID_LENGTH, CHASSIS_ID_VALUE, null),
@@ -123,30 +131,83 @@ public class LLDPTest {
                 awaitedBytes((byte) 0b00010010, SYSTEM_CAPABILITIES_LENGTH,
                         SYSTEM_CAPABILITIES_VALUE, null),
                 awaitedBytes((byte) 0b11111110, CUSTOM_SUBTYPE_A_LENGTH, CUSTOM_SUBTYPE_A_VALUE,
-                        bytesBeforeCustomA),
+                        BYTES_BEFORE_CUSTOM_A),
                 awaitedBytes((byte) 0b11111110, CUSTOM_SUBTYPE_B_LENGTH, CUSTOM_SUBTYPE_B_VALUE,
-                        bytesBeforeCustomB));
+                        BYTES_BEFORE_CUSTOM_B));
 
-        lldp.deserialize(rawLldpTlv, 0, rawLldpTlv.length * NetUtils.NumBitsInAByte);
-        Assert.assertEquals("chassis", new String(lldp.getChassisId().getValue()));
-        Assert.assertArrayEquals(TTL_VALUE, lldp.getTtl().getValue());
-        Assert.assertEquals("dummy port id", new String(lldp.getPortId().getValue()));
-        Assert.assertEquals("dummy system name", new String(lldp.getSystemNameId().getValue()));
+        lldpBuilder.deserialize(rawLldpTlv, 0, rawLldpTlv.length * NetUtils.NumBitsInAByte);
+        Assert.assertEquals("chassis", new String(lldpBuilder.getChassisId().getValue()));
+        Assert.assertArrayEquals(TTL_VALUE, lldpBuilder.getTtl().getValue());
+        Assert.assertEquals("dummy port id", new String(lldpBuilder.getPortId().getValue()));
+        Assert.assertEquals("dummy system name", new String(lldpBuilder.getSystemNameId().getValue()));
 
         // optional items check
-        List<LLDPTLV> tlvOptionalList = lldp.getOptionalTLVList();
-        Assert.assertEquals(1, tlvOptionalList.size());
+        Iterator<LLDPTLV> iteratorTlvOptional = lldpBuilder.getOptionalTLVList().iterator();
+
+        assertTrue(iteratorTlvOptional.hasNext());
+        LLDPTLV item0 = iteratorTlvOptional.next();
+        Assert.assertEquals(5, item0.getType());
+        Assert.assertEquals("dummy system name", new String(item0.getValue()));
+        assertTrue(iteratorTlvOptional.hasNext());
 
-        LLDPTLV itemOpt0 = tlvOptionalList.get(0);
-        Assert.assertEquals(9, itemOpt0.getType());
-        Assert.assertEquals("dummy system capabilities", new String(itemOpt0.getValue()));
+        assertTrue(iteratorTlvOptional.hasNext());
+        LLDPTLV item1 = iteratorTlvOptional.next();
+        Assert.assertEquals(9, item1.getType());
+        Assert.assertEquals("dummy system capabilities", new String(item1.getValue()));
+        assertFalse(iteratorTlvOptional.hasNext());
 
         // custom items check
-        List<LLDPTLV> tlvCustomList = lldp.getCustomTlvList();
-        Assert.assertEquals(2, tlvCustomList.size());
+        Iterable<LLDPTLV> customTlvs = lldpBuilder.getCustomTlvList();
+        Iterator<LLDPTLV> iteratorLLDPTLV = customTlvs.iterator();
+        assertEquals(true, iteratorLLDPTLV.hasNext());
+        checkCustomTlv(iteratorLLDPTLV.next(), "first custom value A");
+        assertEquals(true, iteratorLLDPTLV.hasNext());
+        checkCustomTlv(iteratorLLDPTLV.next(), "second custom value B");
+        assertEquals(false, iteratorLLDPTLV.hasNext());
+    }
 
-        checkCustomTlv(tlvCustomList.get(0), "first custom value A");
-        checkCustomTlv(tlvCustomList.get(1), "second custom value B");
+    /**
+     * Test of {@link LLDP#addCustomTLV(LLDPTLV)}
+     * @throws PacketException
+     */
+    @Test
+    public void testAddCustomTLV() throws PacketException {
+        byte[] customA = awaitedBytes((byte) 0b11111110, CUSTOM_SUBTYPE_A_LENGTH, CUSTOM_SUBTYPE_A_VALUE,
+                BYTES_BEFORE_CUSTOM_A);
+        byte[] customB = awaitedBytes((byte) 0b11111110, CUSTOM_SUBTYPE_B_LENGTH, CUSTOM_SUBTYPE_B_VALUE,
+                BYTES_BEFORE_CUSTOM_B);
+
+        Packet lldptlvA = new LLDPTLV().deserialize(customA, 0, customA.length);
+        assertTrue(lldptlvA instanceof LLDPTLV);
+        Packet lldptlvB = new LLDPTLV().deserialize(customB, 0, customB.length);
+        assertTrue(lldptlvB instanceof LLDPTLV);
+
+        lldpBuilder.addCustomTLV((LLDPTLV) lldptlvA);
+        lldpBuilder.addCustomTLV((LLDPTLV) lldptlvB);
+
+        Iterator<LLDPTLV> customTLVsIterator = lldpBuilder.getCustomTlvList().iterator();
+        assertTrue(customTLVsIterator.hasNext());
+        customTLVsIterator.next();
+        assertTrue(customTLVsIterator.hasNext());
+        customTLVsIterator.next();
+        assertFalse(customTLVsIterator.hasNext());
+    }
+
+    @Test
+    public void testGetCustomTLV() throws PacketException {
+        int ouiInt = BitBufferHelper.getInt(OUI);
+        CustomTLVKey key = new CustomTLVKey(ouiInt, OUI_SUBTYPE_A[0]);
+        LLDPTLV customTLV = lldpBuilder.getCustomTLV(key);
+        assertNull(customTLV);
+
+        byte[] customA = awaitedBytes((byte) 0b11111110, CUSTOM_SUBTYPE_A_LENGTH, CUSTOM_SUBTYPE_A_VALUE,
+                BYTES_BEFORE_CUSTOM_A);
+        lldpBuilder.deserialize(customA, 0, customA.length);
+
+        customTLV = lldpBuilder.getCustomTLV(key);
+        assertNotNull(customTLV);
+        assertEquals(ouiInt, LLDPTLV.extractCustomOUI(customTLV));
+        assertEquals(OUI_SUBTYPE_A[0], LLDPTLV.extractCustomSubtype(customTLV));
     }
 
     /**