Merge "Add helper method to generate a v6 NAT-T keepalive packet" am: 977fa60f5d am: 08dd67bf4c
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2616796 Change-Id: I3d212039cdfaf2420fbbab6e1f3ae2099141cd9a Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
committed by
Automerger Merge Worker
commit
67cca8a92f
@@ -29,7 +29,9 @@ import android.system.OsConstants;
|
|||||||
import com.android.net.module.util.IpUtils;
|
import com.android.net.module.util.IpUtils;
|
||||||
|
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -38,6 +40,7 @@ import java.util.Objects;
|
|||||||
@SystemApi
|
@SystemApi
|
||||||
public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
|
public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
|
||||||
private static final int IPV4_HEADER_LENGTH = 20;
|
private static final int IPV4_HEADER_LENGTH = 20;
|
||||||
|
private static final int IPV6_HEADER_LENGTH = 40;
|
||||||
private static final int UDP_HEADER_LENGTH = 8;
|
private static final int UDP_HEADER_LENGTH = 8;
|
||||||
|
|
||||||
// This should only be constructed via static factory methods, such as
|
// This should only be constructed via static factory methods, such as
|
||||||
@@ -59,13 +62,25 @@ public final class NattKeepalivePacketData extends KeepalivePacketData implement
|
|||||||
throw new InvalidPacketException(ERROR_INVALID_PORT);
|
throw new InvalidPacketException(ERROR_INVALID_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
|
// Convert IPv4 mapped v6 address to v4 if any.
|
||||||
|
final InetAddress srcAddr, dstAddr;
|
||||||
|
try {
|
||||||
|
srcAddr = InetAddress.getByAddress(srcAddress.getAddress());
|
||||||
|
dstAddr = InetAddress.getByAddress(dstAddress.getAddress());
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
|
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nattKeepalivePacketv4(
|
if (srcAddr instanceof Inet4Address && dstAddr instanceof Inet4Address) {
|
||||||
(Inet4Address) srcAddress, srcPort,
|
return nattKeepalivePacketv4(
|
||||||
(Inet4Address) dstAddress, dstPort);
|
(Inet4Address) srcAddr, srcPort, (Inet4Address) dstAddr, dstPort);
|
||||||
|
} else if (srcAddr instanceof Inet6Address && dstAddress instanceof Inet6Address) {
|
||||||
|
return nattKeepalivePacketv6(
|
||||||
|
(Inet6Address) srcAddr, srcPort, (Inet6Address) dstAddr, dstPort);
|
||||||
|
} else {
|
||||||
|
// Destination address and source address should be the same IP family.
|
||||||
|
throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static NattKeepalivePacketData nattKeepalivePacketv4(
|
private static NattKeepalivePacketData nattKeepalivePacketv4(
|
||||||
@@ -82,14 +97,14 @@ public final class NattKeepalivePacketData extends KeepalivePacketData implement
|
|||||||
// /proc/sys/net/ipv4/ip_default_ttl. Use hard-coded 64 for simplicity.
|
// /proc/sys/net/ipv4/ip_default_ttl. Use hard-coded 64 for simplicity.
|
||||||
buf.put((byte) 64); // TTL
|
buf.put((byte) 64); // TTL
|
||||||
buf.put((byte) OsConstants.IPPROTO_UDP);
|
buf.put((byte) OsConstants.IPPROTO_UDP);
|
||||||
int ipChecksumOffset = buf.position();
|
final int ipChecksumOffset = buf.position();
|
||||||
buf.putShort((short) 0); // IP checksum
|
buf.putShort((short) 0); // IP checksum
|
||||||
buf.put(srcAddress.getAddress());
|
buf.put(srcAddress.getAddress());
|
||||||
buf.put(dstAddress.getAddress());
|
buf.put(dstAddress.getAddress());
|
||||||
buf.putShort((short) srcPort);
|
buf.putShort((short) srcPort);
|
||||||
buf.putShort((short) dstPort);
|
buf.putShort((short) dstPort);
|
||||||
buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length
|
buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length
|
||||||
int udpChecksumOffset = buf.position();
|
final int udpChecksumOffset = buf.position();
|
||||||
buf.putShort((short) 0); // UDP checksum
|
buf.putShort((short) 0); // UDP checksum
|
||||||
buf.put((byte) 0xff); // NAT-T keepalive
|
buf.put((byte) 0xff); // NAT-T keepalive
|
||||||
buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
|
buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
|
||||||
@@ -98,6 +113,30 @@ public final class NattKeepalivePacketData extends KeepalivePacketData implement
|
|||||||
return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
|
return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static NattKeepalivePacketData nattKeepalivePacketv6(
|
||||||
|
Inet6Address srcAddress, int srcPort, Inet6Address dstAddress, int dstPort)
|
||||||
|
throws InvalidPacketException {
|
||||||
|
final ByteBuffer buf = ByteBuffer.allocate(IPV6_HEADER_LENGTH + UDP_HEADER_LENGTH + 1);
|
||||||
|
buf.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
buf.putInt(0x60000000); // IP version, traffic class and flow label
|
||||||
|
buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // Payload length
|
||||||
|
buf.put((byte) OsConstants.IPPROTO_UDP); // Next header
|
||||||
|
// For native ipv6, this hop limit value should use the per interface v6 hoplimit sysctl.
|
||||||
|
// For 464xlat, this value should use the v4 ttl sysctl.
|
||||||
|
// Either way, for simplicity, just hard code 64.
|
||||||
|
buf.put((byte) 64); // Hop limit
|
||||||
|
buf.put(srcAddress.getAddress());
|
||||||
|
buf.put(dstAddress.getAddress());
|
||||||
|
// UDP
|
||||||
|
buf.putShort((short) srcPort);
|
||||||
|
buf.putShort((short) dstPort);
|
||||||
|
buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length = Payload length
|
||||||
|
final int udpChecksumOffset = buf.position();
|
||||||
|
buf.putShort((short) 0); // UDP checksum
|
||||||
|
buf.put((byte) 0xff); // NAT-T keepalive. 1 byte of data
|
||||||
|
buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV6_HEADER_LENGTH));
|
||||||
|
return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
|
||||||
|
}
|
||||||
/** Parcelable Implementation */
|
/** Parcelable Implementation */
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class NattKeepalivePacketDataTest {
|
|||||||
private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
|
private val TEST_SRC_ADDRV4 = "198.168.0.2".address()
|
||||||
private val TEST_DST_ADDRV4 = "198.168.0.1".address()
|
private val TEST_DST_ADDRV4 = "198.168.0.1".address()
|
||||||
private val TEST_ADDRV6 = "2001:db8::1".address()
|
private val TEST_ADDRV6 = "2001:db8::1".address()
|
||||||
|
private val TEST_ADDRV4MAPPEDV6 = "::ffff:1.2.3.4".address()
|
||||||
|
private val TEST_ADDRV4 = "1.2.3.4".address()
|
||||||
|
|
||||||
private fun String.address() = InetAddresses.parseNumericAddress(this)
|
private fun String.address() = InetAddresses.parseNumericAddress(this)
|
||||||
private fun nattKeepalivePacket(
|
private fun nattKeepalivePacket(
|
||||||
@@ -83,6 +85,28 @@ class NattKeepalivePacketDataTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
|
||||||
|
fun testConstructor_afterR() {
|
||||||
|
// v4 mapped v6 will be translated to a v4 address.
|
||||||
|
assertFailsWith<InvalidPacketException> {
|
||||||
|
nattKeepalivePacket(srcAddress = TEST_ADDRV6, dstAddress = TEST_ADDRV4MAPPEDV6)
|
||||||
|
}
|
||||||
|
assertFailsWith<InvalidPacketException> {
|
||||||
|
nattKeepalivePacket(srcAddress = TEST_ADDRV4MAPPEDV6, dstAddress = TEST_ADDRV6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both src and dst address will be v4 after translation, so it won't cause exception.
|
||||||
|
val packet1 = nattKeepalivePacket(
|
||||||
|
dstAddress = TEST_ADDRV4MAPPEDV6, srcAddress = TEST_ADDRV4MAPPEDV6)
|
||||||
|
assertEquals(TEST_ADDRV4, packet1.srcAddress)
|
||||||
|
assertEquals(TEST_ADDRV4, packet1.dstAddress)
|
||||||
|
|
||||||
|
// Packet with v6 src and v6 dst address is valid.
|
||||||
|
val packet2 = nattKeepalivePacket(srcAddress = TEST_ADDRV6, dstAddress = TEST_ADDRV6)
|
||||||
|
assertEquals(TEST_ADDRV6, packet2.srcAddress)
|
||||||
|
assertEquals(TEST_ADDRV6, packet2.dstAddress)
|
||||||
|
}
|
||||||
|
|
||||||
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
|
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
|
||||||
fun testParcel() {
|
fun testParcel() {
|
||||||
assertParcelingIsLossless(nattKeepalivePacket())
|
assertParcelingIsLossless(nattKeepalivePacket())
|
||||||
|
|||||||
@@ -6856,10 +6856,6 @@ public class ConnectivityServiceTest {
|
|||||||
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv4);
|
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv4);
|
||||||
callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
||||||
|
|
||||||
// NAT-T is only supported for IPv4.
|
|
||||||
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv6, 1234, dstIPv6);
|
|
||||||
callback.expectError(PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
|
||||||
|
|
||||||
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4);
|
ka = mCm.startNattKeepalive(myNet, validKaInterval, callback, myIPv4, 123456, dstIPv4);
|
||||||
callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
|
callback.expectError(PacketKeepalive.ERROR_INVALID_PORT);
|
||||||
|
|
||||||
@@ -7010,13 +7006,6 @@ public class ConnectivityServiceTest {
|
|||||||
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NAT-T is only supported for IPv4.
|
|
||||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
|
||||||
myNet, testSocket, myIPv6, dstIPv6, executor, callback)) {
|
|
||||||
ka.start(validKaInterval);
|
|
||||||
callback.expectError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic check before testing started keepalive.
|
// Basic check before testing started keepalive.
|
||||||
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
try (SocketKeepalive ka = mCm.createSocketKeepalive(
|
||||||
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
|
myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user