From ca34e1d29eb22628a8a7d410492a82fce2d4ea56 Mon Sep 17 00:00:00 2001 From: Yi Tseng Date: Tue, 18 Jul 2017 16:16:25 -0700 Subject: [PATCH] [CORD-1589] DHCPv6 option de/serializers Change-Id: I8bd5ffaffc601dcd0d00ec2de9f570e38a995a1d --- .../src/main/java/org/onlab/packet/DHCP6.java | 92 ++- .../packet/dhcp/Dhcp6ClientIdOption.java | 90 +++ .../java/org/onlab/packet/dhcp/Dhcp6Duid.java | 181 ++++++ .../packet/dhcp/Dhcp6IaAddressOption.java | 203 ++++++ .../onlab/packet/dhcp/Dhcp6IaNaOption.java | 243 ++++++++ .../onlab/packet/dhcp/Dhcp6IaTaOption.java | 178 ++++++ .../org/onlab/packet/dhcp/Dhcp6Option.java | 119 +++- .../onlab/packet/dhcp/Dhcp6RelayOption.java | 70 +++ .../test/java/org/onlab/packet/Dhcp6Test.java | 194 ------ .../org/onlab/packet/dhcp/Dhcp6RelayTest.java | 586 ++++++++++++++++++ .../java/org/onlab/packet/dhcp/Dhcp6Test.java | 524 ++++++++++++++++ .../org/onlab/packet/dhcp/dhcp6_advertise.bin | Bin 0 -> 84 bytes .../packet/dhcp/dhcp6_relay_advertise.bin | Bin 0 -> 122 bytes .../onlab/packet/dhcp/dhcp6_relay_reply.bin | Bin 0 -> 122 bytes .../onlab/packet/dhcp/dhcp6_relay_request.bin | Bin 0 -> 140 bytes .../onlab/packet/dhcp/dhcp6_relay_solicit.bin | Bin 0 -> 94 bytes .../org/onlab/packet/dhcp/dhcp6_reply.bin | Bin 0 -> 84 bytes .../org/onlab/packet/dhcp/dhcp6_request.bin | Bin 0 -> 102 bytes .../org/onlab/packet/dhcp/dhcp6_solicit.bin | Bin 0 -> 56 bytes 19 files changed, 2260 insertions(+), 220 deletions(-) create mode 100644 utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6ClientIdOption.java create mode 100644 utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6Duid.java create mode 100644 utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaAddressOption.java create mode 100644 utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaNaOption.java create mode 100644 utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6IaTaOption.java create mode 100644 utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6RelayOption.java delete mode 100644 utils/misc/src/test/java/org/onlab/packet/Dhcp6Test.java create mode 100644 utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6RelayTest.java create mode 100644 utils/misc/src/test/java/org/onlab/packet/dhcp/Dhcp6Test.java create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_advertise.bin create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_advertise.bin create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_reply.bin create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_request.bin create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_relay_solicit.bin create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_reply.bin create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_request.bin create mode 100644 utils/misc/src/test/resources/org/onlab/packet/dhcp/dhcp6_solicit.bin 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 6310ec59ff..c324d28a07 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 @@ public class DHCP6 extends BasePacket { private static final int TRANSACTION_ID_MASK = 0x00ffffff; // Relay message types - private static final Set RELAY_MSG_TYPES = + public static final Set RELAY_MSG_TYPES = ImmutableSet.of(MsgType.RELAY_FORW.value, MsgType.RELAY_REPL.value); @@ -90,6 +98,13 @@ public class DHCP6 extends BasePacket { } } + private static final Map> 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 options; @@ -138,9 +153,7 @@ public class DHCP6 extends BasePacket { // 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 @@ public class DHCP6 extends BasePacket { } // 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 @@ public class DHCP6 extends BasePacket { } 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); } @@ -267,6 +279,15 @@ public class DHCP6 extends BasePacket { return linkAddress; } + /** + * 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. * @@ -276,6 +297,15 @@ public class DHCP6 extends BasePacket { return peerAddress; } + /** + * Gets IPv6 peer address. + * + * @return the IPv6 peer address + */ + public Ip6Address getIp6PeerAddress() { + return peerAddress == null ? null : Ip6Address.valueOf(peerAddress); + } + /** * Sets message type. * @@ -329,4 +359,24 @@ public class DHCP6 extends BasePacket { 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 0000000000..cec4d97c87 --- /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 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 0000000000..5930c87300 --- /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 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 0000000000..511b5c756f --- /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 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 0000000000..db190c3330 --- /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 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 getOptions() { + return options; + } + + /** + * Sets sub-options. + * + * @param options the sub-options of this option + */ + public void setOptions(List 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 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 0000000000..f075b4d11e --- /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 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 getOptions() { + return options; + } + + /** + * Sets sub-options. + * + * @param options the sub-options of this option + */ + public void setOptions(List 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 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 dc3407554e..4da0a47b97 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 @@ public class Dhcp6Option { } /** - * 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 @@ public class Dhcp6Option { * @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 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 0000000000..bf3a1af565 --- /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 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 7b83c00832..0000000000 --- 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 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 0000000000..f8a0425728 --- /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 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 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 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 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 0000000000..4996ae6c16 --- /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 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 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 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 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 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 0000000000000000000000000000000000000000..edc32db146eddc3a27b3217f54df434b80817764 GIT binary patch literal 84 zcmZQNz4R{wGlK>L0|O%vLjWs-gaV8WrkQ|pjO7drOc6j;d_c^o$RM_bVK-2ck%0*! OqVWI6dxn{5KoI~;)d_w8 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8ea7fa1c93ddec49a4f643c25471750f5f09cebd GIT binary patch literal 122 zcmd;OP+&j?O#lBiAoCd*88{h2m`*SK%fQT_0hD2c%d#>^D8M-&1`|+@u^dQ809Ek; cF{2`b*cOJ}Kq*EBCWwf_{~zxeW~Ko}0EMFrhyVZp literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b08ac4a4a9d7e1a92d97393a390a631148fc656c GIT binary patch literal 122 zcmd;OP+&j?O#lBiAoCd*88{h2*rzUg!obX+0hD2c%d#>^D8M-&1`|+@u^dQ809Ek; cF{2`b*cOJ}Kq*EBCWwf_{~zxeW~Ko}0B$Y}KL7v# literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bbaeb48e689dcbe6118c2e57039b56c9fae87eeb GIT binary patch literal 140 zcmYj{F%CdL6hz;y5E2m$wjfr;VVuJ`TteXjPM~ugy;g;ff=MQsd3hn0fAo3u|BoV| ybB1Y^6-iY^I;tUcD>J0Kg U786j6nSlo^!@wuNz#u9C0JcyE`~Uy| literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3d63acc58ac3c235b049980269cc66400aa37fe5 GIT binary patch literal 84 zcmZRdy6g!9GlK>L0|O%vLjWs-gaV8WrkQ|pjO7drOc6j;d_c^o$RM_bVK-2ck%0*! OqVWI6dxn{5KoI~nUo-5Ge|I~Gspv3Oh7Sa1|F~s1D^l`gQx@m D*)#