diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 21eb8998bc..c3a7a6dff6 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.InetAddresses.parseNumericAddress; 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.Tether4Key; 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 @@ public class EthernetTetheringTest { 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 @@ public class EthernetTetheringTest { @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 @@ public class EthernetTetheringTest { 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 @@ public class EthernetTetheringTest { 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 @@ public class EthernetTetheringTest { } } - void initializeTethering(List upstreamAddresses, List upstreamDnses) - throws Exception { + private TetheringTester initTetheringTester(List upstreamAddresses, + List upstreamDnses) throws Exception { assumeFalse(mEm.isAvailable()); // MyTetheringEventCallback currently only support await first available upstream. Tethering @@ -1104,9 +1053,9 @@ public class EthernetTetheringTest { 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 @@ public class EthernetTetheringTest { 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 @@ public class EthernetTetheringTest { @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 @@ public class EthernetTetheringTest { // 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 @@ public class EthernetTetheringTest { @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 List toList(T... array) { diff --git a/Tethering/tests/integration/src/android/net/TetheringTester.java b/Tethering/tests/integration/src/android/net/TetheringTester.java index 9df636b4e9..4d90d398fb 100644 --- a/Tethering/tests/integration/src/android/net/TetheringTester.java +++ b/Tethering/tests/integration/src/android/net/TetheringTester.java @@ -18,11 +18,13 @@ package android.net; 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.Ipv6Utils; 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.Inet6Address; 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 @@ public final class TetheringTester { 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 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 pioList = new ArrayList<>(); @@ -298,7 +289,7 @@ public final class TetheringTester { 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 options = getRaPrefixOptions(raPacket); @@ -316,7 +307,7 @@ public final class TetheringTester { } } - fail("Could not get ipv6 address"); + fail("No available ipv6 prefix"); return null; } @@ -360,23 +351,18 @@ public final class TetheringTester { } } - 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 @@ public final class TetheringTester { 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); }