Merge "BUG 2820 - LLDP refactor"
authorTony Tkacik <ttkacik@cisco.com>
Tue, 5 May 2015 08:23:31 +0000 (08:23 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 5 May 2015 08:23:32 +0000 (08:23 +0000)
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 01900b421b51b2b6b8191a8b481590b1392bc8a9..9f4fc8d7e5cc02b585ebccb8671f33dfc3fb2369 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));
     }
 
     /**