Merge changes Iade99eeb,I3ede46f5
* changes:
[TestOnly]Create TetheringTester in initTetheringTester
[TestOnly] Move isExpectedUdpPacket into TetheringTester
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 21eb899..c3a7a6d 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -26,7 +26,8 @@
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
-import static android.net.TetheringTester.isIcmpv6Type;
+import static android.net.TetheringTester.isExpectedIcmpv6Packet;
+import static android.net.TetheringTester.isExpectedUdpPacket;
import static android.system.OsConstants.IPPROTO_IP;
import static android.system.OsConstants.IPPROTO_IPV6;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -82,10 +83,7 @@
import com.android.net.module.util.bpf.Tether4Value;
import com.android.net.module.util.bpf.TetherStatsKey;
import com.android.net.module.util.bpf.TetherStatsValue;
-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.UdpHeader;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -332,7 +330,7 @@
final long deadline = SystemClock.uptimeMillis() + timeoutMs;
do {
byte[] pkt = reader.popPacket(timeoutMs);
- if (isIcmpv6Type(pkt, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT)) return;
+ if (isExpectedIcmpv6Packet(pkt, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT)) return;
timeoutMs = deadline - SystemClock.uptimeMillis();
} while (timeoutMs > 0);
@@ -792,38 +790,11 @@
@Test
public void testIcmpv6Echo() throws Exception {
- assumeFalse(mEm.isAvailable());
-
- // MyTetheringEventCallback currently only support await first available upstream. Tethering
- // may select internet network as upstream if test network is not available and not be
- // preferred yet. Create test upstream network before enable tethering.
- mUpstreamTracker = createTestUpstream(toList(TEST_IP4_ADDR, TEST_IP6_ADDR),
- toList(TEST_IP4_DNS, TEST_IP6_DNS));
-
- mDownstreamIface = createTestInterface();
- mEm.setIncludeTestInterfaces(true);
-
- final String iface = mTetheredInterfaceRequester.getInterface();
- assertEquals("TetheredInterfaceCallback for unexpected interface",
- mDownstreamIface.getInterfaceName(), iface);
-
- mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName(),
- mUpstreamTracker.getNetwork());
- assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(),
- mTetheringEventCallback.awaitUpstreamChanged());
-
- mDownstreamReader = makePacketReader(mDownstreamIface);
- mUpstreamReader = makePacketReader(mUpstreamTracker.getTestIface());
-
- runPing6Test(new TetheringTester(mDownstreamReader, mUpstreamReader));
+ runPing6Test(initTetheringTester(toList(TEST_IP4_ADDR, TEST_IP6_ADDR),
+ toList(TEST_IP4_DNS, TEST_IP6_DNS)));
}
private void runPing6Test(TetheringTester tester) throws Exception {
- // Currently tethering don't have API to tell when ipv6 tethering is available. Thus, let
- // TetheringTester test ipv6 tethering connectivity before testing ipv6.
- // TODO: move to a common place to avoid that every IPv6 test needs to call this function.
- tester.waitForIpv6TetherConnectivityVerified();
-
TetheredDevice tethered = tester.createTetheredDevice(MacAddress.fromString("1:2:3:4:5:6"),
true /* hasIpv6 */);
Inet6Address remoteIp6Addr = (Inet6Address) parseNumericAddress("2400:222:222::222");
@@ -832,14 +803,14 @@
tester.verifyUpload(request, p -> {
Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
- return isIcmpv6Type(p, false /* hasEth */, ICMPV6_ECHO_REQUEST_TYPE);
+ return isExpectedIcmpv6Packet(p, false /* hasEth */, ICMPV6_ECHO_REQUEST_TYPE);
});
ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket(remoteIp6Addr, tethered.ipv6Addr);
tester.verifyDownload(reply, p -> {
Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
- return isIcmpv6Type(p, true /* hasEth */, ICMPV6_ECHO_REPLY_TYPE);
+ return isExpectedIcmpv6Packet(p, true /* hasEth */, ICMPV6_ECHO_REPLY_TYPE);
});
}
@@ -872,28 +843,6 @@
private static final ByteBuffer PAYLOAD3 =
ByteBuffer.wrap(new byte[] { (byte) 0x9a, (byte) 0xbc });
- private boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEther,
- boolean isIpv4, @NonNull final ByteBuffer payload) {
- final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
-
- if (hasEther) {
- if (Struct.parse(EthernetHeader.class, buf) == null) return false;
- }
-
- if (isIpv4) {
- if (Struct.parse(Ipv4Header.class, buf) == null) return false;
- } else {
- if (Struct.parse(Ipv6Header.class, buf) == null) return false;
- }
-
- if (Struct.parse(UdpHeader.class, buf) == null) return false;
-
- if (buf.remaining() != payload.limit()) return false;
-
- return Arrays.equals(Arrays.copyOfRange(buf.array(), buf.position(), buf.limit()),
- payload.array());
- }
-
@NonNull
private ByteBuffer buildUdpPacket(
@Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
@@ -1092,8 +1041,8 @@
}
}
- void initializeTethering(List<LinkAddress> upstreamAddresses, List<InetAddress> upstreamDnses)
- throws Exception {
+ private TetheringTester initTetheringTester(List<LinkAddress> upstreamAddresses,
+ List<InetAddress> upstreamDnses) throws Exception {
assumeFalse(mEm.isAvailable());
// MyTetheringEventCallback currently only support await first available upstream. Tethering
@@ -1104,9 +1053,9 @@
mDownstreamIface = createTestInterface();
mEm.setIncludeTestInterfaces(true);
- final String iface = mTetheredInterfaceRequester.getInterface();
+ // Make sure EtherentTracker use "mDownstreamIface" as server mode interface.
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mDownstreamIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), mTetheredInterfaceRequester.getInterface());
mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName(),
mUpstreamTracker.getNetwork());
@@ -1115,13 +1064,23 @@
mDownstreamReader = makePacketReader(mDownstreamIface);
mUpstreamReader = makePacketReader(mUpstreamTracker.getTestIface());
+
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ // Currently tethering don't have API to tell when ipv6 tethering is available. Thus, make
+ // sure tethering already have ipv6 connectivity before testing.
+ if (cm.getLinkProperties(mUpstreamTracker.getNetwork()).hasGlobalIpv6Address()) {
+ waitForRouterAdvertisement(mDownstreamReader, mDownstreamIface.getInterfaceName(),
+ WAIT_RA_TIMEOUT_MS);
+ }
+
+ return new TetheringTester(mDownstreamReader, mUpstreamReader);
}
@Test
@IgnoreAfter(Build.VERSION_CODES.R)
public void testTetherUdpV4UpToR() throws Exception {
- initializeTethering(toList(TEST_IP4_ADDR), toList(TEST_IP4_DNS));
- runUdp4Test(new TetheringTester(mDownstreamReader, mUpstreamReader), false /* usingBpf */);
+ runUdp4Test(initTetheringTester(toList(TEST_IP4_ADDR), toList(TEST_IP4_DNS)),
+ false /* usingBpf */);
}
private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) {
@@ -1155,14 +1114,13 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.R)
public void testTetherUdpV4AfterR() throws Exception {
- initializeTethering(toList(TEST_IP4_ADDR), toList(TEST_IP4_DNS));
final String kernelVersion = VintfRuntimeInfo.getKernelVersion();
boolean usingBpf = isUdpOffloadSupportedByKernel(kernelVersion);
if (!usingBpf) {
Log.i(TAG, "testTetherUdpV4AfterR will skip BPF offload test for kernel "
+ kernelVersion);
}
- runUdp4Test(new TetheringTester(mDownstreamReader, mUpstreamReader), usingBpf);
+ runUdp4Test(initTetheringTester(toList(TEST_IP4_ADDR), toList(TEST_IP4_DNS)), usingBpf);
}
@Nullable
@@ -1264,11 +1222,6 @@
// packet.
//
private void runClatUdpTest(TetheringTester tester) throws Exception {
- // Currently tethering don't have API to tell when ipv6 tethering is available. Thus, let
- // TetheringTester test ipv6 tethering connectivity before testing ipv6.
- // TODO: move to a common place to avoid that every IPv6 test needs to call this function.
- tester.waitForIpv6TetherConnectivityVerified();
-
final TetheredDevice tethered = tester.createTetheredDevice(MacAddress.fromString(
"1:2:3:4:5:6"), true /* hasIpv6 */);
@@ -1304,8 +1257,7 @@
@IgnoreUpTo(Build.VERSION_CODES.R)
public void testTetherClatUdp() throws Exception {
// CLAT only starts on IPv6 only network.
- initializeTethering(toList(TEST_IP6_ADDR), toList(TEST_IP6_DNS));
- runClatUdpTest(new TetheringTester(mDownstreamReader, mUpstreamReader));
+ runClatUdpTest(initTetheringTester(toList(TEST_IP6_ADDR), toList(TEST_IP6_DNS)));
}
private <T> List<T> toList(T... array) {
diff --git a/Tethering/tests/integration/src/android/net/TetheringTester.java b/Tethering/tests/integration/src/android/net/TetheringTester.java
index 9df636b..4d90d39 100644
--- a/Tethering/tests/integration/src/android/net/TetheringTester.java
+++ b/Tethering/tests/integration/src/android/net/TetheringTester.java
@@ -18,11 +18,13 @@
import static android.net.InetAddresses.parseNumericAddress;
import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST;
+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.ICMPV6_ND_OPTION_PIO;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
@@ -49,11 +51,13 @@
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header;
+import com.android.net.module.util.structs.Ipv4Header;
import com.android.net.module.util.structs.Ipv6Header;
import com.android.net.module.util.structs.LlaOption;
import com.android.net.module.util.structs.NsHeader;
import com.android.net.module.util.structs.PrefixInformationOption;
import com.android.net.module.util.structs.RaHeader;
+import com.android.net.module.util.structs.UdpHeader;
import com.android.networkstack.arp.ArpPacket;
import com.android.testutils.TapPacketReader;
@@ -62,6 +66,7 @@
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeoutException;
@@ -252,25 +257,11 @@
return null;
}
- public void waitForIpv6TetherConnectivityVerified() throws Exception {
- Log.d(TAG, "Waiting RA multicast");
-
- // Wait for RA multicast message from router to confirm that the IPv6 tethering
- // connectivity is ready. We don't extract the router mac address from RA because
- // we get the router mac address from IPv4 ARP packet. See #getRouterMacAddressFromArp.
- for (int i = 0; i < READ_RA_ATTEMPTS; i++) {
- final byte[] raPacket = getDownloadPacket((p) -> {
- return isIcmpv6Type(p, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT);
- });
- if (raPacket != null) return;
- }
-
- fail("Could not get RA multicast packet after " + READ_RA_ATTEMPTS + " attempts");
- }
-
private List<PrefixInformationOption> getRaPrefixOptions(byte[] packet) {
ByteBuffer buf = ByteBuffer.wrap(packet);
- if (!isIcmpv6Type(buf, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT)) return null;
+ if (!isExpectedIcmpv6Packet(buf, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT)) {
+ fail("Parsing RA packet fail");
+ }
Struct.parse(RaHeader.class, buf);
final ArrayList<PrefixInformationOption> pioList = new ArrayList<>();
@@ -298,7 +289,7 @@
sendRsPacket(srcMac, dstMac);
final byte[] raPacket = verifyPacketNotNull("Receive RA fail", getDownloadPacket(p -> {
- return isIcmpv6Type(p, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT);
+ return isExpectedIcmpv6Packet(p, true /* hasEth */, ICMPV6_ROUTER_ADVERTISEMENT);
}));
final List<PrefixInformationOption> options = getRaPrefixOptions(raPacket);
@@ -316,7 +307,7 @@
}
}
- fail("Could not get ipv6 address");
+ fail("No available ipv6 prefix");
return null;
}
@@ -360,23 +351,18 @@
}
}
- public static boolean isIcmpv6Type(byte[] packet, boolean hasEth, int type) {
+ public static boolean isExpectedIcmpv6Packet(byte[] packet, boolean hasEth, int type) {
final ByteBuffer buf = ByteBuffer.wrap(packet);
- return isIcmpv6Type(buf, hasEth, type);
+ return isExpectedIcmpv6Packet(buf, hasEth, type);
}
- private static boolean isIcmpv6Type(ByteBuffer buf, boolean hasEth, int type) {
+ private static boolean isExpectedIcmpv6Packet(ByteBuffer buf, boolean hasEth, int type) {
try {
- if (hasEth) {
- final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf);
- if (ethHdr.etherType != ETHER_TYPE_IPV6) return false;
- }
+ if (hasEth && !hasExpectedEtherHeader(buf, false /* isIpv4 */)) return false;
- final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf);
- if (ipv6Hdr.nextHeader != (byte) IPPROTO_ICMPV6) return false;
+ if (!hasExpectedIpHeader(buf, false /* isIpv4 */, IPPROTO_ICMPV6)) return false;
- final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf);
- return icmpv6Hdr.type == (short) type;
+ return Struct.parse(Icmpv6Header.class, buf).type == (short) type;
} catch (Exception e) {
// Parsing packet fail means it is not icmpv6 packet.
}
@@ -384,6 +370,42 @@
return false;
}
+ private static boolean hasExpectedEtherHeader(@NonNull final ByteBuffer buf, boolean isIpv4)
+ throws Exception {
+ final int expected = isIpv4 ? ETHER_TYPE_IPV4 : ETHER_TYPE_IPV6;
+
+ return Struct.parse(EthernetHeader.class, buf).etherType == expected;
+ }
+
+ private static boolean hasExpectedIpHeader(@NonNull final ByteBuffer buf, boolean isIpv4,
+ int ipProto) throws Exception {
+ if (isIpv4) {
+ return Struct.parse(Ipv4Header.class, buf).protocol == (byte) ipProto;
+ } else {
+ return Struct.parse(Ipv6Header.class, buf).nextHeader == (byte) ipProto;
+ }
+ }
+
+ public static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
+ boolean isIpv4, @NonNull final ByteBuffer payload) {
+ final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
+ try {
+ if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false;
+
+ if (!hasExpectedIpHeader(buf, isIpv4, IPPROTO_UDP)) return false;
+
+ if (Struct.parse(UdpHeader.class, buf) == null) return false;
+ } catch (Exception e) {
+ // Parsing packet fail means it is not udp packet.
+ return false;
+ }
+
+ if (buf.remaining() != payload.limit()) return false;
+
+ return Arrays.equals(Arrays.copyOfRange(buf.array(), buf.position(), buf.limit()),
+ payload.array());
+ }
+
private void sendUploadPacket(ByteBuffer packet) throws Exception {
mDownstreamReader.sendResponse(packet);
}