Bug 2820 - problem to add second TLV with type 127. 82/16682/4
authorJozef Gloncak <jgloncak@cisco.com>
Tue, 17 Mar 2015 10:34:03 +0000 (11:34 +0100)
committerJozef Gloncak <jgloncak@cisco.com>
Fri, 20 Mar 2015 07:40:46 +0000 (08:40 +0100)
Custom TLVs aren't saved to map with key type but are stored in
standalone array.

 - fixed list copy (for custom TLV list)
 - extended test
 - involved custom TLV list items into packet length counting
 - published

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

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

index 148a4f3792a70eb77028b0af1f8c82c3af8cf6cb..286c68e677e664383ec70cd81bcd9cc898ee37b6 100644 (file)
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
   </dependencies>
 
   <build>
@@ -35,8 +44,8 @@
         <configuration>
           <instructions>
             <Import-Package>org.slf4j,
         <configuration>
           <instructions>
             <Import-Package>org.slf4j,
-              org.apache.commons.lang3.builder,
-              org.apache.commons.lang3.tuple
+              org.apache.commons.lang3.*,
+              com.google.common.*
             </Import-Package>
             <Export-Package>
               org.opendaylight.controller.liblldp</Export-Package>
             </Import-Package>
             <Export-Package>
               org.opendaylight.controller.liblldp</Export-Package>
index 9b7efbb1e6f21676875cc4b6e2202ea14592d3ac..94f25c03a302523cec0e275f18074020b90e4820 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.controller.liblldp;
 
 
 package org.opendaylight.controller.liblldp;
 
+import java.util.Collections;
+import com.google.common.collect.Iterables;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -29,6 +31,8 @@ public class LLDP extends Packet {
             (byte) 0xc2, 0, 0, (byte) 0xe };
     private Map<Byte, LLDPTLV> tlvList;
 
             (byte) 0xc2, 0, 0, (byte) 0xe };
     private Map<Byte, LLDPTLV> tlvList;
 
+    private List<LLDPTLV> customTlvList = Collections.emptyList();
+
     /**
      * Default constructor that creates the tlvList LinkedHashMap
      */
     /**
      * Default constructor that creates the tlvList LinkedHashMap
      */
@@ -180,6 +184,16 @@ public class LLDP extends Packet {
         return this;
     }
 
         return this;
     }
 
