Snap for 9948759 from 45c3085f1a3d8d188586382d5e221d14cb38784c to mainline-media-swcodec-release Change-Id: I686d5e249362b04699ae6ce8c8acae550cd50531
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index aa24b56..9fe8b1d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml
@@ -43,10 +43,6 @@ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <!-- Signature permission defined in NetworkStackStub --> <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> - <!-- Used by creating test network --> - <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" /> - <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <application android:extractNativeLibs="false" android:persistent="true"
diff --git a/src/android/net/dhcp6/Dhcp6AdvertisePacket.java b/src/android/net/dhcp6/Dhcp6AdvertisePacket.java new file mode 100644 index 0000000..3eb5503 --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6AdvertisePacket.java
@@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * 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 android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 ADVERTISE packet class, a server sends an Advertise message to indicate that it's + * available for DHCP service, in response to a Solicit message received from a client. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6AdvertisePacket extends Dhcp6Packet { + /** + * Generates an advertise packet with the specified parameters. + */ + Dhcp6AdvertisePacket(int transId, @NonNull final byte[] clientDuid, + @NonNull final byte[] serverDuid, final byte[] iapd) { + super(transId, (short) 0 /* secs */, clientDuid, serverDuid, iapd); + } + + /** + * Build a DHCPv6 Advertise message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_ADVERTISE << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, mClientDuid); + addTlv(packet, DHCP6_SERVER_IDENTIFIER, mServerDuid); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6Packet.java b/src/android/net/dhcp6/Dhcp6Packet.java index 1a1a5f3..98c214b 100644 --- a/src/android/net/dhcp6/Dhcp6Packet.java +++ b/src/android/net/dhcp6/Dhcp6Packet.java
@@ -19,8 +19,15 @@ import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_OPTION_LEN; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import com.android.net.module.util.Struct; +import com.android.net.module.util.structs.IaPrefixOption; + +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; /** * Defines basic data and operations needed to build and use packets for the @@ -89,6 +96,8 @@ public static final byte DHCP6_IA_PD = 25; @NonNull protected final byte[] mIaPd; + @NonNull + protected PrefixDelegation mPrefixDelegation; /** * The transaction identifier used in this particular DHCPv6 negotiation @@ -139,6 +148,203 @@ } /** + * A class to take DHCPv6 IA_PD option allocated from server. + * https://www.rfc-editor.org/rfc/rfc8415.html#section-21.21 + */ + public static class PrefixDelegation { + public int iaid; + public int t1; + public int t2; + public final IaPrefixOption ipo; + + PrefixDelegation(int iaid, int t1, int t2, final IaPrefixOption ipo) { + this.iaid = iaid; + this.t1 = t1; + this.t2 = t2; + this.ipo = ipo; + } + + @Override + public String toString() { + return "Prefix Delegation: iaid " + iaid + ", t1 " + t1 + ", t2 " + t2 + + ", prefix " + ipo; + } + } + + /** + * DHCPv6 packet parsing exception. + */ + public static class ParseException extends Exception { + ParseException(String msg) { + super(msg); + } + } + + private static void skipOption(final ByteBuffer packet, int optionLen) + throws BufferUnderflowException { + for (int i = 0; i < optionLen; i++) { + packet.get(); + } + } + + /** + * Reads a string of specified length from the buffer. + * + * TODO: move to a common place which can be shared with DhcpClient. + */ + private static String readAsciiString(@NonNull final ByteBuffer buf, int byteCount, + boolean isNullOk) { + final byte[] bytes = new byte[byteCount]; + buf.get(bytes); + return readAsciiString(bytes, isNullOk); + } + + private static String readAsciiString(@NonNull final byte[] payload, boolean isNullOk) { + final byte[] bytes = payload; + int length = bytes.length; + if (!isNullOk) { + // Stop at the first null byte. This is because some DHCP options (e.g., the domain + // name) are passed to netd via FrameworkListener, which refuses arguments containing + // null bytes. We don't do this by default because vendorInfo is an opaque string which + // could in theory contain null bytes. + for (length = 0; length < bytes.length; length++) { + if (bytes[length] == 0) { + break; + } + } + } + return new String(bytes, 0, length, StandardCharsets.US_ASCII); + } + + /** + * Creates a concrete Dhcp6Packet from the supplied ByteBuffer. + * + * The buffer only starts with a UDP encapsulation (i.e. DHCPv6 message). A subset of the + * optional parameters are parsed and are stored in object fields. Client/Server message + * format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | msg-type | transaction-id | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * . options . + * . (variable number and length) . + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + @VisibleForTesting + static Dhcp6Packet decodePacket(@NonNull final ByteBuffer packet) throws ParseException { + short secs = 0; + byte[] iapd = null; + byte[] serverDuid = null; + byte[] clientDuid = null; + short statusCode = STATUS_SUCCESS; + String statusMsg = null; + + packet.order(ByteOrder.BIG_ENDIAN); + + // DHCPv6 message contents. + final int msgTypeAndTransId = packet.getInt(); + final byte messageType = (byte) (msgTypeAndTransId >> 24); + final int transId = msgTypeAndTransId & 0x0FFF; + + /** + * Parse DHCPv6 options, option format: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | option-code | option-len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | option-data | + * | (option-len octets) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + while (packet.hasRemaining()) { + try { + final short optionType = packet.getShort(); + final int optionLen = packet.getShort() & 0xFFFF; + int expectedLen = 0; + + switch(optionType) { + case DHCP6_SERVER_IDENTIFIER: + expectedLen = optionLen; + final byte[] sduid = new byte[expectedLen]; + packet.get(sduid, 0 /* offset */, expectedLen); + serverDuid = sduid; + break; + case DHCP6_CLIENT_IDENTIFIER: + expectedLen = optionLen; + final byte[] cduid = new byte[expectedLen]; + packet.get(cduid, 0 /* offset */, expectedLen); + clientDuid = cduid; + break; + case DHCP6_IA_PD: + expectedLen = optionLen; + final byte[] bytes = new byte[expectedLen]; + packet.get(bytes, 0 /* offset */, expectedLen); + iapd = bytes; + break; + case DHCP6_ELAPSED_TIME: + expectedLen = 2; + secs = packet.getShort(); + break; + case DHCP6_STATUS_CODE: + expectedLen = optionLen; + statusCode = packet.getShort(); + statusMsg = readAsciiString(packet, expectedLen - 2, false /* isNullOk */); + break; + default: + skipOption(packet, optionLen); + break; + } + if (expectedLen != optionLen) { + throw new ParseException( + "Invalid length " + optionLen + " for option " + optionType + + ", expected " + expectedLen); + } + } catch (BufferUnderflowException e) { + throw new ParseException(e.getMessage()); + } + } + + Dhcp6Packet newPacket; + + switch(messageType) { + case DHCP6_MESSAGE_TYPE_SOLICIT: + newPacket = new Dhcp6SolicitPacket(transId, secs, clientDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_ADVERTISE: + newPacket = new Dhcp6AdvertisePacket(transId, clientDuid, serverDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_REQUEST: + newPacket = new Dhcp6RequestPacket(transId, secs, clientDuid, serverDuid, iapd); + break; + case DHCP6_MESSAGE_TYPE_REPLY: + newPacket = new Dhcp6ReplyPacket(transId, clientDuid, serverDuid, iapd); + break; + default: + throw new ParseException("Unimplemented DHCP6 message type %d" + messageType); + } + + if (iapd != null) { + final ByteBuffer buffer = ByteBuffer.wrap(iapd); + final int iaid = buffer.getInt(); + final int t1 = buffer.getInt(); + final int t2 = buffer.getInt(); + final IaPrefixOption ipo = Struct.parse(IaPrefixOption.class, buffer); + newPacket.mPrefixDelegation = new PrefixDelegation(iaid, t1, t2, ipo); + newPacket.mIaId = iaid; + } + newPacket.mStatusCode = statusCode; + newPacket.mStatusMsg = statusMsg; + + return newPacket; + } + + /** * Adds an optional parameter containing an array of bytes. */ protected static void addTlv(ByteBuffer buf, short type, @NonNull byte[] payload) { @@ -163,9 +369,38 @@ /** * Builds a DHCPv6 SOLICIT packet from the required specified parameters. */ - public static ByteBuffer buildSolicitPacket(int transId, short secs, final byte[] iapd, - final byte[] duid) { - final Dhcp6SolicitPacket pkt = new Dhcp6SolicitPacket(transId, secs, duid, iapd); + public static ByteBuffer buildSolicitPacket(int transId, short secs, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid) { + final Dhcp6SolicitPacket pkt = new Dhcp6SolicitPacket(transId, secs, clientDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 ADVERTISE packet from the required specified parameters. + */ + public static ByteBuffer buildAdvertisePacket(int transId, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid, @NonNull final byte[] serverDuid) { + final Dhcp6AdvertisePacket pkt = + new Dhcp6AdvertisePacket(transId, clientDuid, serverDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 REPLY packet from the required specified parameters. + */ + public static ByteBuffer buildReplyPacket(int transId, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid, @NonNull final byte[] serverDuid) { + final Dhcp6ReplyPacket pkt = new Dhcp6ReplyPacket(transId, clientDuid, serverDuid, iapd); + return pkt.buildPacket(); + } + + /** + * Builds a DHCPv6 REQUEST packet from the required specified parameters. + */ + public static ByteBuffer buildRequestPacket(int transId, short secs, @NonNull final byte[] iapd, + @NonNull final byte[] clientDuid, @NonNull final byte[] serverDuid) { + final Dhcp6RequestPacket pkt = + new Dhcp6RequestPacket(transId, secs, clientDuid, serverDuid, iapd); return pkt.buildPacket(); } }
diff --git a/src/android/net/dhcp6/Dhcp6ReplyPacket.java b/src/android/net/dhcp6/Dhcp6ReplyPacket.java new file mode 100644 index 0000000..4b37478 --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6ReplyPacket.java
@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * 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 android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 REPLY packet class, a server sends an Reply message containing assigned leases + * and configuration parameters in response to a Solicit, Request, Renew or Rebind messages + * received from a client. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6ReplyPacket extends Dhcp6Packet { + /** + * Generates a reply packet with the specified parameters. + */ + Dhcp6ReplyPacket(int transId, @NonNull final byte[] clientDuid, + @NonNull final byte[] serverDuid, final byte[] iapd) { + super(transId, (short) 0 /* secs */, clientDuid, serverDuid, iapd); + } + + /** + * Build a DHCPv6 Reply message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_REPLY << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, mClientDuid); + addTlv(packet, DHCP6_SERVER_IDENTIFIER, mServerDuid); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6RequestPacket.java b/src/android/net/dhcp6/Dhcp6RequestPacket.java new file mode 100644 index 0000000..f2f398d --- /dev/null +++ b/src/android/net/dhcp6/Dhcp6RequestPacket.java
@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * 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 android.net.dhcp6; + +import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; + +/** + * DHCPv6 REQUEST packet class, a client sends a Request message to request configuration + * parameters, including addresses and/or delegated prefixes from a specific server. + * + * https://tools.ietf.org/html/rfc8415#page-24 + */ +public class Dhcp6RequestPacket extends Dhcp6Packet { + /** + * Generates a request packet with the specified parameters. + */ + Dhcp6RequestPacket(int transId, short secs, @NonNull final byte[] clientDuid, + @NonNull final byte[] serverDuid, final byte[] iapd) { + super(transId, secs, clientDuid, serverDuid, iapd); + } + + /** + * Build a DHCPv6 Request message with the specific parameters. + */ + public ByteBuffer buildPacket() { + final ByteBuffer packet = ByteBuffer.allocate(DHCP_MAX_LENGTH); + final int msgTypeAndTransId = (DHCP6_MESSAGE_TYPE_REQUEST << 24) | (mTransId & 0x0FFF); + packet.putInt(msgTypeAndTransId); + + addTlv(packet, DHCP6_SERVER_IDENTIFIER, getServerDuid()); + addTlv(packet, DHCP6_CLIENT_IDENTIFIER, getClientDuid()); + addTlv(packet, DHCP6_ELAPSED_TIME, mSecs); + addTlv(packet, DHCP6_IA_PD, mIaPd); + + packet.flip(); + return packet; + } +}
diff --git a/src/android/net/dhcp6/Dhcp6SolicitPacket.java b/src/android/net/dhcp6/Dhcp6SolicitPacket.java index 23de8c9..909ff2c 100644 --- a/src/android/net/dhcp6/Dhcp6SolicitPacket.java +++ b/src/android/net/dhcp6/Dhcp6SolicitPacket.java
@@ -18,6 +18,8 @@ import static com.android.net.module.util.NetworkStackConstants.DHCP_MAX_LENGTH; +import androidx.annotation.NonNull; + import java.nio.ByteBuffer; /** @@ -29,7 +31,8 @@ /** * Generates a solicit packet with the specified parameters. */ - Dhcp6SolicitPacket(int transId, short secs, final byte[] clientDuid, final byte[] iapd) { + Dhcp6SolicitPacket(int transId, short secs, @NonNull final byte[] clientDuid, + final byte[] iapd) { super(transId, secs, clientDuid, null /* serverDuid */, iapd); }
diff --git a/src/android/net/ip/IpReachabilityMonitor.java b/src/android/net/ip/IpReachabilityMonitor.java index b7cb9ce..ff6d65e 100644 --- a/src/android/net/ip/IpReachabilityMonitor.java +++ b/src/android/net/ip/IpReachabilityMonitor.java
@@ -259,7 +259,7 @@ mCm = context.getSystemService(ConnectivityManager.class); mDependencies = dependencies; mMulticastResolicitEnabled = dependencies.isFeatureEnabled(context, - IP_REACHABILITY_MCAST_RESOLICIT_VERSION, false /* defaultEnabled */); + IP_REACHABILITY_MCAST_RESOLICIT_VERSION, true /* defaultEnabled */); mIgnoreIncompleteIpv6DnsServerEnabled = dependencies.isFeatureEnabled(context, IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, false /* defaultEnabled */);
diff --git a/tests/integration/AndroidManifest.xml b/tests/integration/AndroidManifest.xml index bfd3735..85c971a 100644 --- a/tests/integration/AndroidManifest.xml +++ b/tests/integration/AndroidManifest.xml
@@ -23,7 +23,10 @@ 05-14 00:41:02.723 18330 18330 E AndroidRuntime: java.lang.IllegalStateException: Signature|privileged permissions not in privapp-permissions whitelist: {com.android.server.networkstack.integrationtests: android.permission.CONNECTIVITY_INTERNAL} --> - + <!-- Used by creating test network --> + <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" /> + <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <application android:debuggable="true"> <uses-library android:name="android.test.runner" /> </application>
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java index f4618a6..9790ec9 100644 --- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -2890,8 +2890,12 @@ waitForRouterSolicitation(); mPacketReader.sendResponse(ra); + // Wait until we see both success IPv4 and IPv6 provisioning, then there would be 4 + // addresses in LinkProperties, they are IPv4 address, IPv6 link-local address, stable + // privacy address and privacy address. verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange(argThat(x -> { if (!x.isIpv4Provisioned() || !x.isIpv6Provisioned()) return false; + if (x.getLinkAddresses().size() != 4) return false; lpFuture.complete(x); return true; }));
diff --git a/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt new file mode 100644 index 0000000..264f785 --- /dev/null +++ b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt
@@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * 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 android.net.dhcp6 + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.net.module.util.HexDump +import com.android.testutils.assertThrows +import java.nio.ByteBuffer +import kotlin.test.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class Dhcp6PacketTest { + @Test + fun testDecodeDhcp6SolicitPacket() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // elapsed time option(option_len=2) + "000800020000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25) + "001A001900000000000000004000000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + val packet = Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + assertTrue(packet is Dhcp6SolicitPacket) + } + + @Test + fun testDecodeDhcp6SolicitPacket_incorrectOptionLength() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // elapsed time option(wrong option_len: 4) + "000800040000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25) + "001A001900000000000000004000000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + assertThrows(Dhcp6Packet.ParseException::class.java) { + Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + } + } + + @Test + fun testDecodeDhcp6SolicitPacket_lastTruncatedOption() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12) + "0001000C0003001B024CCBFFFE5F6EA9" + + // elapsed time option(option_len=2) + "000800020000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25, missing one byte) + "001A0019000000000000000040000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + assertThrows(Dhcp6Packet.ParseException::class.java) { + Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + } + } + + @Test + fun testDecodeDhcp6SolicitPacket_middleTruncatedOption() { + val solicitHex = + // Solicit, Transaction ID + "01000F51" + + // client identifier option(option_len=12, missing one byte) + "0001000C0003001B024CCBFFFE5F6E" + + // elapsed time option(option_len=2) + "000800020000" + + // IA_PD option(option_len=41, including IA prefix option) + "00190029DE3570F50000000000000000" + + // IA prefix option(option_len=25) + "001A001900000000000000004000000000000000000000000000000000" + val bytes = HexDump.hexStringToByteArray(solicitHex) + assertThrows(Dhcp6Packet.ParseException::class.java) { + Dhcp6Packet.decodePacket(ByteBuffer.wrap(bytes)) + } + } +}