1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import static android.Manifest.permission.ACCESS_NETWORK_STATE; 20 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; 21 import static android.Manifest.permission.DUMP; 22 import static android.Manifest.permission.MANAGE_TEST_NETWORKS; 23 import static android.Manifest.permission.NETWORK_SETTINGS; 24 import static android.Manifest.permission.TETHER_PRIVILEGED; 25 import static android.net.InetAddresses.parseNumericAddress; 26 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; 27 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; 28 import static android.net.TetheringManager.TETHERING_ETHERNET; 29 import static android.net.TetheringTester.RemoteResponder; 30 import static android.system.OsConstants.IPPROTO_ICMPV6; 31 import static android.system.OsConstants.IPPROTO_IP; 32 import static android.system.OsConstants.IPPROTO_UDP; 33 34 import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA; 35 import static com.android.net.module.util.HexDump.dumpHexString; 36 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4; 37 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6; 38 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 39 import static com.android.testutils.DeviceInfoUtils.KVersion; 40 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork; 41 42 import static org.junit.Assert.assertEquals; 43 import static org.junit.Assert.assertFalse; 44 import static org.junit.Assert.assertNotNull; 45 import static org.junit.Assert.assertNull; 46 import static org.junit.Assert.assertTrue; 47 import static org.junit.Assert.fail; 48 import static org.junit.Assume.assumeFalse; 49 import static org.junit.Assume.assumeTrue; 50 51 import android.app.UiAutomation; 52 import android.content.Context; 53 import android.net.EthernetManager.TetheredInterfaceCallback; 54 import android.net.EthernetManager.TetheredInterfaceRequest; 55 import android.net.TetheringManager.StartTetheringCallback; 56 import android.net.TetheringManager.TetheringEventCallback; 57 import android.net.TetheringManager.TetheringRequest; 58 import android.net.TetheringTester.TetheredDevice; 59 import android.os.Build; 60 import android.os.Handler; 61 import android.os.HandlerThread; 62 import android.os.SystemClock; 63 import android.os.SystemProperties; 64 import android.os.VintfRuntimeInfo; 65 import android.text.TextUtils; 66 import android.util.Base64; 67 import android.util.Log; 68 import android.util.Pair; 69 70 import androidx.annotation.NonNull; 71 import androidx.annotation.Nullable; 72 import androidx.test.InstrumentationRegistry; 73 import androidx.test.filters.MediumTest; 74 import androidx.test.runner.AndroidJUnit4; 75 76 import com.android.net.module.util.PacketBuilder; 77 import com.android.net.module.util.Struct; 78 import com.android.net.module.util.bpf.Tether4Key; 79 import com.android.net.module.util.bpf.Tether4Value; 80 import com.android.net.module.util.bpf.TetherStatsKey; 81 import com.android.net.module.util.bpf.TetherStatsValue; 82 import com.android.net.module.util.structs.EthernetHeader; 83 import com.android.net.module.util.structs.Icmpv6Header; 84 import com.android.net.module.util.structs.Ipv4Header; 85 import com.android.net.module.util.structs.Ipv6Header; 86 import com.android.net.module.util.structs.UdpHeader; 87 import com.android.testutils.DevSdkIgnoreRule; 88 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; 89 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 90 import com.android.testutils.DeviceInfoUtils; 91 import com.android.testutils.DumpTestUtils; 92 import com.android.testutils.HandlerUtils; 93 import com.android.testutils.TapPacketReader; 94 import com.android.testutils.TestNetworkTracker; 95 96 import org.junit.After; 97 import org.junit.Before; 98 import org.junit.Rule; 99 import org.junit.Test; 100 import org.junit.runner.RunWith; 101 102 import java.io.FileDescriptor; 103 import java.net.Inet4Address; 104 import java.net.InetAddress; 105 import java.net.InterfaceAddress; 106 import java.net.NetworkInterface; 107 import java.net.SocketException; 108 import java.nio.ByteBuffer; 109 import java.nio.ByteOrder; 110 import java.util.Arrays; 111 import java.util.Collection; 112 import java.util.HashMap; 113 import java.util.List; 114 import java.util.Map; 115 import java.util.Objects; 116 import java.util.Random; 117 import java.util.Set; 118 import java.util.concurrent.CompletableFuture; 119 import java.util.concurrent.CountDownLatch; 120 import java.util.concurrent.TimeUnit; 121 import java.util.concurrent.TimeoutException; 122 123 @RunWith(AndroidJUnit4.class) 124 @MediumTest 125 public class EthernetTetheringTest { 126 @Rule 127 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 128 129 private static final String TAG = EthernetTetheringTest.class.getSimpleName(); 130 private static final int TIMEOUT_MS = 5000; 131 private static final int TETHER_REACHABILITY_ATTEMPTS = 20; 132 private static final int DUMP_POLLING_MAX_RETRY = 100; 133 private static final int DUMP_POLLING_INTERVAL_MS = 50; 134 // Kernel treats a confirmed UDP connection which active after two seconds as stream mode. 135 // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5. 136 private static final int UDP_STREAM_TS_MS = 2000; 137 // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes. 138 private static final int RX_UDP_PACKET_SIZE = 30; 139 private static final int RX_UDP_PACKET_COUNT = 456; 140 // Per TX UDP packet size: ethhdr (14) + iphdr (20) + udphdr (8) + payload (2) = 44 bytes. 141 private static final int TX_UDP_PACKET_SIZE = 44; 142 private static final int TX_UDP_PACKET_COUNT = 123; 143 144 private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8"); 145 private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64"); 146 private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8"); 147 private static final InetAddress TEST_IP6_DNS = parseNumericAddress("2001:db8:1::888"); 148 private static final ByteBuffer TEST_REACHABILITY_PAYLOAD = 149 ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa }); 150 151 private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap"; 152 private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats"; 153 private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4"; 154 private static final String BASE64_DELIMITER = ","; 155 private static final String LINE_DELIMITER = "\\n"; 156 157 private final Context mContext = InstrumentationRegistry.getContext(); 158 private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class); 159 private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class); 160 161 private TestNetworkInterface mDownstreamIface; 162 private HandlerThread mHandlerThread; 163 private Handler mHandler; 164 private TapPacketReader mDownstreamReader; 165 private TapPacketReader mUpstreamReader; 166 167 private TetheredInterfaceRequester mTetheredInterfaceRequester; 168 private MyTetheringEventCallback mTetheringEventCallback; 169 170 private UiAutomation mUiAutomation = 171 InstrumentationRegistry.getInstrumentation().getUiAutomation(); 172 private boolean mRunTests; 173 174 private TestNetworkTracker mUpstreamTracker; 175 176 @Before setUp()177 public void setUp() throws Exception { 178 // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive 179 // tethered client callbacks. The restricted networks permission is needed to ensure that 180 // EthernetManager#isAvailable will correctly return true on devices where Ethernet is 181 // marked restricted, like cuttlefish. The dump permission is needed to verify bpf related 182 // functions via dumpsys output. 183 mUiAutomation.adoptShellPermissionIdentity( 184 MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE, 185 CONNECTIVITY_USE_RESTRICTED_NETWORKS, DUMP); 186 mHandlerThread = new HandlerThread(getClass().getSimpleName()); 187 mHandlerThread.start(); 188 mHandler = new Handler(mHandlerThread.getLooper()); 189 190 mRunTests = isEthernetTetheringSupported(); 191 assumeTrue(mRunTests); 192 193 mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); 194 } 195 cleanUp()196 private void cleanUp() throws Exception { 197 mTm.setPreferTestNetworks(false); 198 199 if (mUpstreamTracker != null) { 200 mUpstreamTracker.teardown(); 201 mUpstreamTracker = null; 202 } 203 if (mUpstreamReader != null) { 204 TapPacketReader reader = mUpstreamReader; 205 mHandler.post(() -> reader.stop()); 206 mUpstreamReader = null; 207 } 208 209 mTm.stopTethering(TETHERING_ETHERNET); 210 if (mTetheringEventCallback != null) { 211 mTetheringEventCallback.awaitInterfaceUntethered(); 212 mTetheringEventCallback.unregister(); 213 mTetheringEventCallback = null; 214 } 215 if (mDownstreamReader != null) { 216 TapPacketReader reader = mDownstreamReader; 217 mHandler.post(() -> reader.stop()); 218 mDownstreamReader = null; 219 } 220 mTetheredInterfaceRequester.release(); 221 mEm.setIncludeTestInterfaces(false); 222 maybeDeleteTestInterface(); 223 } 224 225 @After tearDown()226 public void tearDown() throws Exception { 227 try { 228 if (mRunTests) cleanUp(); 229 } finally { 230 mHandlerThread.quitSafely(); 231 mUiAutomation.dropShellPermissionIdentity(); 232 } 233 } 234 235 @Test testVirtualEthernetAlreadyExists()236 public void testVirtualEthernetAlreadyExists() throws Exception { 237 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 238 assumeFalse(mEm.isAvailable()); 239 240 mDownstreamIface = createTestInterface(); 241 // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the 242 // interface will be placed in client mode, which will delete the link-local address. 243 // At that point NetworkInterface.getByName() will cease to work on the interface, because 244 // starting in R NetworkInterface can no longer see interfaces without IP addresses. 245 int mtu = getMTU(mDownstreamIface); 246 247 Log.d(TAG, "Including test interfaces"); 248 mEm.setIncludeTestInterfaces(true); 249 250 final String iface = mTetheredInterfaceRequester.getInterface(); 251 assertEquals("TetheredInterfaceCallback for unexpected interface", 252 mDownstreamIface.getInterfaceName(), iface); 253 254 checkVirtualEthernet(mDownstreamIface, mtu); 255 } 256 257 @Test testVirtualEthernet()258 public void testVirtualEthernet() throws Exception { 259 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 260 assumeFalse(mEm.isAvailable()); 261 262 CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface(); 263 264 mEm.setIncludeTestInterfaces(true); 265 266 mDownstreamIface = createTestInterface(); 267 268 final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 269 assertEquals("TetheredInterfaceCallback for unexpected interface", 270 mDownstreamIface.getInterfaceName(), iface); 271 272 checkVirtualEthernet(mDownstreamIface, getMTU(mDownstreamIface)); 273 } 274 275 @Test testStaticIpv4()276 public void testStaticIpv4() throws Exception { 277 assumeFalse(mEm.isAvailable()); 278 279 mEm.setIncludeTestInterfaces(true); 280 281 mDownstreamIface = createTestInterface(); 282 283 final String iface = mTetheredInterfaceRequester.getInterface(); 284 assertEquals("TetheredInterfaceCallback for unexpected interface", 285 mDownstreamIface.getInterfaceName(), iface); 286 287 assertInvalidStaticIpv4Request(iface, null, null); 288 assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); 289 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); 290 assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); 291 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); 292 assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); 293 assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); 294 295 final String localAddr = "192.0.2.3/28"; 296 final String clientAddr = "192.0.2.2/28"; 297 mTetheringEventCallback = enableEthernetTethering(iface, 298 requestWithStaticIpv4(localAddr, clientAddr), null /* any upstream */); 299 300 mTetheringEventCallback.awaitInterfaceTethered(); 301 assertInterfaceHasIpAddress(iface, localAddr); 302 303 byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); 304 byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); 305 306 FileDescriptor fd = mDownstreamIface.getFileDescriptor().getFileDescriptor(); 307 mDownstreamReader = makePacketReader(fd, getMTU(mDownstreamIface)); 308 TetheringTester tester = new TetheringTester(mDownstreamReader); 309 DhcpResults dhcpResults = tester.runDhcp(client1); 310 assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); 311 312 try { 313 tester.runDhcp(client2); 314 fail("Only one client should get an IP address"); 315 } catch (TimeoutException expected) { } 316 317 } 318 isRouterAdvertisement(byte[] pkt)319 private static boolean isRouterAdvertisement(byte[] pkt) { 320 if (pkt == null) return false; 321 322 ByteBuffer buf = ByteBuffer.wrap(pkt); 323 324 final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf); 325 if (ethHdr.etherType != ETHER_TYPE_IPV6) return false; 326 327 final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf); 328 if (ipv6Hdr.nextHeader != (byte) IPPROTO_ICMPV6) return false; 329 330 final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf); 331 return icmpv6Hdr.type == (short) ICMPV6_ROUTER_ADVERTISEMENT; 332 } 333 expectRouterAdvertisement(TapPacketReader reader, String iface, long timeoutMs)334 private static void expectRouterAdvertisement(TapPacketReader reader, String iface, 335 long timeoutMs) { 336 final long deadline = SystemClock.uptimeMillis() + timeoutMs; 337 do { 338 byte[] pkt = reader.popPacket(timeoutMs); 339 if (isRouterAdvertisement(pkt)) return; 340 timeoutMs = deadline - SystemClock.uptimeMillis(); 341 } while (timeoutMs > 0); 342 fail("Did not receive router advertisement on " + iface + " after " 343 + timeoutMs + "ms idle"); 344 } 345 expectLocalOnlyAddresses(String iface)346 private static void expectLocalOnlyAddresses(String iface) throws Exception { 347 final List<InterfaceAddress> interfaceAddresses = 348 NetworkInterface.getByName(iface).getInterfaceAddresses(); 349 350 boolean foundIpv6Ula = false; 351 for (InterfaceAddress ia : interfaceAddresses) { 352 final InetAddress addr = ia.getAddress(); 353 if (isIPv6ULA(addr)) { 354 foundIpv6Ula = true; 355 } 356 final int prefixlen = ia.getNetworkPrefixLength(); 357 final LinkAddress la = new LinkAddress(addr, prefixlen); 358 if (la.isIpv6() && la.isGlobalPreferred()) { 359 fail("Found global IPv6 address on local-only interface: " + interfaceAddresses); 360 } 361 } 362 363 assertTrue("Did not find IPv6 ULA on local-only interface " + iface, 364 foundIpv6Ula); 365 } 366 367 @Test testLocalOnlyTethering()368 public void testLocalOnlyTethering() throws Exception { 369 assumeFalse(mEm.isAvailable()); 370 371 mEm.setIncludeTestInterfaces(true); 372 373 mDownstreamIface = createTestInterface(); 374 375 final String iface = mTetheredInterfaceRequester.getInterface(); 376 assertEquals("TetheredInterfaceCallback for unexpected interface", 377 mDownstreamIface.getInterfaceName(), iface); 378 379 final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET) 380 .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build(); 381 mTetheringEventCallback = enableEthernetTethering(iface, request, 382 null /* any upstream */); 383 mTetheringEventCallback.awaitInterfaceLocalOnly(); 384 385 // makePacketReader only works after tethering is started, because until then the interface 386 // does not have an IP address, and unprivileged apps cannot see interfaces without IP 387 // addresses. This shouldn't be flaky because the TAP interface will buffer all packets even 388 // before the reader is started. 389 mDownstreamReader = makePacketReader(mDownstreamIface); 390 391 expectRouterAdvertisement(mDownstreamReader, iface, 2000 /* timeoutMs */); 392 expectLocalOnlyAddresses(iface); 393 } 394 isAdbOverNetwork()395 private boolean isAdbOverNetwork() { 396 // If adb TCP port opened, this test may running by adb over network. 397 return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) 398 || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); 399 } 400 401 @Test testPhysicalEthernet()402 public void testPhysicalEthernet() throws Exception { 403 assumeTrue(mEm.isAvailable()); 404 // Do not run this test if adb is over network and ethernet is connected. 405 // It is likely the adb run over ethernet, the adb would break when ethernet is switching 406 // from client mode to server mode. See b/160389275. 407 assumeFalse(isAdbOverNetwork()); 408 409 // Get an interface to use. 410 final String iface = mTetheredInterfaceRequester.getInterface(); 411 412 // Enable Ethernet tethering and check that it starts. 413 mTetheringEventCallback = enableEthernetTethering(iface, null /* any upstream */); 414 415 // There is nothing more we can do on a physical interface without connecting an actual 416 // client, which is not possible in this test. 417 } 418 isEthernetTetheringSupported()419 private boolean isEthernetTetheringSupported() throws Exception { 420 final CompletableFuture<Boolean> future = new CompletableFuture<>(); 421 final TetheringEventCallback callback = new TetheringEventCallback() { 422 @Override 423 public void onSupportedTetheringTypes(Set<Integer> supportedTypes) { 424 future.complete(supportedTypes.contains(TETHERING_ETHERNET)); 425 } 426 }; 427 428 try { 429 mTm.registerTetheringEventCallback(mHandler::post, callback); 430 return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 431 } finally { 432 mTm.unregisterTetheringEventCallback(callback); 433 } 434 } 435 436 private static final class MyTetheringEventCallback implements TetheringEventCallback { 437 private final TetheringManager mTm; 438 private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); 439 private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1); 440 private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1); 441 private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1); 442 private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); 443 private final CountDownLatch mUpstreamLatch = new CountDownLatch(1); 444 private final TetheringInterface mIface; 445 private final Network mExpectedUpstream; 446 447 private boolean mAcceptAnyUpstream = false; 448 449 private volatile boolean mInterfaceWasTethered = false; 450 private volatile boolean mInterfaceWasLocalOnly = false; 451 private volatile boolean mUnregistered = false; 452 private volatile Collection<TetheredClient> mClients = null; 453 private volatile Network mUpstream = null; 454 MyTetheringEventCallback(TetheringManager tm, String iface)455 MyTetheringEventCallback(TetheringManager tm, String iface) { 456 this(tm, iface, null); 457 mAcceptAnyUpstream = true; 458 } 459 MyTetheringEventCallback(TetheringManager tm, String iface, Network expectedUpstream)460 MyTetheringEventCallback(TetheringManager tm, String iface, Network expectedUpstream) { 461 mTm = tm; 462 mIface = new TetheringInterface(TETHERING_ETHERNET, iface); 463 mExpectedUpstream = expectedUpstream; 464 } 465 unregister()466 public void unregister() { 467 mTm.unregisterTetheringEventCallback(this); 468 mUnregistered = true; 469 } 470 @Override onTetheredInterfacesChanged(List<String> interfaces)471 public void onTetheredInterfacesChanged(List<String> interfaces) { 472 fail("Should only call callback that takes a Set<TetheringInterface>"); 473 } 474 475 @Override onTetheredInterfacesChanged(Set<TetheringInterface> interfaces)476 public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) { 477 // Ignore stale callbacks registered by previous test cases. 478 if (mUnregistered) return; 479 480 if (!mInterfaceWasTethered && interfaces.contains(mIface)) { 481 // This interface is being tethered for the first time. 482 Log.d(TAG, "Tethering started: " + interfaces); 483 mInterfaceWasTethered = true; 484 mTetheringStartedLatch.countDown(); 485 } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) { 486 Log.d(TAG, "Tethering stopped: " + interfaces); 487 mTetheringStoppedLatch.countDown(); 488 } 489 } 490 491 @Override onLocalOnlyInterfacesChanged(List<String> interfaces)492 public void onLocalOnlyInterfacesChanged(List<String> interfaces) { 493 fail("Should only call callback that takes a Set<TetheringInterface>"); 494 } 495 496 @Override onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces)497 public void onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces) { 498 // Ignore stale callbacks registered by previous test cases. 499 if (mUnregistered) return; 500 501 if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) { 502 // This interface is being put into local-only mode for the first time. 503 Log.d(TAG, "Local-only started: " + interfaces); 504 mInterfaceWasLocalOnly = true; 505 mLocalOnlyStartedLatch.countDown(); 506 } else if (mInterfaceWasLocalOnly && !interfaces.contains(mIface)) { 507 Log.d(TAG, "Local-only stopped: " + interfaces); 508 mLocalOnlyStoppedLatch.countDown(); 509 } 510 } 511 awaitInterfaceTethered()512 public void awaitInterfaceTethered() throws Exception { 513 assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms", 514 mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 515 } 516 awaitInterfaceLocalOnly()517 public void awaitInterfaceLocalOnly() throws Exception { 518 assertTrue("Ethernet not local-only after " + TIMEOUT_MS + "ms", 519 mLocalOnlyStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 520 } 521 awaitInterfaceUntethered()522 public void awaitInterfaceUntethered() throws Exception { 523 // Don't block teardown if the interface was never tethered. 524 // This is racy because the interface might become tethered right after this check, but 525 // that can only happen in tearDown if startTethering timed out, which likely means 526 // the test has already failed. 527 if (!mInterfaceWasTethered && !mInterfaceWasLocalOnly) return; 528 529 if (mInterfaceWasTethered) { 530 assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", 531 mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 532 } else if (mInterfaceWasLocalOnly) { 533 assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", 534 mLocalOnlyStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 535 } else { 536 fail(mIface + " cannot be both tethered and local-only. Update this test class."); 537 } 538 } 539 540 @Override onError(String ifName, int error)541 public void onError(String ifName, int error) { 542 // Ignore stale callbacks registered by previous test cases. 543 if (mUnregistered) return; 544 545 fail("TetheringEventCallback got error:" + error + " on iface " + ifName); 546 } 547 548 @Override onClientsChanged(Collection<TetheredClient> clients)549 public void onClientsChanged(Collection<TetheredClient> clients) { 550 // Ignore stale callbacks registered by previous test cases. 551 if (mUnregistered) return; 552 553 Log.d(TAG, "Got clients changed: " + clients); 554 mClients = clients; 555 if (clients.size() > 0) { 556 mClientConnectedLatch.countDown(); 557 } 558 } 559 awaitClientConnected()560 public Collection<TetheredClient> awaitClientConnected() throws Exception { 561 assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms", 562 mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 563 return mClients; 564 } 565 566 @Override onUpstreamChanged(Network network)567 public void onUpstreamChanged(Network network) { 568 // Ignore stale callbacks registered by previous test cases. 569 if (mUnregistered) return; 570 571 Log.d(TAG, "Got upstream changed: " + network); 572 mUpstream = network; 573 if (mAcceptAnyUpstream || Objects.equals(mUpstream, mExpectedUpstream)) { 574 mUpstreamLatch.countDown(); 575 } 576 } 577 awaitUpstreamChanged()578 public Network awaitUpstreamChanged() throws Exception { 579 if (!mUpstreamLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 580 fail("Did not receive upstream " + (mAcceptAnyUpstream ? "any" : mExpectedUpstream) 581 + " callback after " + TIMEOUT_MS + "ms"); 582 } 583 return mUpstream; 584 } 585 } 586 enableEthernetTethering(String iface, TetheringRequest request, Network expectedUpstream)587 private MyTetheringEventCallback enableEthernetTethering(String iface, 588 TetheringRequest request, Network expectedUpstream) throws Exception { 589 // Enable ethernet tethering with null expectedUpstream means the test accept any upstream 590 // after etherent tethering started. 591 final MyTetheringEventCallback callback; 592 if (expectedUpstream != null) { 593 callback = new MyTetheringEventCallback(mTm, iface, expectedUpstream); 594 } else { 595 callback = new MyTetheringEventCallback(mTm, iface); 596 } 597 mTm.registerTetheringEventCallback(mHandler::post, callback); 598 599 StartTetheringCallback startTetheringCallback = new StartTetheringCallback() { 600 @Override 601 public void onTetheringFailed(int resultCode) { 602 fail("Unexpectedly got onTetheringFailed"); 603 } 604 }; 605 Log.d(TAG, "Starting Ethernet tethering"); 606 mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); 607 608 final int connectivityType = request.getConnectivityScope(); 609 switch (connectivityType) { 610 case CONNECTIVITY_SCOPE_GLOBAL: 611 callback.awaitInterfaceTethered(); 612 break; 613 case CONNECTIVITY_SCOPE_LOCAL: 614 callback.awaitInterfaceLocalOnly(); 615 break; 616 default: 617 fail("Unexpected connectivity type requested: " + connectivityType); 618 } 619 620 return callback; 621 } 622 enableEthernetTethering(String iface, Network expectedUpstream)623 private MyTetheringEventCallback enableEthernetTethering(String iface, Network expectedUpstream) 624 throws Exception { 625 return enableEthernetTethering(iface, 626 new TetheringRequest.Builder(TETHERING_ETHERNET) 627 .setShouldShowEntitlementUi(false).build(), expectedUpstream); 628 } 629 getMTU(TestNetworkInterface iface)630 private int getMTU(TestNetworkInterface iface) throws SocketException { 631 NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); 632 assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); 633 return nif.getMTU(); 634 } 635 makePacketReader(final TestNetworkInterface iface)636 private TapPacketReader makePacketReader(final TestNetworkInterface iface) throws Exception { 637 FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); 638 return makePacketReader(fd, getMTU(iface)); 639 } 640 makePacketReader(FileDescriptor fd, int mtu)641 private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { 642 final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); 643 mHandler.post(() -> reader.start()); 644 HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS); 645 return reader; 646 } 647 checkVirtualEthernet(TestNetworkInterface iface, int mtu)648 private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { 649 FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); 650 mDownstreamReader = makePacketReader(fd, mtu); 651 mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName(), 652 null /* any upstream */); 653 checkTetheredClientCallbacks(mDownstreamReader); 654 } 655 checkTetheredClientCallbacks(TapPacketReader packetReader)656 private void checkTetheredClientCallbacks(TapPacketReader packetReader) throws Exception { 657 // Create a fake client. 658 byte[] clientMacAddr = new byte[6]; 659 new Random().nextBytes(clientMacAddr); 660 661 TetheringTester tester = new TetheringTester(packetReader); 662 DhcpResults dhcpResults = tester.runDhcp(clientMacAddr); 663 664 final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected(); 665 assertEquals(1, clients.size()); 666 final TetheredClient client = clients.iterator().next(); 667 668 // Check the MAC address. 669 assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); 670 assertEquals(TETHERING_ETHERNET, client.getTetheringType()); 671 672 // Check the hostname. 673 assertEquals(1, client.getAddresses().size()); 674 TetheredClient.AddressInfo info = client.getAddresses().get(0); 675 assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname()); 676 677 // Check the address is the one that was handed out in the DHCP ACK. 678 assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); 679 680 // Check that the lifetime is correct +/- 10s. 681 final long now = SystemClock.elapsedRealtime(); 682 final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; 683 final String msg = String.format("IP address should have lifetime of %d, got %d", 684 dhcpResults.leaseDuration, actualLeaseDuration); 685 assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); 686 } 687 688 private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback { 689 private final Handler mHandler; 690 private final EthernetManager mEm; 691 692 private TetheredInterfaceRequest mRequest; 693 private final CompletableFuture<String> mFuture = new CompletableFuture<>(); 694 TetheredInterfaceRequester(Handler handler, EthernetManager em)695 TetheredInterfaceRequester(Handler handler, EthernetManager em) { 696 mHandler = handler; 697 mEm = em; 698 } 699 700 @Override onAvailable(String iface)701 public void onAvailable(String iface) { 702 Log.d(TAG, "Ethernet interface available: " + iface); 703 mFuture.complete(iface); 704 } 705 706 @Override onUnavailable()707 public void onUnavailable() { 708 mFuture.completeExceptionally(new IllegalStateException("onUnavailable received")); 709 } 710 requestInterface()711 public CompletableFuture<String> requestInterface() { 712 assertNull("BUG: more than one tethered interface request", mRequest); 713 Log.d(TAG, "Requesting tethered interface"); 714 mRequest = mEm.requestTetheredInterface(mHandler::post, this); 715 return mFuture; 716 } 717 getInterface()718 public String getInterface() throws Exception { 719 return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 720 } 721 release()722 public void release() { 723 if (mRequest != null) { 724 mFuture.obtrudeException(new IllegalStateException("Request already released")); 725 mRequest.release(); 726 mRequest = null; 727 } 728 } 729 } 730 assertLinkAddressMatches(LinkAddress l1, LinkAddress l2)731 public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { 732 // Check all fields except the deprecation and expiry times. 733 String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); 734 assertTrue(msg, l1.isSameAddressAs(l2)); 735 assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); 736 assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); 737 } 738 requestWithStaticIpv4(String local, String client)739 private TetheringRequest requestWithStaticIpv4(String local, String client) { 740 LinkAddress localAddr = local == null ? null : new LinkAddress(local); 741 LinkAddress clientAddr = client == null ? null : new LinkAddress(client); 742 return new TetheringRequest.Builder(TETHERING_ETHERNET) 743 .setStaticIpv4Addresses(localAddr, clientAddr) 744 .setShouldShowEntitlementUi(false).build(); 745 } 746 assertInvalidStaticIpv4Request(String iface, String local, String client)747 private void assertInvalidStaticIpv4Request(String iface, String local, String client) 748 throws Exception { 749 try { 750 enableEthernetTethering(iface, requestWithStaticIpv4(local, client), 751 null /* any upstream */); 752 fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); 753 } catch (IllegalArgumentException | NullPointerException expected) { } 754 } 755 assertInterfaceHasIpAddress(String iface, String expected)756 private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { 757 LinkAddress expectedAddr = new LinkAddress(expected); 758 NetworkInterface nif = NetworkInterface.getByName(iface); 759 for (InterfaceAddress ia : nif.getInterfaceAddresses()) { 760 final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); 761 if (expectedAddr.equals(addr)) { 762 return; 763 } 764 } 765 fail("Expected " + iface + " to have IP address " + expected + ", found " 766 + nif.getInterfaceAddresses()); 767 } 768 createTestInterface()769 private TestNetworkInterface createTestInterface() throws Exception { 770 TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); 771 TestNetworkInterface iface = tnm.createTapInterface(); 772 Log.d(TAG, "Created test interface " + iface.getInterfaceName()); 773 return iface; 774 } 775 maybeDeleteTestInterface()776 private void maybeDeleteTestInterface() throws Exception { 777 if (mDownstreamIface != null) { 778 mDownstreamIface.getFileDescriptor().close(); 779 Log.d(TAG, "Deleted test interface " + mDownstreamIface.getInterfaceName()); 780 mDownstreamIface = null; 781 } 782 } 783 createTestUpstream(final List<LinkAddress> addresses)784 private TestNetworkTracker createTestUpstream(final List<LinkAddress> addresses) 785 throws Exception { 786 mTm.setPreferTestNetworks(true); 787 788 return initTestNetwork(mContext, addresses, TIMEOUT_MS); 789 } 790 791 @Test testTestNetworkUpstream()792 public void testTestNetworkUpstream() throws Exception { 793 assumeFalse(mEm.isAvailable()); 794 795 // MyTetheringEventCallback currently only support await first available upstream. Tethering 796 // may select internet network as upstream if test network is not available and not be 797 // preferred yet. Create test upstream network before enable tethering. 798 mUpstreamTracker = createTestUpstream(toList(TEST_IP4_ADDR, TEST_IP6_ADDR)); 799 800 mDownstreamIface = createTestInterface(); 801 mEm.setIncludeTestInterfaces(true); 802 803 final String iface = mTetheredInterfaceRequester.getInterface(); 804 assertEquals("TetheredInterfaceCallback for unexpected interface", 805 mDownstreamIface.getInterfaceName(), iface); 806 807 mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName(), 808 mUpstreamTracker.getNetwork()); 809 assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(), 810 mTetheringEventCallback.awaitUpstreamChanged()); 811 812 mDownstreamReader = makePacketReader(mDownstreamIface); 813 // TODO: do basic forwarding test here. 814 } 815 816 // Test network topology: 817 // 818 // public network (rawip) private network 819 // | UE | 820 // +------------+ V +------------+------------+ V +------------+ 821 // | Sever +---------+ Upstream | Downstream +---------+ Client | 822 // +------------+ +------------+------------+ +------------+ 823 // remote ip public ip private ip 824 // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876 825 // 826 private static final Inet4Address REMOTE_IP4_ADDR = 827 (Inet4Address) parseNumericAddress("8.8.8.8"); 828 // Used by public port and private port. Assume port 9876 has not been used yet before the 829 // testing that public port and private port are the same in the testing. Note that NAT port 830 // forwarding could be different between private port and public port. 831 private static final short LOCAL_PORT = 9876; 832 private static final short REMOTE_PORT = 433; 833 private static final byte TYPE_OF_SERVICE = 0; 834 private static final short ID = 27149; 835 private static final short ID2 = 27150; 836 private static final short ID3 = 27151; 837 private static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0 838 private static final byte TIME_TO_LIVE = (byte) 0x40; 839 private static final ByteBuffer PAYLOAD = 840 ByteBuffer.wrap(new byte[] { (byte) 0x12, (byte) 0x34 }); 841 private static final ByteBuffer PAYLOAD2 = 842 ByteBuffer.wrap(new byte[] { (byte) 0x56, (byte) 0x78 }); 843 private static final ByteBuffer PAYLOAD3 = 844 ByteBuffer.wrap(new byte[] { (byte) 0x9a, (byte) 0xbc }); 845 isExpectedUdpPacket(@onNull final byte[] rawPacket, boolean hasEther, @NonNull final ByteBuffer payload)846 private boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEther, 847 @NonNull final ByteBuffer payload) { 848 final ByteBuffer buf = ByteBuffer.wrap(rawPacket); 849 850 if (hasEther) { 851 final EthernetHeader etherHeader = Struct.parse(EthernetHeader.class, buf); 852 if (etherHeader == null) return false; 853 } 854 855 final Ipv4Header ipv4Header = Struct.parse(Ipv4Header.class, buf); 856 if (ipv4Header == null) return false; 857 858 final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf); 859 if (udpHeader == null) return false; 860 861 if (buf.remaining() != payload.limit()) return false; 862 863 return Arrays.equals(Arrays.copyOfRange(buf.array(), buf.position(), buf.limit()), 864 payload.array()); 865 } 866 867 @NonNull buildUdpv4Packet(@ullable final MacAddress srcMac, @Nullable final MacAddress dstMac, short id, @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp, short srcPort, short dstPort, @Nullable final ByteBuffer payload)868 private ByteBuffer buildUdpv4Packet(@Nullable final MacAddress srcMac, 869 @Nullable final MacAddress dstMac, short id, 870 @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp, 871 short srcPort, short dstPort, @Nullable final ByteBuffer payload) 872 throws Exception { 873 final boolean hasEther = (srcMac != null && dstMac != null); 874 final int payloadLen = (payload == null) ? 0 : payload.limit(); 875 final ByteBuffer buffer = PacketBuilder.allocate(hasEther, IPPROTO_IP, IPPROTO_UDP, 876 payloadLen); 877 final PacketBuilder packetBuilder = new PacketBuilder(buffer); 878 879 if (hasEther) packetBuilder.writeL2Header(srcMac, dstMac, (short) ETHER_TYPE_IPV4); 880 packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET, 881 TIME_TO_LIVE, (byte) IPPROTO_UDP, srcIp, dstIp); 882 packetBuilder.writeUdpHeader(srcPort, dstPort); 883 if (payload != null) { 884 buffer.put(payload); 885 // in case data might be reused by caller, restore the position and 886 // limit of bytebuffer. 887 payload.clear(); 888 } 889 890 return packetBuilder.finalizePacket(); 891 } 892 893 @NonNull buildUdpv4Packet(short id, @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp, short srcPort, short dstPort, @Nullable final ByteBuffer payload)894 private ByteBuffer buildUdpv4Packet(short id, @NonNull final Inet4Address srcIp, 895 @NonNull final Inet4Address dstIp, short srcPort, short dstPort, 896 @Nullable final ByteBuffer payload) throws Exception { 897 return buildUdpv4Packet(null /* srcMac */, null /* dstMac */, id, srcIp, dstIp, srcPort, 898 dstPort, payload); 899 } 900 901 // TODO: remove this verification once upstream connected notification race is fixed. 902 // See #runUdp4Test. isIpv4TetherConnectivityVerified(TetheringTester tester, RemoteResponder remote, TetheredDevice tethered)903 private boolean isIpv4TetherConnectivityVerified(TetheringTester tester, 904 RemoteResponder remote, TetheredDevice tethered) throws Exception { 905 final ByteBuffer probePacket = buildUdpv4Packet(tethered.macAddr, 906 tethered.routerMacAddr, ID, tethered.ipv4Addr /* srcIp */, 907 REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /*dstPort */, 908 TEST_REACHABILITY_PAYLOAD); 909 910 // Send a UDP packet from client and check the packet can be found on upstream interface. 911 for (int i = 0; i < TETHER_REACHABILITY_ATTEMPTS; i++) { 912 tester.sendPacket(probePacket); 913 byte[] expectedPacket = remote.getNextMatchedPacket(p -> { 914 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 915 return isExpectedUdpPacket(p, false /* hasEther */, TEST_REACHABILITY_PAYLOAD); 916 }); 917 if (expectedPacket != null) return true; 918 } 919 return false; 920 } 921 runUdp4Test(TetheringTester tester, RemoteResponder remote, boolean usingBpf)922 private void runUdp4Test(TetheringTester tester, RemoteResponder remote, boolean usingBpf) 923 throws Exception { 924 final TetheredDevice tethered = tester.createTetheredDevice(MacAddress.fromString( 925 "1:2:3:4:5:6")); 926 927 // TODO: remove the connectivity verification for upstream connected notification race. 928 // Because async upstream connected notification can't guarantee the tethering routing is 929 // ready to use. Need to test tethering connectivity before testing. 930 // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes 931 // from upstream. That can guarantee that the routing is ready. Long term plan is that 932 // refactors upstream connected notification from async to sync. 933 assertTrue(isIpv4TetherConnectivityVerified(tester, remote, tethered)); 934 935 // Send a UDP packet in original direction. 936 final ByteBuffer originalPacket = buildUdpv4Packet(tethered.macAddr, 937 tethered.routerMacAddr, ID, tethered.ipv4Addr /* srcIp */, 938 REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, REMOTE_PORT /*dstPort */, 939 PAYLOAD /* payload */); 940 tester.verifyUpload(remote, originalPacket, p -> { 941 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 942 return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD); 943 }); 944 945 // Send a UDP packet in reply direction. 946 final Inet4Address publicIp4Addr = (Inet4Address) TEST_IP4_ADDR.getAddress(); 947 final ByteBuffer replyPacket = buildUdpv4Packet(ID2, REMOTE_IP4_ADDR /* srcIp */, 948 publicIp4Addr /* dstIp */, REMOTE_PORT /* srcPort */, LOCAL_PORT /*dstPort */, 949 PAYLOAD2 /* payload */); 950 remote.verifyDownload(tester, replyPacket, p -> { 951 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 952 return isExpectedUdpPacket(p, true/* hasEther */, PAYLOAD2); 953 }); 954 955 if (usingBpf) { 956 // Send second UDP packet in original direction. 957 // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply" 958 // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make 959 // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay 960 // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds 961 // and apply ASSURED flag. 962 // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and 963 // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c 964 Thread.sleep(UDP_STREAM_TS_MS); 965 final ByteBuffer originalPacket2 = buildUdpv4Packet(tethered.macAddr, 966 tethered.routerMacAddr, ID, tethered.ipv4Addr /* srcIp */, 967 REMOTE_IP4_ADDR /* dstIp */, LOCAL_PORT /* srcPort */, 968 REMOTE_PORT /*dstPort */, PAYLOAD3 /* payload */); 969 tester.verifyUpload(remote, originalPacket2, p -> { 970 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 971 return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD3); 972 }); 973 974 // [1] Verify IPv4 upstream rule map. 975 final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump( 976 Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4); 977 assertNotNull(upstreamMap); 978 assertEquals(1, upstreamMap.size()); 979 980 final Map.Entry<Tether4Key, Tether4Value> rule = 981 upstreamMap.entrySet().iterator().next(); 982 983 final Tether4Key upstream4Key = rule.getKey(); 984 assertEquals(IPPROTO_UDP, upstream4Key.l4proto); 985 assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4)); 986 assertEquals(LOCAL_PORT, upstream4Key.srcPort); 987 assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4)); 988 assertEquals(REMOTE_PORT, upstream4Key.dstPort); 989 990 final Tether4Value upstream4Value = rule.getValue(); 991 assertTrue(Arrays.equals(publicIp4Addr.getAddress(), 992 InetAddress.getByAddress(upstream4Value.src46).getAddress())); 993 assertEquals(LOCAL_PORT, upstream4Value.srcPort); 994 assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), 995 InetAddress.getByAddress(upstream4Value.dst46).getAddress())); 996 assertEquals(REMOTE_PORT, upstream4Value.dstPort); 997 998 // [2] Verify stats map. 999 // Transmit packets on both direction for verifying stats. Because we only care the 1000 // packet count in stats test, we just reuse the existing packets to increaes 1001 // the packet count on both direction. 1002 1003 // Send packets on original direction. 1004 for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) { 1005 tester.verifyUpload(remote, originalPacket, p -> { 1006 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 1007 return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD); 1008 }); 1009 } 1010 1011 // Send packets on reply direction. 1012 for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) { 1013 remote.verifyDownload(tester, replyPacket, p -> { 1014 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 1015 return isExpectedUdpPacket(p, true/* hasEther */, PAYLOAD2); 1016 }); 1017 } 1018 1019 // Dump stats map to verify. 1020 final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump( 1021 TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS); 1022 assertNotNull(statsMap); 1023 assertEquals(1, statsMap.size()); 1024 1025 final Map.Entry<TetherStatsKey, TetherStatsValue> stats = 1026 statsMap.entrySet().iterator().next(); 1027 1028 // TODO: verify the upstream index in TetherStatsKey. 1029 1030 final TetherStatsValue statsValue = stats.getValue(); 1031 assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets); 1032 assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes); 1033 assertEquals(0, statsValue.rxErrors); 1034 assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets); 1035 assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes); 1036 assertEquals(0, statsValue.txErrors); 1037 } 1038 } 1039 initializeTethering()1040 void initializeTethering() throws Exception { 1041 assumeFalse(mEm.isAvailable()); 1042 1043 // MyTetheringEventCallback currently only support await first available upstream. Tethering 1044 // may select internet network as upstream if test network is not available and not be 1045 // preferred yet. Create test upstream network before enable tethering. 1046 mUpstreamTracker = createTestUpstream(toList(TEST_IP4_ADDR)); 1047 1048 mDownstreamIface = createTestInterface(); 1049 mEm.setIncludeTestInterfaces(true); 1050 1051 final String iface = mTetheredInterfaceRequester.getInterface(); 1052 assertEquals("TetheredInterfaceCallback for unexpected interface", 1053 mDownstreamIface.getInterfaceName(), iface); 1054 1055 mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName(), 1056 mUpstreamTracker.getNetwork()); 1057 assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(), 1058 mTetheringEventCallback.awaitUpstreamChanged()); 1059 1060 mDownstreamReader = makePacketReader(mDownstreamIface); 1061 mUpstreamReader = makePacketReader(mUpstreamTracker.getTestIface()); 1062 } 1063 1064 @Test 1065 @IgnoreAfter(Build.VERSION_CODES.R) testTetherUdpV4UpToR()1066 public void testTetherUdpV4UpToR() throws Exception { 1067 initializeTethering(); 1068 runUdp4Test(new TetheringTester(mDownstreamReader), new RemoteResponder(mUpstreamReader), 1069 false /* usingBpf */); 1070 } 1071 isUdpOffloadSupportedByKernel(final String kernelVersion)1072 private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) { 1073 final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion); 1074 return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0)) 1075 || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0)) 1076 || current.isAtLeast(new KVersion(5, 4, 98)); 1077 } 1078 1079 @Test testIsUdpOffloadSupportedByKernel()1080 public void testIsUdpOffloadSupportedByKernel() throws Exception { 1081 assertFalse(isUdpOffloadSupportedByKernel("4.14.221")); 1082 assertTrue(isUdpOffloadSupportedByKernel("4.14.222")); 1083 assertTrue(isUdpOffloadSupportedByKernel("4.16.0")); 1084 assertTrue(isUdpOffloadSupportedByKernel("4.18.0")); 1085 assertFalse(isUdpOffloadSupportedByKernel("4.19.0")); 1086 1087 assertFalse(isUdpOffloadSupportedByKernel("4.19.175")); 1088 assertTrue(isUdpOffloadSupportedByKernel("4.19.176")); 1089 assertTrue(isUdpOffloadSupportedByKernel("5.2.0")); 1090 assertTrue(isUdpOffloadSupportedByKernel("5.3.0")); 1091 assertFalse(isUdpOffloadSupportedByKernel("5.4.0")); 1092 1093 assertFalse(isUdpOffloadSupportedByKernel("5.4.97")); 1094 assertTrue(isUdpOffloadSupportedByKernel("5.4.98")); 1095 assertTrue(isUdpOffloadSupportedByKernel("5.10.0")); 1096 } 1097 1098 // TODO: refactor test testTetherUdpV4* into IPv4 UDP non-offload and offload tests. 1099 // That can be easier to know which feature is verified from test results. 1100 @Test 1101 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherUdpV4AfterR()1102 public void testTetherUdpV4AfterR() throws Exception { 1103 initializeTethering(); 1104 final String kernelVersion = VintfRuntimeInfo.getKernelVersion(); 1105 boolean usingBpf = isUdpOffloadSupportedByKernel(kernelVersion); 1106 if (!usingBpf) { 1107 Log.i(TAG, "testTetherUdpV4AfterR will skip BPF offload test for kernel " 1108 + kernelVersion); 1109 } 1110 runUdp4Test(new TetheringTester(mDownstreamReader), new RemoteResponder(mUpstreamReader), 1111 usingBpf); 1112 } 1113 1114 @Nullable parseMapKeyValue( Class<K> keyClass, Class<V> valueClass, @NonNull String dumpStr)1115 private <K extends Struct, V extends Struct> Pair<K, V> parseMapKeyValue( 1116 Class<K> keyClass, Class<V> valueClass, @NonNull String dumpStr) { 1117 Log.w(TAG, "Parsing string: " + dumpStr); 1118 1119 String[] keyValueStrs = dumpStr.split(BASE64_DELIMITER); 1120 if (keyValueStrs.length != 2 /* key + value */) { 1121 fail("The length is " + keyValueStrs.length + " but expect 2. " 1122 + "Split string(s): " + TextUtils.join(",", keyValueStrs)); 1123 } 1124 1125 final byte[] keyBytes = Base64.decode(keyValueStrs[0], Base64.DEFAULT); 1126 Log.d(TAG, "keyBytes: " + dumpHexString(keyBytes)); 1127 final ByteBuffer keyByteBuffer = ByteBuffer.wrap(keyBytes); 1128 keyByteBuffer.order(ByteOrder.nativeOrder()); 1129 final K k = Struct.parse(keyClass, keyByteBuffer); 1130 1131 final byte[] valueBytes = Base64.decode(keyValueStrs[1], Base64.DEFAULT); 1132 Log.d(TAG, "valueBytes: " + dumpHexString(valueBytes)); 1133 final ByteBuffer valueByteBuffer = ByteBuffer.wrap(valueBytes); 1134 valueByteBuffer.order(ByteOrder.nativeOrder()); 1135 final V v = Struct.parse(valueClass, valueByteBuffer); 1136 1137 return new Pair<>(k, v); 1138 } 1139 1140 @NonNull dumpAndParseRawMap( Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)1141 private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap( 1142 Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg) 1143 throws Exception { 1144 final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg}; 1145 final String rawMapStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args); 1146 final HashMap<K, V> map = new HashMap<>(); 1147 1148 for (final String line : rawMapStr.split(LINE_DELIMITER)) { 1149 final Pair<K, V> rule = parseMapKeyValue(keyClass, valueClass, line.trim()); 1150 map.put(rule.first, rule.second); 1151 } 1152 return map; 1153 } 1154 1155 @Nullable pollRawMapFromDump( Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)1156 private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump( 1157 Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg) 1158 throws Exception { 1159 for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) { 1160 final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg); 1161 if (!map.isEmpty()) return map; 1162 1163 Thread.sleep(DUMP_POLLING_INTERVAL_MS); 1164 } 1165 1166 fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms"); 1167 return null; 1168 } 1169 toList(T... array)1170 private <T> List<T> toList(T... array) { 1171 return Arrays.asList(array); 1172 } 1173 } 1174