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.net.InetAddresses.parseNumericAddress; 20 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; 21 import static android.net.TetheringManager.TETHERING_ETHERNET; 22 import static android.net.TetheringTester.TestDnsPacket; 23 import static android.net.TetheringTester.buildIcmpEchoPacketV4; 24 import static android.net.TetheringTester.buildUdpPacket; 25 import static android.net.TetheringTester.isExpectedIcmpPacket; 26 import static android.net.TetheringTester.isExpectedUdpDnsPacket; 27 import static android.system.OsConstants.ICMP_ECHO; 28 import static android.system.OsConstants.ICMP_ECHOREPLY; 29 30 import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA; 31 import static com.android.net.module.util.HexDump.dumpHexString; 32 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE; 33 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; 34 35 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertTrue; 38 import static org.junit.Assert.fail; 39 import static org.junit.Assume.assumeFalse; 40 import static org.junit.Assume.assumeTrue; 41 42 import android.net.TetheringManager.TetheringRequest; 43 import android.net.TetheringTester.TetheredDevice; 44 import android.os.Build; 45 import android.os.SystemClock; 46 import android.os.SystemProperties; 47 import android.util.Log; 48 49 import androidx.annotation.NonNull; 50 import androidx.test.filters.LargeTest; 51 import androidx.test.runner.AndroidJUnit4; 52 53 import com.android.net.module.util.Ipv6Utils; 54 import com.android.net.module.util.Struct; 55 import com.android.net.module.util.structs.Ipv4Header; 56 import com.android.net.module.util.structs.UdpHeader; 57 import com.android.testutils.DevSdkIgnoreRule; 58 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 59 import com.android.testutils.NetworkStackModuleTest; 60 import com.android.testutils.TapPacketReader; 61 62 import org.junit.BeforeClass; 63 import org.junit.Rule; 64 import org.junit.Test; 65 import org.junit.runner.RunWith; 66 67 import java.io.FileDescriptor; 68 import java.net.Inet4Address; 69 import java.net.Inet6Address; 70 import java.net.InetAddress; 71 import java.net.InterfaceAddress; 72 import java.net.NetworkInterface; 73 import java.nio.ByteBuffer; 74 import java.util.Arrays; 75 import java.util.Collection; 76 import java.util.List; 77 import java.util.Random; 78 import java.util.concurrent.CompletableFuture; 79 import java.util.concurrent.TimeUnit; 80 import java.util.concurrent.TimeoutException; 81 82 @RunWith(AndroidJUnit4.class) 83 @LargeTest 84 public class EthernetTetheringTest extends EthernetTetheringTestBase { 85 @Rule 86 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 87 88 private static final String TAG = EthernetTetheringTest.class.getSimpleName(); 89 90 private static final short DNS_PORT = 53; 91 private static final short ICMPECHO_ID = 0x0; 92 private static final short ICMPECHO_SEQ = 0x0; 93 94 // TODO: use class DnsPacket to build DNS query and reply message once DnsPacket supports 95 // building packet for given arguments. 96 private static final ByteBuffer DNS_QUERY = ByteBuffer.wrap(new byte[] { 97 // scapy.DNS( 98 // id=0xbeef, 99 // qr=0, 100 // qd=scapy.DNSQR(qname="hello.example.com")) 101 // 102 /* Header */ 103 (byte) 0xbe, (byte) 0xef, /* Transaction ID: 0xbeef */ 104 (byte) 0x01, (byte) 0x00, /* Flags: rd */ 105 (byte) 0x00, (byte) 0x01, /* Questions: 1 */ 106 (byte) 0x00, (byte) 0x00, /* Answer RRs: 0 */ 107 (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */ 108 (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */ 109 /* Queries */ 110 (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c, 111 (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65, 112 (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, 113 (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63, 114 (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */ 115 (byte) 0x00, (byte) 0x01, /* Type: A */ 116 (byte) 0x00, (byte) 0x01 /* Class: IN */ 117 }); 118 119 private static final byte[] DNS_REPLY = new byte[] { 120 // scapy.DNS( 121 // id=0, 122 // qr=1, 123 // qd=scapy.DNSQR(qname="hello.example.com"), 124 // an=scapy.DNSRR(rrname="hello.example.com", rdata='1.2.3.4')) 125 // 126 /* Header */ 127 (byte) 0x00, (byte) 0x00, /* Transaction ID: 0x0, must be updated by dns query id */ 128 (byte) 0x81, (byte) 0x00, /* Flags: qr rd */ 129 (byte) 0x00, (byte) 0x01, /* Questions: 1 */ 130 (byte) 0x00, (byte) 0x01, /* Answer RRs: 1 */ 131 (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */ 132 (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */ 133 /* Queries */ 134 (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c, 135 (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65, 136 (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, 137 (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63, 138 (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */ 139 (byte) 0x00, (byte) 0x01, /* Type: A */ 140 (byte) 0x00, (byte) 0x01, /* Class: IN */ 141 /* Answers */ 142 (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c, 143 (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65, 144 (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, 145 (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63, 146 (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */ 147 (byte) 0x00, (byte) 0x01, /* Type: A */ 148 (byte) 0x00, (byte) 0x01, /* Class: IN */ 149 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* Time to live: 0 */ 150 (byte) 0x00, (byte) 0x04, /* Data length: 4 */ 151 (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */ 152 }; 153 154 /** Enable/disable tethering once before running the tests. */ 155 @BeforeClass setUpOnce()156 public static void setUpOnce() throws Exception { 157 // The first test case may experience tethering restart with IP conflict handling. 158 // Tethering would cache the last upstreams so that the next enabled tethering avoids 159 // picking up the address that is in conflict with the upstreams. To protect subsequent 160 // tests, turn tethering on and off before running them. 161 MyTetheringEventCallback callback = null; 162 TestNetworkInterface testIface = null; 163 assumeTrue(sEm != null); 164 try { 165 // If the physical ethernet interface is available, do nothing. 166 if (isInterfaceForTetheringAvailable()) return; 167 168 testIface = createTestInterface(); 169 setIncludeTestInterfaces(true); 170 171 callback = enableEthernetTethering(testIface.getInterfaceName(), null); 172 callback.awaitUpstreamChanged(true /* throwTimeoutException */); 173 } catch (TimeoutException e) { 174 Log.d(TAG, "WARNNING " + e); 175 } finally { 176 maybeCloseTestInterface(testIface); 177 maybeUnregisterTetheringEventCallback(callback); 178 179 setIncludeTestInterfaces(false); 180 } 181 } 182 183 @Test testVirtualEthernetAlreadyExists()184 public void testVirtualEthernetAlreadyExists() throws Exception { 185 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 186 assumeFalse(isInterfaceForTetheringAvailable()); 187 188 TestNetworkInterface downstreamIface = null; 189 MyTetheringEventCallback tetheringEventCallback = null; 190 TapPacketReader downstreamReader = null; 191 192 try { 193 downstreamIface = createTestInterface(); 194 // This must be done now because as soon as setIncludeTestInterfaces(true) is called, 195 // the interface will be placed in client mode, which will delete the link-local 196 // address. At that point NetworkInterface.getByName() will cease to work on the 197 // interface, because starting in R NetworkInterface can no longer see interfaces 198 // without IP addresses. 199 int mtu = getMTU(downstreamIface); 200 201 Log.d(TAG, "Including test interfaces"); 202 setIncludeTestInterfaces(true); 203 204 final String iface = getTetheredInterface(); 205 assertEquals("TetheredInterfaceCallback for unexpected interface", 206 downstreamIface.getInterfaceName(), iface); 207 208 // Check virtual ethernet. 209 FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); 210 downstreamReader = makePacketReader(fd, mtu); 211 tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(), 212 null /* any upstream */); 213 checkTetheredClientCallbacks(downstreamReader, tetheringEventCallback); 214 } finally { 215 maybeStopTapPacketReader(downstreamReader); 216 maybeCloseTestInterface(downstreamIface); 217 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 218 } 219 } 220 221 @Test testVirtualEthernet()222 public void testVirtualEthernet() throws Exception { 223 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 224 assumeFalse(isInterfaceForTetheringAvailable()); 225 226 CompletableFuture<String> futureIface = requestTetheredInterface(); 227 228 setIncludeTestInterfaces(true); 229 230 TestNetworkInterface downstreamIface = null; 231 MyTetheringEventCallback tetheringEventCallback = null; 232 TapPacketReader downstreamReader = null; 233 234 try { 235 downstreamIface = createTestInterface(); 236 237 final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); 238 assertEquals("TetheredInterfaceCallback for unexpected interface", 239 downstreamIface.getInterfaceName(), iface); 240 241 // Check virtual ethernet. 242 FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); 243 downstreamReader = makePacketReader(fd, getMTU(downstreamIface)); 244 tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(), 245 null /* any upstream */); 246 checkTetheredClientCallbacks(downstreamReader, tetheringEventCallback); 247 } finally { 248 maybeStopTapPacketReader(downstreamReader); 249 maybeCloseTestInterface(downstreamIface); 250 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 251 } 252 } 253 254 @Test testStaticIpv4()255 public void testStaticIpv4() throws Exception { 256 assumeFalse(isInterfaceForTetheringAvailable()); 257 258 setIncludeTestInterfaces(true); 259 260 TestNetworkInterface downstreamIface = null; 261 MyTetheringEventCallback tetheringEventCallback = null; 262 TapPacketReader downstreamReader = null; 263 264 try { 265 downstreamIface = createTestInterface(); 266 267 final String iface = getTetheredInterface(); 268 assertEquals("TetheredInterfaceCallback for unexpected interface", 269 downstreamIface.getInterfaceName(), iface); 270 271 assertInvalidStaticIpv4Request(iface, null, null); 272 assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); 273 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); 274 assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); 275 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); 276 assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); 277 assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); 278 279 final String localAddr = "192.0.2.3/28"; 280 final String clientAddr = "192.0.2.2/28"; 281 tetheringEventCallback = enableEthernetTethering(iface, 282 requestWithStaticIpv4(localAddr, clientAddr), null /* any upstream */); 283 284 tetheringEventCallback.awaitInterfaceTethered(); 285 assertInterfaceHasIpAddress(iface, localAddr); 286 287 byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); 288 byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); 289 290 FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); 291 downstreamReader = makePacketReader(fd, getMTU(downstreamIface)); 292 TetheringTester tester = new TetheringTester(downstreamReader); 293 DhcpResults dhcpResults = tester.runDhcp(client1); 294 assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); 295 296 try { 297 tester.runDhcp(client2); 298 fail("Only one client should get an IP address"); 299 } catch (TimeoutException expected) { } 300 } finally { 301 maybeStopTapPacketReader(downstreamReader); 302 maybeCloseTestInterface(downstreamIface); 303 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 304 } 305 } 306 expectLocalOnlyAddresses(String iface)307 private static void expectLocalOnlyAddresses(String iface) throws Exception { 308 final List<InterfaceAddress> interfaceAddresses = 309 NetworkInterface.getByName(iface).getInterfaceAddresses(); 310 311 boolean foundIpv6Ula = false; 312 for (InterfaceAddress ia : interfaceAddresses) { 313 final InetAddress addr = ia.getAddress(); 314 if (isIPv6ULA(addr)) { 315 foundIpv6Ula = true; 316 } 317 final int prefixlen = ia.getNetworkPrefixLength(); 318 final LinkAddress la = new LinkAddress(addr, prefixlen); 319 if (la.isIpv6() && la.isGlobalPreferred()) { 320 fail("Found global IPv6 address on local-only interface: " + interfaceAddresses); 321 } 322 } 323 324 assertTrue("Did not find IPv6 ULA on local-only interface " + iface, 325 foundIpv6Ula); 326 } 327 328 @Test testLocalOnlyTethering()329 public void testLocalOnlyTethering() throws Exception { 330 assumeFalse(isInterfaceForTetheringAvailable()); 331 332 setIncludeTestInterfaces(true); 333 334 TestNetworkInterface downstreamIface = null; 335 MyTetheringEventCallback tetheringEventCallback = null; 336 TapPacketReader downstreamReader = null; 337 338 try { 339 downstreamIface = createTestInterface(); 340 341 final String iface = getTetheredInterface(); 342 assertEquals("TetheredInterfaceCallback for unexpected interface", 343 downstreamIface.getInterfaceName(), iface); 344 345 final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET) 346 .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build(); 347 tetheringEventCallback = enableEthernetTethering(iface, request, 348 null /* any upstream */); 349 tetheringEventCallback.awaitInterfaceLocalOnly(); 350 351 // makePacketReader only works after tethering is started, because until then the 352 // interface does not have an IP address, and unprivileged apps cannot see interfaces 353 // without IP addresses. This shouldn't be flaky because the TAP interface will buffer 354 // all packets even before the reader is started. 355 downstreamReader = makePacketReader(downstreamIface); 356 357 waitForRouterAdvertisement(downstreamReader, iface, WAIT_RA_TIMEOUT_MS); 358 expectLocalOnlyAddresses(iface); 359 360 // After testing the IPv6 local address, the DHCP server may still be in the process 361 // of being created. If the downstream interface is killed by the test while the 362 // DHCP server is starting, a DHCP server error may occur. To ensure that the DHCP 363 // server has started completely before finishing the test, also test the dhcp server 364 // by calling runDhcp. 365 final TetheringTester tester = new TetheringTester(downstreamReader); 366 tester.runDhcp(MacAddress.fromString("1:2:3:4:5:6").toByteArray()); 367 } finally { 368 maybeStopTapPacketReader(downstreamReader); 369 maybeCloseTestInterface(downstreamIface); 370 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 371 } 372 } 373 isAdbOverNetwork()374 private boolean isAdbOverNetwork() { 375 // If adb TCP port opened, this test may running by adb over network. 376 return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) 377 || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); 378 } 379 380 @Test testPhysicalEthernet()381 public void testPhysicalEthernet() throws Exception { 382 assumeTrue(isInterfaceForTetheringAvailable()); 383 // Do not run this test if adb is over network and ethernet is connected. 384 // It is likely the adb run over ethernet, the adb would break when ethernet is switching 385 // from client mode to server mode. See b/160389275. 386 assumeFalse(isAdbOverNetwork()); 387 388 MyTetheringEventCallback tetheringEventCallback = null; 389 try { 390 // Get an interface to use. 391 final String iface = getTetheredInterface(); 392 393 // Enable Ethernet tethering and check that it starts. 394 tetheringEventCallback = enableEthernetTethering(iface, null /* any upstream */); 395 } finally { 396 stopEthernetTethering(tetheringEventCallback); 397 } 398 // There is nothing more we can do on a physical interface without connecting an actual 399 // client, which is not possible in this test. 400 } 401 checkTetheredClientCallbacks(final TapPacketReader packetReader, final MyTetheringEventCallback tetheringEventCallback)402 private void checkTetheredClientCallbacks(final TapPacketReader packetReader, 403 final MyTetheringEventCallback tetheringEventCallback) throws Exception { 404 // Create a fake client. 405 byte[] clientMacAddr = new byte[6]; 406 new Random().nextBytes(clientMacAddr); 407 408 TetheringTester tester = new TetheringTester(packetReader); 409 DhcpResults dhcpResults = tester.runDhcp(clientMacAddr); 410 411 final Collection<TetheredClient> clients = tetheringEventCallback.awaitClientConnected(); 412 assertEquals(1, clients.size()); 413 final TetheredClient client = clients.iterator().next(); 414 415 // Check the MAC address. 416 assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); 417 assertEquals(TETHERING_ETHERNET, client.getTetheringType()); 418 419 // Check the hostname. 420 assertEquals(1, client.getAddresses().size()); 421 TetheredClient.AddressInfo info = client.getAddresses().get(0); 422 assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname()); 423 424 // Check the address is the one that was handed out in the DHCP ACK. 425 assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); 426 427 // Check that the lifetime is correct +/- 10s. 428 final long now = SystemClock.elapsedRealtime(); 429 final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; 430 final String msg = String.format("IP address should have lifetime of %d, got %d", 431 dhcpResults.leaseDuration, actualLeaseDuration); 432 assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); 433 } 434 assertLinkAddressMatches(LinkAddress l1, LinkAddress l2)435 public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { 436 // Check all fields except the deprecation and expiry times. 437 String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); 438 assertTrue(msg, l1.isSameAddressAs(l2)); 439 assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); 440 assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); 441 } 442 requestWithStaticIpv4(String local, String client)443 private TetheringRequest requestWithStaticIpv4(String local, String client) { 444 LinkAddress localAddr = local == null ? null : new LinkAddress(local); 445 LinkAddress clientAddr = client == null ? null : new LinkAddress(client); 446 return new TetheringRequest.Builder(TETHERING_ETHERNET) 447 .setStaticIpv4Addresses(localAddr, clientAddr) 448 .setShouldShowEntitlementUi(false).build(); 449 } 450 assertInvalidStaticIpv4Request(String iface, String local, String client)451 private void assertInvalidStaticIpv4Request(String iface, String local, String client) 452 throws Exception { 453 try { 454 enableEthernetTethering(iface, requestWithStaticIpv4(local, client), 455 null /* any upstream */); 456 fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); 457 } catch (IllegalArgumentException | NullPointerException expected) { } 458 } 459 assertInterfaceHasIpAddress(String iface, String expected)460 private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { 461 LinkAddress expectedAddr = new LinkAddress(expected); 462 NetworkInterface nif = NetworkInterface.getByName(iface); 463 for (InterfaceAddress ia : nif.getInterfaceAddresses()) { 464 final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); 465 if (expectedAddr.equals(addr)) { 466 return; 467 } 468 } 469 fail("Expected " + iface + " to have IP address " + expected + ", found " 470 + nif.getInterfaceAddresses()); 471 } 472 473 @Test testIcmpv6Echo()474 public void testIcmpv6Echo() throws Exception { 475 runPing6Test(initTetheringTester(toList(TEST_IP4_ADDR, TEST_IP6_ADDR), 476 toList(TEST_IP4_DNS, TEST_IP6_DNS))); 477 } 478 runPing6Test(TetheringTester tester)479 private void runPing6Test(TetheringTester tester) throws Exception { 480 TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 481 Inet6Address remoteIp6Addr = (Inet6Address) parseNumericAddress("2400:222:222::222"); 482 ByteBuffer request = Ipv6Utils.buildEchoRequestPacket(tethered.macAddr, 483 tethered.routerMacAddr, tethered.ipv6Addr, remoteIp6Addr); 484 tester.verifyUpload(request, p -> { 485 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 486 487 return isExpectedIcmpPacket(p, false /* hasEth */, false /* isIpv4 */, 488 ICMPV6_ECHO_REQUEST_TYPE); 489 }); 490 491 ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket(remoteIp6Addr, tethered.ipv6Addr); 492 tester.verifyDownload(reply, p -> { 493 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 494 495 return isExpectedIcmpPacket(p, true /* hasEth */, false /* isIpv4 */, 496 ICMPV6_ECHO_REPLY_TYPE); 497 }); 498 } 499 500 @Test testTetherUdpV6()501 public void testTetherUdpV6() throws Exception { 502 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 503 toList(TEST_IP6_DNS)); 504 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 505 sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, 506 tethered.ipv6Addr, REMOTE_IP6_ADDR, tester, false /* is4To6 */); 507 sendDownloadPacketUdp(REMOTE_IP6_ADDR, tethered.ipv6Addr, tester, false /* is6To4 */); 508 509 // TODO: test BPF offload maps {rule, stats}. 510 } 511 512 // Test network topology: 513 // 514 // public network (rawip) private network 515 // | UE | 516 // +------------+ V +------------+------------+ V +------------+ 517 // | Sever +---------+ Upstream | Downstream +---------+ Client | 518 // +------------+ +------------+------------+ +------------+ 519 // remote ip public ip private ip 520 // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876 521 // runUdp4Test()522 private void runUdp4Test() throws Exception { 523 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 524 toList(TEST_IP4_DNS)); 525 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 526 527 // TODO: remove the connectivity verification for upstream connected notification race. 528 // Because async upstream connected notification can't guarantee the tethering routing is 529 // ready to use. Need to test tethering connectivity before testing. 530 // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes 531 // from upstream. That can guarantee that the routing is ready. Long term plan is that 532 // refactors upstream connected notification from async to sync. 533 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 534 535 final MacAddress srcMac = tethered.macAddr; 536 final MacAddress dstMac = tethered.routerMacAddr; 537 final InetAddress remoteIp = REMOTE_IP4_ADDR; 538 final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress(); 539 final InetAddress clientIp = tethered.ipv4Addr; 540 sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */); 541 sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */); 542 } 543 544 /** 545 * Basic IPv4 UDP tethering test. Verify that UDP tethered packets are transferred no matter 546 * using which data path. 547 */ 548 @Test testTetherUdpV4()549 public void testTetherUdpV4() throws Exception { 550 runUdp4Test(); 551 } 552 553 // Test network topology: 554 // 555 // public network (rawip) private network 556 // | UE (CLAT support) | 557 // +---------------+ V +------------+------------+ V +------------+ 558 // | NAT64 Gateway +---------+ Upstream | Downstream +---------+ Client | 559 // +---------------+ +------------+------------+ +------------+ 560 // remote ip public ip private ip 561 // [64:ff9b::808:808]:443 [clat ipv6]:9876 [TetheredDevice ipv4]:9876 562 // 563 // Note that CLAT IPv6 address is generated by ClatCoordinator. Get the CLAT IPv6 address by 564 // sending out an IPv4 packet and extracting the source address from CLAT translated IPv6 565 // packet. 566 // runClatUdpTest()567 private void runClatUdpTest() throws Exception { 568 // CLAT only starts on IPv6 only network. 569 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 570 toList(TEST_IP6_DNS)); 571 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 572 573 // Get CLAT IPv6 address. 574 final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); 575 576 // Send an IPv4 UDP packet in original direction. 577 // IPv4 packet -- CLAT translation --> IPv6 packet 578 sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, tethered.ipv4Addr, 579 REMOTE_IP4_ADDR, tester, true /* is4To6 */); 580 581 // Send an IPv6 UDP packet in reply direction. 582 // IPv6 packet -- CLAT translation --> IPv4 packet 583 sendDownloadPacketUdp(REMOTE_NAT64_ADDR, clatIp6, tester, true /* is6To4 */); 584 585 // TODO: test CLAT bpf maps. 586 } 587 588 // TODO: support R device. See b/234727688. 589 @Test 590 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherClatUdp()591 public void testTetherClatUdp() throws Exception { 592 runClatUdpTest(); 593 } 594 595 @Test testIcmpv4Echo()596 public void testIcmpv4Echo() throws Exception { 597 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 598 toList(TEST_IP4_DNS)); 599 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 600 601 // TODO: remove the connectivity verification for upstream connected notification race. 602 // See the same reason in runUdp4Test(). 603 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 604 605 final ByteBuffer request = buildIcmpEchoPacketV4(tethered.macAddr /* srcMac */, 606 tethered.routerMacAddr /* dstMac */, tethered.ipv4Addr /* srcIp */, 607 REMOTE_IP4_ADDR /* dstIp */, ICMP_ECHO, ICMPECHO_ID, ICMPECHO_SEQ); 608 tester.verifyUpload(request, p -> { 609 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 610 611 return isExpectedIcmpPacket(p, false /* hasEth */, true /* isIpv4 */, ICMP_ECHO); 612 }); 613 614 final ByteBuffer reply = buildIcmpEchoPacketV4(REMOTE_IP4_ADDR /* srcIp*/, 615 (Inet4Address) TEST_IP4_ADDR.getAddress() /* dstIp */, ICMP_ECHOREPLY, ICMPECHO_ID, 616 ICMPECHO_SEQ); 617 tester.verifyDownload(reply, p -> { 618 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 619 620 return isExpectedIcmpPacket(p, true /* hasEth */, true /* isIpv4 */, ICMP_ECHOREPLY); 621 }); 622 } 623 624 // TODO: support R device. See b/234727688. 625 @Test 626 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherClatIcmp()627 public void testTetherClatIcmp() throws Exception { 628 // CLAT only starts on IPv6 only network. 629 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 630 toList(TEST_IP6_DNS)); 631 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 632 633 // Get CLAT IPv6 address. 634 final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); 635 636 // Send an IPv4 ICMP packet in original direction. 637 // IPv4 packet -- CLAT translation --> IPv6 packet 638 final ByteBuffer request = buildIcmpEchoPacketV4(tethered.macAddr /* srcMac */, 639 tethered.routerMacAddr /* dstMac */, tethered.ipv4Addr /* srcIp */, 640 (Inet4Address) REMOTE_IP4_ADDR /* dstIp */, ICMP_ECHO, ICMPECHO_ID, ICMPECHO_SEQ); 641 tester.verifyUpload(request, p -> { 642 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 643 644 return isExpectedIcmpPacket(p, false /* hasEth */, false /* isIpv4 */, 645 ICMPV6_ECHO_REQUEST_TYPE); 646 }); 647 648 // Send an IPv6 ICMP packet in reply direction. 649 // IPv6 packet -- CLAT translation --> IPv4 packet 650 final ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket( 651 (Inet6Address) REMOTE_NAT64_ADDR /* srcIp */, clatIp6 /* dstIp */); 652 tester.verifyDownload(reply, p -> { 653 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 654 655 return isExpectedIcmpPacket(p, true /* hasEth */, true /* isIpv4 */, ICMP_ECHOREPLY); 656 }); 657 } 658 659 @NonNull buildDnsReplyMessageById(short id)660 private ByteBuffer buildDnsReplyMessageById(short id) { 661 byte[] replyMessage = Arrays.copyOf(DNS_REPLY, DNS_REPLY.length); 662 // Assign transaction id of reply message pattern with a given DNS transaction id. 663 replyMessage[0] = (byte) ((id >> 8) & 0xff); 664 replyMessage[1] = (byte) (id & 0xff); 665 Log.d(TAG, "Built DNS reply: " + dumpHexString(replyMessage)); 666 667 return ByteBuffer.wrap(replyMessage); 668 } 669 670 @NonNull sendDownloadPacketDnsV4(@onNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp, short srcPort, short dstPort, short dnsId, @NonNull final TetheringTester tester)671 private void sendDownloadPacketDnsV4(@NonNull final Inet4Address srcIp, 672 @NonNull final Inet4Address dstIp, short srcPort, short dstPort, short dnsId, 673 @NonNull final TetheringTester tester) throws Exception { 674 // DNS response transaction id must be copied from DNS query. Used by the requester 675 // to match up replies to outstanding queries. See RFC 1035 section 4.1.1. 676 final ByteBuffer dnsReplyMessage = buildDnsReplyMessageById(dnsId); 677 final ByteBuffer testPacket = buildUdpPacket((InetAddress) srcIp, 678 (InetAddress) dstIp, srcPort, dstPort, dnsReplyMessage); 679 680 tester.verifyDownload(testPacket, p -> { 681 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 682 return isExpectedUdpDnsPacket(p, true /* hasEther */, true /* isIpv4 */, 683 dnsReplyMessage); 684 }); 685 } 686 687 // Send IPv4 UDP DNS packet and return the forwarded DNS packet on upstream. 688 @NonNull sendUploadPacketDnsV4(@onNull final MacAddress srcMac, @NonNull final MacAddress dstMac, @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp, short srcPort, short dstPort, @NonNull final TetheringTester tester)689 private byte[] sendUploadPacketDnsV4(@NonNull final MacAddress srcMac, 690 @NonNull final MacAddress dstMac, @NonNull final Inet4Address srcIp, 691 @NonNull final Inet4Address dstIp, short srcPort, short dstPort, 692 @NonNull final TetheringTester tester) throws Exception { 693 final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp, 694 srcPort, dstPort, DNS_QUERY); 695 696 return tester.verifyUpload(testPacket, p -> { 697 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 698 return isExpectedUdpDnsPacket(p, false /* hasEther */, true /* isIpv4 */, 699 DNS_QUERY); 700 }); 701 } 702 703 @Test testTetherUdpV4Dns()704 public void testTetherUdpV4Dns() throws Exception { 705 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 706 toList(TEST_IP4_DNS)); 707 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 708 709 // TODO: remove the connectivity verification for upstream connected notification race. 710 // See the same reason in runUdp4Test(). 711 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 712 713 // [1] Send DNS query. 714 // tethered device --> downstream --> dnsmasq forwarding --> upstream --> DNS server 715 // 716 // Need to extract DNS transaction id and source port from dnsmasq forwarded DNS query 717 // packet. dnsmasq forwarding creats new query which means UDP source port and DNS 718 // transaction id are changed from original sent DNS query. See forward_query() in 719 // external/dnsmasq/src/forward.c. Note that #TetheringTester.isExpectedUdpDnsPacket 720 // guarantees that |forwardedQueryPacket| is a valid DNS packet. So we can parse it as DNS 721 // packet. 722 final MacAddress srcMac = tethered.macAddr; 723 final MacAddress dstMac = tethered.routerMacAddr; 724 final Inet4Address clientIp = tethered.ipv4Addr; 725 final Inet4Address gatewayIp = tethered.ipv4Gatway; 726 final byte[] forwardedQueryPacket = sendUploadPacketDnsV4(srcMac, dstMac, clientIp, 727 gatewayIp, LOCAL_PORT, DNS_PORT, tester); 728 final ByteBuffer buf = ByteBuffer.wrap(forwardedQueryPacket); 729 Struct.parse(Ipv4Header.class, buf); 730 final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf); 731 final TestDnsPacket dnsQuery = TestDnsPacket.getTestDnsPacket(buf); 732 assertNotNull(dnsQuery); 733 Log.d(TAG, "Forwarded UDP source port: " + udpHeader.srcPort + ", DNS query id: " 734 + dnsQuery.getHeader().getId()); 735 736 // [2] Send DNS reply. 737 // DNS server --> upstream --> dnsmasq forwarding --> downstream --> tethered device 738 // 739 // DNS reply transaction id must be copied from DNS query. Used by the requester to match 740 // up replies to outstanding queries. See RFC 1035 section 4.1.1. 741 final Inet4Address remoteIp = (Inet4Address) TEST_IP4_DNS; 742 final Inet4Address tetheringUpstreamIp = (Inet4Address) TEST_IP4_ADDR.getAddress(); 743 sendDownloadPacketDnsV4(remoteIp, tetheringUpstreamIp, DNS_PORT, 744 (short) udpHeader.srcPort, (short) dnsQuery.getHeader().getId(), tester); 745 } 746 747 @Test testTetherTcpV4()748 public void testTetherTcpV4() throws Exception { 749 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 750 toList(TEST_IP4_DNS)); 751 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 752 753 // TODO: remove the connectivity verification for upstream connected notification race. 754 // See the same reason in runUdp4Test(). 755 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 756 757 runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */, 758 tethered.ipv4Addr /* uploadSrcIp */, REMOTE_IP4_ADDR /* uploadDstIp */, 759 REMOTE_IP4_ADDR /* downloadSrcIp */, TEST_IP4_ADDR.getAddress() /* downloadDstIp */, 760 tester, false /* isClat */); 761 } 762 763 @Test testTetherTcpV6()764 public void testTetherTcpV6() throws Exception { 765 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 766 toList(TEST_IP6_DNS)); 767 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 768 769 runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */, 770 tethered.ipv6Addr /* uploadSrcIp */, REMOTE_IP6_ADDR /* uploadDstIp */, 771 REMOTE_IP6_ADDR /* downloadSrcIp */, tethered.ipv6Addr /* downloadDstIp */, 772 tester, false /* isClat */); 773 } 774 775 // TODO: support R device. See b/234727688. 776 @Test 777 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherClatTcp()778 public void testTetherClatTcp() throws Exception { 779 // CLAT only starts on IPv6 only network. 780 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 781 toList(TEST_IP6_DNS)); 782 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 783 784 // Get CLAT IPv6 address. 785 final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); 786 787 runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */, 788 tethered.ipv4Addr /* uploadSrcIp */, REMOTE_IP4_ADDR /* uploadDstIp */, 789 REMOTE_NAT64_ADDR /* downloadSrcIp */, clatIp6 /* downloadDstIp */, 790 tester, true /* isClat */); 791 } 792 793 private static final byte[] ZeroLengthDhcpPacket = new byte[] { 794 // scapy.Ether( 795 // dst="ff:ff:ff:ff:ff:ff") 796 // scapy.IP( 797 // dst="255.255.255.255") 798 // scapy.UDP(sport=68, dport=67) 799 /* Ethernet Header */ 800 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 801 (byte) 0xe0, (byte) 0x4f, (byte) 0x43, (byte) 0xe6, (byte) 0xfb, (byte) 0xd2, 802 (byte) 0x08, (byte) 0x00, 803 /* Ip header */ 804 (byte) 0x45, (byte) 0x00, (byte) 0x00, (byte) 0x1c, (byte) 0x00, (byte) 0x01, 805 (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x11, (byte) 0xb6, (byte) 0x58, 806 (byte) 0x64, (byte) 0x4f, (byte) 0x60, (byte) 0x29, (byte) 0xff, (byte) 0xff, 807 (byte) 0xff, (byte) 0xff, 808 /* UDP header */ 809 (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43, 810 (byte) 0x00, (byte) 0x08, (byte) 0x3a, (byte) 0xdf 811 }; 812 813 // This test requires the update in NetworkStackModule(See b/269692093). 814 @NetworkStackModuleTest 815 @Test testTetherZeroLengthDhcpPacket()816 public void testTetherZeroLengthDhcpPacket() throws Exception { 817 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 818 toList(TEST_IP4_DNS)); 819 tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 820 821 // Send a zero-length DHCP packet to upstream DHCP server. 822 final ByteBuffer packet = ByteBuffer.wrap(ZeroLengthDhcpPacket); 823 tester.sendUploadPacket(packet); 824 825 // Send DHCPDISCOVER packet from another downstream tethered device to verify that 826 // upstream DHCP server doesn't close the listening socket and stop reading, then we 827 // can still receive the next DHCP packet from server. 828 final MacAddress macAddress = MacAddress.fromString("11:22:33:44:55:66"); 829 assertTrue(tester.testDhcpServerAlive(macAddress)); 830 } 831 } 832