+    /**
+     * @param customTLVList
+     *            the list of custom TLVs to set
+     * @return this LLDP
+     */
+    public LLDP setCustomTLVList(final List<LLDPTLV> customTLVList) {
+        this.customTlvList = new ArrayList<>(customTLVList);
+        return this;
+    }
+
     @Override
     public Packet deserialize(byte[] data, int bitOffset, int size)
             throws PacketException {
     @Override
     public Packet deserialize(byte[] data, int bitOffset, int size)
             throws PacketException {
@@ -212,8 +226,8 @@ public class LLDP extends Packet {
         int startOffset = 0;
         byte[] serializedBytes = new byte[getLLDPPacketLength()];
 
         int startOffset = 0;
         byte[] serializedBytes = new byte[getLLDPPacketLength()];
 
-        for (Map.Entry<Byte, LLDPTLV> entry : tlvList.entrySet()) {
-            LLDPTLV tlv = entry.getValue();
+        final Iterable<LLDPTLV> allTlvs = Iterables.concat(tlvList.values(), customTlvList);
+        for (LLDPTLV tlv : allTlvs) {
             int numBits = tlv.getTLVSize();
             try {
                 BitBufferHelper.setBytes(serializedBytes, tlv.serialize(),
             int numBits = tlv.getTLVSize();
             try {
                 BitBufferHelper.setBytes(serializedBytes, tlv.serialize(),
@@ -252,6 +266,11 @@ public class LLDP extends Packet {
             tlv = entry.getValue();
             len += tlv.getTLVSize();
         }
             tlv = entry.getValue();
             len += tlv.getTLVSize();
         }
+
+        for (LLDPTLV customTlv : this.customTlvList) {
+            len += customTlv.getTLVSize();
+        }
+
         len += LLDP.emptyTLV.getTLVSize();
 
         return len / NetUtils.NumBitsInAByte;
         len += LLDP.emptyTLV.getTLVSize();
 
         return len / NetUtils.NumBitsInAByte;
diff --git a/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/liblldp/LLDPTest.java b/opendaylight/commons/liblldp/src/test/java/org/opendaylight/controller/liblldp/LLDPTest.java
new file mode 100644 (file)
index 0000000..a9cfa72
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.liblldp;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.util.ArrayList;
+
+import java.util.List;
+import org.junit.internal.ArrayComparisonFailure;
+import org.junit.Test;
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Test of {@link LLDP} serialization feature (TODO: and deserialization)
+ */
+public class LLDPTest {
+
+    private static final byte[] CHASSIS_ID_VALUE = "chassis".getBytes();
+    private static final short CHASSIS_ID_LENGTH = (short) CHASSIS_ID_VALUE.length;
+
+    private static final byte[] TTL_VALUE = new byte[] { (byte) 0, (byte) 100 };
+    private static final short TTL_LENGTH = (short) TTL_VALUE.length;
+
+    private static final byte[] PORT_VALUE = "dummy port id".getBytes();
+    private static final short PORT_LENGTH = (short) PORT_VALUE.length;
+
+    private static final byte[] SYSTEM_NAME_VALUE = "dummy system name".getBytes();
+    private static final short SYSTEM_NAME_LENGTH = (short) SYSTEM_NAME_VALUE.length;
+
+    private static final byte SYSTEM_CAPABILITIES_TLV = 8;
+    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_SUBTYPE_A = new byte[] { (byte) 0 };
+    private static final byte[] CUSTOM_SUBTYPE_A_VALUE = "first custom value A".getBytes();
+    private static final short CUSTOM_SUBTYPE_A_LENGTH = (short) (OUI.length + OUI_SUBTYPE_A.length + CUSTOM_SUBTYPE_A_VALUE.length);
+
+    private static final byte[] OUI_SUBTYPE_B = new byte[] { (byte) 1 };
+    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);
+
+    /**
+     * Tests whether serialization of LLDP packet is correct
+     * @see LLDP#serialize()
+     * @throws PacketException
+     */
+    @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));
+        lldpBuilder.setSystemNameId(dummyTlv(LLDPTLV.TLVType.SystemName.getValue(), SYSTEM_NAME_LENGTH,
+                SYSTEM_NAME_VALUE));
+
+        // adding optional TLVs for which doesn't exist special set* methods in LLDP
+        final List<LLDPTLV> optionalTLVs = new ArrayList<>();
+        // System Capabilities TLV (type = 7)
+        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);
+
+        byte[] serialized = lldpBuilder.serialize();
+
+        int offset = 0;
+        offset = checkTLV(serialized, offset, (byte) 0b00000010, "ChassisID", CHASSIS_ID_LENGTH, CHASSIS_ID_VALUE);
+        offset = checkTLV(serialized, offset, (byte) 0b00000110, "TTL", TTL_LENGTH, TTL_VALUE);
+        offset = checkTLV(serialized, offset, (byte) 0b00000100, "PortID", PORT_LENGTH, PORT_VALUE);
+        offset = checkTLV(serialized, offset, (byte) 0b00001010, "SystemName", SYSTEM_NAME_LENGTH, SYSTEM_NAME_VALUE);
+        offset = checkTLV(serialized, offset, (byte) 0b00010000, "System capabilities", SYSTEM_CAPABILITIES_LENGTH,
+                SYSTEM_CAPABILITIES_VALUE);
+        offset = checkTLV(serialized, offset, (byte) 0b11111110, "Custom subtype A", CUSTOM_SUBTYPE_A_LENGTH,
+                CUSTOM_SUBTYPE_A_VALUE, OUI[0], OUI[1], OUI[2], OUI_SUBTYPE_A[0]);
+        offset = checkTLV(serialized, offset, (byte) 0b11111110, "Custom subtype B", CUSTOM_SUBTYPE_B_LENGTH,
+                CUSTOM_SUBTYPE_B_VALUE, OUI[0], OUI[1], OUI[2], OUI_SUBTYPE_B[0]);
+
+    }
+
+    private static int checkTLV(byte[] serializedData, int offset, byte typeTLVBits, String typeTLVName, short lengthTLV,
+            byte[] valueTLV, byte... bytesBeforeValue) throws ArrayComparisonFailure {
+        byte[] concreteTlvAwaited = awaitedBytes(typeTLVBits, lengthTLV, valueTLV, bytesBeforeValue);
+        int concreteTlvAwaitLength = concreteTlvAwaited.length;
+        assertArrayEquals("Serialization problem " + typeTLVName, concreteTlvAwaited,
+                ArrayUtils.subarray(serializedData, offset, offset + concreteTlvAwaitLength));
+        return offset + concreteTlvAwaitLength;
+    }
+
+    private static byte[] awaitedBytes(byte typeTLV, short length, byte[] value, byte[] bytesBeforeValue) {
+        byte[] awaited = ArrayUtils.EMPTY_BYTE_ARRAY;
+
+        // 0 - the less meaning byte (right), 1 most meaning byte (left)
+        byte lengthByte0 = (byte) length;
+        byte lengthByte1 = (byte) (length >> 8);
+
+        awaited = ArrayUtils.addAll(awaited, (byte) (typeTLV | lengthByte1), lengthByte0);
+        awaited = ArrayUtils.addAll(awaited, bytesBeforeValue);
+        awaited = ArrayUtils.addAll(awaited, value);
+        return awaited;
+    }
+
+    private static LLDPTLV dummyCustomTlv(final byte tlvType, byte[] oui, byte[] ouiSubtype, short customLength,
+            byte[] subtypeValue) {
+        byte[] fullCustomValue = new byte[0];
+        fullCustomValue = ArrayUtils.addAll(fullCustomValue, oui);
+        fullCustomValue = ArrayUtils.addAll(fullCustomValue, ouiSubtype);
+        fullCustomValue = ArrayUtils.addAll(fullCustomValue, subtypeValue);
+        return dummyTlv(tlvType, customLength, fullCustomValue);
+    }
+
+    private static LLDPTLV dummyTlv(final byte concreteTlv, final short concreteLength, final byte[] concreteValue) {
+        LLDPTLV tlv = new LLDPTLV();
+        tlv.setType(concreteTlv).setLength(concreteLength).setValue(concreteValue);
+        return tlv;
+    }
+}