PacketBuilder: add IPv6 fragment packets support
Add IPv6 fragment packets support in PacketBuilder class, refer to
https://tools.ietf.org/html/rfc2460.
Bug: 285124667
Test: atest NetworkStaticLibTests
Change-Id: Ie898f8a4847f12677b11dfaa4db26dc4e168a68a
diff --git a/staticlibs/device/com/android/net/module/util/PacketBuilder.java b/staticlibs/device/com/android/net/module/util/PacketBuilder.java
index 33e5bfa..a2dbd81 100644
--- a/staticlibs/device/com/android/net/module/util/PacketBuilder.java
+++ b/staticlibs/device/com/android/net/module/util/PacketBuilder.java
@@ -24,10 +24,13 @@
import static com.android.net.module.util.IpUtils.ipChecksum;
import static com.android.net.module.util.IpUtils.tcpChecksum;
import static com.android.net.module.util.IpUtils.udpChecksum;
+import static com.android.net.module.util.NetworkStackConstants.IPPROTO_FRAGMENT;
import static com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET;
import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_FRAGMENT_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.IPV6_LEN_OFFSET;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
import static com.android.net.module.util.NetworkStackConstants.TCP_CHECKSUM_OFFSET;
import static com.android.net.module.util.NetworkStackConstants.UDP_CHECKSUM_OFFSET;
import static com.android.net.module.util.NetworkStackConstants.UDP_LENGTH_OFFSET;
@@ -37,6 +40,7 @@
import androidx.annotation.NonNull;
import com.android.net.module.util.structs.EthernetHeader;
+import com.android.net.module.util.structs.FragmentHeader;
import com.android.net.module.util.structs.Ipv4Header;
import com.android.net.module.util.structs.Ipv6Header;
import com.android.net.module.util.structs.TcpHeader;
@@ -47,6 +51,10 @@
import java.net.Inet6Address;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
/**
* The class is used to build a packet.
@@ -210,6 +218,20 @@
}
}
+ private void writeFragmentHeader(ByteBuffer buffer, short nextHeader, int offset,
+ boolean mFlag, int id) throws IOException {
+ if ((offset & 7) != 0) {
+ throw new IOException("Invalid offset value, must be multiple of 8");
+ }
+ final FragmentHeader fragmentHeader = new FragmentHeader(nextHeader,
+ offset | (mFlag ? 1 : 0), id);
+ try {
+ fragmentHeader.writeToByteBuffer(buffer);
+ } catch (IllegalArgumentException | BufferOverflowException e) {
+ throw new IOException("Error writing to buffer: ", e);
+ }
+ }
+
/**
* Finalize the packet.
*
@@ -219,9 +241,31 @@
*/
@NonNull
public ByteBuffer finalizePacket() throws IOException {
+ // If the packet is finalized with L2 mtu greater than or equal to its current size, it will
+ // either return a List of size 1 or throw an IOException if something goes wrong.
+ return finalizePacket(mBuffer.position()).get(0);
+ }
+
+ /**
+ * Finalizes the packet with specified link MTU.
+ *
+ * Call after writing L4 header (no payload) or L4 payload to the buffer used by the builder.
+ * L3 header length, L3 header checksum and L4 header checksum are calculated and written back
+ * after finalization.
+ *
+ * @param l2mtu the maximum size, in bytes, of each individual packet. If the packet size
+ * exceeds the l2mtu, it will be fragmented into smaller packets.
+ * @return a list of packet(s), each containing a portion of the original L3 payload.
+ */
+ @NonNull
+ public List<ByteBuffer> finalizePacket(int l2mtu) throws IOException {
// [1] Finalize IPv4 or IPv6 header.
int ipHeaderOffset = INVALID_OFFSET;
if (mIpv4HeaderOffset != INVALID_OFFSET) {
+ if (mBuffer.position() > l2mtu) {
+ throw new IOException("IPv4 fragmentation is not supported");
+ }
+
ipHeaderOffset = mIpv4HeaderOffset;
// Populate the IPv4 totalLength field.
@@ -243,12 +287,15 @@
}
// [2] Finalize TCP or UDP header.
+ final int ipPayloadOffset;
if (mTcpHeaderOffset != INVALID_OFFSET) {
+ ipPayloadOffset = mTcpHeaderOffset;
// Populate the TCP header checksum field.
mBuffer.putShort(mTcpHeaderOffset + TCP_CHECKSUM_OFFSET, tcpChecksum(mBuffer,
ipHeaderOffset /* ipOffset */, mTcpHeaderOffset /* transportOffset */,
mBuffer.position() - mTcpHeaderOffset /* transportLen */));
} else if (mUdpHeaderOffset != INVALID_OFFSET) {
+ ipPayloadOffset = mUdpHeaderOffset;
// Populate the UDP header length field.
mBuffer.putShort(mUdpHeaderOffset + UDP_LENGTH_OFFSET,
(short) (mBuffer.position() - mUdpHeaderOffset));
@@ -257,11 +304,81 @@
mBuffer.putShort(mUdpHeaderOffset + UDP_CHECKSUM_OFFSET, udpChecksum(mBuffer,
ipHeaderOffset /* ipOffset */, mUdpHeaderOffset /* transportOffset */));
} else {
- throw new IOException("Packet is missing neither TCP nor UDP header");
+ throw new IOException("Packet has neither TCP nor UDP header");
}
- mBuffer.flip();
- return mBuffer;
+ if (mBuffer.position() <= l2mtu) {
+ mBuffer.flip();
+ return Arrays.asList(mBuffer);
+ }
+
+ // IPv6 Packet is fragmented into multiple smaller packets that would fit within the link
+ // MTU.
+ // Refer to https://tools.ietf.org/html/rfc2460
+ //
+ // original packet:
+ // +------------------+--------------+--------------+--//--+----------+
+ // | Unfragmentable | first | second | | last |
+ // | Part | fragment | fragment | .... | fragment |
+ // +------------------+--------------+--------------+--//--+----------+
+ //
+ // fragment packets:
+ // +------------------+--------+--------------+
+ // | Unfragmentable |Fragment| first |
+ // | Part | Header | fragment |
+ // +------------------+--------+--------------+
+ //
+ // +------------------+--------+--------------+
+ // | Unfragmentable |Fragment| second |
+ // | Part | Header | fragment |
+ // +------------------+--------+--------------+
+ // o
+ // o
+ // o
+ // +------------------+--------+----------+
+ // | Unfragmentable |Fragment| last |
+ // | Part | Header | fragment |
+ // +------------------+--------+----------+
+ final List<ByteBuffer> fragments = new ArrayList<>();
+ final int totalPayloadLen = mBuffer.position() - ipPayloadOffset;
+ final int perPacketPayloadLen = l2mtu - ipPayloadOffset - IPV6_FRAGMENT_HEADER_LEN;
+ final short protocol = (short) Byte.toUnsignedInt(
+ mBuffer.get(mIpv6HeaderOffset + IPV6_PROTOCOL_OFFSET));
+ Random random = new Random();
+ final int id = random.nextInt(Integer.MAX_VALUE);
+ int startOffset = 0;
+ // Copy the packet content to a byte array.
+ byte[] packet = new byte[mBuffer.position()];
+ // The ByteBuffer#get(int index, byte[] dst) method is only available in API level 35 and
+ // above. Here, we use a more primitive approach: reposition the ByteBuffer to the beginning
+ // before copying, then return its position to the end afterward.
+ mBuffer.position(0);
+ mBuffer.get(packet);
+ mBuffer.position(packet.length);
+ while (startOffset < totalPayloadLen) {
+ int copyPayloadLen = Math.min(perPacketPayloadLen, totalPayloadLen - startOffset);
+ // The data portion must be broken into segments aligned with 8-octet boundaries.
+ // Therefore, the payload length should be a multiple of 8 bytes for all fragments
+ // except the last one.
+ // See https://datatracker.ietf.org/doc/html/rfc791 section 3.2
+ if (copyPayloadLen != totalPayloadLen - startOffset) {
+ copyPayloadLen &= ~7;
+ }
+ ByteBuffer fragment = ByteBuffer.allocate(ipPayloadOffset + IPV6_FRAGMENT_HEADER_LEN
+ + copyPayloadLen);
+ fragment.put(packet, 0, ipPayloadOffset);
+ writeFragmentHeader(fragment, protocol, startOffset,
+ startOffset + copyPayloadLen < totalPayloadLen, id);
+ fragment.put(packet, ipPayloadOffset + startOffset, copyPayloadLen);
+ fragment.putShort(mIpv6HeaderOffset + IPV6_LEN_OFFSET,
+ (short) (IPV6_FRAGMENT_HEADER_LEN + copyPayloadLen));
+ fragment.put(mIpv6HeaderOffset + IPV6_PROTOCOL_OFFSET, (byte) IPPROTO_FRAGMENT);
+ fragment.flip();
+ fragments.add(fragment);
+ startOffset += copyPayloadLen;
+ }
+
+ return fragments;
}
/**
diff --git a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
index 319d51a..a8c50d8 100644
--- a/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
+++ b/staticlibs/framework/com/android/net/module/util/NetworkStackConstants.java
@@ -129,7 +129,9 @@
public static final int IPV6_PROTOCOL_OFFSET = 6;
public static final int IPV6_SRC_ADDR_OFFSET = 8;
public static final int IPV6_DST_ADDR_OFFSET = 24;
+ public static final int IPV6_FRAGMENT_ID_OFFSET = 4;
public static final int IPV6_MIN_MTU = 1280;
+ public static final int IPV6_FRAGMENT_ID_LEN = 4;
public static final int IPV6_FRAGMENT_HEADER_LEN = 8;
public static final int RFC7421_PREFIX_LENGTH = 64;
// getSockOpt() for v6 MTU
@@ -141,6 +143,8 @@
public static final Inet6Address IPV6_ADDR_ALL_HOSTS_MULTICAST =
(Inet6Address) InetAddresses.parseNumericAddress("ff02::3");
+ public static final int IPPROTO_FRAGMENT = 44;
+
/**
* ICMP constants.
*
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/PacketBuilderTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/PacketBuilderTest.java
index e40cd6b..886336c 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/PacketBuilderTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/PacketBuilderTest.java
@@ -21,9 +21,13 @@
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_FRAGMENT_ID_LEN;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_FRAGMENT_ID_OFFSET;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN;
import static com.android.net.module.util.NetworkStackConstants.TCPHDR_ACK;
import static com.android.net.module.util.NetworkStackConstants.TCP_HEADER_MIN_LEN;
import static com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN;
@@ -54,6 +58,8 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -489,10 +495,103 @@
(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef
};
+ private static final byte[] TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_NO_FRAG =
+ new byte[] {
+ // packet = Ether(src="11:22:33:44:55:66", dst="aa:bb:cc:dd:ee:ff", type='IPv6')/
+ // IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, fl=0x515ca,
+ // hlim=0x40)/UDP(sport=9876, dport=433)/
+ // Raw([i%256 for i in range(0, 500)]);
+ // Ether header
+ (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd,
+ (byte) 0xee, (byte) 0xff, (byte) 0x11, (byte) 0x22,
+ (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66,
+ (byte) 0x86, (byte) 0xdd,
+ // IPv6 header
+ (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca,
+ (byte) 0x01, (byte) 0xfc, (byte) 0x11, (byte) 0x40,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+ // UDP header
+ (byte) 0x26, (byte) 0x94, (byte) 0x01, (byte) 0xb1,
+ (byte) 0x01, (byte) 0xfc, (byte) 0xd3, (byte) 0x9e,
+ // Data
+ // 500 bytes of repeated 0x00~0xff
+ };
+
+ private static final byte[] TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_FRAG1 =
+ new byte[] {
+ // packet = Ether(src="11:22:33:44:55:66", dst="aa:bb:cc:dd:ee:ff", type='IPv6')/
+ // IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, fl=0x515ca,
+ // hlim=0x40)/UDP(sport=9876, dport=433)/
+ // Raw([i%256 for i in range(0, 500)]);
+ // packets=fragment6(packet, 400);
+ // Ether header
+ (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd,
+ (byte) 0xee, (byte) 0xff, (byte) 0x11, (byte) 0x22,
+ (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66,
+ (byte) 0x86, (byte) 0xdd,
+ // IPv6 header
+ (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca,
+ (byte) 0x01, (byte) 0x58, (byte) 0x2c, (byte) 0x40,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+ // Fragement Header
+ (byte) 0x11, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // UDP header
+ (byte) 0x26, (byte) 0x94, (byte) 0x01, (byte) 0xb1,
+ (byte) 0x01, (byte) 0xfc, (byte) 0xd3, (byte) 0x9e,
+ // Data
+ // 328 bytes of repeated 0x00~0xff, start:0x00 end:0x47
+ };
+
+ private static final byte[] TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_FRAG2 =
+ new byte[] {
+ // packet = Ether(src="11:22:33:44:55:66", dst="aa:bb:cc:dd:ee:ff", type='IPv6')/
+ // IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, fl=0x515ca,
+ // hlim=0x40)/UDP(sport=9876, dport=433)/
+ // Raw([i%256 for i in range(0, 500)]);
+ // packets=fragment6(packet, 400);
+ // Ether header
+ (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd,
+ (byte) 0xee, (byte) 0xff, (byte) 0x11, (byte) 0x22,
+ (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66,
+ (byte) 0x86, (byte) 0xdd,
+ // IPv6 header
+ (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca,
+ (byte) 0x00, (byte) 0xb4, (byte) 0x2c, (byte) 0x40,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+ // Fragement Header
+ (byte) 0x11, (byte) 0x00, (byte) 0x01, (byte) 0x50,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // Data
+ // 172 bytes of repeated 0x00~0xff, start:0x48 end:0xf3
+ };
+
/**
* Build a packet which has ether header, IP header, TCP/UDP header and data.
* The ethernet header and data are optional. Note that both source mac address and
- * destination mac address are required for ethernet header.
+ * destination mac address are required for ethernet header. The packet will be fragmented into
+ * multiple smaller packets if the packet size exceeds L2 mtu.
*
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Layer 2 header (EthernetHeader) | (optional)
@@ -511,11 +610,12 @@
* @param l4proto the layer 4 protocol. Only {@code IPPROTO_TCP} and {@code IPPROTO_UDP}
* currently supported.
* @param payload the payload.
+ * @param l2mtu the Link MTU. It's the upper bound of each packet size. Zero means no limit.
*/
@NonNull
- private ByteBuffer buildPacket(@Nullable final MacAddress srcMac,
+ private List<ByteBuffer> buildPackets(@Nullable final MacAddress srcMac,
@Nullable final MacAddress dstMac, final int l3proto, final int l4proto,
- @Nullable final ByteBuffer payload)
+ @Nullable final ByteBuffer payload, int l2mtu)
throws Exception {
if (l3proto != IPPROTO_IP && l3proto != IPPROTO_IPV6) {
fail("Unsupported layer 3 protocol " + l3proto);
@@ -562,7 +662,15 @@
payload.clear();
}
- return packetBuilder.finalizePacket();
+ return packetBuilder.finalizePacket(l2mtu > 0 ? l2mtu : Integer.MAX_VALUE);
+ }
+
+ @NonNull
+ private ByteBuffer buildPacket(@Nullable final MacAddress srcMac,
+ @Nullable final MacAddress dstMac, final int l3proto, final int l4proto,
+ @Nullable final ByteBuffer payload)
+ throws Exception {
+ return buildPackets(srcMac, dstMac, l3proto, l4proto, payload, 0).get(0);
}
/**
@@ -874,6 +982,66 @@
assertArrayEquals(TEST_PACKET_IPV6HDR_UDPHDR_DATA, packet.array());
}
+ private void checkIpv6PacketIgnoreFragmentId(byte[] expected, byte[] actual) {
+ final int offset = ETHER_HEADER_LEN + IPV6_HEADER_LEN + IPV6_FRAGMENT_ID_OFFSET;
+ assertArrayEquals(Arrays.copyOf(expected, offset), Arrays.copyOf(actual, offset));
+ assertArrayEquals(
+ Arrays.copyOfRange(expected, offset + IPV6_FRAGMENT_ID_LEN, expected.length),
+ Arrays.copyOfRange(actual, offset + IPV6_FRAGMENT_ID_LEN, actual.length));
+ }
+
+ @Test
+ public void testBuildPacketIPv6FragmentUdpData() throws Exception {
+ // A UDP packet with 500 bytes payload will be fragmented into two UDP packets each carrying
+ // 328 and 172 bytes of payload if the Link MTU is 400. Note that only the first packet
+ // contains the original UDP header.
+ final int payloadLen = 500;
+ final int payloadLen1 = 328;
+ final int payloadLen2 = 172;
+ final int l2mtu = 400;
+ final byte[] payload = new byte[payloadLen];
+ // Initialize the payload with repeated values from 0x00 to 0xff.
+ for (int i = 0; i < payload.length; i++) {
+ payload[i] = (byte) (i & 0xff);
+ }
+
+ // Verify original UDP packet.
+ final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IPV6, IPPROTO_UDP,
+ ByteBuffer.wrap(payload));
+ final int headerLen = TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_NO_FRAG.length;
+ assertArrayEquals(TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_NO_FRAG,
+ Arrays.copyOf(packet.array(), headerLen));
+ assertArrayEquals(payload,
+ Arrays.copyOfRange(packet.array(), headerLen, headerLen + payloadLen));
+
+ // Verify fragments of UDP packet.
+ final List<ByteBuffer> packets = buildPackets(SRC_MAC, DST_MAC, IPPROTO_IPV6, IPPROTO_UDP,
+ ByteBuffer.wrap(payload), l2mtu);
+ assertEquals(2, packets.size());
+
+ // Verify first fragment.
+ int headerLen1 = TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_FRAG1.length;
+ // (1) Compare packet content up to the UDP header, excluding the fragment ID as it's a
+ // random value.
+ checkIpv6PacketIgnoreFragmentId(TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_FRAG1,
+ Arrays.copyOf(packets.get(0).array(), headerLen1));
+ // (2) Compare UDP payload.
+ assertArrayEquals(Arrays.copyOf(payload, payloadLen1),
+ Arrays.copyOfRange(packets.get(0).array(), headerLen1, headerLen1 + payloadLen1));
+
+ // Verify second fragment (similar to the first one).
+ int headerLen2 = TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_FRAG2.length;
+ checkIpv6PacketIgnoreFragmentId(TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA_FRAG2,
+ Arrays.copyOf(packets.get(1).array(), headerLen2));
+ assertArrayEquals(Arrays.copyOfRange(payload, payloadLen1, payloadLen1 + payloadLen2),
+ Arrays.copyOfRange(packets.get(1).array(), headerLen2, headerLen2 + payloadLen2));
+ // Verify that the fragment IDs in the first and second fragments are the same.
+ final int offset = ETHER_HEADER_LEN + IPV6_HEADER_LEN + IPV6_FRAGMENT_ID_OFFSET;
+ assertArrayEquals(
+ Arrays.copyOfRange(packets.get(0).array(), offset, offset + IPV6_FRAGMENT_ID_LEN),
+ Arrays.copyOfRange(packets.get(1).array(), offset, offset + IPV6_FRAGMENT_ID_LEN));
+ }
+
@Test
public void testFinalizePacketWithoutIpv4Header() throws Exception {
final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */, IPPROTO_IP,