diff --git a/src/main/java/net/floodlightcontroller/packet/LLDPOrganizationalTLV.java b/src/main/java/net/floodlightcontroller/packet/LLDPOrganizationalTLV.java new file mode 100644 index 0000000000000000000000000000000000000000..a0930bd0fcf73c977ae778ed6dcaf53cbbe15c24 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/packet/LLDPOrganizationalTLV.java @@ -0,0 +1,181 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package net.floodlightcontroller.packet; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * The class representing LLDP Organizationally Specific TLV. + * + * @author Sho Shimizu (sho.shimizu@gmail.com) + */ +public class LLDPOrganizationalTLV extends LLDPTLV { + public static final int OUI_LENGTH = 3; + public static final int SUBTYPE_LENGTH = 1; + public static final byte ORGANIZATIONAL_TLV_TYPE = 127; + public static final int MAX_INFOSTRING_LENGTH = 507; + + protected byte[] oui; + protected byte subType; + private byte[] infoString; + + public LLDPOrganizationalTLV() { + type = ORGANIZATIONAL_TLV_TYPE; + } + + /** + * Set the value of OUI. + * @param oui The value of OUI to be set. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setOUI(byte[] oui) { + if (oui.length != OUI_LENGTH) { + throw new IllegalArgumentException("The length of OUI must be " + OUI_LENGTH + + ", but it is " + oui.length); + } + this.oui = Arrays.copyOf(oui, oui.length); + return this; + } + + /** + * Returns the value of the OUI. + * @return The value of the OUI . + */ + public byte[] getOUI() { + return Arrays.copyOf(oui, oui.length); + } + + /** + * Set the value of sub type. + * @param subType The value of sub type to be set. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setSubType(byte subType) { + this.subType = subType; + return this; + } + + /** + * Returns the value of the sub type. + * @return The value of the sub type. + */ + public byte getSubType() { + return subType; + } + + /** + * Set the value of information string. + * @param infoString the byte array of the value of information string. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setInfoString(byte[] infoString) { + if (infoString.length > MAX_INFOSTRING_LENGTH) { + throw new IllegalArgumentException("The length of infoString cannot exceed " + MAX_INFOSTRING_LENGTH); + } + this.infoString = Arrays.copyOf(infoString, infoString.length); + return this; + } + + /** + * Set the value of information string. + * The String value is automatically converted into byte array with UTF-8 encoding. + * @param infoString the String value of information string. + * @return This LLDP Organizationally Specific TLV. + */ + public LLDPOrganizationalTLV setInfoString(String infoString) { + byte[] infoStringBytes = infoString.getBytes(Charset.forName("UTF-8")); + return setInfoString(infoStringBytes); + } + + /** + * Returns the value of information string. + * @return the value of information string. + */ + public byte[] getInfoString() { + return Arrays.copyOf(infoString, infoString.length); + } + + @Override + public byte[] serialize() { + int valueLength = OUI_LENGTH + SUBTYPE_LENGTH + infoString.length; + value = new byte[valueLength]; + ByteBuffer bb = ByteBuffer.wrap(value); + bb.put(oui); + bb.put(subType); + bb.put(infoString); + return super.serialize(); + } + + @Override + public LLDPTLV deserialize(ByteBuffer bb) { + super.deserialize(bb); + ByteBuffer optionalField = ByteBuffer.wrap(value); + + byte[] oui = new byte[OUI_LENGTH]; + optionalField.get(oui); + setOUI(oui); + + setSubType(optionalField.get()); + + byte[] infoString = new byte[getLength() - OUI_LENGTH - SUBTYPE_LENGTH]; + optionalField.get(infoString); + setInfoString(infoString); + return this; + } + + @Override + public int hashCode() { + final int prime = 1423; + int result = 1; + result = prime * result + type; + result = prime * result + length; + result = prime * result + Arrays.hashCode(oui); + result = prime * result + subType; + result = prime * result + Arrays.hashCode(infoString); + return result; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof LLDPOrganizationalTLV)) { + return false; + } + + LLDPOrganizationalTLV other = (LLDPOrganizationalTLV)o; + if (this.type != other.type) { + return false; + } + if (this.length != other.length) { + return false; + } + if (!Arrays.equals(this.oui, other.oui)) { + return false; + } + if (this.subType != other.subType) { + return false; + } + if (!Arrays.equals(this.infoString, other.infoString)) { + return false; + } + + return true; + } +} diff --git a/src/test/java/net/floodlightcontroller/packet/LLDPOrganizationalTLVTest.java b/src/test/java/net/floodlightcontroller/packet/LLDPOrganizationalTLVTest.java new file mode 100644 index 0000000000000000000000000000000000000000..88fb26f2e75ed8dbf34d85d2f4a7e7b32e1e1e2b --- /dev/null +++ b/src/test/java/net/floodlightcontroller/packet/LLDPOrganizationalTLVTest.java @@ -0,0 +1,95 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package net.floodlightcontroller.packet; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class LLDPOrganizationalTLVTest { + private final byte[] expected = new byte[] { + // Type: 127, Length: 13 + (byte) 254, 13, + // OpenFlow OUI: 00-26-E1 + 0x0, 0x26, (byte)0xe1, + // SubType: 12 + 0xc, + // Bytes in "ExtraInfo" + 0x45, 0x78, 0x74, 0x72, 0x61, 0x49, 0x6e, 0x66, 0x6f + }; + + @Test(expected = IllegalArgumentException.class) + public void testShortOUI() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setOUI(new byte[2]); + } + + @Test(expected = IllegalArgumentException.class) + public void testLongOUI() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setOUI(new byte[4]); + } + + @Test(expected = IllegalArgumentException.class) + public void testLongInfoString() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setInfoString(new byte[LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH + 1]); + } + + @Test + public void testMaxInfoString() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setInfoString(new byte[LLDPOrganizationalTLV.MAX_INFOSTRING_LENGTH]); + } + + @Test + public void testInfoString() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setInfoString("ExtraInfo"); + assertThat(tlv.getInfoString(), is("ExtraInfo".getBytes(Charset.forName("UTF-8")))); + } + + @Test + public void testSerialize() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.setLength((short) 13); + // OpenFlow OUI is 00-26-E1 + tlv.setOUI(new byte[] {0x0, 0x26, (byte) 0xe1}); + tlv.setSubType((byte) 12); + tlv.setInfoString("ExtraInfo".getBytes(Charset.forName("UTF-8"))); + + assertThat(tlv.getType(), is((byte)127)); + assertThat(tlv.getLength(), is((short)13)); + assertThat(tlv.getOUI(), is(new byte[] {0x0, 0x26, (byte) 0xe1})); + assertThat(tlv.getSubType(), is((byte)12)); + assertThat(tlv.serialize(), is(expected)); + } + + @Test + public void testDeserialize() { + LLDPOrganizationalTLV tlv = new LLDPOrganizationalTLV(); + tlv.deserialize(ByteBuffer.wrap(expected)); + + assertThat(tlv.getType(), is((byte)127)); + assertThat(tlv.getLength(), is((short)13)); + assertThat(tlv.getOUI(), is(new byte[] {0x0, 0x26, (byte) 0xe1})); + assertThat(tlv.getSubType(), is((byte)12)); + assertThat(tlv.getInfoString(), is("ExtraInfo".getBytes(Charset.forName("UTF-8")))); + } +}