Merge changes I471014c1,I5365c4da into main
* changes: Handle ICMP in testRequestIpSecTransformStateForTx Add awaitEspPacket that requires sequence number match
This commit is contained in:
commit
60cc6a145b
4 changed files with 94 additions and 30 deletions
|
|
@ -23,10 +23,13 @@ import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA256;
|
|||
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
|
||||
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
|
||||
import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
|
||||
import static android.net.cts.PacketUtils.ICMP_HDRLEN;
|
||||
import static android.net.cts.PacketUtils.IP6_HDRLEN;
|
||||
import static android.system.OsConstants.FIONREAD;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
|
|
@ -152,10 +155,17 @@ public class IpSecBaseTest {
|
|||
final IpSecTransformState transformState =
|
||||
futureIpSecTransform.get(SOCK_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
|
||||
assertEquals(txHighestSeqNum, transformState.getTxHighestSequenceNumber());
|
||||
// There might be ICMPv6(Router Solicitation) packets. Thus we can only check the lower
|
||||
// bound of the outgoing traffic.
|
||||
final long icmpV6RsCnt = transformState.getTxHighestSequenceNumber() - txHighestSeqNum;
|
||||
assertTrue(icmpV6RsCnt >= 0);
|
||||
|
||||
final long adjustedPacketCnt = packetCnt + icmpV6RsCnt;
|
||||
final long adjustedByteCnt = byteCnt + icmpV6RsCnt * (IP6_HDRLEN + ICMP_HDRLEN);
|
||||
|
||||
assertEquals(adjustedPacketCnt, transformState.getPacketCount());
|
||||
assertEquals(adjustedByteCnt, transformState.getByteCount());
|
||||
assertEquals(rxHighestSeqNum, transformState.getRxHighestSequenceNumber());
|
||||
assertEquals(packetCnt, transformState.getPacketCount());
|
||||
assertEquals(byteCnt, transformState.getByteCount());
|
||||
assertArrayEquals(replayBitmap, transformState.getReplayBitmap());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ import android.platform.test.annotations.AppModeFull;
|
|||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
import com.android.testutils.DevSdkIgnoreRule;
|
||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||
|
||||
|
|
@ -119,7 +120,7 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
|
||||
private static final int TIMEOUT_MS = 500;
|
||||
|
||||
private static final int PACKET_COUNT = 5000;
|
||||
private static final int PACKET_COUNT = 100;
|
||||
|
||||
// Static state to reduce setup/teardown
|
||||
private static ConnectivityManager sCM;
|
||||
|
|
@ -1088,6 +1089,27 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
UdpEncapsulationSocket encapSocket,
|
||||
IpSecTunnelTestRunnable test)
|
||||
throws Exception {
|
||||
return buildTunnelNetworkAndRunTests(
|
||||
localInner,
|
||||
remoteInner,
|
||||
localOuter,
|
||||
remoteOuter,
|
||||
spi,
|
||||
encapSocket,
|
||||
test,
|
||||
true /* enableEncrypt */);
|
||||
}
|
||||
|
||||
private int buildTunnelNetworkAndRunTests(
|
||||
InetAddress localInner,
|
||||
InetAddress remoteInner,
|
||||
InetAddress localOuter,
|
||||
InetAddress remoteOuter,
|
||||
int spi,
|
||||
UdpEncapsulationSocket encapSocket,
|
||||
IpSecTunnelTestRunnable test,
|
||||
boolean enableEncrypt)
|
||||
throws Exception {
|
||||
int innerPrefixLen = localInner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
|
||||
TestNetworkCallback testNetworkCb = null;
|
||||
int innerSocketPort;
|
||||
|
|
@ -1115,8 +1137,12 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
|
||||
// Configure Transform parameters
|
||||
IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
|
||||
transformBuilder.setEncryption(
|
||||
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
|
||||
|
||||
if (enableEncrypt) {
|
||||
transformBuilder.setEncryption(
|
||||
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
|
||||
}
|
||||
|
||||
transformBuilder.setAuthentication(
|
||||
new IpSecAlgorithm(
|
||||
IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
|
||||
|
|
@ -1167,8 +1193,8 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
return innerSocketPort;
|
||||
}
|
||||
|
||||
private int buildTunnelNetworkAndRunTestsSimple(int spi, IpSecTunnelTestRunnable test)
|
||||
throws Exception {
|
||||
private int buildTunnelNetworkAndRunTestsSimple(
|
||||
int spi, IpSecTunnelTestRunnable test, boolean enableEncrypt) throws Exception {
|
||||
return buildTunnelNetworkAndRunTests(
|
||||
LOCAL_INNER_6,
|
||||
REMOTE_INNER_6,
|
||||
|
|
@ -1176,7 +1202,8 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
REMOTE_OUTER_6,
|
||||
spi,
|
||||
null /* encapSocket */,
|
||||
test);
|
||||
test,
|
||||
enableEncrypt);
|
||||
}
|
||||
|
||||
private static void receiveAndValidatePacket(JavaUdpSocket socket) throws Exception {
|
||||
|
|
@ -1787,10 +1814,11 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
PACKET_COUNT,
|
||||
PACKET_COUNT,
|
||||
PACKET_COUNT * (long) innerPacketSize,
|
||||
newReplayBitmap(REPLAY_BITMAP_LEN_BYTE * 8));
|
||||
newReplayBitmap(PACKET_COUNT));
|
||||
|
||||
return innerSocketPort;
|
||||
});
|
||||
},
|
||||
true /* enableEncrypt */);
|
||||
}
|
||||
|
||||
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
|
|
@ -1814,17 +1842,22 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
ipsecNetwork.bindSocket(outSocket.mSocket);
|
||||
int innerSocketPort = outSocket.getPort();
|
||||
|
||||
int expectedPacketSize =
|
||||
getPacketSize(
|
||||
AF_INET6,
|
||||
AF_INET6,
|
||||
false /* useEncap */,
|
||||
false /* transportInTunnelMode */);
|
||||
int outSeqNum = 1;
|
||||
int receivedTestDataEspCnt = 0;
|
||||
|
||||
for (int i = 0; i < PACKET_COUNT; i++) {
|
||||
while (receivedTestDataEspCnt < PACKET_COUNT) {
|
||||
outSocket.sendTo(TEST_DATA, REMOTE_INNER_6, innerSocketPort);
|
||||
tunUtils.awaitEspPacketNoPlaintext(
|
||||
spi, TEST_DATA, false /* useEncap */, expectedPacketSize);
|
||||
|
||||
byte[] pkt = null;
|
||||
|
||||
// If it is an ESP that contains the TEST_DATA, move to the next
|
||||
// loop. Otherwise, the ESP may contain an ICMPv6(Router Solicitation).
|
||||
// In this case, just increase the expected sequence number and continue
|
||||
// waiting for the ESP with TEST_DATA
|
||||
do {
|
||||
pkt = tunUtils.awaitEspPacket(spi, false /* useEncap */, outSeqNum++);
|
||||
} while (CollectionUtils.indexOfSubArray(pkt, TEST_DATA) == -1);
|
||||
receivedTestDataEspCnt++;
|
||||
}
|
||||
|
||||
final int innerPacketSize =
|
||||
|
|
@ -1838,6 +1871,7 @@ public class IpSecManagerTunnelTest extends IpSecBaseTest {
|
|||
newReplayBitmap(0));
|
||||
|
||||
return innerSocketPort;
|
||||
});
|
||||
},
|
||||
false /* enableEncrypt */);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ public class PacketUtils {
|
|||
static final int TCP_HDRLEN = 20;
|
||||
static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
|
||||
static final int ESP_HDRLEN = 8;
|
||||
static final int ICMP_HDRLEN = 8;
|
||||
static final int ESP_BLK_SIZE = 4; // ESP has to be 4-byte aligned
|
||||
static final int ESP_TRAILER_LEN = 2;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ public class TunUtils {
|
|||
protected static final int IP4_PROTO_OFFSET = 9;
|
||||
protected static final int IP6_PROTO_OFFSET = 6;
|
||||
|
||||
private static final int SEQ_NUM_MATCH_NOT_REQUIRED = -1;
|
||||
|
||||
private static final int DATA_BUFFER_LEN = 4096;
|
||||
private static final int TIMEOUT = 2000;
|
||||
|
||||
|
|
@ -146,16 +148,30 @@ public class TunUtils {
|
|||
return espPkt; // We've found the packet we're looking for.
|
||||
}
|
||||
|
||||
/** Await the expected ESP packet */
|
||||
public byte[] awaitEspPacket(int spi, boolean useEncap) throws Exception {
|
||||
return awaitPacket((pkt) -> isEsp(pkt, spi, useEncap));
|
||||
return awaitEspPacket(spi, useEncap, SEQ_NUM_MATCH_NOT_REQUIRED);
|
||||
}
|
||||
|
||||
private static boolean isSpiEqual(byte[] pkt, int espOffset, int spi) {
|
||||
/** Await the expected ESP packet with a matching sequence number */
|
||||
public byte[] awaitEspPacket(int spi, boolean useEncap, int seqNum) throws Exception {
|
||||
return awaitPacket((pkt) -> isEsp(pkt, spi, seqNum, useEncap));
|
||||
}
|
||||
|
||||
private static boolean isMatchingEspPacket(byte[] pkt, int espOffset, int spi, int seqNum) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(pkt);
|
||||
buffer.get(new byte[espOffset]); // Skip IP, UDP header
|
||||
int actualSpi = buffer.getInt();
|
||||
int actualSeqNum = buffer.getInt();
|
||||
|
||||
return actualSpi == spi;
|
||||
if (actualSeqNum < 0) {
|
||||
throw new UnsupportedOperationException(
|
||||
"actualSeqNum overflowed and needs to be converted to an unsigned integer");
|
||||
}
|
||||
|
||||
boolean isSeqNumMatched = (seqNum == SEQ_NUM_MATCH_NOT_REQUIRED || seqNum == actualSeqNum);
|
||||
|
||||
return actualSpi == spi && isSeqNumMatched;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -173,29 +189,32 @@ public class TunUtils {
|
|||
fail("Banned plaintext packet found");
|
||||
}
|
||||
|
||||
return isEsp(pkt, spi, encap);
|
||||
return isEsp(pkt, spi, SEQ_NUM_MATCH_NOT_REQUIRED, encap);
|
||||
}
|
||||
|
||||
private static boolean isEsp(byte[] pkt, int spi, boolean encap) {
|
||||
private static boolean isEsp(byte[] pkt, int spi, int seqNum, boolean encap) {
|
||||
if (isIpv6(pkt)) {
|
||||
if (encap) {
|
||||
return pkt[IP6_PROTO_OFFSET] == IPPROTO_UDP
|
||||
&& isSpiEqual(pkt, IP6_HDRLEN + UDP_HDRLEN, spi);
|
||||
&& isMatchingEspPacket(pkt, IP6_HDRLEN + UDP_HDRLEN, spi, seqNum);
|
||||
} else {
|
||||
return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP6_HDRLEN, spi);
|
||||
return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP
|
||||
&& isMatchingEspPacket(pkt, IP6_HDRLEN, spi, seqNum);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Use default IPv4 header length (assuming no options)
|
||||
if (encap) {
|
||||
return pkt[IP4_PROTO_OFFSET] == IPPROTO_UDP
|
||||
&& isSpiEqual(pkt, IP4_HDRLEN + UDP_HDRLEN, spi);
|
||||
&& isMatchingEspPacket(pkt, IP4_HDRLEN + UDP_HDRLEN, spi, seqNum);
|
||||
} else {
|
||||
return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP4_HDRLEN, spi);
|
||||
return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP
|
||||
&& isMatchingEspPacket(pkt, IP4_HDRLEN, spi, seqNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isIpv6(byte[] pkt) {
|
||||
// First nibble shows IP version. 0x60 for IPv6
|
||||
return (pkt[0] & (byte) 0xF0) == (byte) 0x60;
|
||||
|
|
|
|||
Loading…
Reference in a new issue