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.MANAGE_TEST_NETWORKS; 22 import static android.Manifest.permission.NETWORK_SETTINGS; 23 import static android.Manifest.permission.TETHER_PRIVILEGED; 24 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; 25 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; 26 import static android.net.TetheringManager.TETHERING_ETHERNET; 27 import static android.system.OsConstants.IPPROTO_ICMPV6; 28 29 import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA; 30 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6; 31 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertNotNull; 35 import static org.junit.Assert.assertNull; 36 import static org.junit.Assert.assertTrue; 37 import static org.junit.Assert.fail; 38 import static org.junit.Assume.assumeFalse; 39 import static org.junit.Assume.assumeTrue; 40 41 import android.app.UiAutomation; 42 import android.content.Context; 43 import android.net.EthernetManager.TetheredInterfaceCallback; 44 import android.net.EthernetManager.TetheredInterfaceRequest; 45 import android.net.TetheringManager.StartTetheringCallback; 46 import android.net.TetheringManager.TetheringEventCallback; 47 import android.net.TetheringManager.TetheringRequest; 48 import android.net.dhcp.DhcpAckPacket; 49 import android.net.dhcp.DhcpOfferPacket; 50 import android.net.dhcp.DhcpPacket; 51 import android.os.Handler; 52 import android.os.HandlerThread; 53 import android.os.SystemClock; 54 import android.os.SystemProperties; 55 import android.system.Os; 56 import android.util.Log; 57 58 import androidx.test.InstrumentationRegistry; 59 import androidx.test.filters.MediumTest; 60 import androidx.test.runner.AndroidJUnit4; 61 62 import com.android.net.module.util.Struct; 63 import com.android.net.module.util.structs.EthernetHeader; 64 import com.android.net.module.util.structs.Icmpv6Header; 65 import com.android.net.module.util.structs.Ipv6Header; 66 import com.android.testutils.HandlerUtils; 67 import com.android.testutils.TapPacketReader; 68 69 import org.junit.After; 70 import org.junit.Before; 71 import org.junit.Test; 72 import org.junit.runner.RunWith; 73 74 import java.io.FileDescriptor; 75 import java.net.Inet4Address; 76 import java.net.InetAddress; 77 import java.net.InterfaceAddress; 78 import java.net.NetworkInterface; 79 import java.net.SocketException; 80 import java.nio.ByteBuffer; 81 import java.util.Collection; 82 import java.util.List; 83 import java.util.Random; 84 import java.util.Set; 85 import java.util.concurrent.CompletableFuture; 86 import java.util.concurrent.CountDownLatch; 87 import java.util.concurrent.TimeUnit; 88 import java.util.concurrent.TimeoutException; 89 90 @RunWith(AndroidJUnit4.class) 91 @MediumTest 92 public class EthernetTetheringTest { 93 94 private static final String TAG = EthernetTetheringTest.class.getSimpleName(); 95 private static final int TIMEOUT_MS = 5000; 96 private static final int PACKET_READ_TIMEOUT_MS = 100; 97 private static final int DHCP_DISCOVER_ATTEMPTS = 10; 98 private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { 99 DhcpPacket.DHCP_SUBNET_MASK, 100 DhcpPacket.DHCP_ROUTER, 101 DhcpPacket.DHCP_DNS_SERVER, 102 DhcpPacket.DHCP_LEASE_TIME, 103 }; 104 private static final String DHCP_HOSTNAME = "testhostname"; 105 106 private final Context mContext = InstrumentationRegistry.getContext(); 107 private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class); 108 private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class); 109 110 private TestNetworkInterface mTestIface; 111 private HandlerThread mHandlerThread; 112 private Handler mHandler; 113 private TapPacketReader mTapPacketReader; 114 115 private TetheredInterfaceRequester mTetheredInterfaceRequester; 116 private MyTetheringEventCallback mTetheringEventCallback; 117 118 private UiAutomation mUiAutomation = 119 InstrumentationRegistry.getInstrumentation().getUiAutomation(); 120 private boolean mRunTests; 121 122 @Before setUp()123 public void setUp() throws Exception { 124 // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive 125 // tethered client callbacks. The restricted networks permission is needed to ensure that 126 // EthernetManager#isAvailable will correctly return true on devices where Ethernet is 127 // marked restricted, like cuttlefish. 128 mUiAutomation.adoptShellPermissionIdentity( 129 MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE, 130 CONNECTIVITY_USE_RESTRICTED_NETWORKS); 131 mRunTests = mTm.isTetheringSupported() && mEm != null; 132 assumeTrue(mRunTests); 133 134 mHandlerThread = new HandlerThread(getClass().getSimpleName()); 135 mHandlerThread.start(); 136 mHandler = new Handler(mHandlerThread.getLooper()); 137 mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm); 138 } 139 cleanUp()140 private void cleanUp() throws Exception { 141 mTm.stopTethering(TETHERING_ETHERNET); 142 if (mTetheringEventCallback != null) { 143 mTetheringEventCallback.awaitInterfaceUntethered(); 144 mTetheringEventCallback.unregister(); 145 mTetheringEventCallback = null; 146 } 147 if (mTapPacketReader != null) { 148 TapPacketReader reader = mTapPacketReader; 149 mHandler.post(() -> reader.stop()); 150 mTapPacketReader = null; 151 } 152 mHandlerThread.quitSafely(); 153 mTetheredInterfaceRequester.release(); 154 mEm.setIncludeTestInterfaces(false); 155 maybeDeleteTestInterface(); 156 } 157 158 @After tearDown()159 public void tearDown() throws Exception { 160 try { 161 if (mRunTests) cleanUp(); 162 } finally { 163 mUiAutomation.dropShellPermissionIdentity(); 164 } 165 } 166 167 @Test testVirtualEthernetAlreadyExists()168 public void testVirtualEthernetAlreadyExists() throws Exception { 169 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 170 assumeFalse(mEm.isAvailable()); 171 172 mTestIface = createTestInterface(); 173 // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the 174 // interface will be placed in client mode, which will delete the link-local address. 175 // At that point NetworkInterface.getByName() will cease to work on the interface, because 176 // starting in R NetworkInterface can no longer see interfaces without IP addresses. 177 int mtu = getMTU(mTestIface); 178 179 Log.d(TAG, "Including test interfaces"); 180 mEm.setIncludeTestInterfaces(true); 181 182 final String iface = mTetheredInterfaceRequester.getInterface(); 183 assertEquals("TetheredInterfaceCallback for unexpected interface", 184 mTestIface.getInterfaceName(), iface); 185 186 checkVirtualEthernet(mTestIface, mtu); 187 } 188 189 @Test testVirtualEthernet()190 public void testVirtualEthernet() throws Exception { 191 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 192 assumeFalse(mEm.isAvailable()); 193 194 CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface(); 195 196 mEm.setIncludeTestInterfaces(true); 197 198 mTestIface = createTestInterface(); 199 200 final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 201 assertEquals("TetheredInterfaceCallback for unexpected interface", 202 mTestIface.getInterfaceName(), iface); 203 204 checkVirtualEthernet(mTestIface, getMTU(mTestIface)); 205 } 206 207 @Test testStaticIpv4()208 public void testStaticIpv4() throws Exception { 209 assumeFalse(mEm.isAvailable()); 210 211 mEm.setIncludeTestInterfaces(true); 212 213 mTestIface = createTestInterface(); 214 215 final String iface = mTetheredInterfaceRequester.getInterface(); 216 assertEquals("TetheredInterfaceCallback for unexpected interface", 217 mTestIface.getInterfaceName(), iface); 218 219 assertInvalidStaticIpv4Request(iface, null, null); 220 assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); 221 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); 222 assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); 223 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); 224 assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); 225 assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); 226 227 final String localAddr = "192.0.2.3/28"; 228 final String clientAddr = "192.0.2.2/28"; 229 mTetheringEventCallback = enableEthernetTethering(iface, 230 requestWithStaticIpv4(localAddr, clientAddr)); 231 232 mTetheringEventCallback.awaitInterfaceTethered(); 233 assertInterfaceHasIpAddress(iface, localAddr); 234 235 byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); 236 byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); 237 238 FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); 239 mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); 240 DhcpResults dhcpResults = runDhcp(fd, client1); 241 assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); 242 243 try { 244 runDhcp(fd, client2); 245 fail("Only one client should get an IP address"); 246 } catch (TimeoutException expected) { } 247 248 } 249 isRouterAdvertisement(byte[] pkt)250 private static boolean isRouterAdvertisement(byte[] pkt) { 251 if (pkt == null) return false; 252 253 ByteBuffer buf = ByteBuffer.wrap(pkt); 254 255 final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf); 256 if (ethHdr.etherType != ETHER_TYPE_IPV6) return false; 257 258 final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf); 259 if (ipv6Hdr.nextHeader != (byte) IPPROTO_ICMPV6) return false; 260 261 final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf); 262 return icmpv6Hdr.type == (short) ICMPV6_ROUTER_ADVERTISEMENT; 263 } 264 expectRouterAdvertisement(TapPacketReader reader, String iface, long timeoutMs)265 private static void expectRouterAdvertisement(TapPacketReader reader, String iface, 266 long timeoutMs) { 267 final long deadline = SystemClock.uptimeMillis() + timeoutMs; 268 do { 269 byte[] pkt = reader.popPacket(timeoutMs); 270 if (isRouterAdvertisement(pkt)) return; 271 timeoutMs = deadline - SystemClock.uptimeMillis(); 272 } while (timeoutMs > 0); 273 fail("Did not receive router advertisement on " + iface + " after " 274 + timeoutMs + "ms idle"); 275 } 276 expectLocalOnlyAddresses(String iface)277 private static void expectLocalOnlyAddresses(String iface) throws Exception { 278 final List<InterfaceAddress> interfaceAddresses = 279 NetworkInterface.getByName(iface).getInterfaceAddresses(); 280 281 boolean foundIpv6Ula = false; 282 for (InterfaceAddress ia : interfaceAddresses) { 283 final InetAddress addr = ia.getAddress(); 284 if (isIPv6ULA(addr)) { 285 foundIpv6Ula = true; 286 } 287 final int prefixlen = ia.getNetworkPrefixLength(); 288 final LinkAddress la = new LinkAddress(addr, prefixlen); 289 if (la.isIpv6() && la.isGlobalPreferred()) { 290 fail("Found global IPv6 address on local-only interface: " + interfaceAddresses); 291 } 292 } 293 294 assertTrue("Did not find IPv6 ULA on local-only interface " + iface, 295 foundIpv6Ula); 296 } 297 298 @Test testLocalOnlyTethering()299 public void testLocalOnlyTethering() throws Exception { 300 assumeFalse(mEm.isAvailable()); 301 302 mEm.setIncludeTestInterfaces(true); 303 304 mTestIface = createTestInterface(); 305 306 final String iface = mTetheredInterfaceRequester.getInterface(); 307 assertEquals("TetheredInterfaceCallback for unexpected interface", 308 mTestIface.getInterfaceName(), iface); 309 310 final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET) 311 .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build(); 312 mTetheringEventCallback = enableEthernetTethering(iface, request); 313 mTetheringEventCallback.awaitInterfaceLocalOnly(); 314 315 // makePacketReader only works after tethering is started, because until then the interface 316 // does not have an IP address, and unprivileged apps cannot see interfaces without IP 317 // addresses. This shouldn't be flaky because the TAP interface will buffer all packets even 318 // before the reader is started. 319 FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); 320 mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); 321 322 expectRouterAdvertisement(mTapPacketReader, iface, 2000 /* timeoutMs */); 323 expectLocalOnlyAddresses(iface); 324 } 325 isAdbOverNetwork()326 private boolean isAdbOverNetwork() { 327 // If adb TCP port opened, this test may running by adb over network. 328 return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) 329 || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); 330 } 331 332 @Test testPhysicalEthernet()333 public void testPhysicalEthernet() throws Exception { 334 assumeTrue(mEm.isAvailable()); 335 // Do not run this test if adb is over network and ethernet is connected. 336 // It is likely the adb run over ethernet, the adb would break when ethernet is switching 337 // from client mode to server mode. See b/160389275. 338 assumeFalse(isAdbOverNetwork()); 339 340 // Get an interface to use. 341 final String iface = mTetheredInterfaceRequester.getInterface(); 342 343 // Enable Ethernet tethering and check that it starts. 344 mTetheringEventCallback = enableEthernetTethering(iface); 345 346 // There is nothing more we can do on a physical interface without connecting an actual 347 // client, which is not possible in this test. 348 } 349 350 private static final class MyTetheringEventCallback implements TetheringEventCallback { 351 private final TetheringManager mTm; 352 private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); 353 private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1); 354 private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1); 355 private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1); 356 private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); 357 private final TetheringInterface mIface; 358 359 private volatile boolean mInterfaceWasTethered = false; 360 private volatile boolean mInterfaceWasLocalOnly = false; 361 private volatile boolean mUnregistered = false; 362 private volatile Collection<TetheredClient> mClients = null; 363 MyTetheringEventCallback(TetheringManager tm, String iface)364 MyTetheringEventCallback(TetheringManager tm, String iface) { 365 mTm = tm; 366 mIface = new TetheringInterface(TETHERING_ETHERNET, iface); 367 } 368 unregister()369 public void unregister() { 370 mTm.unregisterTetheringEventCallback(this); 371 mUnregistered = true; 372 } 373 @Override onTetheredInterfacesChanged(List<String> interfaces)374 public void onTetheredInterfacesChanged(List<String> interfaces) { 375 fail("Should only call callback that takes a Set<TetheringInterface>"); 376 } 377 378 @Override onTetheredInterfacesChanged(Set<TetheringInterface> interfaces)379 public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) { 380 // Ignore stale callbacks registered by previous test cases. 381 if (mUnregistered) return; 382 383 if (!mInterfaceWasTethered && interfaces.contains(mIface)) { 384 // This interface is being tethered for the first time. 385 Log.d(TAG, "Tethering started: " + interfaces); 386 mInterfaceWasTethered = true; 387 mTetheringStartedLatch.countDown(); 388 } else if (mInterfaceWasTethered && !interfaces.contains(mIface)) { 389 Log.d(TAG, "Tethering stopped: " + interfaces); 390 mTetheringStoppedLatch.countDown(); 391 } 392 } 393 394 @Override onLocalOnlyInterfacesChanged(List<String> interfaces)395 public void onLocalOnlyInterfacesChanged(List<String> interfaces) { 396 fail("Should only call callback that takes a Set<TetheringInterface>"); 397 } 398 399 @Override onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces)400 public void onLocalOnlyInterfacesChanged(Set<TetheringInterface> interfaces) { 401 // Ignore stale callbacks registered by previous test cases. 402 if (mUnregistered) return; 403 404 if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) { 405 // This interface is being put into local-only mode for the first time. 406 Log.d(TAG, "Local-only started: " + interfaces); 407 mInterfaceWasLocalOnly = true; 408 mLocalOnlyStartedLatch.countDown(); 409 } else if (mInterfaceWasLocalOnly && !interfaces.contains(mIface)) { 410 Log.d(TAG, "Local-only stopped: " + interfaces); 411 mLocalOnlyStoppedLatch.countDown(); 412 } 413 } 414 awaitInterfaceTethered()415 public void awaitInterfaceTethered() throws Exception { 416 assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms", 417 mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 418 } 419 awaitInterfaceLocalOnly()420 public void awaitInterfaceLocalOnly() throws Exception { 421 assertTrue("Ethernet not local-only after " + TIMEOUT_MS + "ms", 422 mLocalOnlyStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 423 } 424 awaitInterfaceUntethered()425 public void awaitInterfaceUntethered() throws Exception { 426 // Don't block teardown if the interface was never tethered. 427 // This is racy because the interface might become tethered right after this check, but 428 // that can only happen in tearDown if startTethering timed out, which likely means 429 // the test has already failed. 430 if (!mInterfaceWasTethered && !mInterfaceWasLocalOnly) return; 431 432 if (mInterfaceWasTethered) { 433 assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", 434 mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 435 } else if (mInterfaceWasLocalOnly) { 436 assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", 437 mLocalOnlyStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 438 } else { 439 fail(mIface + " cannot be both tethered and local-only. Update this test class."); 440 } 441 } 442 443 @Override onError(String ifName, int error)444 public void onError(String ifName, int error) { 445 // Ignore stale callbacks registered by previous test cases. 446 if (mUnregistered) return; 447 448 fail("TetheringEventCallback got error:" + error + " on iface " + ifName); 449 } 450 451 @Override onClientsChanged(Collection<TetheredClient> clients)452 public void onClientsChanged(Collection<TetheredClient> clients) { 453 // Ignore stale callbacks registered by previous test cases. 454 if (mUnregistered) return; 455 456 Log.d(TAG, "Got clients changed: " + clients); 457 mClients = clients; 458 if (clients.size() > 0) { 459 mClientConnectedLatch.countDown(); 460 } 461 } 462 awaitClientConnected()463 public Collection<TetheredClient> awaitClientConnected() throws Exception { 464 assertTrue("Did not receive client connected callback after " + TIMEOUT_MS + "ms", 465 mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 466 return mClients; 467 } 468 } 469 enableEthernetTethering(String iface, TetheringRequest request)470 private MyTetheringEventCallback enableEthernetTethering(String iface, 471 TetheringRequest request) throws Exception { 472 MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface); 473 mTm.registerTetheringEventCallback(mHandler::post, callback); 474 475 StartTetheringCallback startTetheringCallback = new StartTetheringCallback() { 476 @Override 477 public void onTetheringFailed(int resultCode) { 478 fail("Unexpectedly got onTetheringFailed"); 479 } 480 }; 481 Log.d(TAG, "Starting Ethernet tethering"); 482 mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); 483 484 final int connectivityType = request.getConnectivityScope(); 485 switch (connectivityType) { 486 case CONNECTIVITY_SCOPE_GLOBAL: 487 callback.awaitInterfaceTethered(); 488 break; 489 case CONNECTIVITY_SCOPE_LOCAL: 490 callback.awaitInterfaceLocalOnly(); 491 break; 492 default: 493 fail("Unexpected connectivity type requested: " + connectivityType); 494 } 495 496 return callback; 497 } 498 enableEthernetTethering(String iface)499 private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { 500 return enableEthernetTethering(iface, 501 new TetheringRequest.Builder(TETHERING_ETHERNET) 502 .setShouldShowEntitlementUi(false).build()); 503 } 504 getMTU(TestNetworkInterface iface)505 private int getMTU(TestNetworkInterface iface) throws SocketException { 506 NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); 507 assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); 508 return nif.getMTU(); 509 } 510 makePacketReader(FileDescriptor fd, int mtu)511 private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { 512 final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); 513 mHandler.post(() -> reader.start()); 514 HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS); 515 return reader; 516 } 517 checkVirtualEthernet(TestNetworkInterface iface, int mtu)518 private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { 519 FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); 520 mTapPacketReader = makePacketReader(fd, mtu); 521 mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName()); 522 checkTetheredClientCallbacks(fd); 523 } 524 runDhcp(FileDescriptor fd, byte[] clientMacAddr)525 private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception { 526 // We have to retransmit DHCP requests because IpServer declares itself to be ready before 527 // its DhcpServer is actually started. TODO: fix this race and remove this loop. 528 DhcpPacket offerPacket = null; 529 for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) { 530 Log.d(TAG, "Sending DHCP discover"); 531 sendDhcpDiscover(fd, clientMacAddr); 532 offerPacket = getNextDhcpPacket(); 533 if (offerPacket instanceof DhcpOfferPacket) break; 534 } 535 if (!(offerPacket instanceof DhcpOfferPacket)) { 536 throw new TimeoutException("No DHCPOFFER received on interface within timeout"); 537 } 538 539 sendDhcpRequest(fd, offerPacket, clientMacAddr); 540 DhcpPacket ackPacket = getNextDhcpPacket(); 541 if (!(ackPacket instanceof DhcpAckPacket)) { 542 throw new TimeoutException("No DHCPACK received on interface within timeout"); 543 } 544 545 return ackPacket.toDhcpResults(); 546 } 547 checkTetheredClientCallbacks(FileDescriptor fd)548 private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { 549 // Create a fake client. 550 byte[] clientMacAddr = new byte[6]; 551 new Random().nextBytes(clientMacAddr); 552 553 DhcpResults dhcpResults = runDhcp(fd, clientMacAddr); 554 555 final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected(); 556 assertEquals(1, clients.size()); 557 final TetheredClient client = clients.iterator().next(); 558 559 // Check the MAC address. 560 assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); 561 assertEquals(TETHERING_ETHERNET, client.getTetheringType()); 562 563 // Check the hostname. 564 assertEquals(1, client.getAddresses().size()); 565 TetheredClient.AddressInfo info = client.getAddresses().get(0); 566 assertEquals(DHCP_HOSTNAME, info.getHostname()); 567 568 // Check the address is the one that was handed out in the DHCP ACK. 569 assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); 570 571 // Check that the lifetime is correct +/- 10s. 572 final long now = SystemClock.elapsedRealtime(); 573 final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; 574 final String msg = String.format("IP address should have lifetime of %d, got %d", 575 dhcpResults.leaseDuration, actualLeaseDuration); 576 assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); 577 } 578 getNextDhcpPacket()579 private DhcpPacket getNextDhcpPacket() throws ParseException { 580 byte[] packet; 581 while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) { 582 try { 583 return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2); 584 } catch (DhcpPacket.ParseException e) { 585 // Not a DHCP packet. Continue. 586 } 587 } 588 return null; 589 } 590 591 private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback { 592 private final Handler mHandler; 593 private final EthernetManager mEm; 594 595 private TetheredInterfaceRequest mRequest; 596 private final CompletableFuture<String> mFuture = new CompletableFuture<>(); 597 TetheredInterfaceRequester(Handler handler, EthernetManager em)598 TetheredInterfaceRequester(Handler handler, EthernetManager em) { 599 mHandler = handler; 600 mEm = em; 601 } 602 603 @Override onAvailable(String iface)604 public void onAvailable(String iface) { 605 Log.d(TAG, "Ethernet interface available: " + iface); 606 mFuture.complete(iface); 607 } 608 609 @Override onUnavailable()610 public void onUnavailable() { 611 mFuture.completeExceptionally(new IllegalStateException("onUnavailable received")); 612 } 613 requestInterface()614 public CompletableFuture<String> requestInterface() { 615 assertNull("BUG: more than one tethered interface request", mRequest); 616 Log.d(TAG, "Requesting tethered interface"); 617 mRequest = mEm.requestTetheredInterface(mHandler::post, this); 618 return mFuture; 619 } 620 getInterface()621 public String getInterface() throws Exception { 622 return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 623 } 624 release()625 public void release() { 626 if (mRequest != null) { 627 mFuture.obtrudeException(new IllegalStateException("Request already released")); 628 mRequest.release(); 629 mRequest = null; 630 } 631 } 632 } 633 sendDhcpDiscover(FileDescriptor fd, byte[] macAddress)634 private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception { 635 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2, 636 new Random().nextInt() /* transactionId */, (short) 0 /* secs */, 637 macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS, 638 false /* rapid commit */, DHCP_HOSTNAME); 639 sendPacket(fd, packet); 640 } 641 sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)642 private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress) 643 throws Exception { 644 DhcpResults results = offerPacket.toDhcpResults(); 645 Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress(); 646 Inet4Address serverIdentifier = results.serverAddress; 647 ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2, 648 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */, 649 false /* broadcast */, macAddress, clientIp /* requestedIpAddress */, 650 serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME); 651 sendPacket(fd, packet); 652 } 653 sendPacket(FileDescriptor fd, ByteBuffer packet)654 private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception { 655 assertNotNull("Only tests on virtual interfaces can send packets", fd); 656 Os.write(fd, packet); 657 } 658 assertLinkAddressMatches(LinkAddress l1, LinkAddress l2)659 public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { 660 // Check all fields except the deprecation and expiry times. 661 String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); 662 assertTrue(msg, l1.isSameAddressAs(l2)); 663 assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); 664 assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); 665 } 666 requestWithStaticIpv4(String local, String client)667 private TetheringRequest requestWithStaticIpv4(String local, String client) { 668 LinkAddress localAddr = local == null ? null : new LinkAddress(local); 669 LinkAddress clientAddr = client == null ? null : new LinkAddress(client); 670 return new TetheringRequest.Builder(TETHERING_ETHERNET) 671 .setStaticIpv4Addresses(localAddr, clientAddr) 672 .setShouldShowEntitlementUi(false).build(); 673 } 674 assertInvalidStaticIpv4Request(String iface, String local, String client)675 private void assertInvalidStaticIpv4Request(String iface, String local, String client) 676 throws Exception { 677 try { 678 enableEthernetTethering(iface, requestWithStaticIpv4(local, client)); 679 fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); 680 } catch (IllegalArgumentException | NullPointerException expected) { } 681 } 682 assertInterfaceHasIpAddress(String iface, String expected)683 private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { 684 LinkAddress expectedAddr = new LinkAddress(expected); 685 NetworkInterface nif = NetworkInterface.getByName(iface); 686 for (InterfaceAddress ia : nif.getInterfaceAddresses()) { 687 final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); 688 if (expectedAddr.equals(addr)) { 689 return; 690 } 691 } 692 fail("Expected " + iface + " to have IP address " + expected + ", found " 693 + nif.getInterfaceAddresses()); 694 } 695 createTestInterface()696 private TestNetworkInterface createTestInterface() throws Exception { 697 TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); 698 TestNetworkInterface iface = tnm.createTapInterface(); 699 Log.d(TAG, "Created test interface " + iface.getInterfaceName()); 700 return iface; 701 } 702 maybeDeleteTestInterface()703 private void maybeDeleteTestInterface() throws Exception { 704 if (mTestIface != null) { 705 mTestIface.getFileDescriptor().close(); 706 Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName()); 707 mTestIface = null; 708 } 709 } 710 } 711