DHCP util refactor
Move options to packet.dhcp package
Deprecated DHCPPacketType, add MsgType to DHCP class
Change-Id: I85ce7fa5e6f3fdc916fbbeba9a4e10e75064a054
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCP.java b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
index 012443e..ed1741e 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCP.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCP.java
@@ -18,12 +18,16 @@
package org.onlab.packet;
-import java.io.UnsupportedEncodingException;
+import com.google.common.collect.ImmutableMap;
+import org.onlab.packet.dhcp.DhcpOption;
+import org.onlab.packet.dhcp.DhcpRelayAgentOption;
+
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.ListIterator;
+import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onlab.packet.PacketUtils.checkInput;
@@ -53,20 +57,33 @@
public static final int MIN_HEADER_LENGTH = 240;
public static final byte OPCODE_REQUEST = 0x1;
public static final byte OPCODE_REPLY = 0x2;
-
public static final byte HWTYPE_ETHERNET = 0x1;
+ private static final Map<Byte, Deserializer<? extends DhcpOption>> OPTION_DESERIALIZERS =
+ ImmutableMap.of(DHCPOptionCode.OptionCode_CircuitID.value, DhcpRelayAgentOption.deserializer());
+ private static final int UNSIGNED_BYTE_MASK = 0xff;
+ private static final int BASE_OPTION_LEN = 60;
+ private static final int MIN_DHCP_LEN = 240;
+ private static final int BASE_HW_ADDR_LEN = 16;
+ private static final byte PAD_BYTE = 0;
+ private static final int BASE_SERVER_NAME_LEN = 64;
+ private static final int BASE_BOOT_FILE_NAME_LEN = 128;
+ private static final int MAGIC_COOKIE = 0x63825363;
+
public enum DHCPOptionCode {
- OptionCode_SubnetMask((byte) 1), OptionCode_RouterAddress((byte) 3), OptionCode_DomainServer((byte) 6),
- OptionCode_HostName((byte) 12), OptionCode_DomainName((byte) 15), OptionCode_BroadcastAddress((byte) 28),
- OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime((byte) 51), OptionCode_MessageType((byte) 53),
+ OptionCode_Pad((byte) 0), OptionCode_SubnetMask((byte) 1),
+ OptionCode_RouterAddress((byte) 3), OptionCode_DomainServer((byte) 6),
+ OptionCode_HostName((byte) 12), OptionCode_DomainName((byte) 15),
+ OptionCode_BroadcastAddress((byte) 28), OptionCode_RequestedIP((byte) 50),
+ OptionCode_LeaseTime((byte) 51), OptionCode_MessageType((byte) 53),
OptionCode_DHCPServerIp((byte) 54), OptionCode_RequestedParameters((byte) 55),
- OptionCode_RenewalTime((byte) 58), OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID((byte) 61),
- OptionCode_CircuitID((byte) 82), OptionCode_END((byte) 255);
+ OptionCode_RenewalTime((byte) 58), OPtionCode_RebindingTime((byte) 59),
+ OptionCode_ClientID((byte) 61), OptionCode_CircuitID((byte) 82),
+ OptionCode_END((byte) 255);
protected byte value;
- private DHCPOptionCode(final byte value) {
+ DHCPOptionCode(final byte value) {
this.value = value;
}
@@ -75,6 +92,65 @@
}
}
+ public enum MsgType {
+ // From RFC 1533
+ DHCPDISCOVER(1), DHCPOFFER(2), DHCPREQUEST(3), DHCPDECLINE(4), DHCPACK(5),
+ DHCPNAK(6), DHCPRELEASE(7),
+
+ // From RFC2132
+ DHCPINFORM(8),
+
+ // From RFC3203
+ DHCPFORCERENEW(9),
+
+ // From RFC4388
+ DHCPLEASEQUERY(10), DHCPLEASEUNASSIGNED(11), DHCPLEASEUNKNOWN(12),
+ DHCPLEASEACTIVE(13);
+
+ protected int value;
+
+ MsgType(final int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return this.value;
+ }
+
+ public static MsgType getType(final int value) {
+ switch (value) {
+ case 1:
+ return DHCPDISCOVER;
+ case 2:
+ return DHCPOFFER;
+ case 3:
+ return DHCPREQUEST;
+ case 4:
+ return DHCPDECLINE;
+ case 5:
+ return DHCPACK;
+ case 6:
+ return DHCPNAK;
+ case 7:
+ return DHCPRELEASE;
+ case 8:
+ return DHCPINFORM;
+ case 9:
+ return DHCPFORCERENEW;
+ case 10:
+ return DHCPLEASEQUERY;
+ case 11:
+ return DHCPLEASEUNASSIGNED;
+ case 12:
+ return DHCPLEASEUNKNOWN;
+ case 13:
+ return DHCPLEASEACTIVE;
+ default:
+ return null;
+ }
+ }
+ }
+
protected byte opCode;
protected byte hardwareType;
protected byte hardwareAddressLength;
@@ -89,7 +165,7 @@
protected byte[] clientHardwareAddress;
protected String serverName;
protected String bootFileName;
- protected List<DHCPOption> options = new ArrayList<DHCPOption>();
+ protected List<DhcpOption> options = new ArrayList<DhcpOption>();
/**
* @return the opCode
@@ -302,9 +378,9 @@
* The option code to get
* @return The value of the option if it exists, null otherwise
*/
- public DHCPOption getOption(final DHCPOptionCode optionCode) {
- for (final DHCPOption opt : this.options) {
- if (opt.code == optionCode.value) {
+ public DhcpOption getOption(final DHCPOptionCode optionCode) {
+ for (final DhcpOption opt : this.options) {
+ if (opt.getCode() == optionCode.getValue()) {
return opt;
}
}
@@ -314,7 +390,7 @@
/**
* @return the options
*/
- public List<DHCPOption> getOptions() {
+ public List<DhcpOption> getOptions() {
return this.options;
}
@@ -323,7 +399,7 @@
* the options to set
* @return this
*/
- public DHCP setOptions(final List<DHCPOption> options) {
+ public DHCP setOptions(final List<DhcpOption> options) {
this.options = options;
return this;
}
@@ -331,16 +407,15 @@
/**
* @return the packetType base on option 53
*/
- public DHCPPacketType getPacketType() {
- final ListIterator<DHCPOption> lit = this.options.listIterator();
- while (lit.hasNext()) {
- final DHCPOption option = lit.next();
- // only care option 53
- if (option.getCode() == 53) {
- return DHCPPacketType.getType(option.getData()[0]);
- }
- }
- return null;
+ public MsgType getPacketType() {
+ return this.options.parallelStream()
+ .filter(op -> op.getCode() == DHCPOptionCode.OptionCode_MessageType.getValue())
+ .map(DhcpOption::getData)
+ .filter(data -> data.length != 0)
+ .map(data -> data[0])
+ .map(MsgType::getType)
+ .findFirst()
+ .orElse(null);
}
/**
@@ -385,19 +460,20 @@
// minimum size 240 including magic cookie, options generally padded to
// 300
int optionsLength = 0;
- for (final DHCPOption option : this.options) {
- if (option.getCode() == 0 || option.getCode() == ((byte) 255)) {
+ for (final DhcpOption option : this.options) {
+ if (option.getCode() == DHCPOptionCode.OptionCode_Pad.getValue() ||
+ option.getCode() == DHCPOptionCode.OptionCode_END.getValue()) {
optionsLength += 1;
} else {
- optionsLength += 2 + (0xff & option.getLength());
+ optionsLength += 2 + (UNSIGNED_BYTE_MASK & option.getLength());
}
}
int optionsPadLength = 0;
- if (optionsLength < 60) {
- optionsPadLength = 60 - optionsLength;
+ if (optionsLength < BASE_OPTION_LEN) {
+ optionsPadLength = BASE_OPTION_LEN - optionsLength;
}
- final byte[] data = new byte[240 + optionsLength + optionsPadLength];
+ final byte[] data = new byte[MIN_DHCP_LEN + optionsLength + optionsPadLength];
final ByteBuffer bb = ByteBuffer.wrap(data);
bb.put(this.opCode);
bb.put(this.hardwareType);
@@ -410,127 +486,44 @@
bb.putInt(this.yourIPAddress);
bb.putInt(this.serverIPAddress);
bb.putInt(this.gatewayIPAddress);
- checkArgument(this.clientHardwareAddress.length <= 16,
+ checkArgument(this.clientHardwareAddress.length <= BASE_HW_ADDR_LEN,
"Hardware address is too long (%s bytes)", this.clientHardwareAddress.length);
bb.put(this.clientHardwareAddress);
- if (this.clientHardwareAddress.length < 16) {
- for (int i = 0; i < 16 - this.clientHardwareAddress.length; ++i) {
- bb.put((byte) 0x0);
+ if (this.clientHardwareAddress.length < BASE_HW_ADDR_LEN) {
+ for (int i = 0; i < BASE_HW_ADDR_LEN - this.clientHardwareAddress.length; ++i) {
+ bb.put(PAD_BYTE);
}
}
- this.writeString(this.serverName, bb, 64);
- this.writeString(this.bootFileName, bb, 128);
+ this.writeString(this.serverName, bb, BASE_SERVER_NAME_LEN);
+ this.writeString(this.bootFileName, bb, BASE_BOOT_FILE_NAME_LEN);
// magic cookie
- bb.put((byte) 0x63);
- bb.put((byte) 0x82);
- bb.put((byte) 0x53);
- bb.put((byte) 0x63);
- for (final DHCPOption option : this.options) {
- dhcpOptionToByteArray(option, bb);
+ bb.putInt(MAGIC_COOKIE);
+ for (final DhcpOption option : this.options) {
+ bb.put(option.serialize());
}
// assume the rest is padded out with zeroes
return data;
}
- public static ByteBuffer dhcpOptionToByteArray(DHCPOption option, ByteBuffer bb) {
- final int code = option.getCode() & 0xff;
- bb.put((byte) code);
- if (code != 0 && code != 255) {
- bb.put(option.getLength());
- bb.put(option.getData());
- }
- return bb;
- }
-
@Override
public IPacket deserialize(final byte[] data, final int offset,
final int length) {
- final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
- if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
- return this;
+ try {
+ return deserializer().deserialize(data, offset, length);
+ } catch (DeserializationException e) {
+ return null;
}
-
- this.opCode = bb.get();
- this.hardwareType = bb.get();
- this.hardwareAddressLength = bb.get();
- this.hops = bb.get();
- this.transactionId = bb.getInt();
- this.seconds = bb.getShort();
- this.flags = bb.getShort();
- this.clientIPAddress = bb.getInt();
- this.yourIPAddress = bb.getInt();
- this.serverIPAddress = bb.getInt();
- this.gatewayIPAddress = bb.getInt();
- final int hardwareAddressLength = 0xff & this.hardwareAddressLength;
- this.clientHardwareAddress = new byte[hardwareAddressLength];
-
- bb.get(this.clientHardwareAddress);
- for (int i = hardwareAddressLength; i < 16; ++i) {
- bb.get();
- }
- this.serverName = this.readString(bb, 64);
- this.bootFileName = this.readString(bb, 128);
- // read the magic cookie
- // magic cookie
- bb.get();
- bb.get();
- bb.get();
- bb.get();
- // read options
- while (bb.hasRemaining()) {
- final DHCPOption option = new DHCPOption();
- int code = 0xff & bb.get(); // convert signed byte to int in range
- // [0,255]
- option.setCode((byte) code);
- if (code == 0) {
- // skip these
- continue;
- } else if (code != 255) {
- if (bb.hasRemaining()) {
- final int l = 0xff & bb.get(); // convert signed byte to
- // int in range [0,255]
- option.setLength((byte) l);
- if (bb.remaining() >= l) {
- final byte[] optionData = new byte[l];
- bb.get(optionData);
- option.setData(optionData);
- } else {
- // Skip the invalid option and set the END option
- code = 0xff;
- option.setCode((byte) code);
- option.setLength((byte) 0);
- }
- } else {
- // Skip the invalid option and set the END option
- code = 0xff;
- option.setCode((byte) code);
- option.setLength((byte) 0);
- }
- }
- this.options.add(option);
- if (code == 255) {
- // remaining bytes are supposed to be 0, but ignore them just in
- // case
- break;
- }
- }
-
- return this;
}
protected void writeString(final String string, final ByteBuffer bb,
final int maxLength) {
if (string == null) {
for (int i = 0; i < maxLength; ++i) {
- bb.put((byte) 0x0);
+ bb.put(PAD_BYTE);
}
} else {
- byte[] bytes = null;
- try {
- bytes = string.getBytes("ascii");
- } catch (final UnsupportedEncodingException e) {
- throw new RuntimeException("Failure encoding server name", e);
- }
+ byte[] bytes;
+ bytes = string.getBytes(StandardCharsets.US_ASCII);
int writeLength = bytes.length;
if (writeLength > maxLength) {
writeLength = maxLength;
@@ -545,12 +538,8 @@
private static String readString(final ByteBuffer bb, final int maxLength) {
final byte[] bytes = new byte[maxLength];
bb.get(bytes);
- String result = null;
- try {
- result = new String(bytes, "ascii").trim();
- } catch (final UnsupportedEncodingException e) {
- throw new RuntimeException("Failure decoding string", e);
- }
+ String result;
+ result = new String(bytes, StandardCharsets.US_ASCII).trim();
return result;
}
@@ -577,56 +566,64 @@
dhcp.yourIPAddress = bb.getInt();
dhcp.serverIPAddress = bb.getInt();
dhcp.gatewayIPAddress = bb.getInt();
- final int hardwareAddressLength = 0xff & dhcp.hardwareAddressLength;
+ final int hardwareAddressLength = UNSIGNED_BYTE_MASK & dhcp.hardwareAddressLength;
dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
bb.get(dhcp.clientHardwareAddress);
- for (int i = hardwareAddressLength; i < 16; ++i) {
+ for (int i = hardwareAddressLength; i < BASE_HW_ADDR_LEN; ++i) {
bb.get();
}
- dhcp.serverName = readString(bb, 64);
- dhcp.bootFileName = readString(bb, 128);
+ dhcp.serverName = readString(bb, BASE_SERVER_NAME_LEN);
+ dhcp.bootFileName = readString(bb, BASE_BOOT_FILE_NAME_LEN);
// read the magic cookie
// magic cookie
- bb.get();
- bb.get();
- bb.get();
- bb.get();
+ bb.getInt();
// read options
boolean foundEndOptionsMarker = false;
while (bb.hasRemaining()) {
- final DHCPOption option = new DHCPOption();
- int code = 0xff & bb.get(); // convert signed byte to int in range
- // [0,255]
- option.setCode((byte) code);
- if (code == 0) {
- // skip these
+ DhcpOption option;
+
+ int pos = bb.position();
+ int optCode = UNSIGNED_BYTE_MASK & bb.array()[pos]; // to unsigned integer
+ int optLen;
+ byte[] optData;
+
+ if (optCode == DHCPOptionCode.OptionCode_Pad.value) {
+ // pad, skip
+ // read option code
+ bb.get();
continue;
- } else if (code != 255) {
- if (bb.hasRemaining()) {
- final int l = 0xff & bb.get(); // convert signed byte to
- // int in range [0,255]
- option.setLength((byte) l);
- if (bb.remaining() >= l) {
- final byte[] optionData = new byte[l];
- bb.get(optionData);
- option.setData(optionData);
- dhcp.options.add(option);
- } else {
- throw new DeserializationException(
- "Buffer underflow while reading DHCP option");
- }
- }
- } else if (code == 255) {
- DHCPOption end = new DHCPOption();
- end.setCode((byte) 255);
- dhcp.options.add(end);
- // remaining bytes are supposed to be 0, but ignore them just in
- // case
+ }
+ if (optCode == (UNSIGNED_BYTE_MASK & DHCPOptionCode.OptionCode_END.value)) {
+ // end of dhcp options or invalid option and set the END option
+ option = new DhcpOption();
+ option.setCode((byte) optCode);
+ dhcp.options.add(option);
foundEndOptionsMarker = true;
break;
}
+
+ if (bb.remaining() < 2) {
+ // No option length
+ throw new DeserializationException("Buffer underflow while reading DHCP option");
+ }
+
+ optLen = UNSIGNED_BYTE_MASK & bb.array()[pos + 1];
+ if (bb.remaining() < DhcpOption.DEFAULT_LEN + optLen) {
+ // Invalid option length
+ throw new DeserializationException("Buffer underflow while reading DHCP option");
+ }
+ optData = new byte[DhcpOption.DEFAULT_LEN + optLen];
+ bb.get(optData);
+ if (OPTION_DESERIALIZERS.containsKey((byte) optCode)) {
+ option = OPTION_DESERIALIZERS.get((byte) optCode).deserialize(optData, 0, optData.length);
+ dhcp.options.add(option);
+ } else {
+ // default option
+ option = DhcpOption.deserializer().deserialize(optData, 0, optData.length);
+ dhcp.options.add(option);
+ }
}
if (!foundEndOptionsMarker) {
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCP6.java b/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
index b6aa691..6310ec5 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import org.onlab.packet.dhcp.Dhcp6Option;
import java.nio.ByteBuffer;
import java.util.List;
@@ -91,7 +92,7 @@
// general field
private byte msgType; // 1 byte
- private List<DHCP6Option> options;
+ private List<Dhcp6Option> options;
// non-relay field
private int transactionId; // 3 bytes
@@ -111,7 +112,7 @@
@Override
public byte[] serialize() {
int payloadLength = options.stream()
- .mapToInt(DHCP6Option::getLength)
+ .mapToInt(Dhcp6Option::getLength)
.sum();
// 2 bytes code and 2 bytes length
@@ -186,7 +187,7 @@
dhcp6.options = Lists.newArrayList();
while (bb.remaining() >= OPT_CODE_SIZE) {
- DHCP6Option option = new DHCP6Option();
+ Dhcp6Option option = new Dhcp6Option();
short code = bb.getShort();
if (bb.remaining() < OPT_LEN_SIZE) {
throw new DeserializationException(
@@ -235,7 +236,7 @@
*
* @return DHCPv6 options
*/
- public List<DHCP6Option> getOptions() {
+ public List<Dhcp6Option> getOptions() {
return options;
}
@@ -289,7 +290,7 @@
*
* @param options the options
*/
- public void setOptions(List<DHCP6Option> options) {
+ public void setOptions(List<Dhcp6Option> options) {
this.options = options;
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java b/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
deleted file mode 100644
index fa41753..0000000
--- a/utils/misc/src/main/java/org/onlab/packet/DHCPOption.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2014-present Open Networking Laboratory
- *
- * 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 org.onlab.packet;
-
-import java.util.Arrays;
-
-/**
- * Representation of DHCPOption field.
- */
-public class DHCPOption {
- protected byte code;
- protected byte length;
- protected byte[] data;
-
- /**
- * @return the code
- */
- public byte getCode() {
- return this.code;
- }
-
- /**
- * @param code the code to set
- * @return this
- */
- public DHCPOption setCode(final byte code) {
- this.code = code;
- return this;
- }
-
- /**
- * @return the length
- */
- public byte getLength() {
- return this.length;
- }
-
- /**
- * @param length the length to set
- * @return this
- */
- public DHCPOption setLength(final byte length) {
- this.length = length;
- return this;
- }
-
- /**
- * @return the data
- */
- public byte[] getData() {
- return this.data;
- }
-
- /**
- * @param data the data to set
- * @return this
- */
- public DHCPOption setData(final byte[] data) {
- this.data = data;
- return this;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + this.code;
- result = prime * result + Arrays.hashCode(this.data);
- result = prime * result + this.length;
- return result;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (!(obj instanceof DHCPOption)) {
- return false;
- }
- final DHCPOption other = (DHCPOption) obj;
- if (this.code != other.code) {
- return false;
- }
- if (!Arrays.equals(this.data, other.data)) {
- return false;
- }
- if (this.length != other.length) {
- return false;
- }
- return true;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "DHCPOption [code=" + this.code + ", length=" + this.length
- + ", data=" + Arrays.toString(this.data) + "]";
- }
-}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java b/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
index 0447feb..ea78872 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCPPacketType.java
@@ -18,6 +18,10 @@
package org.onlab.packet;
+/**
+ * @deprecated 1.11 Loon, move into DHCP class.
+ */
+@Deprecated
public enum DHCPPacketType {
// From RFC 1533
DHCPDISCOVER(1), DHCPOFFER(2), DHCPREQUEST(3), DHCPDECLINE(4), DHCPACK(5), DHCPNAK(
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/CircuitId.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/CircuitId.java
new file mode 100644
index 0000000..afa28ec
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/CircuitId.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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 org.onlab.packet.dhcp;
+
+import com.google.common.collect.Lists;
+import org.onlab.packet.VlanId;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Representation of DHCP option 82 Circuit id.
+ */
+public class CircuitId {
+ private static final String SEPARATOR = ":";
+ private static final String CIRCUIT_ID_FORMAT = "%s" + SEPARATOR + "%s";
+ private String connectPoint;
+ private VlanId vlanId;
+
+ /**
+ * Creates a circuit id by given information.
+ *
+ * @param connectPoint the connect point of circuit id
+ * @param vlanId the vlan id of circuit id
+ */
+ public CircuitId(String connectPoint, VlanId vlanId) {
+ this.connectPoint = connectPoint;
+ this.vlanId = vlanId;
+ }
+
+ /**
+ * Combines connect point with vlan id with separator ':' as circuit id.
+ * e.g. of:0000000000000204/1:100
+ *
+ * @return serialized circuit id for connect point and vlan ID
+ */
+ public byte[] serialize() {
+ return String
+ .format(CIRCUIT_ID_FORMAT, connectPoint, vlanId.toString())
+ .getBytes(StandardCharsets.US_ASCII);
+ }
+
+ /**
+ * Deserialize circuit id from byte string.
+ *
+ * @param circuitId the circuit id byte string
+ * @return a Circuit Id
+ */
+ public static CircuitId deserialize(byte[] circuitId) {
+ String cIdString = new String(circuitId, StandardCharsets.US_ASCII);
+ List<String> split = Lists.newArrayList(cIdString.split(SEPARATOR));
+ checkArgument(split.size() > 1, "Illegal circuit id.");
+ // remove last element (vlan id)
+ String vlanId = split.remove(split.size() - 1);
+ String connectPoint = String.join(SEPARATOR, split);
+ return new CircuitId(connectPoint, VlanId.vlanId(vlanId));
+ }
+
+ /**
+ * Gets the connect point of circuit id.
+ *
+ * @return the connect point
+ */
+ public String connectPoint() {
+ return connectPoint;
+ }
+
+ /**
+ * Gets the vlan id of circuit id.
+ *
+ * @return the vlan id
+ */
+ public VlanId vlanId() {
+ return vlanId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof CircuitId)) {
+ return false;
+ }
+ CircuitId that = (CircuitId) obj;
+ return Objects.equals(this.connectPoint, that.connectPoint) &&
+ Objects.equals(this.vlanId, that.vlanId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(connectPoint, vlanId);
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/DHCP6Option.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Option.java
similarity index 96%
rename from utils/misc/src/main/java/org/onlab/packet/DHCP6Option.java
rename to utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Option.java
index f70a1c5..dc34075 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCP6Option.java
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Option.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package org.onlab.packet;
+package org.onlab.packet.dhcp;
/**
* Representation of an DHCPv6 Option.
* Base on RFC-3315.
*/
-public class DHCP6Option {
+public class Dhcp6Option {
private short code;
private short length;
private byte[] data;
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/DhcpOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/DhcpOption.java
new file mode 100644
index 0000000..a1b5923
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/DhcpOption.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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 org.onlab.packet.dhcp;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Objects;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Default DHCP option.
+ */
+public class DhcpOption extends BasePacket {
+ public static final int OPT_CODE_LEN = 1;
+ public static final int DEFAULT_LEN = 2;
+ private final Logger log = getLogger(getClass());
+ protected byte code;
+ protected byte length;
+ protected byte[] data;
+
+ @Override
+ public byte[] serialize() {
+ ByteBuffer byteBuffer;
+ if (data != null) {
+ byteBuffer = ByteBuffer.allocate(DEFAULT_LEN + data.length);
+ byteBuffer.put(code);
+ byteBuffer.put(length);
+ byteBuffer.put(data);
+ } else {
+ byteBuffer = ByteBuffer.allocate(OPT_CODE_LEN);
+ byteBuffer.put(code);
+ }
+ return byteBuffer.array();
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ try {
+ return deserializer().deserialize(data, offset, length);
+ } catch (DeserializationException e) {
+ log.warn("Can't deserialize DhcpOption {}", e);
+ return null;
+ }
+ }
+
+ /**
+ * Deserializer function for DHCP option.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<DhcpOption> deserializer() {
+ return (data, offset, length) -> {
+ DhcpOption dhcpOption = new DhcpOption();
+ ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, length);
+ dhcpOption.code = byteBuffer.get();
+ if (byteBuffer.hasRemaining()) {
+ dhcpOption.length = byteBuffer.get();
+ dhcpOption.data = new byte[dhcpOption.length];
+ byteBuffer.get(dhcpOption.data);
+ } else {
+ dhcpOption.length = 0;
+ dhcpOption.data = null;
+ }
+ return dhcpOption;
+ };
+ }
+
+ /**
+ * @return the code
+ */
+ public byte getCode() {
+ return this.code;
+ }
+
+ /**
+ * @param code the code to set
+ * @return this
+ */
+ public DhcpOption setCode(final byte code) {
+ this.code = code;
+ return this;
+ }
+
+ /**
+ * @return the length
+ */
+ public byte getLength() {
+ return this.length;
+ }
+
+ /**
+ * @param length the length to set
+ * @return this
+ */
+ public DhcpOption setLength(final byte length) {
+ this.length = length;
+ return this;
+ }
+
+ /**
+ * @return the data
+ */
+ public byte[] getData() {
+ return this.data;
+ }
+
+ /**
+ * @param data the data to set
+ * @return this
+ */
+ public DhcpOption setData(final byte[] data) {
+ this.data = data;
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(code, length, data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DhcpOption)) {
+ return false;
+ }
+ final DhcpOption other = (DhcpOption) obj;
+ return Objects.equals(this.code, other.code) &&
+ Objects.equals(this.length, other.length) &&
+ Arrays.equals(this.data, other.data);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "DhcpOption [code=" + this.code + ", length=" + this.length
+ + ", data=" + Arrays.toString(this.data) + "]";
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/DhcpRelayAgentOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/DhcpRelayAgentOption.java
new file mode 100644
index 0000000..498704d
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/DhcpRelayAgentOption.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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 org.onlab.packet.dhcp;
+
+import com.google.common.collect.Maps;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Representation of DHCP relay agent option (option 82).
+ */
+public class DhcpRelayAgentOption extends DhcpOption {
+ private static final int SUB_OPT_DEFAULT_LEN = 2;
+ private final Logger log = getLogger(getClass());
+ private final Map<Byte, DhcpOption> subOptions = Maps.newHashMap();
+
+ // Sub-option codes for option 82
+ public enum RelayAgentInfoOptions {
+ CIRCUIT_ID((byte) 1),
+ REMOTE_ID((byte) 2),
+ DOCSIS((byte) 4),
+ LINK_SELECTION((byte) 5),
+ SUBSCRIBER_ID((byte) 6),
+ RADIUS((byte) 7),
+ AUTH((byte) 8),
+ VENDOR_SPECIFIC((byte) 9),
+ RELAY_AGENT_FLAGS((byte) 10),
+ SERVER_ID_OVERRIDE((byte) 11),
+ VIRTUAL_SUBNET_SELECTION((byte) 151),
+ VIRTUAL_SUBNET_SELECTION_CTRL((byte) 152);
+
+ private byte value;
+ public byte getValue() {
+ return value;
+ }
+ RelayAgentInfoOptions(byte value) {
+ this.value = value;
+ }
+ }
+
+ @Override
+ public byte[] serialize() {
+ int totalLen = 0;
+ totalLen += subOptions.size() * SUB_OPT_DEFAULT_LEN;
+ totalLen += subOptions.values().stream().mapToInt(DhcpOption::getLength).sum();
+ totalLen += DEFAULT_LEN;
+ ByteBuffer byteBuffer = ByteBuffer.allocate(totalLen);
+ byteBuffer.put(code);
+ byteBuffer.put(length);
+ subOptions.values().forEach(subOpt -> {
+ byteBuffer.put(subOpt.code);
+ byteBuffer.put(subOpt.length);
+ byteBuffer.put(subOpt.data);
+ });
+ return byteBuffer.array();
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ try {
+ return deserializer().deserialize(data, offset, length);
+ } catch (DeserializationException e) {
+ log.warn("can't deserialize DHCP relay agent information option {}", e);
+ return null;
+ }
+ }
+
+ /**
+ * Deserializer function for DHCP relay agent option.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<DhcpOption> deserializer() {
+ return (data, offset, length) -> {
+ DhcpRelayAgentOption relayOption = new DhcpRelayAgentOption();
+ ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, length);
+ relayOption.code = byteBuffer.get();
+ relayOption.length = byteBuffer.get();
+
+ while (byteBuffer.remaining() >= DEFAULT_LEN) {
+ byte subOptCode = byteBuffer.get();
+ byte subOptLen = byteBuffer.get();
+ byte[] subOptData = new byte[subOptLen];
+ byteBuffer.get(subOptData);
+
+ DhcpOption subOption = new DhcpOption();
+ subOption.code = subOptCode;
+ subOption.length = subOptLen;
+ subOption.data = subOptData;
+ relayOption.subOptions.put(subOptCode, subOption);
+ }
+
+ return relayOption;
+ };
+ }
+
+ /**
+ * Gets sub-option from this option by given option code.
+ *
+ * @param code the option code
+ * @return sub-option of given code; null if there is no sub-option for given
+ * code
+ */
+ public DhcpOption getSubOption(byte code) {
+ return subOptions.get(code);
+ }
+
+ /**
+ * Adds a sub-option for this option.
+ *
+ * @param subOption the sub-option
+ */
+ public void addSubOption(DhcpOption subOption) {
+ this.length += SUB_OPT_DEFAULT_LEN + subOption.length;
+ this.subOptions.put(subOption.getCode(), subOption);
+ }
+
+ /**
+ * Removes a sub-option by given sub-option code.
+ *
+ * @param code the code for sub-option
+ * @return sub-option removed; null of sub-option not exists
+ */
+ public DhcpOption removeSubOption(byte code) {
+ DhcpOption subOption = subOptions.remove(code);
+ if (subOption != null) {
+ this.length -= SUB_OPT_DEFAULT_LEN + subOption.length;
+ }
+ return subOption;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof DhcpRelayAgentOption)) {
+ return false;
+ }
+ DhcpRelayAgentOption that = (DhcpRelayAgentOption) obj;
+ return Objects.equals(this.subOptions, that.subOptions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), subOptions);
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/package-info.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/package-info.java
new file mode 100644
index 0000000..1033b5d
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.
+ */
+/**
+ * Utilities for decoding and encoding DHCP options.
+ */
+package org.onlab.packet.dhcp;
\ No newline at end of file