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