/* * 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 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; import org.apache.commons.lang3.ArrayUtils; import org.junit.Assert; import org.junit.Test; import org.junit.internal.ArrayComparisonFailure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.primitives.Bytes; /** * Test of {@link LLDP} serialization feature (TODO: and deserialization) */ public class LLDPTest { private static final Logger LOG = LoggerFactory.getLogger(LLDPTest.class); 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 = 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(); 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); 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 * * @see LLDP#serialize() * @throws PacketException */ @Test public void testSerialize() throws PacketException { 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 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(); 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]); } /** * Tests whether serialization of LLDP packet is correct * * @see LLDP#deserialize(byte[], int, int) * @throws Exception */ @Test public void testDeserialize() throws Exception { byte[] rawLldpTlv = Bytes .concat(awaitedBytes((byte) 0b00000010, CHASSIS_ID_LENGTH, CHASSIS_ID_VALUE, null), awaitedBytes((byte) 0b00000110, TTL_LENGTH, TTL_VALUE, null), awaitedBytes((byte) 0b00000100, PORT_LENGTH, PORT_VALUE, null), awaitedBytes((byte) 0b00001010, SYSTEM_NAME_LENGTH, SYSTEM_NAME_VALUE, null), awaitedBytes((byte) 0b00010010, SYSTEM_CAPABILITIES_LENGTH, SYSTEM_CAPABILITIES_VALUE, null), awaitedBytes((byte) 0b11111110, CUSTOM_SUBTYPE_A_LENGTH, CUSTOM_SUBTYPE_A_VALUE, BYTES_BEFORE_CUSTOM_A), awaitedBytes((byte) 0b11111110, CUSTOM_SUBTYPE_B_LENGTH, CUSTOM_SUBTYPE_B_VALUE, BYTES_BEFORE_CUSTOM_B)); 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 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()); 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 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()); } /** * 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)); } /** * @param customItem * @param expectedValue */ private static void checkCustomTlv(final LLDPTLV customItem, final String expectedValue) { Assert.assertEquals(127, customItem.getType()); LOG.debug("custom TLV1.length: {}", customItem.getLength()); Assert.assertEquals(expectedValue, new String(LLDPTLV.getCustomString(customItem.getValue(), customItem.getLength()))); } private static int checkTLV(final byte[] serializedData, final int offset, final byte typeTLVBits, final String typeTLVName, final short lengthTLV, final byte[] valueTLV, final 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(final byte typeTLV, final short length, final byte[] value, final 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, final byte[] oui, final byte[] ouiSubtype, final short customLength, final 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; } }