Merge changes I62771a8f,I5edf0ffb

* changes:
  PacketBuilderTest: add IPv6 TCP test
  PacketBuilder: add IPv6 support
This commit is contained in:
Treehugger Robot
2022-05-27 09:59:17 +00:00
committed by Gerrit Code Review
2 changed files with 528 additions and 71 deletions

View File

@@ -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,10 +218,10 @@ 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,
@@ -198,20 +230,30 @@ public class PacketBuilder {
// Populate the IPv4 header checksum field.
mBuffer.putShort(mIpv4HeaderOffset + IPV4_CHECKSUM_OFFSET,
ipChecksum(mBuffer, mIpv4HeaderOffset /* headerOffset */));
} else if (mIpv6HeaderOffset != INVALID_OFFSET) {
ipHeaderOffset = mIpv6HeaderOffset;
if (mTcpHeaderOffset > 0) {
// 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");
}
// 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;

View File

@@ -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,247 @@ 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_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,
// 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 +515,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);
// [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 +577,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,19 +631,66 @@ 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 (hasEther) {
checkEtherHeader(actual);
if (l4proto != IPPROTO_TCP && l4proto != IPPROTO_UDP) {
fail("Unsupported layer 4 protocol " + l4proto);
}
checkIpv4Header(IPPROTO_TCP, hasData, actual);
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 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.
*/
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);
}
// [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);
@@ -380,35 +699,59 @@ 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());
}
}
/**
* 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);
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 +759,132 @@ 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 */);
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 = 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);
checkTcpPacket(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 */);
checkTcpPacket(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);
checkTcpPacket(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 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,
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 +936,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);
}
}