[CORD-1589] DHCPv6 option de/serializers
Change-Id: I8bd5ffaffc601dcd0d00ec2de9f570e38a995a1d
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 6310ec5..c324d28 100644
--- a/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
+++ b/utils/misc/src/main/java/org/onlab/packet/DHCP6.java
@@ -16,14 +16,22 @@
package org.onlab.packet;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
+import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
+import org.onlab.packet.dhcp.Dhcp6IaNaOption;
+import org.onlab.packet.dhcp.Dhcp6IaTaOption;
import org.onlab.packet.dhcp.Dhcp6Option;
+import org.onlab.packet.dhcp.Dhcp6RelayOption;
import java.nio.ByteBuffer;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
/**
@@ -47,7 +55,7 @@
private static final int TRANSACTION_ID_MASK = 0x00ffffff;
// Relay message types
- private static final Set<Byte> RELAY_MSG_TYPES =
+ public static final Set<Byte> RELAY_MSG_TYPES =
ImmutableSet.of(MsgType.RELAY_FORW.value,
MsgType.RELAY_REPL.value);
@@ -90,6 +98,13 @@
}
}
+ private static final Map<Short, Deserializer<Dhcp6Option>> OPT_DESERIALIZERS =
+ ImmutableMap.of(OptionCode.IA_NA.value, Dhcp6IaNaOption.deserializer(),
+ OptionCode.IA_TA.value, Dhcp6IaTaOption.deserializer(),
+ OptionCode.IAADDR.value, Dhcp6IaAddressOption.deserializer(),
+ OptionCode.RELAY_MSG.value, Dhcp6RelayOption.deserializer(),
+ OptionCode.CLIENTID.value, Dhcp6ClientIdOption.deserializer());
+
// general field
private byte msgType; // 1 byte
private List<Dhcp6Option> options;
@@ -138,9 +153,7 @@
// serialize options
options.forEach(option -> {
- bb.putShort(option.getCode());
- bb.putShort(option.getLength());
- bb.put(option.getData());
+ bb.put(option.serialize());
});
return bb.array();
@@ -170,7 +183,7 @@
}
// peek message type
- dhcp6.msgType = bb.array()[0];
+ dhcp6.msgType = (byte) (0xff & bb.array()[offset]);
if (RELAY_MSG_TYPES.contains(dhcp6.msgType)) {
bb.get(); // drop message type
dhcp6.hopCount = bb.get();
@@ -186,26 +199,25 @@
}
dhcp6.options = Lists.newArrayList();
- while (bb.remaining() >= OPT_CODE_SIZE) {
- Dhcp6Option option = new Dhcp6Option();
- short code = bb.getShort();
- if (bb.remaining() < OPT_LEN_SIZE) {
+ while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
+ // create temporary byte buffer for reading code and length
+ ByteBuffer optByteBuffer =
+ ByteBuffer.wrap(data, bb.position(), length - bb.position());
+ short code = optByteBuffer.getShort();
+ short optionLen = (short) (0xffff & optByteBuffer.getShort());
+ if (optByteBuffer.remaining() < optionLen) {
throw new DeserializationException(
"Buffer underflow while reading DHCPv6 option");
}
-
- short optionLen = bb.getShort();
- if (bb.remaining() < optionLen) {
- throw new DeserializationException(
- "Buffer underflow while reading DHCPv6 option");
- }
-
- byte[] optionData = new byte[optionLen];
+ Dhcp6Option option;
+ byte[] optionData = new byte[Dhcp6Option.DEFAULT_LEN + optionLen];
bb.get(optionData);
-
- option.setCode(code);
- option.setLength(optionLen);
- option.setData(optionData);
+ if (OPT_DESERIALIZERS.containsKey(code)) {
+ option = OPT_DESERIALIZERS.get(code).deserialize(optionData, 0, optionData.length);
+ } else {
+ option = Dhcp6Option.deserializer().deserialize(optionData, 0, optionData.length);
+ }
+ option.setParent(dhcp6);
dhcp6.options.add(option);
}
@@ -268,6 +280,15 @@
}
/**
+ * Gets IPv6 link address.
+ *
+ * @return the IPv6 link address
+ */
+ public Ip6Address getIp6LinkAddress() {
+ return linkAddress == null ? null : Ip6Address.valueOf(linkAddress);
+ }
+
+ /**
* Gets the peer address of this DHCPv6 relay message.
*
* @return the link address
@@ -277,6 +298,15 @@
}
/**
+ * Gets IPv6 peer address.
+ *
+ * @return the IPv6 peer address
+ */
+ public Ip6Address getIp6PeerAddress() {
+ return peerAddress == null ? null : Ip6Address.valueOf(peerAddress);
+ }
+
+ /**
* Sets message type.
*
* @param msgType the message type
@@ -329,4 +359,24 @@
public void setPeerAddress(byte[] peerAddress) {
this.peerAddress = peerAddress;
}
+
+ @Override
+ public String toString() {
+ if (RELAY_MSG_TYPES.contains(msgType)) {
+ // relay message
+ return toStringHelper(getClass())
+ .add("msgType", msgType)
+ .add("hopCount", hopCount)
+ .add("linkAddress", Ip6Address.valueOf(linkAddress))
+ .add("peerAddress", Ip6Address.valueOf(peerAddress))
+ .add("options", options)
+ .toString();
+ } else {
+ return toStringHelper(getClass())
+ .add("msgType", msgType)
+ .add("transactionId", transactionId)
+ .add("options", options)
+ .toString();
+ }
+ }
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6ClientIdOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6ClientIdOption.java
new file mode 100644
index 0000000..cec4d97
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6ClientIdOption.java
@@ -0,0 +1,90 @@
+/*
+ * 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.DHCP6;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+
+import java.nio.ByteBuffer;
+
+/**
+ * DHCPv6 Client Identifier Option.
+ */
+public final class Dhcp6ClientIdOption extends Dhcp6Option {
+ @Override
+ public short getCode() {
+ return DHCP6.OptionCode.CLIENTID.value();
+ }
+
+ @Override
+ public short getLength() {
+ return (short) payload.serialize().length;
+ }
+
+ @Override
+ public byte[] getData() {
+ return payload.serialize();
+ }
+
+ @Override
+ public void setData(byte[] data) {
+ try {
+ Dhcp6Duid duid = Dhcp6Duid.deserializer().deserialize(data, 0, data.length);
+ this.setDuid(duid);
+ } catch (DeserializationException e) {
+ throw new RuntimeException("Invalid DUID");
+ }
+
+ }
+
+ public Dhcp6Duid getDuid() {
+ return (Dhcp6Duid) payload;
+ }
+
+ public void setDuid(Dhcp6Duid duid) {
+ this.setPayload(duid);
+ duid.setParent(this);
+ }
+
+ public static Deserializer<Dhcp6Option> deserializer() {
+ return (data, offset, length) -> {
+ Dhcp6Option dhcp6Option =
+ Dhcp6Option.deserializer().deserialize(data, offset, length);
+ Dhcp6ClientIdOption clientIdentifier = new Dhcp6ClientIdOption();
+
+ if (dhcp6Option.getLength() < DEFAULT_LEN) {
+ throw new DeserializationException("Invalid length of Client Id option");
+ }
+
+ Dhcp6Duid duid =
+ Dhcp6Duid.deserializer().deserialize(dhcp6Option.getData(), 0, dhcp6Option.getLength());
+ clientIdentifier.setPayload(duid);
+ return clientIdentifier;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ ByteBuffer bb = ByteBuffer.allocate(this.getLength() + Dhcp6Option.DEFAULT_LEN);
+ bb.putShort(getCode());
+ bb.putShort(getLength());
+ bb.put(payload.serialize());
+ return bb.array();
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Duid.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Duid.java
new file mode 100644
index 0000000..5930c87
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Duid.java
@@ -0,0 +1,181 @@
+/*
+ * 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.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+
+public class Dhcp6Duid extends BasePacket {
+ private static final int DEFAULT_LLT_LEN = 8;
+ private static final int DEFAULT_EN_LEN = 6;
+ private static final int DEFAULT_LL_LEN = 4;
+ public enum DuidType {
+ DUID_LLT((short) 1),
+ DUID_EN((short) 2),
+ DUID_LL((short) 3);
+
+ private short value;
+ DuidType(short value) {
+ this.value = value;
+ }
+
+ public short getValue() {
+ return value;
+ }
+
+ public static DuidType of(short type) {
+ switch (type) {
+ case 1:
+ return DUID_LLT;
+ case 2:
+ return DUID_EN;
+ case 3:
+ return DUID_LL;
+ default:
+ throw new RuntimeException("Unknown type: " + type);
+ }
+ }
+ }
+ // general field
+ private DuidType duidType;
+
+ // fields for DUID_LLT & DUID_LL
+ private short hardwareType;
+ private int duidTime;
+ private byte[] linkLayerAddress;
+
+ // fields for DUID_EN
+ private int enterpriseNumber;
+ private byte[] identifier;
+
+ public DuidType getDuidType() {
+ return duidType;
+ }
+
+ public void setDuidType(DuidType duidType) {
+ this.duidType = duidType;
+ }
+
+ public short getHardwareType() {
+ return hardwareType;
+ }
+
+ public void setHardwareType(short hardwareType) {
+ this.hardwareType = hardwareType;
+ }
+
+ public int getDuidTime() {
+ return duidTime;
+ }
+
+ public void setDuidTime(int duidTime) {
+ this.duidTime = duidTime;
+ }
+
+ public byte[] getLinkLayerAddress() {
+ return linkLayerAddress;
+ }
+
+ public void setLinkLayerAddress(byte[] linkLayerAddress) {
+ this.linkLayerAddress = linkLayerAddress;
+ }
+
+ public int getEnterpriseNumber() {
+ return enterpriseNumber;
+ }
+
+ public void setEnterpriseNumber(int enterpriseNumber) {
+ this.enterpriseNumber = enterpriseNumber;
+ }
+
+ public byte[] getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(byte[] identifier) {
+ this.identifier = identifier;
+ }
+
+ @Override
+ public byte[] serialize() {
+ ByteBuffer byteBuffer;
+ switch (duidType) {
+ case DUID_LLT:
+ byteBuffer = ByteBuffer.allocate(DEFAULT_LLT_LEN + linkLayerAddress.length);
+ byteBuffer.putShort(duidType.value);
+ byteBuffer.putShort(hardwareType);
+ byteBuffer.putInt(duidTime);
+ byteBuffer.put(linkLayerAddress);
+ break;
+ case DUID_EN:
+ byteBuffer = ByteBuffer.allocate(DEFAULT_EN_LEN + identifier.length);
+ byteBuffer.putShort(duidType.value);
+ byteBuffer.putInt(enterpriseNumber);
+ byteBuffer.put(identifier);
+ break;
+ case DUID_LL:
+ byteBuffer = ByteBuffer.allocate(DEFAULT_LL_LEN + linkLayerAddress.length);
+ byteBuffer.putShort(duidType.value);
+ byteBuffer.putShort(hardwareType);
+ byteBuffer.put(linkLayerAddress);
+ break;
+ default:
+ throw new RuntimeException("Unknown duidType: " + duidType.toString());
+ }
+ return byteBuffer.array();
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ return null;
+ }
+
+ public static Deserializer<Dhcp6Duid> deserializer() {
+ return (data, offset, length) -> {
+ Dhcp6Duid duid = new Dhcp6Duid();
+ ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, length);
+
+ DuidType duidType = DuidType.of(byteBuffer.getShort());
+ duid.setDuidType(duidType);
+ switch (duidType) {
+ case DUID_LLT:
+ duid.setHardwareType(byteBuffer.getShort());
+ duid.setDuidTime(byteBuffer.getInt());
+ duid.linkLayerAddress = new byte[length - DEFAULT_LLT_LEN];
+ byteBuffer.get(duid.linkLayerAddress);
+ break;
+ case DUID_EN:
+ duid.setEnterpriseNumber(byteBuffer.getInt());
+ duid.identifier = new byte[length - DEFAULT_EN_LEN];
+ byteBuffer.get(duid.identifier);
+ break;
+ case DUID_LL:
+ duid.setHardwareType(byteBuffer.getShort());
+ duid.linkLayerAddress = new byte[length - DEFAULT_LL_LEN];
+ byteBuffer.get(duid.linkLayerAddress);
+ break;
+ default:
+ throw new RuntimeException("Unknown type: " + duidType);
+ }
+ return duid;
+ };
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaAddressOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaAddressOption.java
new file mode 100644
index 0000000..511b5c7
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaAddressOption.java
@@ -0,0 +1,203 @@
+/*
+ * 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.DHCP6;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.Ip6Address;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * IA Address option for DHCPv6.
+ * Based on RFC-3315.
+ */
+public final class Dhcp6IaAddressOption extends Dhcp6Option {
+ public static final int DEFAULT_LEN = 24;
+
+ private Ip6Address ip6Address;
+ private int preferredLifetime;
+ private int validLifetime;
+ private IPacket options;
+
+ @Override
+ public short getCode() {
+ return DHCP6.OptionCode.IAADDR.value();
+ }
+
+ @Override
+ public short getLength() {
+ return (short) (options == null ? DEFAULT_LEN : DEFAULT_LEN + options.serialize().length);
+ }
+
+ /**
+ * Sets IPv6 address.
+ *
+ * @param ip6Address the IPv6 address
+ */
+ public void setIp6Address(Ip6Address ip6Address) {
+ this.ip6Address = ip6Address;
+ }
+
+ /**
+ * Sets preferred lifetime.
+ *
+ * @param preferredLifetime the preferred lifetime
+ */
+ public void setPreferredLifetime(int preferredLifetime) {
+ this.preferredLifetime = preferredLifetime;
+ }
+
+ /**
+ * Sets valid lifetime.
+ *
+ * @param validLifetime the valid lifetime
+ */
+ public void setValidLifetime(int validLifetime) {
+ this.validLifetime = validLifetime;
+ }
+
+ /**
+ * Sets options data.
+ *
+ * @param options the options data
+ */
+ public void setOptions(IPacket options) {
+ this.options = options;
+ }
+
+ /**
+ * Gets IPv6 address.
+ *
+ * @return the IPv6 address
+ */
+ public Ip6Address getIp6Address() {
+ return ip6Address;
+ }
+
+ /**
+ * Gets preferred lifetime.
+ *
+ * @return the preferred lifetime
+ */
+ public int getPreferredLifetime() {
+ return preferredLifetime;
+ }
+
+ /**
+ * Gets valid lifetime.
+ *
+ * @return the valid lifetime
+ */
+ public int getValidLifetime() {
+ return validLifetime;
+ }
+
+ /**
+ * Gets options of IA Address option.
+ *
+ * @return the options data
+ */
+ public IPacket getOptions() {
+ return options;
+ }
+
+ public static Deserializer<Dhcp6Option> deserializer() {
+ return (data, offset, length) -> {
+ Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+ Dhcp6Option dhcp6Option =
+ Dhcp6Option.deserializer().deserialize(data, offset, length);
+ iaAddressOption.setPayload(dhcp6Option.getPayload());
+ if (dhcp6Option.getLength() < DEFAULT_LEN) {
+ throw new DeserializationException("Invalid length of IA address option");
+ }
+ ByteBuffer bb = ByteBuffer.wrap(dhcp6Option.getData());
+ byte[] ipv6Addr = new byte[16];
+ bb.get(ipv6Addr);
+ iaAddressOption.ip6Address = Ip6Address.valueOf(ipv6Addr);
+ iaAddressOption.preferredLifetime = bb.getInt();
+ iaAddressOption.validLifetime = bb.getInt();
+
+ // options length of IA Address option
+ int optionsLen = dhcp6Option.getLength() - DEFAULT_LEN;
+ if (optionsLen > 0) {
+ byte[] optionsData = new byte[optionsLen];
+ bb.get(optionsData);
+ iaAddressOption.options =
+ Data.deserializer().deserialize(optionsData, 0, optionsLen);
+ }
+ return iaAddressOption;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ int payloadLen = options == null ? DEFAULT_LEN : DEFAULT_LEN + options.serialize().length;
+ ByteBuffer bb = ByteBuffer.allocate(payloadLen + Dhcp6Option.DEFAULT_LEN);
+ bb.putShort(DHCP6.OptionCode.IAADDR.value());
+ bb.putShort((short) payloadLen);
+ bb.put(ip6Address.toOctets());
+ bb.putInt(preferredLifetime);
+ bb.putInt(validLifetime);
+ if (options != null) {
+ bb.put(options.serialize());
+ }
+ return bb.array();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), ip6Address, preferredLifetime,
+ validLifetime, options);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Dhcp6IaAddressOption)) {
+ return false;
+ }
+ final Dhcp6IaAddressOption other = (Dhcp6IaAddressOption) obj;
+
+ return Objects.equals(getCode(), other.getCode()) &&
+ Objects.equals(getLength(), other.getLength()) &&
+ Objects.equals(ip6Address, other.ip6Address) &&
+ Objects.equals(preferredLifetime, other.preferredLifetime) &&
+ Objects.equals(validLifetime, other.validLifetime) &&
+ Objects.equals(options, other.options);
+ }
+
+ @Override
+ public String toString() {
+ return getToStringHelper()
+ .add("ip6Address", ip6Address)
+ .add("preferredLifetime", preferredLifetime)
+ .add("validLifetime", validLifetime)
+ .add("options", options)
+ .toString();
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaNaOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaNaOption.java
new file mode 100644
index 0000000..db190c3
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaNaOption.java
@@ -0,0 +1,243 @@
+/*
+ * 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.DHCP6;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * DHCPv6 Identity Association for Non-temporary Addresses Option.
+ * Based on RFC-3315
+ */
+public final class Dhcp6IaNaOption extends Dhcp6Option {
+ public static final int DEFAULT_LEN = 12;
+ private int iaId;
+ private int t1;
+ private int t2;
+ private List<Dhcp6Option> options;
+
+ @Override
+ public short getCode() {
+ return DHCP6.OptionCode.IA_NA.value();
+ }
+
+ @Override
+ public short getLength() {
+ return (short) (DEFAULT_LEN + options.stream()
+ .mapToInt(opt -> (int) opt.getLength() + Dhcp6Option.DEFAULT_LEN)
+ .sum());
+
+ }
+
+ /**
+ * Gets Identity Association ID.
+ *
+ * @return the Identity Association ID
+ */
+ public int getIaId() {
+ return iaId;
+ }
+
+ /**
+ * Sets Identity Association ID.
+ *
+ * @param iaId the Identity Association ID.
+ */
+ public void setIaId(int iaId) {
+ this.iaId = iaId;
+ }
+
+ /**
+ * Gets time 1.
+ * The time at which the client contacts the
+ * server from which the addresses in the IA_NA
+ * were obtained to extend the lifetimes of the
+ * addresses assigned to the IA_NA; T1 is a
+ * time duration relative to the current time
+ * expressed in units of seconds.
+ *
+ * @return the value of time 1
+ */
+ public int getT1() {
+ return t1;
+ }
+
+ /**
+ * Sets time 1.
+ *
+ * @param t1 the value of time 1
+ */
+ public void setT1(int t1) {
+ this.t1 = t1;
+ }
+
+ /**
+ * Gets time 2.
+ * The time at which the client contacts any
+ * available server to extend the lifetimes of
+ * the addresses assigned to the IA_NA; T2 is a
+ * time duration relative to the current time
+ * expressed in units of seconds.
+ *
+ * @return the value of time 2
+ */
+ public int getT2() {
+ return t2;
+ }
+
+ /**
+ * Sets time 2.
+ *
+ * @param t2 the value of time 2
+ */
+ public void setT2(int t2) {
+ this.t2 = t2;
+ }
+
+ /**
+ * Gets sub-options.
+ *
+ * @return sub-options of this option
+ */
+ public List<Dhcp6Option> getOptions() {
+ return options;
+ }
+
+ /**
+ * Sets sub-options.
+ *
+ * @param options the sub-options of this option
+ */
+ public void setOptions(List<Dhcp6Option> options) {
+ this.options = options;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public Dhcp6IaNaOption() {
+ }
+
+ /**
+ * Constructs a DHCPv6 IA NA option with DHCPv6 option.
+ *
+ * @param dhcp6Option the DHCPv6 option
+ */
+ public Dhcp6IaNaOption(Dhcp6Option dhcp6Option) {
+ super(dhcp6Option);
+ }
+
+ /**
+ * Gets deserializer.
+ *
+ * @return the deserializer
+ */
+ public static Deserializer<Dhcp6Option> deserializer() {
+ return (data, offset, length) -> {
+ Dhcp6Option dhcp6Option =
+ Dhcp6Option.deserializer().deserialize(data, offset, length);
+ if (dhcp6Option.getLength() < DEFAULT_LEN) {
+ throw new DeserializationException("Invalid IA NA option data");
+ }
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption(dhcp6Option);
+ byte[] optionData = iaNaOption.getData();
+ ByteBuffer bb = ByteBuffer.wrap(optionData);
+ iaNaOption.iaId = bb.getInt();
+ iaNaOption.t1 = bb.getInt();
+ iaNaOption.t2 = bb.getInt();
+
+ iaNaOption.options = Lists.newArrayList();
+ while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
+ Dhcp6Option option;
+ ByteBuffer optByteBuffer = ByteBuffer.wrap(optionData,
+ bb.position(),
+ optionData.length - bb.position());
+ short code = optByteBuffer.getShort();
+ short len = optByteBuffer.getShort();
+ byte[] subOptData = new byte[Dhcp6Option.DEFAULT_LEN + len];
+ bb.get(subOptData);
+
+ // TODO: put more sub-options?
+ if (code == DHCP6.OptionCode.IAADDR.value()) {
+ option = Dhcp6IaAddressOption.deserializer()
+ .deserialize(subOptData, 0, subOptData.length);
+ } else {
+ option = Dhcp6Option.deserializer()
+ .deserialize(subOptData, 0, subOptData.length);
+ }
+ iaNaOption.options.add(option);
+ }
+ return iaNaOption;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ int payloadLen = DEFAULT_LEN + options.stream()
+ .mapToInt(opt -> (int) opt.getLength() + Dhcp6Option.DEFAULT_LEN)
+ .sum();
+ int len = Dhcp6Option.DEFAULT_LEN + payloadLen;
+ ByteBuffer bb = ByteBuffer.allocate(len);
+ bb.putShort(DHCP6.OptionCode.IA_NA.value());
+ bb.putShort((short) payloadLen);
+ bb.putInt(iaId);
+ bb.putInt(t1);
+ bb.putInt(t2);
+
+ options.stream().map(Dhcp6Option::serialize).forEach(bb::put);
+ return bb.array();
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * super.hashCode() + Objects.hash(iaId, t1, t2, options);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ final Dhcp6IaNaOption other = (Dhcp6IaNaOption) obj;
+ return Objects.equals(this.iaId, other.iaId)
+ && Objects.equals(this.t1, other.t1)
+ && Objects.equals(this.t2, other.t2)
+ && Objects.equals(this.options, other.options);
+ }
+
+ @Override
+ public String toString() {
+ return getToStringHelper()
+ .add("iaId", iaId)
+ .add("t1", t1)
+ .add("t2", t2)
+ .add("options", options)
+ .toString();
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaTaOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaTaOption.java
new file mode 100644
index 0000000..f075b4d
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaTaOption.java
@@ -0,0 +1,178 @@
+/*
+ * 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.DHCP6;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Objects;
+
+public final class Dhcp6IaTaOption extends Dhcp6Option {
+ public static final int DEFAULT_LEN = 4;
+ private int iaId;
+ private List<Dhcp6Option> options;
+
+ @Override
+ public short getCode() {
+ return DHCP6.OptionCode.IA_TA.value();
+ }
+
+ @Override
+ public short getLength() {
+ return (short) (DEFAULT_LEN + options.stream()
+ .mapToInt(opt -> (int) opt.getLength() + Dhcp6Option.DEFAULT_LEN)
+ .sum());
+ }
+
+ /**
+ * Gets Identity Association ID.
+ *
+ * @return the Identity Association ID
+ */
+ public int getIaId() {
+ return iaId;
+ }
+
+ /**
+ * Sets Identity Association ID.
+ *
+ * @param iaId the Identity Association ID.
+ */
+ public void setIaId(int iaId) {
+ this.iaId = iaId;
+ }
+
+ /**
+ * Gets sub-options.
+ *
+ * @return sub-options of this option
+ */
+ public List<Dhcp6Option> getOptions() {
+ return options;
+ }
+
+ /**
+ * Sets sub-options.
+ *
+ * @param options the sub-options of this option
+ */
+ public void setOptions(List<Dhcp6Option> options) {
+ this.options = options;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public Dhcp6IaTaOption() {
+ }
+
+ /**
+ * Constructs a DHCPv6 IA TA option with DHCPv6 option.
+ *
+ * @param dhcp6Option the DHCPv6 option
+ */
+ public Dhcp6IaTaOption(Dhcp6Option dhcp6Option) {
+ super(dhcp6Option);
+ }
+
+ /**
+ * Gets deserializer.
+ *
+ * @return the deserializer
+ */
+ public static Deserializer<Dhcp6Option> deserializer() {
+ return (data, offset, length) -> {
+ Dhcp6Option dhcp6Option =
+ Dhcp6Option.deserializer().deserialize(data, offset, length);
+ if (dhcp6Option.getLength() < DEFAULT_LEN) {
+ throw new DeserializationException("Invalid IA NA option data");
+ }
+ Dhcp6IaTaOption iaTaOption = new Dhcp6IaTaOption(dhcp6Option);
+ byte[] optionData = iaTaOption.getData();
+ ByteBuffer bb = ByteBuffer.wrap(optionData);
+ iaTaOption.iaId = bb.getInt();
+
+ iaTaOption.options = Lists.newArrayList();
+ while (bb.remaining() >= Dhcp6Option.DEFAULT_LEN) {
+ Dhcp6Option option;
+ ByteBuffer optByteBuffer = ByteBuffer.wrap(optionData,
+ bb.position(),
+ optionData.length - bb.position());
+ short code = optByteBuffer.getShort();
+ short len = optByteBuffer.getShort();
+ byte[] subOptData = new byte[Dhcp6Option.DEFAULT_LEN + len];
+ bb.get(subOptData);
+
+ // TODO: put more sub-options?
+ if (code == DHCP6.OptionCode.IAADDR.value()) {
+ option = Dhcp6IaAddressOption.deserializer()
+ .deserialize(subOptData, 0, subOptData.length);
+ } else {
+ option = Dhcp6Option.deserializer()
+ .deserialize(subOptData, 0, subOptData.length);
+ }
+ iaTaOption.options.add(option);
+ }
+ return iaTaOption;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ int payloadLen = DEFAULT_LEN + options.stream()
+ .mapToInt(opt -> (int) opt.getLength())
+ .sum();
+ int len = Dhcp6Option.DEFAULT_LEN + payloadLen;
+ ByteBuffer bb = ByteBuffer.allocate(len);
+ bb.putShort(DHCP6.OptionCode.IA_TA.value());
+ bb.putShort((short) payloadLen);
+ bb.putInt(iaId);
+ options.stream().map(Dhcp6Option::serialize).forEach(bb::put);
+ return bb.array();
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * super.hashCode() + Objects.hash(iaId, options);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ final Dhcp6IaTaOption other = (Dhcp6IaTaOption) obj;
+ return Objects.equals(this.iaId, other.iaId)
+ && Objects.equals(this.options, other.options);
+ }
+
+ @Override
+ public String toString() {
+ return getToStringHelper()
+ .add("iaId", iaId)
+ .add("options", options)
+ .toString();
+ }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Option.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Option.java
index dc34075..4da0a47 100644
--- a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Option.java
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Option.java
@@ -16,14 +16,45 @@
package org.onlab.packet.dhcp;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.Data;
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
/**
* Representation of an DHCPv6 Option.
* Base on RFC-3315.
*/
-public class Dhcp6Option {
+public class Dhcp6Option extends BasePacket {
+ public static final int DEFAULT_LEN = 4;
private short code;
private short length;
- private byte[] data;
+ // XXX: use "payload" from BasePacket for option data.
+
+ /**
+ * Default constructor.
+ */
+ public Dhcp6Option() {
+ }
+
+ /**
+ * Constructs a DHCPv6 option based on information from other DHCPv6 option.
+ *
+ * @param dhcp6Option other DHCPv6 option
+ */
+ public Dhcp6Option(Dhcp6Option dhcp6Option) {
+ this.code = (short) (0xffff & dhcp6Option.code);
+ this.length = (short) (0xffff & dhcp6Option.length);
+ this.payload = dhcp6Option.payload;
+ this.payload.setParent(this);
+ }
/**
* Sets the code of this option.
@@ -35,12 +66,16 @@
}
/**
- * Sets the data and length of this option.
+ * Sets the data of this option.
*
* @param data the data to set
*/
public void setData(byte[] data) {
- this.data = data;
+ try {
+ this.payload = Data.deserializer().deserialize(data, 0, data.length);
+ } catch (DeserializationException e) {
+ throw new RuntimeException("Invalid data");
+ }
}
/**
@@ -76,6 +111,80 @@
* @return the data of this option
*/
public byte[] getData() {
- return data;
+ return payload.serialize();
+ }
+
+ /**
+ * Gets deserializer of DHCPv6 option.
+ *
+ * @return the deserializer of DHCPv6 option
+ */
+ public static Deserializer<Dhcp6Option> deserializer() {
+ return (data, offset, len) -> {
+ Dhcp6Option dhcp6Option = new Dhcp6Option();
+ if (len < DEFAULT_LEN) {
+ throw new DeserializationException("DHCPv6 option code length" +
+ "should be at least 4 bytes");
+ }
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, len);
+ dhcp6Option.code = (short) (0xff & bb.getShort());
+ dhcp6Option.length = (short) (0xff & bb.getShort());
+ byte[] optData = new byte[dhcp6Option.length];
+ bb.get(optData);
+ dhcp6Option.setData(optData);
+ return dhcp6Option;
+ };
+ }
+
+ @Override
+ public byte[] serialize() {
+ ByteBuffer bb = ByteBuffer.allocate(DEFAULT_LEN + getLength());
+ bb.putShort(getCode());
+ bb.putShort(getLength());
+ bb.put(payload.serialize());
+ return bb.array();
+ }
+
+ @Override
+ public IPacket deserialize(byte[] data, int offset, int length) {
+ try {
+ return deserializer().deserialize(data, offset, length);
+ } catch (DeserializationException e) {
+ throw new RuntimeException("Can't deserialize data for DHCPv6 option.", e);
+ }
+ }
+
+ protected ToStringHelper getToStringHelper() {
+ return toStringHelper(Dhcp6Option.class)
+ .add("code", code)
+ .add("length", length);
+ }
+
+ @Override
+ public String toString() {
+ return getToStringHelper()
+ .add("data", payload.toString())
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * super.hashCode() + Objects.hash(code, length);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ final Dhcp6Option other = (Dhcp6Option) obj;
+ return Objects.equals(this.code, other.code)
+ && Objects.equals(this.length, other.length);
}
}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6RelayOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6RelayOption.java
new file mode 100644
index 0000000..bf3a1af
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6RelayOption.java
@@ -0,0 +1,70 @@
+/*
+ * 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.DHCP6;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.IPacket;
+
+/**
+ * Relay option for DHCPv6.
+ * Based on RFC-3315.
+ */
+public final class Dhcp6RelayOption extends Dhcp6Option {
+ @Override
+ public short getCode() {
+ return DHCP6.OptionCode.RELAY_MSG.value();
+ }
+
+ @Override
+ public short getLength() {
+ return (short) payload.serialize().length;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public Dhcp6RelayOption() {
+ }
+
+ /**
+ * Constructs a DHCPv6 relay option with DHCPv6 option.
+ *
+ * @param dhcp6Option the DHCPv6 option
+ */
+ public Dhcp6RelayOption(Dhcp6Option dhcp6Option) {
+ super(dhcp6Option);
+ }
+
+ /**
+ * Gets deserializer for DHCPv6 relay option.
+ *
+ * @return the deserializer
+ */
+ public static Deserializer<Dhcp6Option> deserializer() {
+ return (data, offset, len) -> {
+ Dhcp6Option dhcp6Option = Dhcp6Option.deserializer().deserialize(data, offset, len);
+ IPacket payload = DHCP6.deserializer()
+ .deserialize(dhcp6Option.getData(), 0, dhcp6Option.getLength());
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption(dhcp6Option);
+ relayOption.setPayload(payload);
+ payload.setParent(relayOption);
+ return relayOption;
+ };
+ }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/Dhcp6Test.java b/utils/misc/src/test/java/org/onlab/packet/Dhcp6Test.java
deleted file mode 100644
index 7b83c00..0000000
--- a/utils/misc/src/test/java/org/onlab/packet/Dhcp6Test.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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;
-
-import com.google.common.collect.ImmutableList;
-import org.junit.Test;
-import org.onlab.packet.dhcp.Dhcp6Option;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import java.nio.ByteBuffer;
-
-public class Dhcp6Test {
- private static final int OPT_CLIENT_ID = 0xBEEFBEEF;
- private static final byte[] OPT_CLIENT_ID_BYTE_ARR =
- {(byte) 0xBE, (byte) 0xEF, (byte) 0xBE, (byte) 0xEF};
- private static final short OPT_CLIENT_ID_SIZE = 4;
- private static final int OPT_AUTH = 0xBA11BA11;
- private static final byte[] OPT_AUTH_BYTE_AR =
- {(byte) 0xBA, 0x11, (byte) 0xBA, 0x11};
- private static final short OPT_AUTH_SIZE = 4;
- private static final int TRANSACTION_ID = 0xC0FFEE;
- private static final byte[] TRANSACTION_ID_BYTE_ARR =
- {(byte) 0xC0, (byte) 0xFF, (byte) 0xEE};
- private static final byte HOP_COUNT = 3;
- private static final Ip6Address LINK_ADDRESS = Ip6Address.valueOf("1111:2222::8888");
- private static final Ip6Address PEER_ADDRESS = Ip6Address.valueOf("3333:4444::9999");
- Deserializer<DHCP6> deserializer = DHCP6.deserializer();
- private byte[] byteHeader;
-
- @Test
- public void testDeserializeBadInput() throws Exception {
- PacketTestUtils.testDeserializeBadInput(deserializer);
- }
-
- /**
- * Truncated a simple DHCPv6 payload.
- */
- @Test
- public void testDeserializeTruncated() throws Exception {
- ByteBuffer bb = ByteBuffer.allocate(4);
- bb.put(DHCP6.MsgType.REQUEST.value());
- bb.put(TRANSACTION_ID_BYTE_ARR);
- byteHeader = bb.array();
-
- PacketTestUtils.testDeserializeTruncated(deserializer, byteHeader);
- }
-
- /**
- * Basic DHCPv6 header with one msg type and one transaction id.
- */
- @Test
- public void testDeserializeDefaultPayload() throws Exception {
- ByteBuffer bb = ByteBuffer.allocate(12);
- bb.put(DHCP6.MsgType.REQUEST.value());
- bb.put(TRANSACTION_ID_BYTE_ARR);
-
- // put a simple client id (4 bytes)
- bb.putShort(DHCP6.OptionCode.CLIENTID.value());
- bb.putShort(OPT_CLIENT_ID_SIZE);
- bb.putInt(OPT_CLIENT_ID);
- byteHeader = bb.array();
-
- DHCP6 dhcp6 = deserializer.deserialize(byteHeader, 0, byteHeader.length);
- assertEquals(dhcp6.getMsgType(), DHCP6.MsgType.REQUEST.value());
- assertEquals(dhcp6.getTransactionId(), TRANSACTION_ID);
- assertEquals(dhcp6.getOptions().size(), 1);
-
- Dhcp6Option clientIdOption = dhcp6.getOptions().get(0);
- assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
- assertArrayEquals(clientIdOption.getData(), OPT_CLIENT_ID_BYTE_ARR);
- }
-
- /**
- * DHCPv6 header with relay agent information.
- */
- @Test
- public void testDeserializeRelayAgent() throws Exception {
- ByteBuffer bb = ByteBuffer.allocate(42);
- bb.put(DHCP6.MsgType.RELAY_FORW.value());
- bb.put(HOP_COUNT);
-
- bb.put(LINK_ADDRESS.toOctets());
- bb.put(PEER_ADDRESS.toOctets());
-
- // put a simple client id (4 bytes)
- bb.putShort(DHCP6.OptionCode.CLIENTID.value());
- bb.putShort(OPT_CLIENT_ID_SIZE);
- bb.putInt(OPT_CLIENT_ID);
- byteHeader = bb.array();
-
- DHCP6 dhcp6 = deserializer.deserialize(byteHeader, 0, byteHeader.length);
- assertEquals(dhcp6.getMsgType(), DHCP6.MsgType.RELAY_FORW.value());
- assertEquals(dhcp6.getHopCount(), HOP_COUNT);
- assertArrayEquals(dhcp6.getLinkAddress(), LINK_ADDRESS.toOctets());
- assertArrayEquals(dhcp6.getPeerAddress(), PEER_ADDRESS.toOctets());
- assertEquals(dhcp6.getOptions().size(), 1);
-
- Dhcp6Option clientIdOption = dhcp6.getOptions().get(0);
- assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
- assertArrayEquals(clientIdOption.getData(), OPT_CLIENT_ID_BYTE_ARR);
- }
-
- /**
- * Serialize DHCPv6 header with default payload and options.
- */
- @Test
- public void testSerializeDefaultPayload() throws Exception {
- DHCP6 dhcp6 = new DHCP6();
- dhcp6.setMsgType(DHCP6.MsgType.REQUEST.value());
- dhcp6.setTransactionId(TRANSACTION_ID);
-
- Dhcp6Option opt1 = new Dhcp6Option();
- opt1.setCode(DHCP6.OptionCode.CLIENTID.value());
- opt1.setLength(OPT_CLIENT_ID_SIZE);
- opt1.setData(OPT_CLIENT_ID_BYTE_ARR);
-
-
- Dhcp6Option opt2 = new Dhcp6Option();
- opt2.setCode(DHCP6.OptionCode.AUTH.value());
- opt2.setLength(OPT_AUTH_SIZE);
- opt2.setData(OPT_AUTH_BYTE_AR);
-
- dhcp6.setOptions(ImmutableList.of(opt1, opt2));
-
- byte[] serialized = dhcp6.serialize();
- ByteBuffer expected = ByteBuffer.allocate(20)
- .put(DHCP6.MsgType.REQUEST.value())
- .put(TRANSACTION_ID_BYTE_ARR)
- .putShort(DHCP6.OptionCode.CLIENTID.value())
- .putShort(OPT_CLIENT_ID_SIZE)
- .putInt(OPT_CLIENT_ID)
- .putShort(DHCP6.OptionCode.AUTH.value())
- .putShort(OPT_AUTH_SIZE)
- .putInt(OPT_AUTH);
-
- assertArrayEquals(serialized, expected.array());
- }
-
- /**
- * Serialize DHCPv6 header with relay agent payload and options.
- */
- @Test
- public void testSerializeRelayAgent() throws Exception {
- DHCP6 dhcp6 = new DHCP6();
- dhcp6.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
- dhcp6.setHopCount(HOP_COUNT);
- dhcp6.setLinkAddress(LINK_ADDRESS.toOctets());
- dhcp6.setPeerAddress(PEER_ADDRESS.toOctets());
-
- Dhcp6Option opt1 = new Dhcp6Option();
- opt1.setCode(DHCP6.OptionCode.CLIENTID.value());
- opt1.setLength(OPT_CLIENT_ID_SIZE);
- opt1.setData(OPT_CLIENT_ID_BYTE_ARR);
-
-
- Dhcp6Option opt2 = new Dhcp6Option();
- opt2.setCode(DHCP6.OptionCode.AUTH.value());
- opt2.setLength(OPT_AUTH_SIZE);
- opt2.setData(OPT_AUTH_BYTE_AR);
-
- dhcp6.setOptions(ImmutableList.of(opt1, opt2));
-
- byte[] serialized = dhcp6.serialize();
- ByteBuffer expected = ByteBuffer.allocate(50)
- .put(DHCP6.MsgType.RELAY_FORW.value())
- .put(HOP_COUNT)
- .put(LINK_ADDRESS.toOctets())
- .put(PEER_ADDRESS.toOctets())
- .putShort(DHCP6.OptionCode.CLIENTID.value())
- .putShort(OPT_CLIENT_ID_SIZE)
- .putInt(OPT_CLIENT_ID)
- .putShort(DHCP6.OptionCode.AUTH.value())
- .putShort(OPT_AUTH_SIZE)
- .putInt(OPT_AUTH);
-
- assertArrayEquals(serialized, expected.array());
- }
-}
diff --git a/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java b/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java
new file mode 100644
index 0000000..f8a0425
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java
@@ -0,0 +1,586 @@
+/*
+ * 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.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.onlab.packet.DHCP6;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.MacAddress;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test serializing/deserializing DHCPv6 relay message.
+ */
+public class Dhcp6RelayTest {
+ private static final String SOLICIT = "dhcp6_relay_solicit.bin";
+ private static final String ADVERTISE = "dhcp6_relay_advertise.bin";
+ private static final String REQUEST = "dhcp6_relay_request.bin";
+ private static final String REPLY = "dhcp6_relay_reply.bin";
+
+ private static final int HOP_COUNT = 0;
+ private static final Ip6Address LINK_ADDRESS = Ip6Address.valueOf("2000::2ff");
+ private static final Ip6Address PEER_ADDRESS = Ip6Address.valueOf("fe80::1");
+ private static final int XID_1 = 13346301;
+ private static final int XID_2 = 9807588;
+ private static final int IA_ID = 1;
+ private static final int T1_CLIENT = 3600;
+ private static final int T2_CLIENT = 5400;
+ private static final int T1_SERVER = 0;
+ private static final int T2_SERVER = 0;
+ private static final Ip6Address IA_ADDRESS = Ip6Address.valueOf("2000::201");
+ private static final int PREFFERRED_LT_SERVER = 375;
+ private static final int VALID_LT_SERVER = 600;
+ private static final int PREFFERRED_LT_REQ = 7200;
+ private static final int VALID_LT_REQ = 7500;
+ private static final MacAddress CLIENT_MAC = MacAddress.valueOf("00:bb:00:00:00:01");
+ private static final int CLIENT_DUID_TIME = 0x210016b4;
+
+ /**
+ * Test deserialize relay message with solicit message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void deserializeSolicit() throws Exception {
+ byte[] data = IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(SOLICIT));
+ DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
+ assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_FORW.value());
+ assertEquals(relayMsg.getHopCount(), HOP_COUNT);
+ assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
+ assertEquals(relayMsg.getIp6PeerAddress(), PEER_ADDRESS);
+
+ assertEquals(relayMsg.getOptions().size(), 1);
+ Dhcp6Option option = relayMsg.getOptions().get(0);
+ assertEquals(option.getCode(), DHCP6.OptionCode.RELAY_MSG.value());
+ assertEquals(option.getLength(), 56);
+ assertTrue(option.getPayload() instanceof DHCP6);
+
+ DHCP6 relaiedDhcp6 = (DHCP6) option.getPayload();
+ assertEquals(relaiedDhcp6.getMsgType(), DHCP6.MsgType.SOLICIT.value());
+ assertEquals(relaiedDhcp6.getTransactionId(), XID_1);
+ assertEquals(relaiedDhcp6.getOptions().size(), 4);
+
+ // Client ID
+ option = relaiedDhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // ORO
+ option = relaiedDhcp6.getOptions().get(1);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ORO.value());
+ assertEquals(option.getLength(), 8);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+
+ // ELAPSED_TIME
+ option = relaiedDhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ELAPSED_TIME.value());
+ assertEquals(option.getLength(), 2);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 0});
+
+ // IA NA
+ option = relaiedDhcp6.getOptions().get(3);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 12);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_CLIENT);
+ assertEquals(iaNaOption.getT2(), T2_CLIENT);
+ assertEquals(iaNaOption.getOptions().size(), 0);
+
+ assertArrayEquals(data, relayMsg.serialize());
+ }
+
+ /**
+ * Test serialize relay message with solicit message.
+ *
+ * @throws Exception exception while serialize the DHCPv6 payload
+ */
+ @Test
+ public void serializeSolicit() throws Exception {
+ DHCP6 relayMsg = new DHCP6();
+ relayMsg.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
+ relayMsg.setHopCount((byte) HOP_COUNT);
+ relayMsg.setLinkAddress(LINK_ADDRESS.toOctets());
+ relayMsg.setPeerAddress(PEER_ADDRESS.toOctets());
+
+ DHCP6 relaiedDhcp6 = new DHCP6();
+ relaiedDhcp6.setMsgType(DHCP6.MsgType.SOLICIT.value());
+ relaiedDhcp6.setTransactionId(XID_1);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Option request
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ORO.value());
+ option.setLength((short) 8);
+ option.setData(new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+ options.add(option);
+
+ // Elapsed Time
+ option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ELAPSED_TIME.value());
+ option.setLength((short) 2);
+ option.setData(new byte[]{0, 0});
+ options.add(option);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_CLIENT);
+ iaNaOption.setT2(T2_CLIENT);
+ iaNaOption.setOptions(Collections.emptyList());
+ options.add(iaNaOption);
+ relaiedDhcp6.setOptions(options);
+
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(relaiedDhcp6);
+
+ relayMsg.setOptions(ImmutableList.of(relayOption));
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(SOLICIT)),
+ relayMsg.serialize());
+ }
+
+ /**
+ * Test deserialize relay message with advertise message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void deserializeAdvertise() throws Exception {
+ byte[] data = IOUtils.toByteArray(getClass().getResource(ADVERTISE));
+ DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
+
+ assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_REPL.value());
+ assertEquals(relayMsg.getHopCount(), HOP_COUNT);
+ assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
+ assertEquals(relayMsg.getIp6PeerAddress(), PEER_ADDRESS);
+
+ assertEquals(relayMsg.getOptions().size(), 1);
+ Dhcp6Option option = relayMsg.getOptions().get(0);
+ assertEquals(option.getCode(), DHCP6.OptionCode.RELAY_MSG.value());
+ assertEquals(option.getLength(), 84);
+ assertTrue(option.getPayload() instanceof DHCP6);
+
+ DHCP6 relaiedDhcp6 = (DHCP6) option.getPayload();
+ assertEquals(relaiedDhcp6.getMsgType(), DHCP6.MsgType.ADVERTISE.value());
+ assertEquals(relaiedDhcp6.getTransactionId(), XID_1);
+ assertEquals(relaiedDhcp6.getOptions().size(), 3);
+
+ // IA NA
+ option = relaiedDhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 40);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_SERVER);
+ assertEquals(iaNaOption.getT2(), T2_SERVER);
+ assertEquals(iaNaOption.getOptions().size(), 1);
+
+ // IA Address (in IA NA)
+ assertTrue(iaNaOption.getOptions().get(0) instanceof Dhcp6IaAddressOption);
+ Dhcp6IaAddressOption iaAddressOption =
+ (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
+ assertEquals(iaAddressOption.getIp6Address(), IA_ADDRESS);
+ assertEquals(iaAddressOption.getPreferredLifetime(), PREFFERRED_LT_SERVER);
+ assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_SERVER);
+ assertNull(iaAddressOption.getOptions());
+
+ // Client ID
+ option = relaiedDhcp6.getOptions().get(1);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // Server ID
+ option = relaiedDhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
+ assertEquals(option.getLength(), 14);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+
+ assertArrayEquals(data, relayMsg.serialize());
+ }
+
+ /**
+ * Test serialize relay message with advertise message.
+ *
+ * @throws Exception exception while serialize the DHCPv6 payload
+ */
+ @Test
+ public void serializeAdvertise() throws Exception {
+ DHCP6 relayMsg = new DHCP6();
+ relayMsg.setMsgType(DHCP6.MsgType.RELAY_REPL.value());
+ relayMsg.setHopCount((byte) HOP_COUNT);
+ relayMsg.setLinkAddress(LINK_ADDRESS.toOctets());
+ relayMsg.setPeerAddress(PEER_ADDRESS.toOctets());
+
+ DHCP6 relaiedDhcp6 = new DHCP6();
+ relaiedDhcp6.setMsgType(DHCP6.MsgType.ADVERTISE.value());
+ relaiedDhcp6.setTransactionId(XID_1);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // IA address
+ Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+ iaAddressOption.setIp6Address(IA_ADDRESS);
+ iaAddressOption.setPreferredLifetime(PREFFERRED_LT_SERVER);
+ iaAddressOption.setValidLifetime(VALID_LT_SERVER);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_SERVER);
+ iaNaOption.setT2(T2_SERVER);
+ iaNaOption.setOptions(ImmutableList.of(iaAddressOption));
+ options.add(iaNaOption);
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Server ID
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.SERVERID.value());
+ option.setLength((short) 14);
+ option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+ options.add(option);
+
+ relaiedDhcp6.setOptions(options);
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(relaiedDhcp6);
+
+ relayMsg.setOptions(ImmutableList.of(relayOption));
+
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(ADVERTISE)),
+ relayMsg.serialize());
+ }
+
+ /**
+ * Test deserialize relay message with request message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void deserializeRequest() throws Exception {
+ byte[] data = IOUtils.toByteArray(getClass().getResource(REQUEST));
+ DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
+
+ assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_FORW.value());
+ assertEquals(relayMsg.getHopCount(), HOP_COUNT);
+ assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
+ assertEquals(relayMsg.getIp6PeerAddress(), PEER_ADDRESS);
+
+ assertEquals(relayMsg.getOptions().size(), 1);
+ Dhcp6Option option = relayMsg.getOptions().get(0);
+ assertEquals(option.getCode(), DHCP6.OptionCode.RELAY_MSG.value());
+ assertEquals(option.getLength(), 102);
+ assertTrue(option.getPayload() instanceof DHCP6);
+
+ DHCP6 relaiedDhcp6 = (DHCP6) option.getPayload();
+ assertEquals(relaiedDhcp6.getMsgType(), DHCP6.MsgType.REQUEST.value());
+ assertEquals(relaiedDhcp6.getTransactionId(), XID_2);
+ assertEquals(relaiedDhcp6.getOptions().size(), 5);
+
+ // Client ID
+ option = relaiedDhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // Server ID
+ option = relaiedDhcp6.getOptions().get(1);
+ assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
+ assertEquals(option.getLength(), 14);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+
+ // Option Request
+ option = relaiedDhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ORO.value());
+ assertEquals(option.getLength(), 8);
+ assertArrayEquals(option.getData(), new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+
+ // ELAPSED_TIME
+ option = relaiedDhcp6.getOptions().get(3);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ELAPSED_TIME.value());
+ assertEquals(option.getLength(), 2);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 0});
+
+ // IA NA
+ option = relaiedDhcp6.getOptions().get(4);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 40);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_CLIENT);
+ assertEquals(iaNaOption.getT2(), T2_CLIENT);
+ assertEquals(iaNaOption.getOptions().size(), 1);
+
+ // IA Address (in IA NA)
+ assertTrue(iaNaOption.getOptions().get(0) instanceof Dhcp6IaAddressOption);
+ Dhcp6IaAddressOption iaAddressOption =
+ (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
+ assertEquals(iaAddressOption.getIp6Address(), IA_ADDRESS);
+ assertEquals(iaAddressOption.getPreferredLifetime(), PREFFERRED_LT_REQ);
+ assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_REQ);
+ assertNull(iaAddressOption.getOptions());
+
+ assertArrayEquals(data, relayMsg.serialize());
+ }
+
+ /**
+ * Test serialize relay message with request message.
+ *
+ * @throws Exception exception while serialize the DHCPv6 payload
+ */
+ @Test
+ public void serializeRequest() throws Exception {
+ DHCP6 relayMsg = new DHCP6();
+ relayMsg.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
+ relayMsg.setHopCount((byte) HOP_COUNT);
+ relayMsg.setLinkAddress(LINK_ADDRESS.toOctets());
+ relayMsg.setPeerAddress(PEER_ADDRESS.toOctets());
+
+ DHCP6 relaiedDhcp6 = new DHCP6();
+ relaiedDhcp6.setMsgType(DHCP6.MsgType.REQUEST.value());
+ relaiedDhcp6.setTransactionId(XID_2);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Server ID
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.SERVERID.value());
+ option.setLength((short) 14);
+ option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+ options.add(option);
+
+ // Option request
+ option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ORO.value());
+ option.setLength((short) 8);
+ option.setData(new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+ options.add(option);
+
+ // Elapsed Time
+ option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ELAPSED_TIME.value());
+ option.setLength((short) 2);
+ option.setData(new byte[]{0, 0});
+ options.add(option);
+
+ // IA address
+ Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+ iaAddressOption.setIp6Address(IA_ADDRESS);
+ iaAddressOption.setPreferredLifetime(PREFFERRED_LT_REQ);
+ iaAddressOption.setValidLifetime(VALID_LT_REQ);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_CLIENT);
+ iaNaOption.setT2(T2_CLIENT);
+ iaNaOption.setOptions(ImmutableList.of(iaAddressOption));
+ options.add(iaNaOption);
+
+ relaiedDhcp6.setOptions(options);
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(relaiedDhcp6);
+
+ relayMsg.setOptions(ImmutableList.of(relayOption));
+
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(REQUEST)),
+ relayMsg.serialize());
+ }
+
+ /**
+ * Test deserialize relay message with reply message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void deserializeReply() throws Exception {
+ byte[] data = IOUtils.toByteArray(getClass().getResource(REPLY));
+ DHCP6 relayMsg = DHCP6.deserializer().deserialize(data, 0, data.length);
+
+ assertEquals(relayMsg.getMsgType(), DHCP6.MsgType.RELAY_REPL.value());
+ assertEquals(relayMsg.getHopCount(), HOP_COUNT);
+ assertEquals(relayMsg.getIp6LinkAddress(), LINK_ADDRESS);
+ assertEquals(relayMsg.getIp6PeerAddress(), PEER_ADDRESS);
+
+ assertEquals(relayMsg.getOptions().size(), 1);
+ Dhcp6Option option = relayMsg.getOptions().get(0);
+ assertEquals(option.getCode(), DHCP6.OptionCode.RELAY_MSG.value());
+ assertEquals(option.getLength(), 84);
+ assertTrue(option.getPayload() instanceof DHCP6);
+
+ DHCP6 relaiedDhcp6 = (DHCP6) option.getPayload();
+ assertEquals(relaiedDhcp6.getMsgType(), DHCP6.MsgType.REPLY.value());
+ assertEquals(relaiedDhcp6.getTransactionId(), XID_2);
+ assertEquals(relaiedDhcp6.getOptions().size(), 3);
+
+ // IA NA
+ option = relaiedDhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 40);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_SERVER);
+ assertEquals(iaNaOption.getT2(), T2_SERVER);
+ assertEquals(iaNaOption.getOptions().size(), 1);
+
+ // IA Address (in IA NA)
+ assertTrue(iaNaOption.getOptions().get(0) instanceof Dhcp6IaAddressOption);
+ Dhcp6IaAddressOption iaAddressOption =
+ (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
+ assertEquals(iaAddressOption.getIp6Address(), IA_ADDRESS);
+ assertEquals(iaAddressOption.getPreferredLifetime(), PREFFERRED_LT_SERVER);
+ assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_SERVER);
+ assertNull(iaAddressOption.getOptions());
+
+ // Client ID
+ option = relaiedDhcp6.getOptions().get(1);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // Server ID
+ option = relaiedDhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
+ assertEquals(option.getLength(), 14);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+
+ assertArrayEquals(data, relayMsg.serialize());
+ }
+
+ @Test
+ public void serializeReply() throws Exception {
+ DHCP6 relayMsg = new DHCP6();
+ relayMsg.setMsgType(DHCP6.MsgType.RELAY_REPL.value());
+ relayMsg.setHopCount((byte) HOP_COUNT);
+ relayMsg.setLinkAddress(LINK_ADDRESS.toOctets());
+ relayMsg.setPeerAddress(PEER_ADDRESS.toOctets());
+
+ DHCP6 relaiedDhcp6 = new DHCP6();
+ relaiedDhcp6.setMsgType(DHCP6.MsgType.REPLY.value());
+ relaiedDhcp6.setTransactionId(XID_2);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // IA address
+ Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+ iaAddressOption.setIp6Address(IA_ADDRESS);
+ iaAddressOption.setPreferredLifetime(PREFFERRED_LT_SERVER);
+ iaAddressOption.setValidLifetime(VALID_LT_SERVER);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_SERVER);
+ iaNaOption.setT2(T2_SERVER);
+ iaNaOption.setOptions(ImmutableList.of(iaAddressOption));
+ options.add(iaNaOption);
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Server ID
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.SERVERID.value());
+ option.setLength((short) 14);
+ option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+ options.add(option);
+
+ relaiedDhcp6.setOptions(options);
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(relaiedDhcp6);
+
+ relayMsg.setOptions(ImmutableList.of(relayOption));
+
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(REPLY)),
+ relayMsg.serialize());
+ }
+}
diff --git a/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6Test.java b/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6Test.java
new file mode 100644
index 0000000..4996ae6
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6Test.java
@@ -0,0 +1,524 @@
+/*
+ * 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.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.onlab.packet.DHCP6;
+import org.onlab.packet.Deserializer;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.PacketTestUtils;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertNull;
+
+public class Dhcp6Test {
+ private static final String SOLICIT = "dhcp6_solicit.bin";
+ private static final String ADVERTISE = "dhcp6_advertise.bin";
+ private static final String REQUEST = "dhcp6_request.bin";
+ private static final String REPLY = "dhcp6_reply.bin";
+
+ private static final int XID_1 = 13346301;
+ private static final int XID_2 = 9807588;
+ private static final int IA_ID = 1;
+ private static final int T1_CLIENT = 3600;
+ private static final int T2_CLIENT = 5400;
+ private static final int T1_SERVER = 0;
+ private static final int T2_SERVER = 0;
+ private static final Ip6Address IA_ADDRESS = Ip6Address.valueOf("2000::201");
+ private static final int PREFFERRED_LT_SERVER = 375;
+ private static final int VALID_LT_SERVER = 600;
+ private static final int PREFFERRED_LT_REQ = 7200;
+ private static final int VALID_LT_REQ = 7500;
+ private static final MacAddress CLIENT_MAC = MacAddress.valueOf("00:bb:00:00:00:01");
+ private static final int CLIENT_DUID_TIME = 0x210016b4;
+
+
+ private Deserializer<DHCP6> deserializer = DHCP6.deserializer();
+
+
+ @Test
+ public void testDeserializeBadInput() throws Exception {
+ PacketTestUtils.testDeserializeBadInput(deserializer);
+ }
+
+ /**
+ * Truncated a simple DHCPv6 payload.
+ */
+ @Test
+ public void testDeserializeTruncated() throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(4);
+ bb.put(DHCP6.MsgType.REQUEST.value());
+ bb.put(new byte[]{0x00, 0x00});
+ PacketTestUtils.testDeserializeTruncated(deserializer, bb.array());
+ }
+
+ /**
+ * Test DHCPv6 solicit message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void testDeserializeSolicit() throws Exception {
+ byte[] data = IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(SOLICIT));
+ DHCP6 dhcp6 = DHCP6.deserializer().deserialize(data, 0, data.length);
+ assertEquals(dhcp6.getMsgType(), DHCP6.MsgType.SOLICIT.value());
+ assertEquals(dhcp6.getTransactionId(), XID_1);
+ assertEquals(dhcp6.getOptions().size(), 4);
+
+ // Client ID
+ Dhcp6Option option = dhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // ORO
+ option = dhcp6.getOptions().get(1);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ORO.value());
+ assertEquals(option.getLength(), 8);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+
+ // ELAPSED_TIME
+ option = dhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ELAPSED_TIME.value());
+ assertEquals(option.getLength(), 2);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 0});
+
+ // IA NA
+ option = dhcp6.getOptions().get(3);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 12);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_CLIENT);
+ assertEquals(iaNaOption.getT2(), T2_CLIENT);
+ assertEquals(iaNaOption.getOptions().size(), 0);
+
+ assertArrayEquals(data, dhcp6.serialize());
+ }
+
+ /**
+ * Test serialize solicit message.
+ *
+ * @throws Exception exception while serialize the DHCPv6 payload
+ */
+ @Test
+ public void serializeSolicit() throws Exception {
+ DHCP6 dhcp6 = new DHCP6();
+ dhcp6.setMsgType(DHCP6.MsgType.SOLICIT.value());
+ dhcp6.setTransactionId(XID_1);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Option request
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ORO.value());
+ option.setLength((short) 8);
+ option.setData(new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+ options.add(option);
+
+ // Elapsed Time
+ option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ELAPSED_TIME.value());
+ option.setLength((short) 2);
+ option.setData(new byte[]{0, 0});
+ options.add(option);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_CLIENT);
+ iaNaOption.setT2(T2_CLIENT);
+ iaNaOption.setOptions(Collections.emptyList());
+ options.add(iaNaOption);
+ dhcp6.setOptions(options);
+
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(dhcp6);
+
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(SOLICIT)),
+ dhcp6.serialize());
+ }
+
+ /**
+ * Test deserialize advertise message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void deserializeAdvertise() throws Exception {
+ byte[] data = IOUtils.toByteArray(getClass().getResource(ADVERTISE));
+
+
+ DHCP6 dhcp6 = DHCP6.deserializer().deserialize(data, 0, data.length);
+ assertEquals(dhcp6.getMsgType(), DHCP6.MsgType.ADVERTISE.value());
+ assertEquals(dhcp6.getTransactionId(), XID_1);
+ assertEquals(dhcp6.getOptions().size(), 3);
+
+ // IA NA
+ Dhcp6Option option = dhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 40);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_SERVER);
+ assertEquals(iaNaOption.getT2(), T2_SERVER);
+ assertEquals(iaNaOption.getOptions().size(), 1);
+
+ // IA Address (in IA NA)
+ assertTrue(iaNaOption.getOptions().get(0) instanceof Dhcp6IaAddressOption);
+ Dhcp6IaAddressOption iaAddressOption =
+ (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
+ assertEquals(iaAddressOption.getIp6Address(), IA_ADDRESS);
+ assertEquals(iaAddressOption.getPreferredLifetime(), PREFFERRED_LT_SERVER);
+ assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_SERVER);
+ assertNull(iaAddressOption.getOptions());
+
+ // Client ID
+ option = dhcp6.getOptions().get(1);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // Server ID
+ option = dhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
+ assertEquals(option.getLength(), 14);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+
+ assertArrayEquals(data, dhcp6.serialize());
+ }
+
+ /**
+ * Test serialize advertise message.
+ *
+ * @throws Exception exception while serialize the DHCPv6 payload
+ */
+ @Test
+ public void serializeAdvertise() throws Exception {
+ DHCP6 dhcp6 = new DHCP6();
+ dhcp6.setMsgType(DHCP6.MsgType.ADVERTISE.value());
+ dhcp6.setTransactionId(XID_1);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // IA address
+ Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+ iaAddressOption.setIp6Address(IA_ADDRESS);
+ iaAddressOption.setPreferredLifetime(PREFFERRED_LT_SERVER);
+ iaAddressOption.setValidLifetime(VALID_LT_SERVER);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_SERVER);
+ iaNaOption.setT2(T2_SERVER);
+ iaNaOption.setOptions(ImmutableList.of(iaAddressOption));
+ options.add(iaNaOption);
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Server ID
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.SERVERID.value());
+ option.setLength((short) 14);
+ option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+ options.add(option);
+
+ dhcp6.setOptions(options);
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(dhcp6);
+
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(ADVERTISE)),
+ dhcp6.serialize());
+ }
+
+ /**
+ * Test deserialize request message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void deserializeRequest() throws Exception {
+ byte[] data = IOUtils.toByteArray(getClass().getResource(REQUEST));
+ DHCP6 dhcp6 = DHCP6.deserializer().deserialize(data, 0, data.length);
+ assertEquals(dhcp6.getMsgType(), DHCP6.MsgType.REQUEST.value());
+ assertEquals(dhcp6.getTransactionId(), XID_2);
+ assertEquals(dhcp6.getOptions().size(), 5);
+
+ // Client ID
+ Dhcp6Option option = dhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // Server ID
+ option = dhcp6.getOptions().get(1);
+ assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
+ assertEquals(option.getLength(), 14);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+
+ // Option Request
+ option = dhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ORO.value());
+ assertEquals(option.getLength(), 8);
+ assertArrayEquals(option.getData(), new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+
+ // ELAPSED_TIME
+ option = dhcp6.getOptions().get(3);
+ assertEquals(option.getCode(), DHCP6.OptionCode.ELAPSED_TIME.value());
+ assertEquals(option.getLength(), 2);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 0});
+
+ // IA NA
+ option = dhcp6.getOptions().get(4);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 40);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_CLIENT);
+ assertEquals(iaNaOption.getT2(), T2_CLIENT);
+ assertEquals(iaNaOption.getOptions().size(), 1);
+
+ // IA Address (in IA NA)
+ assertTrue(iaNaOption.getOptions().get(0) instanceof Dhcp6IaAddressOption);
+ Dhcp6IaAddressOption iaAddressOption =
+ (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
+ assertEquals(iaAddressOption.getIp6Address(), IA_ADDRESS);
+ assertEquals(iaAddressOption.getPreferredLifetime(), PREFFERRED_LT_REQ);
+ assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_REQ);
+ assertNull(iaAddressOption.getOptions());
+
+ assertArrayEquals(data, dhcp6.serialize());
+ }
+
+ /**
+ * Test serialize request message.
+ *
+ * @throws Exception exception while serialize the DHCPv6 payload
+ */
+ @Test
+ public void serializeRequest() throws Exception {
+ DHCP6 dhcp6 = new DHCP6();
+ dhcp6.setMsgType(DHCP6.MsgType.REQUEST.value());
+ dhcp6.setTransactionId(XID_2);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Server ID
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.SERVERID.value());
+ option.setLength((short) 14);
+ option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+ options.add(option);
+
+ // Option request
+ option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ORO.value());
+ option.setLength((short) 8);
+ option.setData(new byte[]{0, 23, 0, 24, 0, 39, 0, 31});
+ options.add(option);
+
+ // Elapsed Time
+ option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.ELAPSED_TIME.value());
+ option.setLength((short) 2);
+ option.setData(new byte[]{0, 0});
+ options.add(option);
+
+ // IA address
+ Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+ iaAddressOption.setIp6Address(IA_ADDRESS);
+ iaAddressOption.setPreferredLifetime(PREFFERRED_LT_REQ);
+ iaAddressOption.setValidLifetime(VALID_LT_REQ);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_CLIENT);
+ iaNaOption.setT2(T2_CLIENT);
+ iaNaOption.setOptions(ImmutableList.of(iaAddressOption));
+ options.add(iaNaOption);
+
+ dhcp6.setOptions(options);
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(dhcp6);
+
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(REQUEST)),
+ dhcp6.serialize());
+ }
+
+ /**
+ * Test deserialize relay message with reply message.
+ *
+ * @throws Exception exception while deserialize the DHCPv6 payload
+ */
+ @Test
+ public void deserializeReply() throws Exception {
+ byte[] data = IOUtils.toByteArray(getClass().getResource(REPLY));
+
+ DHCP6 dhcp6 = DHCP6.deserializer().deserialize(data, 0, data.length);
+ assertEquals(dhcp6.getMsgType(), DHCP6.MsgType.REPLY.value());
+ assertEquals(dhcp6.getTransactionId(), XID_2);
+ assertEquals(dhcp6.getOptions().size(), 3);
+
+ // IA NA
+ Dhcp6Option option = dhcp6.getOptions().get(0);
+ assertTrue(option instanceof Dhcp6IaNaOption);
+ Dhcp6IaNaOption iaNaOption = (Dhcp6IaNaOption) option;
+ assertEquals(iaNaOption.getCode(), DHCP6.OptionCode.IA_NA.value());
+ assertEquals(iaNaOption.getLength(), 40);
+ assertEquals(iaNaOption.getIaId(), IA_ID);
+ assertEquals(iaNaOption.getT1(), T1_SERVER);
+ assertEquals(iaNaOption.getT2(), T2_SERVER);
+ assertEquals(iaNaOption.getOptions().size(), 1);
+
+ // IA Address (in IA NA)
+ assertTrue(iaNaOption.getOptions().get(0) instanceof Dhcp6IaAddressOption);
+ Dhcp6IaAddressOption iaAddressOption =
+ (Dhcp6IaAddressOption) iaNaOption.getOptions().get(0);
+ assertEquals(iaAddressOption.getIp6Address(), IA_ADDRESS);
+ assertEquals(iaAddressOption.getPreferredLifetime(), PREFFERRED_LT_SERVER);
+ assertEquals(iaAddressOption.getValidLifetime(), VALID_LT_SERVER);
+ assertNull(iaAddressOption.getOptions());
+
+ // Client ID
+ option = dhcp6.getOptions().get(1);
+ assertTrue(option instanceof Dhcp6ClientIdOption);
+ Dhcp6ClientIdOption clientIdOption = (Dhcp6ClientIdOption) option;
+ assertEquals(clientIdOption.getCode(), DHCP6.OptionCode.CLIENTID.value());
+ assertEquals(clientIdOption.getLength(), 14);
+ assertEquals(clientIdOption.getDuid().getDuidType(), Dhcp6Duid.DuidType.DUID_LLT);
+ assertEquals(clientIdOption.getDuid().getHardwareType(), 1);
+ assertEquals(clientIdOption.getDuid().getDuidTime(), CLIENT_DUID_TIME);
+ assertArrayEquals(clientIdOption.getDuid().getLinkLayerAddress(), CLIENT_MAC.toBytes());
+
+ // Server ID
+ option = dhcp6.getOptions().get(2);
+ assertEquals(option.getCode(), DHCP6.OptionCode.SERVERID.value());
+ assertEquals(option.getLength(), 14);
+ assertArrayEquals(option.getData(),
+ new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+
+ assertArrayEquals(data, dhcp6.serialize());
+ }
+
+ @Test
+ public void serializeReply() throws Exception {
+ DHCP6 dhcp6 = new DHCP6();
+ dhcp6.setMsgType(DHCP6.MsgType.REPLY.value());
+ dhcp6.setTransactionId(XID_2);
+ List<Dhcp6Option> options = Lists.newArrayList();
+
+ // IA address
+ Dhcp6IaAddressOption iaAddressOption = new Dhcp6IaAddressOption();
+ iaAddressOption.setIp6Address(IA_ADDRESS);
+ iaAddressOption.setPreferredLifetime(PREFFERRED_LT_SERVER);
+ iaAddressOption.setValidLifetime(VALID_LT_SERVER);
+
+ // IA NA
+ Dhcp6IaNaOption iaNaOption = new Dhcp6IaNaOption();
+ iaNaOption.setIaId(IA_ID);
+ iaNaOption.setT1(T1_SERVER);
+ iaNaOption.setT2(T2_SERVER);
+ iaNaOption.setOptions(ImmutableList.of(iaAddressOption));
+ options.add(iaNaOption);
+
+ // Client ID
+ Dhcp6Duid duid = new Dhcp6Duid();
+ duid.setDuidType(Dhcp6Duid.DuidType.DUID_LLT);
+ duid.setHardwareType((short) 1);
+ duid.setDuidTime(CLIENT_DUID_TIME);
+ duid.setLinkLayerAddress(CLIENT_MAC.toBytes());
+ Dhcp6ClientIdOption clientIdOption = new Dhcp6ClientIdOption();
+ clientIdOption.setDuid(duid);
+ options.add(clientIdOption);
+
+ // Server ID
+ Dhcp6Option option = new Dhcp6Option();
+ option.setCode(DHCP6.OptionCode.SERVERID.value());
+ option.setLength((short) 14);
+ option.setData(new byte[]{0, 1, 0, 1, 32, -1, -8, -17, 0, -103, 102, 0, 0, 1});
+ options.add(option);
+
+ dhcp6.setOptions(options);
+
+ Dhcp6RelayOption relayOption = new Dhcp6RelayOption();
+ relayOption.setPayload(dhcp6);
+
+ assertArrayEquals(IOUtils.toByteArray(Dhcp6RelayTest.class.getResource(REPLY)),
+ dhcp6.serialize());
+ }
+}
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_advertise.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_advertise.bin
new file mode 100644
index 0000000..edc32db
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_advertise.bin
Binary files differ
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_advertise.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_advertise.bin
new file mode 100644
index 0000000..8ea7fa1
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_advertise.bin
Binary files differ
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_reply.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_reply.bin
new file mode 100644
index 0000000..b08ac4a
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_reply.bin
Binary files differ
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_request.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_request.bin
new file mode 100644
index 0000000..bbaeb48
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_request.bin
Binary files differ
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_solicit.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_solicit.bin
new file mode 100644
index 0000000..0890d69
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_solicit.bin
Binary files differ
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_reply.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_reply.bin
new file mode 100644
index 0000000..3d63acc
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_reply.bin
Binary files differ
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_request.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_request.bin
new file mode 100644
index 0000000..a8ec6bf
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_request.bin
Binary files differ
diff --git a/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_solicit.bin b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_solicit.bin
new file mode 100644
index 0000000..9ab2d29
--- /dev/null
+++ b/utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_solicit.bin
Binary files differ