From 8ffa79ce34958ec99318200a31b6f4f2daf2c4ec Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Sat, 14 May 2022 21:38:26 +0800 Subject: [PATCH 1/2] PacketBuilder: add IPv6 support Support IPv6 packet builder and verify with UDP packet unit test. Bug: 215655463 Test: atest NetworkStaticLibTests Change-Id: I5edf0ffbc9b37ecf0e0a6da0dc7d04716fc4c38c --- .../net/module/util/PacketBuilder.java | 89 +++-- .../net/module/util/PacketBuilderTest.java | 336 +++++++++++++++--- 2 files changed, 357 insertions(+), 68 deletions(-) diff --git a/staticlibs/device/com/android/net/module/util/PacketBuilder.java b/staticlibs/device/com/android/net/module/util/PacketBuilder.java index c9085289e8..8acb2965f8 100644 --- a/staticlibs/device/com/android/net/module/util/PacketBuilder.java +++ b/staticlibs/device/com/android/net/module/util/PacketBuilder.java @@ -17,6 +17,7 @@ package com.android.net.module.util; import static android.system.OsConstants.IPPROTO_IP; +import static android.system.OsConstants.IPPROTO_IPV6; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; @@ -25,6 +26,7 @@ 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.IPV4_CHECKSUM_OFFSET; import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET; +import static com.android.net.module.util.NetworkStackConstants.IPV6_LEN_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; @@ -35,11 +37,13 @@ import androidx.annotation.NonNull; import com.android.net.module.util.structs.EthernetHeader; import com.android.net.module.util.structs.Ipv4Header; +import com.android.net.module.util.structs.Ipv6Header; import com.android.net.module.util.structs.TcpHeader; import com.android.net.module.util.structs.UdpHeader; import java.io.IOException; import java.net.Inet4Address; +import java.net.Inet6Address; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; @@ -49,7 +53,7 @@ import java.nio.ByteBuffer; * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Layer 2 header (EthernetHeader) | (optional) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Layer 3 header (Ipv4Header) | + * | Layer 3 header (Ipv4Header, Ipv6Header) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Layer 4 header (TcpHeader, UdpHeader) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -74,11 +78,14 @@ import java.nio.ByteBuffer; * sendPacket(buf); */ public class PacketBuilder { + private static final int INVALID_OFFSET = -1; + private final ByteBuffer mBuffer; - private int mIpv4HeaderOffset = -1; - private int mTcpHeaderOffset = -1; - private int mUdpHeaderOffset = -1; + private int mIpv4HeaderOffset = INVALID_OFFSET; + private int mIpv6HeaderOffset = INVALID_OFFSET; + private int mTcpHeaderOffset = INVALID_OFFSET; + private int mUdpHeaderOffset = INVALID_OFFSET; public PacketBuilder(@NonNull ByteBuffer buffer) { mBuffer = buffer; @@ -129,6 +136,31 @@ public class PacketBuilder { } } + /** + * Write an IPv6 header. + * The IP header length is calculated and written back in #finalizePacket. + * + * @param vtf version, traffic class and flow label + * @param nextHeader the transport layer protocol + * @param hopLimit hop limit + * @param srcIp source IP address + * @param dstIp destination IP address + */ + public void writeIpv6Header(int vtf, byte nextHeader, short hopLimit, + @NonNull final Inet6Address srcIp, @NonNull final Inet6Address dstIp) + throws IOException { + mIpv6HeaderOffset = mBuffer.position(); + final Ipv6Header ipv6Header = new Ipv6Header(vtf, + (short) 0 /* payloadLength, calculate in #finalizePacket */, nextHeader, + hopLimit, srcIp, dstIp); + + try { + ipv6Header.writeToByteBuffer(mBuffer); + } catch (IllegalArgumentException | BufferOverflowException e) { + throw new IOException("Error writing to buffer: ", e); + } + } + /** * Write a TCP header. * The TCP header checksum is calculated and written back in #finalizePacket. @@ -186,32 +218,42 @@ public class PacketBuilder { */ @NonNull public ByteBuffer finalizePacket() throws IOException { - if (mIpv4HeaderOffset < 0) { - // TODO: add support for IPv6 - throw new IOException("Packet is missing IPv4 header"); + // Finalize IPv4 or IPv6 header. + int ipHeaderOffset = INVALID_OFFSET; + if (mIpv4HeaderOffset != INVALID_OFFSET) { + ipHeaderOffset = mIpv4HeaderOffset; + + // Populate the IPv4 totalLength field. + mBuffer.putShort(mIpv4HeaderOffset + IPV4_LENGTH_OFFSET, + (short) (mBuffer.position() - mIpv4HeaderOffset)); + + // Populate the IPv4 header checksum field. + mBuffer.putShort(mIpv4HeaderOffset + IPV4_CHECKSUM_OFFSET, + ipChecksum(mBuffer, mIpv4HeaderOffset /* headerOffset */)); + } else if (mIpv6HeaderOffset != INVALID_OFFSET) { + ipHeaderOffset = mIpv6HeaderOffset; + + // Populate the IPv6 payloadLength field. + mBuffer.putShort(mIpv6HeaderOffset + IPV6_LEN_OFFSET, + (short) (mBuffer.position() - mIpv6HeaderOffset)); + } else { + throw new IOException("Packet is missing neither IPv4 nor IPv6 header"); } - // Populate the IPv4 totalLength field. - mBuffer.putShort(mIpv4HeaderOffset + IPV4_LENGTH_OFFSET, - (short) (mBuffer.position() - mIpv4HeaderOffset)); - - // Populate the IPv4 header checksum field. - mBuffer.putShort(mIpv4HeaderOffset + IPV4_CHECKSUM_OFFSET, - ipChecksum(mBuffer, mIpv4HeaderOffset /* headerOffset */)); - - if (mTcpHeaderOffset > 0) { + // Finalize TCP or UDP header. + if (mTcpHeaderOffset != INVALID_OFFSET) { // Populate the TCP header checksum field. mBuffer.putShort(mTcpHeaderOffset + TCP_CHECKSUM_OFFSET, tcpChecksum(mBuffer, - mIpv4HeaderOffset /* ipOffset */, mTcpHeaderOffset /* transportOffset */, + ipHeaderOffset /* ipOffset */, mTcpHeaderOffset /* transportOffset */, mBuffer.position() - mTcpHeaderOffset /* transportLen */)); - } else if (mUdpHeaderOffset > 0) { + } else if (mUdpHeaderOffset != INVALID_OFFSET) { // Populate the UDP header length field. mBuffer.putShort(mUdpHeaderOffset + UDP_LENGTH_OFFSET, (short) (mBuffer.position() - mUdpHeaderOffset)); // Populate the UDP header checksum field. mBuffer.putShort(mUdpHeaderOffset + UDP_CHECKSUM_OFFSET, udpChecksum(mBuffer, - mIpv4HeaderOffset /* ipOffset */, mUdpHeaderOffset /* transportOffset */)); + ipHeaderOffset /* ipOffset */, mUdpHeaderOffset /* transportOffset */)); } else { throw new IOException("Packet is missing neither TCP nor UDP header"); } @@ -225,15 +267,15 @@ public class PacketBuilder { * * @param hasEther has ethernet header. Set this flag to indicate that the packet has an * ethernet header. - * @param l3proto the layer 3 protocol. Only {@code IPPROTO_IP} currently supported. + * @param l3proto the layer 3 protocol. Only {@code IPPROTO_IP} and {@code IPPROTO_IPV6} + * currently supported. * @param l4proto the layer 4 protocol. Only {@code IPPROTO_TCP} and {@code IPPROTO_UDP} * currently supported. * @param payloadLen length of the payload. */ @NonNull public static ByteBuffer allocate(boolean hasEther, int l3proto, int l4proto, int payloadLen) { - if (l3proto != IPPROTO_IP) { - // TODO: add support for IPv6 + if (l3proto != IPPROTO_IP && l3proto != IPPROTO_IPV6) { throw new IllegalArgumentException("Unsupported layer 3 protocol " + l3proto); } @@ -247,7 +289,8 @@ public class PacketBuilder { int packetLen = 0; if (hasEther) packetLen += Struct.getSize(EthernetHeader.class); - packetLen += Struct.getSize(Ipv4Header.class); + packetLen += (l3proto == IPPROTO_IP) ? Struct.getSize(Ipv4Header.class) + : Struct.getSize(Ipv6Header.class); packetLen += (l4proto == IPPROTO_TCP) ? Struct.getSize(TcpHeader.class) : Struct.getSize(UdpHeader.class); packetLen += payloadLen; 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 8f9a1f996e..c02763a07d 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 @@ -17,11 +17,14 @@ package com.android.net.module.util; import static android.system.OsConstants.IPPROTO_IP; +import static android.system.OsConstants.IPPROTO_IPV6; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; 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_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; @@ -41,6 +44,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.net.module.util.structs.EthernetHeader; import com.android.net.module.util.structs.Ipv4Header; +import com.android.net.module.util.structs.Ipv6Header; import com.android.net.module.util.structs.TcpHeader; import com.android.net.module.util.structs.UdpHeader; @@ -49,6 +53,7 @@ import org.junit.runner.RunWith; import java.io.IOException; import java.net.Inet4Address; +import java.net.Inet6Address; import java.nio.ByteBuffer; @RunWith(AndroidJUnit4.class) @@ -56,8 +61,10 @@ import java.nio.ByteBuffer; public class PacketBuilderTest { private static final MacAddress SRC_MAC = MacAddress.fromString("11:22:33:44:55:66"); private static final MacAddress DST_MAC = MacAddress.fromString("aa:bb:cc:dd:ee:ff"); - private static final Inet4Address IPV4_SRC_ADDR = addr("192.0.2.1"); - private static final Inet4Address IPV4_DST_ADDR = addr("198.51.100.1"); + private static final Inet4Address IPV4_SRC_ADDR = addr4("192.0.2.1"); + private static final Inet4Address IPV4_DST_ADDR = addr4("198.51.100.1"); + private static final Inet6Address IPV6_SRC_ADDR = addr6("2001:db8::1"); + private static final Inet6Address IPV6_DST_ADDR = addr6("2001:db8::2"); private static final short SRC_PORT = 9876; private static final short DST_PORT = 433; private static final short SEQ_NO = 13579; @@ -68,6 +75,9 @@ public class PacketBuilderTest { private static final byte TIME_TO_LIVE = (byte) 0x40; private static final short WINDOW = (short) 0x2000; private static final short URGENT_POINTER = 0; + // version=6, traffic class=0x80, flowlabel=0x515ca; + private static final int VERSION_TRAFFICCLASS_FLOWLABEL = 0x680515ca; + private static final short HOP_LIMIT = 0x40; private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef }); @@ -256,15 +266,123 @@ public class PacketBuilderTest { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef }; + private static final byte[] TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR = + new byte[] { + // packet = (scapy.Ether(src="11:22:33:44:55:66", dst="aa:bb:cc:dd:ee:ff", + // type='IPv6') / + // scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=48, hlim=0x40) / + // scapy.UDP(sport=9876, dport=433)) + // Note that plen(48) = ipv6hdr(40) + udphdr(8). + // 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, + // IP header + (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca, + (byte) 0x00, (byte) 0x30, (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) 0x00, (byte) 0x08, (byte) 0x7c, (byte) 0x24 + }; + + private static final byte[] TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA = + new byte[] { + // packet = (scapy.Ether(src="11:22:33:44:55:66", dst="aa:bb:cc:dd:ee:ff", + // type='IPv6') / + // scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=52, hlim=0x40) / + // scapy.UDP(sport=9876, dport=433) / + // b'\xde\xad\xbe\xef') + // Note that plen(52) = ipv6hdr(40) + udphdr(8) + data(4). + // 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, + // IP header + (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca, + (byte) 0x00, (byte) 0x34, (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) 0x00, (byte) 0x0c, (byte) 0xde, (byte) 0x7e, + // Data + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef + }; + + private static final byte[] TEST_PACKET_IPV6HDR_UDPHDR = + new byte[] { + // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=48, hlim=0x40) / + // scapy.UDP(sport=9876, dport=433)) + // Note that plen(48) = ipv6hdr(40) + udphdr(8). + // IP header + (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca, + (byte) 0x00, (byte) 0x30, (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) 0x00, (byte) 0x08, (byte) 0x7c, (byte) 0x24 + }; + + private static final byte[] TEST_PACKET_IPV6HDR_UDPHDR_DATA = + new byte[] { + // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=52, hlim=0x40) / + // scapy.UDP(sport=9876, dport=433) / + // b'\xde\xad\xbe\xef') + // Note that plen(52) = ipv6hdr(40) + udphdr(8) + data(4). + // IP header + (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca, + (byte) 0x00, (byte) 0x34, (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) 0x00, (byte) 0x0c, (byte) 0xde, (byte) 0x7e, + // Data + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef + }; + /** - * Build an IPv4 packet which has ether header, IPv4 header, TCP/UDP header and data. + * 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. * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Layer 2 header (EthernetHeader) | (optional) * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Layer 3 header (Ipv4Header) | + * | Layer 3 header (Ipv4Header, Ipv6Header) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Layer 4 header (TcpHeader, UdpHeader) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -273,33 +391,55 @@ public class PacketBuilderTest { * * @param srcMac source MAC address. used by L2 ether header. * @param dstMac destination MAC address. used by L2 ether header. - * @param l4proto the layer 4 protocol. support either IPPROTO_TCP or IPPROTO_UDP. + * @param l3proto the layer 3 protocol. Only {@code IPPROTO_IP} and {@code IPPROTO_IPV6} + * currently supported. + * @param l4proto the layer 4 protocol. Only {@code IPPROTO_TCP} and {@code IPPROTO_UDP} + * currently supported. * @param payload the payload. */ @NonNull - private ByteBuffer buildIpv4Packet(@Nullable final MacAddress srcMac, - @Nullable final MacAddress dstMac, final int l4proto, + private ByteBuffer buildPacket(@Nullable final MacAddress srcMac, + @Nullable final MacAddress dstMac, final int l3proto, final int l4proto, @Nullable final ByteBuffer payload) throws Exception { + if (l3proto != IPPROTO_IP && l3proto != IPPROTO_IPV6) { + fail("Unsupported layer 3 protocol " + l3proto); + } + if (l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP) { fail("Unsupported layer 4 protocol " + l4proto); } final boolean hasEther = (srcMac != null && dstMac != null); final int payloadLen = (payload == null) ? 0 : payload.limit(); - final ByteBuffer buffer = PacketBuilder.allocate(hasEther, IPPROTO_IP, l4proto, + final ByteBuffer buffer = PacketBuilder.allocate(hasEther, l3proto, l4proto, payloadLen); final PacketBuilder packetBuilder = new PacketBuilder(buffer); - if (hasEther) packetBuilder.writeL2Header(srcMac, dstMac, (short) ETHER_TYPE_IPV4); - packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET, - TIME_TO_LIVE, (byte) l4proto, IPV4_SRC_ADDR, IPV4_DST_ADDR); + // [1] Build ether header. + if (hasEther) { + final int etherType = (l3proto == IPPROTO_IP) ? ETHER_TYPE_IPV4 : ETHER_TYPE_IPV6; + packetBuilder.writeL2Header(srcMac, dstMac, (short) etherType); + } + + // [2] Build IP header. + if (l3proto == IPPROTO_IP) { + packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET, + TIME_TO_LIVE, (byte) l4proto, IPV4_SRC_ADDR, IPV4_DST_ADDR); + } else if (l3proto == IPPROTO_IPV6) { + packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, + (byte) l4proto, HOP_LIMIT, IPV6_SRC_ADDR, IPV6_DST_ADDR); + } + + // [3] Build TCP or UDP header. if (l4proto == IPPROTO_TCP) { packetBuilder.writeTcpHeader(SRC_PORT, DST_PORT, SEQ_NO, ACK_NO, TCPHDR_ACK, WINDOW, URGENT_POINTER); } else if (l4proto == IPPROTO_UDP) { packetBuilder.writeUdpHeader(SRC_PORT, DST_PORT); } + + // [4] Build payload. if (payload != null) { buffer.put(payload); // in case data might be reused by caller, restore the position and @@ -313,19 +453,27 @@ public class PacketBuilderTest { /** * Check ethernet header. * + * @param l3proto the layer 3 protocol. Only {@code IPPROTO_IP} and {@code IPPROTO_IPV6} + * currently supported. * @param actual the packet to check. */ - private void checkEtherHeader(final ByteBuffer actual) { + private void checkEtherHeader(final int l3proto, final ByteBuffer actual) { + if (l3proto != IPPROTO_IP && l3proto != IPPROTO_IPV6) { + fail("Unsupported layer 3 protocol " + l3proto); + } + final EthernetHeader eth = Struct.parse(EthernetHeader.class, actual); assertEquals(SRC_MAC, eth.srcMac); assertEquals(DST_MAC, eth.dstMac); - assertEquals(ETHER_TYPE_IPV4, eth.etherType); + final int expectedEtherType = (l3proto == IPPROTO_IP) ? ETHER_TYPE_IPV4 : ETHER_TYPE_IPV6; + assertEquals(expectedEtherType, eth.etherType); } /** * Check IPv4 header. * - * @param l4proto the layer 4 protocol. support either IPPROTO_TCP or IPPROTO_UDP. + * @param l4proto the layer 4 protocol. Only {@code IPPROTO_TCP} and {@code IPPROTO_UDP} + * currently supported. * @param hasData true if the packet has data payload; false otherwise. * @param actual the packet to check. */ @@ -359,16 +507,57 @@ public class PacketBuilderTest { } /** - * Check TCPv4 packet. + * Check IPv6 header. * - * @param hasEther true if the packet has ether header; false otherwise. + * @param l4proto the layer 4 protocol. Only {@code IPPROTO_TCP} and {@code IPPROTO_UDP} + * currently supported. * @param hasData true if the packet has data payload; false otherwise. * @param actual the packet to check. */ - private void checkTcpv4Packet(final boolean hasEther, final boolean hasData, + private void checkIpv6Header(final int l4proto, final boolean hasData, final ByteBuffer actual) { + if (l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP) { + fail("Unsupported layer 4 protocol " + l4proto); + } + + final Ipv6Header ipv6Header = Struct.parse(Ipv6Header.class, actual); + + assertEquals(VERSION_TRAFFICCLASS_FLOWLABEL, ipv6Header.vtf); + assertEquals(HOP_LIMIT, ipv6Header.hopLimit); + assertEquals(IPV6_SRC_ADDR, ipv6Header.srcIp); + assertEquals(IPV6_DST_ADDR, ipv6Header.dstIp); + + final int dataLength = hasData ? DATA.limit() : 0; + if (l4proto == IPPROTO_TCP) { + assertEquals(IPV6_HEADER_LEN + TCP_HEADER_MIN_LEN + dataLength, + ipv6Header.payloadLength); + assertEquals((byte) IPPROTO_TCP, ipv6Header.nextHeader); + } else if (l4proto == IPPROTO_UDP) { + assertEquals(IPV6_HEADER_LEN + UDP_HEADER_LEN + dataLength, + ipv6Header.payloadLength); + assertEquals((byte) IPPROTO_UDP, ipv6Header.nextHeader); + } + } + + /** + * Check TCPv4 packet. + * + * @param hasEther true if the packet has ether header; false otherwise. + * @param l3proto the layer 3 protocol. Only {@code IPPROTO_IP} and {@code IPPROTO_IPV6} + * currently supported. + * @param hasData true if the packet has data payload; false otherwise. + * @param actual the packet to check. + * + * TODO: support IPv6 + */ + private void checkTcpv4Packet(final boolean hasEther, final int l3proto, final boolean hasData, + final ByteBuffer actual) { + if (l3proto != IPPROTO_IP && l3proto != IPPROTO_IPV6) { + fail("Unsupported layer 3 protocol " + l3proto); + } + if (hasEther) { - checkEtherHeader(actual); + checkEtherHeader(l3proto, actual); } checkIpv4Header(IPPROTO_TCP, hasData, actual); @@ -389,26 +578,45 @@ public class PacketBuilderTest { } /** - * Check UDPv4 packet. + * Check UDP packet. * * @param hasEther true if the packet has ether header; false otherwise. + * @param l3proto the layer 3 protocol. Only {@code IPPROTO_IP} and {@code IPPROTO_IPV6} + * currently supported. * @param hasData true if the packet has data payload; false otherwise. * @param actual the packet to check. */ - private void checkUdpv4Packet(final boolean hasEther, final boolean hasData, + private void checkUdpPacket(final boolean hasEther, final int l3proto, final boolean hasData, final ByteBuffer actual) { - if (hasEther) { - checkEtherHeader(actual); + if (l3proto != IPPROTO_IP && l3proto != IPPROTO_IPV6) { + fail("Unsupported layer 3 protocol " + l3proto); } - checkIpv4Header(IPPROTO_UDP, hasData, actual); + // [1] Check ether header. + if (hasEther) { + checkEtherHeader(l3proto, actual); + } + + // [2] Check IP header. + if (l3proto == IPPROTO_IP) { + checkIpv4Header(IPPROTO_UDP, hasData, actual); + } else if (l3proto == IPPROTO_IPV6) { + checkIpv6Header(IPPROTO_UDP, hasData, actual); + } + + // [3] Check UDP header. final UdpHeader udpHeader = Struct.parse(UdpHeader.class, actual); assertEquals(SRC_PORT, udpHeader.srcPort); assertEquals(DST_PORT, udpHeader.dstPort); final int dataLength = hasData ? DATA.limit() : 0; assertEquals(UDP_HEADER_LEN + dataLength, udpHeader.length); - assertEquals(hasData ? (short) 0x4dbd : (short) 0xeb62, udpHeader.checksum); + if (l3proto == IPPROTO_IP) { + assertEquals(hasData ? (short) 0x4dbd : (short) 0xeb62, udpHeader.checksum); + } else if (l3proto == IPPROTO_IPV6) { + assertEquals(hasData ? (short) 0xde7e : (short) 0x7c24, udpHeader.checksum); + } + // [4] Check payload. if (hasData) { assertEquals(0xdeadbeef, actual.getInt()); } @@ -416,65 +624,99 @@ public class PacketBuilderTest { @Test public void testBuildPacketEtherIPv4Tcp() throws Exception { - final ByteBuffer packet = buildIpv4Packet(SRC_MAC, DST_MAC, IPPROTO_TCP, null /* data */); - checkTcpv4Packet(true /* hasEther */, false /* hasData */, packet); + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IP, IPPROTO_TCP, + null /* data */); + checkTcpv4Packet(true /* hasEther */, IPPROTO_IP, false /* hasData */, packet); assertArrayEquals(TEST_PACKET_ETHERHDR_IPV4HDR_TCPHDR, packet.array()); } @Test public void testBuildPacketEtherIPv4TcpData() throws Exception { - final ByteBuffer packet = buildIpv4Packet(SRC_MAC, DST_MAC, IPPROTO_TCP, DATA); - checkTcpv4Packet(true /* hasEther */, true /* hasData */, packet); + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IP, IPPROTO_TCP, DATA); + checkTcpv4Packet(true /* hasEther */, IPPROTO_IP, true /* hasData */, packet); assertArrayEquals(TEST_PACKET_ETHERHDR_IPV4HDR_TCPHDR_DATA, packet.array()); } @Test public void testBuildPacketIPv4Tcp() throws Exception { - final ByteBuffer packet = buildIpv4Packet(null /* srcMac */, null /* dstMac */, - IPPROTO_TCP, null /* data */); - checkTcpv4Packet(false /* hasEther */, false /* hasData */, packet); + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, + IPPROTO_IP, IPPROTO_TCP, null /* data */); + checkTcpv4Packet(false /* hasEther */, IPPROTO_IP, false /* hasData */, packet); assertArrayEquals(TEST_PACKET_IPV4HDR_TCPHDR, packet.array()); } @Test public void testBuildPacketIPv4TcpData() throws Exception { - final ByteBuffer packet = buildIpv4Packet(null /* srcMac */, null /* dstMac */, - IPPROTO_TCP, DATA); - checkTcpv4Packet(false /* hasEther */, true /* hasData */, packet); + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, + IPPROTO_IP, IPPROTO_TCP, DATA); + checkTcpv4Packet(false /* hasEther */, IPPROTO_IP, true /* hasData */, packet); assertArrayEquals(TEST_PACKET_IPV4HDR_TCPHDR_DATA, packet.array()); } @Test public void testBuildPacketEtherIPv4Udp() throws Exception { - final ByteBuffer packet = buildIpv4Packet(SRC_MAC, DST_MAC, IPPROTO_UDP, null /* data */); - checkUdpv4Packet(true /* hasEther */, false /* hasData */, packet); + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IP, IPPROTO_UDP, + null /* data */); + checkUdpPacket(true /* hasEther */, IPPROTO_IP, false /* hasData */, packet); assertArrayEquals(TEST_PACKET_ETHERHDR_IPV4HDR_UDPHDR, packet.array()); } @Test public void testBuildPacketEtherIPv4UdpData() throws Exception { - final ByteBuffer packet = buildIpv4Packet(SRC_MAC, DST_MAC, IPPROTO_UDP, DATA); - checkUdpv4Packet(true /* hasEther */, true /* hasData */, packet); + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IP, IPPROTO_UDP, DATA); + checkUdpPacket(true /* hasEther */, IPPROTO_IP, true /* hasData */, packet); assertArrayEquals(TEST_PACKET_ETHERHDR_IPV4HDR_UDPHDR_DATA, packet.array()); } @Test public void testBuildPacketIPv4Udp() throws Exception { - final ByteBuffer packet = buildIpv4Packet(null /* srcMac */, null /* dstMac */, - IPPROTO_UDP, null /*data*/); - checkUdpv4Packet(false /* hasEther */, false /* hasData */, packet); + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, + IPPROTO_IP, IPPROTO_UDP, null /*data*/); + checkUdpPacket(false /* hasEther */, IPPROTO_IP, false /* hasData */, packet); assertArrayEquals(TEST_PACKET_IPV4HDR_UDPHDR, packet.array()); } @Test public void testBuildPacketIPv4UdpData() throws Exception { - final ByteBuffer packet = buildIpv4Packet(null /* srcMac */, null /* dstMac */, - IPPROTO_UDP, DATA); - checkUdpv4Packet(false /* hasEther */, true /* hasData */, packet); + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, + IPPROTO_IP, IPPROTO_UDP, DATA); + checkUdpPacket(false /* hasEther */, IPPROTO_IP, true /* hasData */, packet); assertArrayEquals(TEST_PACKET_IPV4HDR_UDPHDR_DATA, packet.array()); } + @Test + public void testBuildPacketEtherIPv6Udp() throws Exception { + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IPV6, IPPROTO_UDP, + null /* data */); + checkUdpPacket(true /* hasEther */, IPPROTO_IPV6, false /* hasData */, packet); + assertArrayEquals(TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR, packet.array()); + } + + @Test + public void testBuildPacketEtherIPv6UdpData() throws Exception { + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IPV6, IPPROTO_UDP, + DATA); + checkUdpPacket(true /* hasEther */, IPPROTO_IPV6, true /* hasData */, packet); + assertArrayEquals(TEST_PACKET_ETHERHDR_IPV6HDR_UDPHDR_DATA, packet.array()); + } + + @Test + public void testBuildPacketIPv6Udp() throws Exception { + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, + IPPROTO_IPV6, IPPROTO_UDP, null /*data*/); + checkUdpPacket(false /* hasEther */, IPPROTO_IPV6, false /* hasData */, packet); + assertArrayEquals(TEST_PACKET_IPV6HDR_UDPHDR, packet.array()); + } + + @Test + public void testBuildPacketIPv6UdpData() throws Exception { + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, + IPPROTO_IPV6, IPPROTO_UDP, DATA); + checkUdpPacket(false /* hasEther */, IPPROTO_IPV6, true /* hasData */, packet); + assertArrayEquals(TEST_PACKET_IPV6HDR_UDPHDR_DATA, packet.array()); + } + @Test public void testFinalizePacketWithoutIpv4Header() throws Exception { final ByteBuffer buffer = PacketBuilder.allocate(false /* hasEther */, IPPROTO_IP, @@ -526,7 +768,11 @@ public class PacketBuilderTest { assertThrows(IOException.class, () -> packetBuilder.writeUdpHeader(SRC_PORT, DST_PORT)); } - private static Inet4Address addr(String addr) { + private static Inet4Address addr4(String addr) { return (Inet4Address) InetAddresses.parseNumericAddress(addr); } + + private static Inet6Address addr6(String addr) { + return (Inet6Address) InetAddresses.parseNumericAddress(addr); + } } From 3288afaede471324eb79cb87831eb21f8bd9e174 Mon Sep 17 00:00:00 2001 From: Hungming Chen Date: Tue, 17 May 2022 22:31:06 +0800 Subject: [PATCH 2/2] PacketBuilderTest: add IPv6 TCP test Bug: 215655463 Test: atest NetworkStaticLibTests Change-Id: I62771a8f2549c7785919504f4816a28f30551953 --- .../net/module/util/PacketBuilderTest.java | 188 +++++++++++++++++- 1 file changed, 178 insertions(+), 10 deletions(-) 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 c02763a07d..1a0752a022 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 @@ -327,6 +327,130 @@ public class PacketBuilderTest { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef }; + private static final byte[] TEST_PACKET_ETHERHDR_IPV6HDR_TCPHDR_DATA = + new byte[] { + // packet = (scapy.Ether(src="11:22:33:44:55:66", dst="aa:bb:cc:dd:ee:ff", + // type='IPv6') / + // scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=64, hlim=0x40) / + // scapy.TCP(sport=9876, dport=433, seq=13579, ack=24680, + // flags='A', window=8192, urgptr=0) / + // b'\xde\xad\xbe\xef') + // Note that plen(64) = ipv6hdr(40) + udphdr(20) + data(4). + // 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) 0x40, (byte) 0x06, (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, + // TCP header + (byte) 0x26, (byte) 0x94, (byte) 0x01, (byte) 0xb1, + (byte) 0x00, (byte) 0x00, (byte) 0x35, (byte) 0x0b, + (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x68, + (byte) 0x50, (byte) 0x10, (byte) 0x20, (byte) 0x00, + (byte) 0xd9, (byte) 0x05, (byte) 0x00, (byte) 0x00, + // Data + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef + }; + + private static final byte[] TEST_PACKET_ETHERHDR_IPV6HDR_TCPHDR = + new byte[] { + // packet = (scapy.Ether(src="11:22:33:44:55:66", dst="aa:bb:cc:dd:ee:ff", + // type='IPv6') / + // scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=60, hlim=0x40) / + // scapy.TCP(sport=9876, dport=433, seq=13579, ack=24680, + // flags='A', window=8192, urgptr=0)) + // Note that plen(60) = ipv6hdr(40) + udphdr(20). + // 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) 0x3c, (byte) 0x06, (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, + // TCP header + (byte) 0x26, (byte) 0x94, (byte) 0x01, (byte) 0xb1, + (byte) 0x00, (byte) 0x00, (byte) 0x35, (byte) 0x0b, + (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x68, + (byte) 0x50, (byte) 0x10, (byte) 0x20, (byte) 0x00, + (byte) 0x76, (byte) 0xa7, (byte) 0x00, (byte) 0x00 + }; + + private static final byte[] TEST_PACKET_IPV6HDR_TCPHDR_DATA = + new byte[] { + // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=64, hlim=0x40) / + // scapy.TCP(sport=9876, dport=433, seq=13579, ack=24680, + // flags='A', window=8192, urgptr=0) / + // b'\xde\xad\xbe\xef') + // Note that plen(64) = ipv6hdr(40) + udphdr(20) + data(4). + // IPv6 header + (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca, + (byte) 0x00, (byte) 0x40, (byte) 0x06, (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, + // TCP header + (byte) 0x26, (byte) 0x94, (byte) 0x01, (byte) 0xb1, + (byte) 0x00, (byte) 0x00, (byte) 0x35, (byte) 0x0b, + (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x68, + (byte) 0x50, (byte) 0x10, (byte) 0x20, (byte) 0x00, + (byte) 0xd9, (byte) 0x05, (byte) 0x00, (byte) 0x00, + // Data + (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef + }; + + private static final byte[] TEST_PACKET_IPV6HDR_TCPHDR = + new byte[] { + // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, + // fl=0x515ca, plen=60, hlim=0x40) / + // scapy.TCP(sport=9876, dport=433, seq=13579, ack=24680, + // flags='A', window=8192, urgptr=0)) + // Note that plen(60) = ipv6hdr(40) + udphdr(20). + // IPv6 header + (byte) 0x68, (byte) 0x05, (byte) 0x15, (byte) 0xca, + (byte) 0x00, (byte) 0x3c, (byte) 0x06, (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, + // TCP header + (byte) 0x26, (byte) 0x94, (byte) 0x01, (byte) 0xb1, + (byte) 0x00, (byte) 0x00, (byte) 0x35, (byte) 0x0b, + (byte) 0x00, (byte) 0x00, (byte) 0x60, (byte) 0x68, + (byte) 0x50, (byte) 0x10, (byte) 0x20, (byte) 0x00, + (byte) 0x76, (byte) 0xa7, (byte) 0x00, (byte) 0x00 + }; + private static final byte[] TEST_PACKET_IPV6HDR_UDPHDR = new byte[] { // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80, @@ -540,27 +664,33 @@ public class PacketBuilderTest { } /** - * Check TCPv4 packet. + * Check TCP packet. * * @param hasEther true if the packet has ether header; false otherwise. * @param l3proto the layer 3 protocol. Only {@code IPPROTO_IP} and {@code IPPROTO_IPV6} * currently supported. * @param hasData true if the packet has data payload; false otherwise. * @param actual the packet to check. - * - * TODO: support IPv6 */ - private void checkTcpv4Packet(final boolean hasEther, final int l3proto, final boolean hasData, + private void checkTcpPacket(final boolean hasEther, final int l3proto, final boolean hasData, final ByteBuffer actual) { if (l3proto != IPPROTO_IP && l3proto != IPPROTO_IPV6) { fail("Unsupported layer 3 protocol " + l3proto); } + // [1] Check ether header. if (hasEther) { checkEtherHeader(l3proto, actual); } - checkIpv4Header(IPPROTO_TCP, hasData, actual); + // [2] Check IP header. + if (l3proto == IPPROTO_IP) { + checkIpv4Header(IPPROTO_TCP, hasData, actual); + } else if (l3proto == IPPROTO_IPV6) { + checkIpv6Header(IPPROTO_TCP, hasData, actual); + } + + // [3] Check TCP header. final TcpHeader tcpHeader = Struct.parse(TcpHeader.class, actual); assertEquals(SRC_PORT, tcpHeader.srcPort); assertEquals(DST_PORT, tcpHeader.dstPort); @@ -569,9 +699,14 @@ public class PacketBuilderTest { assertEquals((short) 0x5010 /* offset=5(*4bytes), control bits=ACK */, tcpHeader.dataOffsetAndControlBits); assertEquals(WINDOW, tcpHeader.window); - assertEquals(hasData ? (short) 0x4844 : (short) 0xe5e5, tcpHeader.checksum); assertEquals(URGENT_POINTER, tcpHeader.urgentPointer); + if (l3proto == IPPROTO_IP) { + assertEquals(hasData ? (short) 0x4844 : (short) 0xe5e5, tcpHeader.checksum); + } else if (l3proto == IPPROTO_IPV6) { + assertEquals(hasData ? (short) 0xd905 : (short) 0x76a7, tcpHeader.checksum); + } + // [4] Check payload. if (hasData) { assertEquals(0xdeadbeef, actual.getInt()); } @@ -626,14 +761,14 @@ public class PacketBuilderTest { public void testBuildPacketEtherIPv4Tcp() throws Exception { final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IP, IPPROTO_TCP, null /* data */); - checkTcpv4Packet(true /* hasEther */, IPPROTO_IP, false /* hasData */, packet); + checkTcpPacket(true /* hasEther */, IPPROTO_IP, false /* hasData */, packet); assertArrayEquals(TEST_PACKET_ETHERHDR_IPV4HDR_TCPHDR, packet.array()); } @Test public void testBuildPacketEtherIPv4TcpData() throws Exception { final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IP, IPPROTO_TCP, DATA); - checkTcpv4Packet(true /* hasEther */, IPPROTO_IP, true /* hasData */, packet); + checkTcpPacket(true /* hasEther */, IPPROTO_IP, true /* hasData */, packet); assertArrayEquals(TEST_PACKET_ETHERHDR_IPV4HDR_TCPHDR_DATA, packet.array()); } @@ -642,7 +777,7 @@ public class PacketBuilderTest { public void testBuildPacketIPv4Tcp() throws Exception { final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, IPPROTO_IP, IPPROTO_TCP, null /* data */); - checkTcpv4Packet(false /* hasEther */, IPPROTO_IP, false /* hasData */, packet); + checkTcpPacket(false /* hasEther */, IPPROTO_IP, false /* hasData */, packet); assertArrayEquals(TEST_PACKET_IPV4HDR_TCPHDR, packet.array()); } @@ -650,7 +785,7 @@ public class PacketBuilderTest { public void testBuildPacketIPv4TcpData() throws Exception { final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, IPPROTO_IP, IPPROTO_TCP, DATA); - checkTcpv4Packet(false /* hasEther */, IPPROTO_IP, true /* hasData */, packet); + checkTcpPacket(false /* hasEther */, IPPROTO_IP, true /* hasData */, packet); assertArrayEquals(TEST_PACKET_IPV4HDR_TCPHDR_DATA, packet.array()); } @@ -685,6 +820,39 @@ public class PacketBuilderTest { assertArrayEquals(TEST_PACKET_IPV4HDR_UDPHDR_DATA, packet.array()); } + @Test + public void testBuildPacketEtherIPv6TcpData() throws Exception { + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IPV6, IPPROTO_TCP, DATA); + checkTcpPacket(true /* hasEther */, IPPROTO_IPV6, true /* hasData */, packet); + assertArrayEquals(TEST_PACKET_ETHERHDR_IPV6HDR_TCPHDR_DATA, + packet.array()); + } + + @Test + public void testBuildPacketEtherIPv6Tcp() throws Exception { + final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IPV6, IPPROTO_TCP, + null /*data*/); + checkTcpPacket(true /* hasEther */, IPPROTO_IPV6, false /* hasData */, packet); + assertArrayEquals(TEST_PACKET_ETHERHDR_IPV6HDR_TCPHDR, + packet.array()); + } + + @Test + public void testBuildPacketIPv6TcpData() throws Exception { + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, IPPROTO_IPV6, + IPPROTO_TCP, DATA); + checkTcpPacket(false /* hasEther */, IPPROTO_IPV6, true /* hasData */, packet); + assertArrayEquals(TEST_PACKET_IPV6HDR_TCPHDR_DATA, packet.array()); + } + + @Test + public void testBuildPacketIPv6Tcp() throws Exception { + final ByteBuffer packet = buildPacket(null /* srcMac */, null /* dstMac */, IPPROTO_IPV6, + IPPROTO_TCP, null /*data*/); + checkTcpPacket(false /* hasEther */, IPPROTO_IPV6, false /* hasData */, packet); + assertArrayEquals(TEST_PACKET_IPV6HDR_TCPHDR, packet.array()); + } + @Test public void testBuildPacketEtherIPv6Udp() throws Exception { final ByteBuffer packet = buildPacket(SRC_MAC, DST_MAC, IPPROTO_IPV6, IPPROTO_UDP,