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.DUMP; 20 import static android.Manifest.permission.LOG_COMPAT_CHANGE; 21 import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; 22 import static android.net.InetAddresses.parseNumericAddress; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK; 24 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; 25 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; 26 import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; 27 import static android.net.TetheringManager.TETHERING_ETHERNET; 28 import static android.net.TetheringManager.TETHERING_VIRTUAL; 29 import static android.net.TetheringTester.TestDnsPacket; 30 import static android.net.TetheringTester.buildIcmpEchoPacketV4; 31 import static android.net.TetheringTester.buildUdpPacket; 32 import static android.net.TetheringTester.isExpectedIcmpPacket; 33 import static android.net.TetheringTester.isExpectedUdpDnsPacket; 34 import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS; 35 import static android.provider.DeviceConfig.NAMESPACE_TETHERING; 36 import static android.system.OsConstants.ICMP_ECHO; 37 import static android.system.OsConstants.ICMP_ECHOREPLY; 38 import static android.system.OsConstants.IPPROTO_UDP; 39 40 import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA; 41 import static com.android.net.module.util.HexDump.dumpHexString; 42 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE; 43 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; 44 import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN; 45 import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; 46 import static com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN; 47 import static com.android.testutils.DeviceInfoUtils.KVersion; 48 import static com.android.testutils.TestPermissionUtil.runAsShell; 49 50 import static org.junit.Assert.assertEquals; 51 import static org.junit.Assert.assertFalse; 52 import static org.junit.Assert.assertNotNull; 53 import static org.junit.Assert.assertTrue; 54 import static org.junit.Assert.fail; 55 import static org.junit.Assume.assumeFalse; 56 import static org.junit.Assume.assumeTrue; 57 58 import android.app.compat.CompatChanges; 59 import android.content.Context; 60 import android.net.TetheringManager.TetheringRequest; 61 import android.net.TetheringTester.TetheredDevice; 62 import android.os.Build; 63 import android.os.SystemClock; 64 import android.os.SystemProperties; 65 import android.os.VintfRuntimeInfo; 66 import android.util.Log; 67 import android.util.Pair; 68 69 import androidx.annotation.NonNull; 70 import androidx.annotation.Nullable; 71 import androidx.test.filters.LargeTest; 72 import androidx.test.runner.AndroidJUnit4; 73 74 import com.android.modules.utils.build.SdkLevel; 75 import com.android.net.module.util.BpfDump; 76 import com.android.net.module.util.Ipv6Utils; 77 import com.android.net.module.util.Struct; 78 import com.android.net.module.util.bpf.ClatEgress4Key; 79 import com.android.net.module.util.bpf.ClatEgress4Value; 80 import com.android.net.module.util.bpf.ClatIngress6Key; 81 import com.android.net.module.util.bpf.ClatIngress6Value; 82 import com.android.net.module.util.bpf.Tether4Key; 83 import com.android.net.module.util.bpf.Tether4Value; 84 import com.android.net.module.util.bpf.TetherStatsKey; 85 import com.android.net.module.util.bpf.TetherStatsValue; 86 import com.android.net.module.util.structs.Ipv4Header; 87 import com.android.net.module.util.structs.UdpHeader; 88 import com.android.testutils.AutoReleaseNetworkCallbackRule; 89 import com.android.testutils.DevSdkIgnoreRule; 90 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 91 import com.android.testutils.DeviceConfigRule; 92 import com.android.testutils.DeviceInfoUtils; 93 import com.android.testutils.DumpTestUtils; 94 import com.android.testutils.NetworkStackModuleTest; 95 import com.android.testutils.PollPacketReader; 96 import com.android.testutils.RecorderCallback.CallbackEntry; 97 import com.android.testutils.TestableNetworkCallback; 98 99 import org.junit.After; 100 import org.junit.Rule; 101 import org.junit.Test; 102 import org.junit.runner.RunWith; 103 104 import java.io.File; 105 import java.io.FileDescriptor; 106 import java.net.Inet4Address; 107 import java.net.Inet6Address; 108 import java.net.InetAddress; 109 import java.net.InterfaceAddress; 110 import java.net.NetworkInterface; 111 import java.nio.ByteBuffer; 112 import java.util.Arrays; 113 import java.util.Collection; 114 import java.util.HashMap; 115 import java.util.List; 116 import java.util.Map; 117 import java.util.Random; 118 import java.util.concurrent.TimeoutException; 119 120 @RunWith(AndroidJUnit4.class) 121 @LargeTest 122 public class EthernetTetheringTest extends EthernetTetheringTestBase { 123 @Rule 124 public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); 125 // For manipulating feature flag before and after testing. 126 @Rule 127 public final DeviceConfigRule mDeviceConfigRule = new DeviceConfigRule(); 128 @Rule 129 public final AutoReleaseNetworkCallbackRule 130 mNetworkCallbackRule = new AutoReleaseNetworkCallbackRule(); 131 132 private static final String TAG = EthernetTetheringTest.class.getSimpleName(); 133 134 private static final short DNS_PORT = 53; 135 private static final short ICMPECHO_ID = 0x0; 136 private static final short ICMPECHO_SEQ = 0x0; 137 138 private static final int DUMP_POLLING_MAX_RETRY = 100; 139 private static final int DUMP_POLLING_INTERVAL_MS = 50; 140 // Kernel treats a confirmed UDP connection which active after two seconds as stream mode. 141 // See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5. 142 private static final int UDP_STREAM_TS_MS = 2000; 143 // Give slack time for waiting UDP stream mode because handling conntrack event in user space 144 // may not in precise time. Used to reduce the flaky rate. 145 private static final int UDP_STREAM_SLACK_MS = 500; 146 // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes. 147 private static final int RX_UDP_PACKET_SIZE = 30; 148 private static final int RX_UDP_PACKET_COUNT = 456; 149 // Per TX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes. 150 private static final int TX_UDP_PACKET_SIZE = 30; 151 private static final int TX_UDP_PACKET_COUNT = 123; 152 153 private static final String DUMPSYS_CLAT_RAWMAP_EGRESS4_ARG = "clatEgress4RawBpfMap"; 154 private static final String DUMPSYS_CLAT_RAWMAP_INGRESS6_ARG = "clatIngress6RawBpfMap"; 155 private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap"; 156 private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats"; 157 private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4"; 158 private static final String LINE_DELIMITER = "\\n"; 159 160 // TODO: use class DnsPacket to build DNS query and reply message once DnsPacket supports 161 // building packet for given arguments. 162 private static final ByteBuffer DNS_QUERY = ByteBuffer.wrap(new byte[] { 163 // scapy.DNS( 164 // id=0xbeef, 165 // qr=0, 166 // qd=scapy.DNSQR(qname="hello.example.com")) 167 // 168 /* Header */ 169 (byte) 0xbe, (byte) 0xef, /* Transaction ID: 0xbeef */ 170 (byte) 0x01, (byte) 0x00, /* Flags: rd */ 171 (byte) 0x00, (byte) 0x01, /* Questions: 1 */ 172 (byte) 0x00, (byte) 0x00, /* Answer RRs: 0 */ 173 (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */ 174 (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */ 175 /* Queries */ 176 (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c, 177 (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65, 178 (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, 179 (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63, 180 (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */ 181 (byte) 0x00, (byte) 0x01, /* Type: A */ 182 (byte) 0x00, (byte) 0x01 /* Class: IN */ 183 }); 184 185 private static final byte[] DNS_REPLY = new byte[] { 186 // scapy.DNS( 187 // id=0, 188 // qr=1, 189 // qd=scapy.DNSQR(qname="hello.example.com"), 190 // an=scapy.DNSRR(rrname="hello.example.com", rdata='1.2.3.4')) 191 // 192 /* Header */ 193 (byte) 0x00, (byte) 0x00, /* Transaction ID: 0x0, must be updated by dns query id */ 194 (byte) 0x81, (byte) 0x00, /* Flags: qr rd */ 195 (byte) 0x00, (byte) 0x01, /* Questions: 1 */ 196 (byte) 0x00, (byte) 0x01, /* Answer RRs: 1 */ 197 (byte) 0x00, (byte) 0x00, /* Authority RRs: 0 */ 198 (byte) 0x00, (byte) 0x00, /* Additional RRs: 0 */ 199 /* Queries */ 200 (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c, 201 (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65, 202 (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, 203 (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63, 204 (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */ 205 (byte) 0x00, (byte) 0x01, /* Type: A */ 206 (byte) 0x00, (byte) 0x01, /* Class: IN */ 207 /* Answers */ 208 (byte) 0x05, (byte) 0x68, (byte) 0x65, (byte) 0x6c, 209 (byte) 0x6c, (byte) 0x6f, (byte) 0x07, (byte) 0x65, 210 (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, 211 (byte) 0x6c, (byte) 0x65, (byte) 0x03, (byte) 0x63, 212 (byte) 0x6f, (byte) 0x6d, (byte) 0x00, /* Name: hello.example.com */ 213 (byte) 0x00, (byte) 0x01, /* Type: A */ 214 (byte) 0x00, (byte) 0x01, /* Class: IN */ 215 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, /* Time to live: 0 */ 216 (byte) 0x00, (byte) 0x04, /* Data length: 4 */ 217 (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */ 218 }; 219 220 // Shamelessly copied from TetheringConfiguration. 221 private static final String TETHERING_LOCAL_NETWORK_AGENT = "tethering_local_network_agent"; 222 223 @After tearDown()224 public void tearDown() throws Exception { 225 super.tearDown(); 226 // TODO: See b/318121782#comment4. Register an ethernet InterfaceStateListener, and wait for 227 // the callback to report client mode. This happens as soon as both 228 // TetheredInterfaceRequester and the tethering code itself have released the interface, 229 // i.e. after stopTethering() has completed. 230 Thread.sleep(3000); 231 } 232 233 @Test testVirtualEthernetAlreadyExists()234 public void testVirtualEthernetAlreadyExists() throws Exception { 235 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 236 assumeFalse(isInterfaceForTetheringAvailable()); 237 238 TestNetworkInterface downstreamIface = null; 239 MyTetheringEventCallback tetheringEventCallback = null; 240 PollPacketReader downstreamReader = null; 241 242 try { 243 downstreamIface = createTestInterface(); 244 // This must be done now because as soon as setIncludeTestInterfaces(true) is called, 245 // the interface will be placed in client mode, which will delete the link-local 246 // address. At that point NetworkInterface.getByName() will cease to work on the 247 // interface, because starting in R NetworkInterface can no longer see interfaces 248 // without IP addresses. 249 int mtu = getMTU(downstreamIface); 250 251 Log.d(TAG, "Including test interfaces"); 252 setIncludeTestInterfaces(true); 253 254 final String iface = mTetheredInterfaceRequester.getInterface(); 255 assertEquals("TetheredInterfaceCallback for unexpected interface", 256 downstreamIface.getInterfaceName(), iface); 257 258 // Check virtual ethernet. 259 FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); 260 downstreamReader = makePacketReader(fd, mtu); 261 tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(), 262 null /* any upstream */); 263 checkTetheredClientCallbacks( 264 downstreamReader, TETHERING_ETHERNET, tetheringEventCallback); 265 } finally { 266 maybeStopTapPacketReader(downstreamReader); 267 maybeCloseTestInterface(downstreamIface); 268 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 269 } 270 } 271 272 @Test testVirtualEthernet()273 public void testVirtualEthernet() throws Exception { 274 // This test requires manipulating packets. Skip if there is a physical Ethernet connected. 275 assumeFalse(isInterfaceForTetheringAvailable()); 276 277 setIncludeTestInterfaces(true); 278 279 TestNetworkInterface downstreamIface = null; 280 MyTetheringEventCallback tetheringEventCallback = null; 281 PollPacketReader downstreamReader = null; 282 283 try { 284 downstreamIface = createTestInterface(); 285 286 final String iface = mTetheredInterfaceRequester.getInterface(); 287 assertEquals("TetheredInterfaceCallback for unexpected interface", 288 downstreamIface.getInterfaceName(), iface); 289 290 // Check virtual ethernet. 291 FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); 292 downstreamReader = makePacketReader(fd, getMTU(downstreamIface)); 293 tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(), 294 null /* any upstream */); 295 checkTetheredClientCallbacks( 296 downstreamReader, TETHERING_ETHERNET, tetheringEventCallback); 297 } finally { 298 maybeStopTapPacketReader(downstreamReader); 299 maybeCloseTestInterface(downstreamIface); 300 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 301 } 302 } 303 304 @Test testStaticIpv4()305 public void testStaticIpv4() throws Exception { 306 assumeFalse(isInterfaceForTetheringAvailable()); 307 308 setIncludeTestInterfaces(true); 309 310 TestNetworkInterface downstreamIface = null; 311 MyTetheringEventCallback tetheringEventCallback = null; 312 PollPacketReader downstreamReader = null; 313 314 try { 315 downstreamIface = createTestInterface(); 316 317 final String iface = mTetheredInterfaceRequester.getInterface(); 318 assertEquals("TetheredInterfaceCallback for unexpected interface", 319 downstreamIface.getInterfaceName(), iface); 320 321 assertInvalidStaticIpv4Request(iface, null, null); 322 assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); 323 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); 324 assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); 325 assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); 326 assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); 327 assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); 328 329 final String localAddr = "192.0.2.3/28"; 330 final String clientAddr = "192.0.2.2/28"; 331 tetheringEventCallback = enableTethering(iface, 332 requestWithStaticIpv4(localAddr, clientAddr), null /* any upstream */); 333 334 tetheringEventCallback.awaitInterfaceTethered(); 335 assertInterfaceHasIpAddress(iface, localAddr); 336 337 byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); 338 byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); 339 340 FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); 341 downstreamReader = makePacketReader(fd, getMTU(downstreamIface)); 342 TetheringTester tester = new TetheringTester(downstreamReader); 343 DhcpResults dhcpResults = tester.runDhcp(client1); 344 assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); 345 346 try { 347 tester.runDhcp(client2); 348 fail("Only one client should get an IP address"); 349 } catch (TimeoutException expected) { } 350 } finally { 351 maybeStopTapPacketReader(downstreamReader); 352 maybeCloseTestInterface(downstreamIface); 353 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 354 } 355 } 356 expectLocalOnlyAddresses(String iface)357 private static void expectLocalOnlyAddresses(String iface) throws Exception { 358 final List<InterfaceAddress> interfaceAddresses = 359 NetworkInterface.getByName(iface).getInterfaceAddresses(); 360 361 boolean foundIpv6Ula = false; 362 for (InterfaceAddress ia : interfaceAddresses) { 363 final InetAddress addr = ia.getAddress(); 364 if (isIPv6ULA(addr)) { 365 foundIpv6Ula = true; 366 } 367 final int prefixlen = ia.getNetworkPrefixLength(); 368 final LinkAddress la = new LinkAddress(addr, prefixlen); 369 if (la.isIpv6() && la.isGlobalPreferred()) { 370 fail("Found global IPv6 address on local-only interface: " + interfaceAddresses); 371 } 372 } 373 374 assertTrue("Did not find IPv6 ULA on local-only interface " + iface, 375 foundIpv6Ula); 376 } 377 378 @Test testLocalOnlyTethering()379 public void testLocalOnlyTethering() throws Exception { 380 assumeFalse(isInterfaceForTetheringAvailable()); 381 382 setIncludeTestInterfaces(true); 383 384 TestNetworkInterface downstreamIface = null; 385 MyTetheringEventCallback tetheringEventCallback = null; 386 PollPacketReader downstreamReader = null; 387 388 try { 389 downstreamIface = createTestInterface(); 390 391 final String iface = mTetheredInterfaceRequester.getInterface(); 392 assertEquals("TetheredInterfaceCallback for unexpected interface", 393 downstreamIface.getInterfaceName(), iface); 394 395 final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET) 396 .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build(); 397 tetheringEventCallback = enableTethering(iface, request, null /* any upstream */); 398 tetheringEventCallback.awaitInterfaceLocalOnly(); 399 400 // makePacketReader only works after tethering is started, because until then the 401 // interface does not have an IP address, and unprivileged apps cannot see interfaces 402 // without IP addresses. This shouldn't be flaky because the TAP interface will buffer 403 // all packets even before the reader is started. 404 downstreamReader = makePacketReader(downstreamIface); 405 406 waitForRouterAdvertisement(downstreamReader, iface, WAIT_RA_TIMEOUT_MS); 407 expectLocalOnlyAddresses(iface); 408 409 // After testing the IPv6 local address, the DHCP server may still be in the process 410 // of being created. If the downstream interface is killed by the test while the 411 // DHCP server is starting, a DHCP server error may occur. To ensure that the DHCP 412 // server has started completely before finishing the test, also test the dhcp server 413 // by calling runDhcp. 414 final TetheringTester tester = new TetheringTester(downstreamReader); 415 tester.runDhcp(MacAddress.fromString("1:2:3:4:5:6").toByteArray()); 416 } finally { 417 maybeStopTapPacketReader(downstreamReader); 418 maybeCloseTestInterface(downstreamIface); 419 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 420 } 421 } 422 isAdbOverNetwork()423 private boolean isAdbOverNetwork() { 424 // If adb TCP port opened, this test may running by adb over network. 425 return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) 426 || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1); 427 } 428 429 @Test testPhysicalEthernet()430 public void testPhysicalEthernet() throws Exception { 431 assumeTrue(isInterfaceForTetheringAvailable()); 432 // Do not run this test if adb is over network and ethernet is connected. 433 // It is likely the adb run over ethernet, the adb would break when ethernet is switching 434 // from client mode to server mode. See b/160389275. 435 assumeFalse(isAdbOverNetwork()); 436 437 MyTetheringEventCallback tetheringEventCallback = null; 438 try { 439 // Get an interface to use. 440 final String iface = mTetheredInterfaceRequester.getInterface(); 441 442 // Enable Ethernet tethering and check that it starts. 443 tetheringEventCallback = enableEthernetTethering(iface, null /* any upstream */); 444 } finally { 445 stopEthernetTethering(tetheringEventCallback); 446 } 447 // There is nothing more we can do on a physical interface without connecting an actual 448 // client, which is not possible in this test. 449 } 450 checkTetheredClientCallbacks(final PollPacketReader packetReader, final int tetheringType, final MyTetheringEventCallback tetheringEventCallback)451 private void checkTetheredClientCallbacks(final PollPacketReader packetReader, 452 final int tetheringType, 453 final MyTetheringEventCallback tetheringEventCallback) throws Exception { 454 // Create a fake client. 455 byte[] clientMacAddr = new byte[6]; 456 new Random().nextBytes(clientMacAddr); 457 458 TetheringTester tester = new TetheringTester(packetReader); 459 DhcpResults dhcpResults = tester.runDhcp(clientMacAddr); 460 461 final Collection<TetheredClient> clients = tetheringEventCallback.awaitClientConnected(); 462 assertEquals(1, clients.size()); 463 final TetheredClient client = clients.iterator().next(); 464 465 // Check the MAC address. 466 assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); 467 assertEquals(tetheringType, client.getTetheringType()); 468 469 // Check the hostname. 470 assertEquals(1, client.getAddresses().size()); 471 TetheredClient.AddressInfo info = client.getAddresses().get(0); 472 assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname()); 473 474 // Check the address is the one that was handed out in the DHCP ACK. 475 assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); 476 477 // Check that the lifetime is correct +/- 10s. 478 final long now = SystemClock.elapsedRealtime(); 479 final long actualLeaseDuration = (info.getAddress().getExpirationTime() - now) / 1000; 480 final String msg = String.format("IP address should have lifetime of %d, got %d", 481 dhcpResults.leaseDuration, actualLeaseDuration); 482 assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10); 483 } 484 assertLinkAddressMatches(LinkAddress l1, LinkAddress l2)485 public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) { 486 // Check all fields except the deprecation and expiry times. 487 String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2); 488 assertTrue(msg, l1.isSameAddressAs(l2)); 489 assertEquals("LinkAddress flags do not match", l1.getFlags(), l2.getFlags()); 490 assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); 491 } 492 requestWithStaticIpv4(String local, String client)493 private TetheringRequest requestWithStaticIpv4(String local, String client) { 494 LinkAddress localAddr = local == null ? null : new LinkAddress(local); 495 LinkAddress clientAddr = client == null ? null : new LinkAddress(client); 496 return new TetheringRequest.Builder(TETHERING_ETHERNET) 497 .setStaticIpv4Addresses(localAddr, clientAddr) 498 .setShouldShowEntitlementUi(false).build(); 499 } 500 assertInvalidStaticIpv4Request(String iface, String local, String client)501 private void assertInvalidStaticIpv4Request(String iface, String local, String client) 502 throws Exception { 503 try { 504 enableTethering(iface, requestWithStaticIpv4(local, client), null /* any upstream */); 505 fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); 506 } catch (IllegalArgumentException | NullPointerException expected) { } 507 } 508 assertInterfaceHasIpAddress(String iface, String expected)509 private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { 510 LinkAddress expectedAddr = new LinkAddress(expected); 511 NetworkInterface nif = NetworkInterface.getByName(iface); 512 for (InterfaceAddress ia : nif.getInterfaceAddresses()) { 513 final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); 514 if (expectedAddr.equals(addr)) { 515 return; 516 } 517 } 518 fail("Expected " + iface + " to have IP address " + expected + ", found " 519 + nif.getInterfaceAddresses()); 520 } 521 522 @Test testIcmpv6Echo()523 public void testIcmpv6Echo() throws Exception { 524 runPing6Test(initTetheringTester(toList(TEST_IP4_ADDR, TEST_IP6_ADDR), 525 toList(TEST_IP4_DNS, TEST_IP6_DNS))); 526 } 527 runPing6Test(TetheringTester tester)528 private void runPing6Test(TetheringTester tester) throws Exception { 529 TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 530 Inet6Address remoteIp6Addr = (Inet6Address) parseNumericAddress("2400:222:222::222"); 531 ByteBuffer request = Ipv6Utils.buildEchoRequestPacket(tethered.macAddr, 532 tethered.routerMacAddr, tethered.ipv6Addr, remoteIp6Addr); 533 tester.verifyUpload(request, p -> { 534 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 535 536 return isExpectedIcmpPacket(p, false /* hasEth */, false /* isIpv4 */, 537 ICMPV6_ECHO_REQUEST_TYPE); 538 }); 539 540 ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket(remoteIp6Addr, tethered.ipv6Addr); 541 tester.verifyDownload(reply, p -> { 542 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 543 544 return isExpectedIcmpPacket(p, true /* hasEth */, false /* isIpv4 */, 545 ICMPV6_ECHO_REPLY_TYPE); 546 }); 547 } 548 549 @Test testTetherUdpV6()550 public void testTetherUdpV6() throws Exception { 551 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 552 toList(TEST_IP6_DNS)); 553 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 554 sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, 555 tethered.ipv6Addr, REMOTE_IP6_ADDR, tester, false /* is4To6 */); 556 sendDownloadPacketUdp(REMOTE_IP6_ADDR, tethered.ipv6Addr, tester, false /* is6To4 */); 557 558 // TODO: test BPF offload maps {rule, stats}. 559 } 560 561 562 /** 563 * Basic IPv4 UDP tethering test. Verify that UDP tethered packets are transferred no matter 564 * using which data path. 565 */ 566 @Test testTetherUdpV4()567 public void testTetherUdpV4() throws Exception { 568 // Test network topology: 569 // 570 // public network (rawip) private network 571 // | UE | 572 // +------------+ V +------------+------------+ V +------------+ 573 // | Sever +---------+ Upstream | Downstream +---------+ Client | 574 // +------------+ +------------+------------+ +------------+ 575 // remote ip public ip private ip 576 // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876 577 // 578 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 579 toList(TEST_IP4_DNS)); 580 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 581 582 // TODO: remove the connectivity verification for upstream connected notification race. 583 // Because async upstream connected notification can't guarantee the tethering routing is 584 // ready to use. Need to test tethering connectivity before testing. 585 // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes 586 // from upstream. That can guarantee that the routing is ready. Long term plan is that 587 // refactors upstream connected notification from async to sync. 588 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 589 590 final MacAddress srcMac = tethered.macAddr; 591 final MacAddress dstMac = tethered.routerMacAddr; 592 final InetAddress remoteIp = REMOTE_IP4_ADDR; 593 final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress(); 594 final InetAddress clientIp = tethered.ipv4Addr; 595 sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */); 596 sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */); 597 } 598 599 // Test network topology: 600 // 601 // public network (rawip) private network 602 // | UE (CLAT support) | 603 // +---------------+ V +------------+------------+ V +------------+ 604 // | NAT64 Gateway +---------+ Upstream | Downstream +---------+ Client | 605 // +---------------+ +------------+------------+ +------------+ 606 // remote ip public ip private ip 607 // [64:ff9b::808:808]:443 [clat ipv6]:9876 [TetheredDevice ipv4]:9876 608 // 609 // Note that CLAT IPv6 address is generated by ClatCoordinator. Get the CLAT IPv6 address by 610 // sending out an IPv4 packet and extracting the source address from CLAT translated IPv6 611 // packet. 612 // runClatUdpTest()613 private void runClatUdpTest() throws Exception { 614 // CLAT only starts on IPv6 only network. 615 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 616 toList(TEST_IP6_DNS)); 617 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 618 619 // Get CLAT IPv6 address. 620 final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); 621 622 // Send an IPv4 UDP packet in original direction. 623 // IPv4 packet -- CLAT translation --> IPv6 packet 624 sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, tethered.ipv4Addr, 625 REMOTE_IP4_ADDR, tester, true /* is4To6 */); 626 627 // Send an IPv6 UDP packet in reply direction. 628 // IPv6 packet -- CLAT translation --> IPv4 packet 629 sendDownloadPacketUdp(REMOTE_NAT64_ADDR, clatIp6, tester, true /* is6To4 */); 630 631 // TODO: test CLAT bpf maps. 632 } 633 634 // TODO: support R device. See b/234727688. 635 @Test 636 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherClatUdp()637 public void testTetherClatUdp() throws Exception { 638 runClatUdpTest(); 639 } 640 641 @Test testIcmpv4Echo()642 public void testIcmpv4Echo() throws Exception { 643 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 644 toList(TEST_IP4_DNS)); 645 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 646 647 // TODO: remove the connectivity verification for upstream connected notification race. 648 // See the same reason in testTetherUdp4(). 649 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 650 651 final ByteBuffer request = buildIcmpEchoPacketV4(tethered.macAddr /* srcMac */, 652 tethered.routerMacAddr /* dstMac */, tethered.ipv4Addr /* srcIp */, 653 REMOTE_IP4_ADDR /* dstIp */, ICMP_ECHO, ICMPECHO_ID, ICMPECHO_SEQ); 654 tester.verifyUpload(request, p -> { 655 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 656 657 return isExpectedIcmpPacket(p, false /* hasEth */, true /* isIpv4 */, ICMP_ECHO); 658 }); 659 660 final ByteBuffer reply = buildIcmpEchoPacketV4(REMOTE_IP4_ADDR /* srcIp*/, 661 (Inet4Address) TEST_IP4_ADDR.getAddress() /* dstIp */, ICMP_ECHOREPLY, ICMPECHO_ID, 662 ICMPECHO_SEQ); 663 tester.verifyDownload(reply, p -> { 664 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 665 666 return isExpectedIcmpPacket(p, true /* hasEth */, true /* isIpv4 */, ICMP_ECHOREPLY); 667 }); 668 } 669 670 // TODO: support R device. See b/234727688. 671 @Test 672 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherClatIcmp()673 public void testTetherClatIcmp() throws Exception { 674 // CLAT only starts on IPv6 only network. 675 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 676 toList(TEST_IP6_DNS)); 677 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 678 679 // Get CLAT IPv6 address. 680 final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); 681 682 // Send an IPv4 ICMP packet in original direction. 683 // IPv4 packet -- CLAT translation --> IPv6 packet 684 final ByteBuffer request = buildIcmpEchoPacketV4(tethered.macAddr /* srcMac */, 685 tethered.routerMacAddr /* dstMac */, tethered.ipv4Addr /* srcIp */, 686 (Inet4Address) REMOTE_IP4_ADDR /* dstIp */, ICMP_ECHO, ICMPECHO_ID, ICMPECHO_SEQ); 687 tester.verifyUpload(request, p -> { 688 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 689 690 return isExpectedIcmpPacket(p, false /* hasEth */, false /* isIpv4 */, 691 ICMPV6_ECHO_REQUEST_TYPE); 692 }); 693 694 // Send an IPv6 ICMP packet in reply direction. 695 // IPv6 packet -- CLAT translation --> IPv4 packet 696 final ByteBuffer reply = Ipv6Utils.buildEchoReplyPacket( 697 (Inet6Address) REMOTE_NAT64_ADDR /* srcIp */, clatIp6 /* dstIp */); 698 tester.verifyDownload(reply, p -> { 699 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 700 701 return isExpectedIcmpPacket(p, true /* hasEth */, true /* isIpv4 */, ICMP_ECHOREPLY); 702 }); 703 } 704 705 @NonNull buildDnsReplyMessageById(short id)706 private ByteBuffer buildDnsReplyMessageById(short id) { 707 byte[] replyMessage = Arrays.copyOf(DNS_REPLY, DNS_REPLY.length); 708 // Assign transaction id of reply message pattern with a given DNS transaction id. 709 replyMessage[0] = (byte) ((id >> 8) & 0xff); 710 replyMessage[1] = (byte) (id & 0xff); 711 Log.d(TAG, "Built DNS reply: " + dumpHexString(replyMessage)); 712 713 return ByteBuffer.wrap(replyMessage); 714 } 715 716 @NonNull sendDownloadPacketDnsV4(@onNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp, short srcPort, short dstPort, short dnsId, @NonNull final TetheringTester tester)717 private void sendDownloadPacketDnsV4(@NonNull final Inet4Address srcIp, 718 @NonNull final Inet4Address dstIp, short srcPort, short dstPort, short dnsId, 719 @NonNull final TetheringTester tester) throws Exception { 720 // DNS response transaction id must be copied from DNS query. Used by the requester 721 // to match up replies to outstanding queries. See RFC 1035 section 4.1.1. 722 final ByteBuffer dnsReplyMessage = buildDnsReplyMessageById(dnsId); 723 final ByteBuffer testPacket = buildUdpPacket((InetAddress) srcIp, 724 (InetAddress) dstIp, srcPort, dstPort, dnsReplyMessage); 725 726 tester.verifyDownload(testPacket, p -> { 727 Log.d(TAG, "Packet in downstream: " + dumpHexString(p)); 728 return isExpectedUdpDnsPacket(p, true /* hasEther */, true /* isIpv4 */, 729 dnsReplyMessage); 730 }); 731 } 732 733 // Send IPv4 UDP DNS packet and return the forwarded DNS packet on upstream. 734 @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)735 private byte[] sendUploadPacketDnsV4(@NonNull final MacAddress srcMac, 736 @NonNull final MacAddress dstMac, @NonNull final Inet4Address srcIp, 737 @NonNull final Inet4Address dstIp, short srcPort, short dstPort, 738 @NonNull final TetheringTester tester) throws Exception { 739 final ByteBuffer testPacket = buildUdpPacket(srcMac, dstMac, srcIp, dstIp, 740 srcPort, dstPort, DNS_QUERY); 741 742 return tester.verifyUpload(testPacket, p -> { 743 Log.d(TAG, "Packet in upstream: " + dumpHexString(p)); 744 return isExpectedUdpDnsPacket(p, false /* hasEther */, true /* isIpv4 */, 745 DNS_QUERY); 746 }); 747 } 748 749 @Test testTetherUdpV4Dns()750 public void testTetherUdpV4Dns() throws Exception { 751 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 752 toList(TEST_IP4_DNS)); 753 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 754 755 // TODO: remove the connectivity verification for upstream connected notification race. 756 // See the same reason in testTetherUdp4(). 757 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 758 759 // [1] Send DNS query. 760 // tethered device --> downstream --> dnsmasq forwarding --> upstream --> DNS server 761 // 762 // Need to extract DNS transaction id and source port from dnsmasq forwarded DNS query 763 // packet. dnsmasq forwarding creats new query which means UDP source port and DNS 764 // transaction id are changed from original sent DNS query. See forward_query() in 765 // external/dnsmasq/src/forward.c. Note that #TetheringTester.isExpectedUdpDnsPacket 766 // guarantees that |forwardedQueryPacket| is a valid DNS packet. So we can parse it as DNS 767 // packet. 768 final MacAddress srcMac = tethered.macAddr; 769 final MacAddress dstMac = tethered.routerMacAddr; 770 final Inet4Address clientIp = tethered.ipv4Addr; 771 final Inet4Address gatewayIp = tethered.ipv4Gatway; 772 final byte[] forwardedQueryPacket = sendUploadPacketDnsV4(srcMac, dstMac, clientIp, 773 gatewayIp, LOCAL_PORT, DNS_PORT, tester); 774 final ByteBuffer buf = ByteBuffer.wrap(forwardedQueryPacket); 775 Struct.parse(Ipv4Header.class, buf); 776 final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf); 777 final TestDnsPacket dnsQuery = TestDnsPacket.getTestDnsPacket(buf); 778 assertNotNull(dnsQuery); 779 Log.d(TAG, "Forwarded UDP source port: " + udpHeader.srcPort + ", DNS query id: " 780 + dnsQuery.getHeader().getId()); 781 782 // [2] Send DNS reply. 783 // DNS server --> upstream --> dnsmasq forwarding --> downstream --> tethered device 784 // 785 // DNS reply transaction id must be copied from DNS query. Used by the requester to match 786 // up replies to outstanding queries. See RFC 1035 section 4.1.1. 787 final Inet4Address remoteIp = (Inet4Address) TEST_IP4_DNS; 788 final Inet4Address tetheringUpstreamIp = (Inet4Address) TEST_IP4_ADDR.getAddress(); 789 sendDownloadPacketDnsV4(remoteIp, tetheringUpstreamIp, DNS_PORT, 790 (short) udpHeader.srcPort, (short) dnsQuery.getHeader().getId(), tester); 791 } 792 793 @Test testTetherTcpV4()794 public void testTetherTcpV4() throws Exception { 795 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 796 toList(TEST_IP4_DNS)); 797 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 798 799 // TODO: remove the connectivity verification for upstream connected notification race. 800 // See the same reason in testTetherUdp4(). 801 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 802 803 runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */, 804 tethered.ipv4Addr /* uploadSrcIp */, REMOTE_IP4_ADDR /* uploadDstIp */, 805 REMOTE_IP4_ADDR /* downloadSrcIp */, TEST_IP4_ADDR.getAddress() /* downloadDstIp */, 806 tester, false /* isClat */); 807 } 808 809 @Test testTetherTcpV6()810 public void testTetherTcpV6() throws Exception { 811 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 812 toList(TEST_IP6_DNS)); 813 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 814 815 runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */, 816 tethered.ipv6Addr /* uploadSrcIp */, REMOTE_IP6_ADDR /* uploadDstIp */, 817 REMOTE_IP6_ADDR /* downloadSrcIp */, tethered.ipv6Addr /* downloadDstIp */, 818 tester, false /* isClat */); 819 } 820 821 // TODO: support R device. See b/234727688. 822 @Test 823 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherClatTcp()824 public void testTetherClatTcp() throws Exception { 825 // CLAT only starts on IPv6 only network. 826 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 827 toList(TEST_IP6_DNS)); 828 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 829 830 // Get CLAT IPv6 address. 831 final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); 832 833 runTcpTest(tethered.macAddr /* uploadSrcMac */, tethered.routerMacAddr /* uploadDstMac */, 834 tethered.ipv4Addr /* uploadSrcIp */, REMOTE_IP4_ADDR /* uploadDstIp */, 835 REMOTE_NAT64_ADDR /* downloadSrcIp */, clatIp6 /* downloadDstIp */, 836 tester, true /* isClat */); 837 } 838 839 private static final byte[] ZeroLengthDhcpPacket = new byte[] { 840 // scapy.Ether( 841 // dst="ff:ff:ff:ff:ff:ff") 842 // scapy.IP( 843 // dst="255.255.255.255") 844 // scapy.UDP(sport=68, dport=67) 845 /* Ethernet Header */ 846 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 847 (byte) 0xe0, (byte) 0x4f, (byte) 0x43, (byte) 0xe6, (byte) 0xfb, (byte) 0xd2, 848 (byte) 0x08, (byte) 0x00, 849 /* Ip header */ 850 (byte) 0x45, (byte) 0x00, (byte) 0x00, (byte) 0x1c, (byte) 0x00, (byte) 0x01, 851 (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x11, (byte) 0xb6, (byte) 0x58, 852 (byte) 0x64, (byte) 0x4f, (byte) 0x60, (byte) 0x29, (byte) 0xff, (byte) 0xff, 853 (byte) 0xff, (byte) 0xff, 854 /* UDP header */ 855 (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43, 856 (byte) 0x00, (byte) 0x08, (byte) 0x3a, (byte) 0xdf 857 }; 858 859 // This test requires the update in NetworkStackModule(See b/269692093). 860 @NetworkStackModuleTest 861 @Test testTetherZeroLengthDhcpPacket()862 public void testTetherZeroLengthDhcpPacket() throws Exception { 863 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 864 toList(TEST_IP4_DNS)); 865 tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 866 867 // Send a zero-length DHCP packet to upstream DHCP server. 868 final ByteBuffer packet = ByteBuffer.wrap(ZeroLengthDhcpPacket); 869 tester.sendUploadPacket(packet); 870 871 // Send DHCPDISCOVER packet from another downstream tethered device to verify that 872 // upstream DHCP server doesn't close the listening socket and stop reading, then we 873 // can still receive the next DHCP packet from server. 874 final MacAddress macAddress = MacAddress.fromString("11:22:33:44:55:66"); 875 assertTrue(tester.testDhcpServerAlive(macAddress)); 876 } 877 isUdpOffloadSupportedByKernel(final String kernelVersion)878 private static boolean isUdpOffloadSupportedByKernel(final String kernelVersion) { 879 final KVersion current = DeviceInfoUtils.getMajorMinorSubminorVersion(kernelVersion); 880 return current.isInRange(new KVersion(4, 14, 222), new KVersion(4, 19, 0)) 881 || current.isInRange(new KVersion(4, 19, 176), new KVersion(5, 4, 0)) 882 || current.isAtLeast(new KVersion(5, 4, 98)); 883 } 884 885 @Test testIsUdpOffloadSupportedByKernel()886 public void testIsUdpOffloadSupportedByKernel() throws Exception { 887 assertFalse(isUdpOffloadSupportedByKernel("4.14.221")); 888 assertTrue(isUdpOffloadSupportedByKernel("4.14.222")); 889 assertTrue(isUdpOffloadSupportedByKernel("4.16.0")); 890 assertTrue(isUdpOffloadSupportedByKernel("4.18.0")); 891 assertFalse(isUdpOffloadSupportedByKernel("4.19.0")); 892 893 assertFalse(isUdpOffloadSupportedByKernel("4.19.175")); 894 assertTrue(isUdpOffloadSupportedByKernel("4.19.176")); 895 assertTrue(isUdpOffloadSupportedByKernel("5.2.0")); 896 assertTrue(isUdpOffloadSupportedByKernel("5.3.0")); 897 assertFalse(isUdpOffloadSupportedByKernel("5.4.0")); 898 899 assertFalse(isUdpOffloadSupportedByKernel("5.4.97")); 900 assertTrue(isUdpOffloadSupportedByKernel("5.4.98")); 901 assertTrue(isUdpOffloadSupportedByKernel("5.10.0")); 902 } 903 assumeKernelSupportBpfOffloadUdpV4()904 private static void assumeKernelSupportBpfOffloadUdpV4() { 905 final String kernelVersion = VintfRuntimeInfo.getKernelVersion(); 906 assumeTrue("Kernel version " + kernelVersion + " doesn't support IPv4 UDP BPF offload", 907 isUdpOffloadSupportedByKernel(kernelVersion)); 908 } 909 910 @Test testKernelSupportBpfOffloadUdpV4()911 public void testKernelSupportBpfOffloadUdpV4() throws Exception { 912 assumeKernelSupportBpfOffloadUdpV4(); 913 } 914 isTetherConfigBpfOffloadEnabled()915 private boolean isTetherConfigBpfOffloadEnabled() throws Exception { 916 final String dumpStr = runAsShell(DUMP, () -> 917 DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short")); 918 919 // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in 920 // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by 921 // RRO to override the enabled default value. Get the tethering config via dumpsys. 922 // $ dumpsys tethering 923 // mIsBpfEnabled: true 924 boolean enabled = dumpStr.contains("mIsBpfEnabled: true"); 925 if (!enabled) { 926 Log.d(TAG, "BPF offload tether config not enabled: " + dumpStr); 927 } 928 return enabled; 929 } 930 931 @Test testTetherConfigBpfOffloadEnabled()932 public void testTetherConfigBpfOffloadEnabled() throws Exception { 933 assumeTrue(isTetherConfigBpfOffloadEnabled()); 934 } 935 936 @NonNull dumpAndParseRawMap( Class<K> keyClass, Class<V> valueClass, @NonNull String service, @NonNull String[] args)937 private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap( 938 Class<K> keyClass, Class<V> valueClass, @NonNull String service, @NonNull String[] args) 939 throws Exception { 940 final String rawMapStr = runAsShell(DUMP, () -> 941 DumpTestUtils.dumpService(service, args)); 942 final HashMap<K, V> map = new HashMap<>(); 943 944 for (final String line : rawMapStr.split(LINE_DELIMITER)) { 945 final Pair<K, V> rule = 946 BpfDump.fromBase64EncodedString(keyClass, valueClass, line.trim()); 947 map.put(rule.first, rule.second); 948 } 949 return map; 950 } 951 952 @Nullable pollRawMapFromDump( Class<K> keyClass, Class<V> valueClass, @NonNull String service, @NonNull String[] args)953 private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump( 954 Class<K> keyClass, Class<V> valueClass, @NonNull String service, @NonNull String[] args) 955 throws Exception { 956 for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) { 957 final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, service, args); 958 if (!map.isEmpty()) return map; 959 960 Thread.sleep(DUMP_POLLING_INTERVAL_MS); 961 } 962 963 fail("Cannot get rules after " + DUMP_POLLING_MAX_RETRY * DUMP_POLLING_INTERVAL_MS + "ms"); 964 return null; 965 } 966 967 // Test network topology: 968 // 969 // public network (rawip) private network 970 // | UE | 971 // +------------+ V +------------+------------+ V +------------+ 972 // | Sever +---------+ Upstream | Downstream +---------+ Client | 973 // +------------+ +------------+------------+ +------------+ 974 // remote ip public ip private ip 975 // 8.8.8.8:443 <Upstream ip>:9876 <TetheredDevice ip>:9876 976 // runUdp4Test()977 private void runUdp4Test() throws Exception { 978 final TetheringTester tester = initTetheringTester(toList(TEST_IP4_ADDR), 979 toList(TEST_IP4_DNS)); 980 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, false /* hasIpv6 */); 981 982 // TODO: remove the connectivity verification for upstream connected notification race. 983 // Because async upstream connected notification can't guarantee the tethering routing is 984 // ready to use. Need to test tethering connectivity before testing. 985 // For short term plan, consider using IPv6 RA to get MAC address because the prefix comes 986 // from upstream. That can guarantee that the routing is ready. Long term plan is that 987 // refactors upstream connected notification from async to sync. 988 probeV4TetheringConnectivity(tester, tethered, false /* is4To6 */); 989 990 final MacAddress srcMac = tethered.macAddr; 991 final MacAddress dstMac = tethered.routerMacAddr; 992 final InetAddress remoteIp = REMOTE_IP4_ADDR; 993 final InetAddress tetheringUpstreamIp = TEST_IP4_ADDR.getAddress(); 994 final InetAddress clientIp = tethered.ipv4Addr; 995 sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */); 996 sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */); 997 998 // Send second UDP packet in original direction. 999 // The BPF coordinator only offloads the ASSURED conntrack entry. The "request + reply" 1000 // packets can make status IPS_SEEN_REPLY to be set. Need one more packet to make 1001 // conntrack status IPS_ASSURED_BIT to be set. Note the third packet needs to delay 1002 // 2 seconds because kernel monitors a UDP connection which still alive after 2 seconds 1003 // and apply ASSURED flag. 1004 // See kernel upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5 and 1005 // nf_conntrack_udp_packet in net/netfilter/nf_conntrack_proto_udp.c 1006 Thread.sleep(UDP_STREAM_TS_MS); 1007 sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, false /* is4To6 */); 1008 1009 // Give a slack time for handling conntrack event in user space. 1010 Thread.sleep(UDP_STREAM_SLACK_MS); 1011 1012 // [1] Verify IPv4 upstream rule map. 1013 final String[] upstreamArgs = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, 1014 DUMPSYS_RAWMAP_ARG_UPSTREAM4}; 1015 final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump( 1016 Tether4Key.class, Tether4Value.class, Context.TETHERING_SERVICE, upstreamArgs); 1017 assertNotNull(upstreamMap); 1018 assertEquals(1, upstreamMap.size()); 1019 1020 final Map.Entry<Tether4Key, Tether4Value> rule = 1021 upstreamMap.entrySet().iterator().next(); 1022 1023 final Tether4Key upstream4Key = rule.getKey(); 1024 assertEquals(IPPROTO_UDP, upstream4Key.l4proto); 1025 assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4)); 1026 assertEquals(LOCAL_PORT, upstream4Key.srcPort); 1027 assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4)); 1028 assertEquals(REMOTE_PORT, upstream4Key.dstPort); 1029 1030 final Tether4Value upstream4Value = rule.getValue(); 1031 assertTrue(Arrays.equals(tetheringUpstreamIp.getAddress(), 1032 InetAddress.getByAddress(upstream4Value.src46).getAddress())); 1033 assertEquals(LOCAL_PORT, upstream4Value.srcPort); 1034 assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), 1035 InetAddress.getByAddress(upstream4Value.dst46).getAddress())); 1036 assertEquals(REMOTE_PORT, upstream4Value.dstPort); 1037 1038 // [2] Verify stats map. 1039 // Transmit packets on both direction for verifying stats. Because we only care the 1040 // packet count in stats test, we just reuse the existing packets to increaes 1041 // the packet count on both direction. 1042 1043 // Send packets on original direction. 1044 for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) { 1045 sendUploadPacketUdp(srcMac, dstMac, clientIp, remoteIp, tester, 1046 false /* is4To6 */); 1047 } 1048 1049 // Send packets on reply direction. 1050 for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) { 1051 sendDownloadPacketUdp(remoteIp, tetheringUpstreamIp, tester, false /* is6To4 */); 1052 } 1053 1054 // Dump stats map to verify. 1055 final String[] statsArgs = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, 1056 DUMPSYS_RAWMAP_ARG_STATS}; 1057 final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump( 1058 TetherStatsKey.class, TetherStatsValue.class, Context.TETHERING_SERVICE, statsArgs); 1059 assertNotNull(statsMap); 1060 assertEquals(1, statsMap.size()); 1061 1062 final Map.Entry<TetherStatsKey, TetherStatsValue> stats = 1063 statsMap.entrySet().iterator().next(); 1064 1065 // TODO: verify the upstream index in TetherStatsKey. 1066 1067 final TetherStatsValue statsValue = stats.getValue(); 1068 assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets); 1069 assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes); 1070 assertEquals(0, statsValue.rxErrors); 1071 assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets); 1072 assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes); 1073 assertEquals(0, statsValue.txErrors); 1074 } 1075 1076 // on S/Sv2 without a new enough DnsResolver apex, NetBpfLoad does not 1077 // get triggered, and thus no mainline programs get loaded. isNetBpfLoadEnabled()1078 private boolean isNetBpfLoadEnabled() { 1079 if (SdkLevel.isAtLeastT()) return true; 1080 if (!SdkLevel.isAtLeastS()) return false; 1081 1082 File f = new File("/apex/com.android.resolv/NetBpfLoad-S.flag"); 1083 return f.isFile(); 1084 } 1085 1086 /** 1087 * BPF offload IPv4 UDP tethering test. Verify that UDP tethered packets are offloaded by BPF. 1088 * Minimum test requirement: 1089 * 1. S+ device. 1090 * 2. Tethering config enables tethering BPF offload. 1091 * 3. Kernel supports IPv4 UDP BPF offload. See #isUdpOffloadSupportedByKernel. 1092 * 1093 * TODO: consider enabling the test even tethering config disables BPF offload. See b/238288883 1094 */ 1095 @Test 1096 @IgnoreUpTo(Build.VERSION_CODES.R) testTetherBpfOffloadUdpV4()1097 public void testTetherBpfOffloadUdpV4() throws Exception { 1098 assumeTrue("Tethering config disabled BPF offload", isTetherConfigBpfOffloadEnabled()); 1099 assumeKernelSupportBpfOffloadUdpV4(); 1100 assumeTrue("Mainline NetBpfLoad not available", isNetBpfLoadEnabled()); 1101 1102 runUdp4Test(); 1103 } 1104 getClatEgress4Value(int clatIfaceIndex)1105 private ClatEgress4Value getClatEgress4Value(int clatIfaceIndex) throws Exception { 1106 // Command: dumpsys connectivity clatEgress4RawBpfMap 1107 final String[] args = new String[] {DUMPSYS_CLAT_RAWMAP_EGRESS4_ARG}; 1108 final HashMap<ClatEgress4Key, ClatEgress4Value> egress4Map = pollRawMapFromDump( 1109 ClatEgress4Key.class, ClatEgress4Value.class, Context.CONNECTIVITY_SERVICE, args); 1110 assertNotNull(egress4Map); 1111 for (Map.Entry<ClatEgress4Key, ClatEgress4Value> entry : egress4Map.entrySet()) { 1112 ClatEgress4Key key = entry.getKey(); 1113 if (key.iif == clatIfaceIndex) { 1114 return entry.getValue(); 1115 } 1116 } 1117 return null; 1118 } 1119 getClatIngress6Value(int ifaceIndex)1120 private ClatIngress6Value getClatIngress6Value(int ifaceIndex) throws Exception { 1121 // Command: dumpsys connectivity clatIngress6RawBpfMap 1122 final String[] args = new String[] {DUMPSYS_CLAT_RAWMAP_INGRESS6_ARG}; 1123 final HashMap<ClatIngress6Key, ClatIngress6Value> ingress6Map = pollRawMapFromDump( 1124 ClatIngress6Key.class, ClatIngress6Value.class, Context.CONNECTIVITY_SERVICE, args); 1125 assertNotNull(ingress6Map); 1126 for (Map.Entry<ClatIngress6Key, ClatIngress6Value> entry : ingress6Map.entrySet()) { 1127 ClatIngress6Key key = entry.getKey(); 1128 if (key.iif == ifaceIndex) { 1129 return entry.getValue(); 1130 } 1131 } 1132 return null; 1133 } 1134 1135 /** 1136 * Test network topology: 1137 * 1138 * public network (rawip) private network 1139 * | UE (CLAT support) | 1140 * +---------------+ V +------------+------------+ V +------------+ 1141 * | NAT64 Gateway +---------+ Upstream | Downstream +---------+ Client | 1142 * +---------------+ +------------+------------+ +------------+ 1143 * remote ip public ip private ip 1144 * [64:ff9b::808:808]:443 [clat ipv6]:9876 [TetheredDevice ipv4]:9876 1145 * 1146 * Note that CLAT IPv6 address is generated by ClatCoordinator. Get the CLAT IPv6 address by 1147 * sending out an IPv4 packet and extracting the source address from CLAT translated IPv6 1148 * packet. 1149 */ 1150 @Test 1151 @IgnoreUpTo(Build.VERSION_CODES.S_V2) testTetherClatBpfOffloadUdp()1152 public void testTetherClatBpfOffloadUdp() throws Exception { 1153 assumeKernelSupportBpfOffloadUdpV4(); 1154 1155 // CLAT only starts on IPv6 only network. 1156 final TetheringTester tester = initTetheringTester(toList(TEST_IP6_ADDR), 1157 toList(TEST_IP6_DNS)); 1158 final TetheredDevice tethered = tester.createTetheredDevice(TEST_MAC, true /* hasIpv6 */); 1159 1160 // Get CLAT IPv6 address. 1161 final Inet6Address clatIp6 = getClatIpv6Address(tester, tethered); 1162 1163 // Get current values before sending packets. 1164 final String ifaceName = getUpstreamInterfaceName(); 1165 final int ifaceIndex = getIndexByName(ifaceName); 1166 final int clatIfaceIndex = getIndexByName("v4-" + ifaceName); 1167 final ClatEgress4Value oldEgress4 = getClatEgress4Value(clatIfaceIndex); 1168 final ClatIngress6Value oldIngress6 = getClatIngress6Value(ifaceIndex); 1169 assertNotNull(oldEgress4); 1170 assertNotNull(oldIngress6); 1171 1172 // Send an IPv4 UDP packet in original direction. 1173 // IPv4 packet -- CLAT translation --> IPv6 packet 1174 for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) { 1175 sendUploadPacketUdp(tethered.macAddr, tethered.routerMacAddr, tethered.ipv4Addr, 1176 REMOTE_IP4_ADDR, tester, true /* is4To6 */); 1177 } 1178 1179 // Send an IPv6 UDP packet in reply direction. 1180 // IPv6 packet -- CLAT translation --> IPv4 packet 1181 for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) { 1182 sendDownloadPacketUdp(REMOTE_NAT64_ADDR, clatIp6, tester, true /* is6To4 */); 1183 } 1184 1185 // Send fragmented IPv6 UDP packets in the reply direction. 1186 // IPv6 frament packet -- CLAT translation --> IPv4 fragment packet 1187 final int payloadLen = 1500; 1188 final int l2mtu = 1000; 1189 final int fragPktCnt = 2; // 1500 bytes of UDP payload were fragmented into two packets. 1190 final long fragRxBytes = payloadLen + UDP_HEADER_LEN + fragPktCnt * IPV4_HEADER_MIN_LEN; 1191 final byte[] payload = new byte[payloadLen]; 1192 // Initialize the payload with random bytes. 1193 Random random = new Random(); 1194 random.nextBytes(payload); 1195 sendDownloadFragmentedUdpPackets(REMOTE_NAT64_ADDR, clatIp6, tester, 1196 ByteBuffer.wrap(payload), l2mtu); 1197 1198 // After sending test packets, get stats again to verify their differences. 1199 final ClatEgress4Value newEgress4 = getClatEgress4Value(clatIfaceIndex); 1200 final ClatIngress6Value newIngress6 = getClatIngress6Value(ifaceIndex); 1201 assertNotNull(newEgress4); 1202 assertNotNull(newIngress6); 1203 1204 assertEquals(RX_UDP_PACKET_COUNT + fragPktCnt, newIngress6.packets - oldIngress6.packets); 1205 assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE + fragRxBytes, 1206 newIngress6.bytes - oldIngress6.bytes); 1207 assertEquals(TX_UDP_PACKET_COUNT, newEgress4.packets - oldEgress4.packets); 1208 // The increase in egress traffic equals the expected size of the translated UDP packets. 1209 // Calculation: 1210 // - Original UDP packet was TX_UDP_PACKET_SIZE bytes (IPv4 header + UDP header + payload). 1211 // - After CLAT translation, each packet is now: 1212 // IPv6 header + unchanged UDP header + unchanged payload 1213 // Therefore, the total size of the translated UDP packet should be: 1214 // TX_UDP_PACKET_SIZE + IPV6_HEADER_LEN - IPV4_HEADER_MIN_LEN 1215 assertEquals( 1216 TX_UDP_PACKET_COUNT * (TX_UDP_PACKET_SIZE + IPV6_HEADER_LEN - IPV4_HEADER_MIN_LEN), 1217 newEgress4.bytes - oldEgress4.bytes); 1218 } 1219 1220 @Test testTetheringVirtual()1221 public void testTetheringVirtual() throws Exception { 1222 assumeFalse(isInterfaceForTetheringAvailable()); 1223 setIncludeTestInterfaces(true); 1224 1225 TestNetworkInterface downstreamIface = null; 1226 MyTetheringEventCallback tetheringEventCallback = null; 1227 PollPacketReader downstreamReader = null; 1228 try { 1229 downstreamIface = createTestInterface(); 1230 String iface = downstreamIface.getInterfaceName(); 1231 final TetheringRequest request = new TetheringRequest.Builder(TETHERING_VIRTUAL) 1232 .setConnectivityScope(CONNECTIVITY_SCOPE_GLOBAL) 1233 .setInterfaceName(iface) 1234 .build(); 1235 tetheringEventCallback = enableTethering(iface, request, null /* any upstream */); 1236 1237 FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); 1238 downstreamReader = makePacketReader(fd, getMTU(downstreamIface)); 1239 checkTetheredClientCallbacks( 1240 downstreamReader, TETHERING_VIRTUAL, tetheringEventCallback); 1241 } finally { 1242 maybeStopTapPacketReader(downstreamReader); 1243 maybeCloseTestInterface(downstreamIface); 1244 maybeUnregisterTetheringEventCallback(tetheringEventCallback); 1245 } 1246 } 1247 1248 @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 1249 @Test testLocalAgent_networkCallbacks()1250 public void testLocalAgent_networkCallbacks() throws Exception { 1251 final boolean isMatchNonThreadLocalNetworksEnabled = runAsShell( 1252 READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE, 1253 () -> CompatChanges.isChangeEnabled(ENABLE_MATCH_NON_THREAD_LOCAL_NETWORKS)); 1254 assumeTrue(isMatchNonThreadLocalNetworksEnabled); 1255 1256 mDeviceConfigRule.setConfig(NAMESPACE_TETHERING, TETHERING_LOCAL_NETWORK_AGENT, "1"); 1257 assumeFalse(isInterfaceForTetheringAvailable()); 1258 setIncludeTestInterfaces(true); 1259 1260 TestNetworkInterface downstreamIface = null; 1261 MyTetheringEventCallback tetheringEventCallback = null; 1262 1263 final TestableNetworkCallback networkCallback = new TestableNetworkCallback(); 1264 final NetworkRequest networkRequest = new NetworkRequest.Builder() 1265 .addCapability(NET_CAPABILITY_LOCAL_NETWORK).build(); 1266 mNetworkCallbackRule.registerNetworkCallback(networkRequest, networkCallback); 1267 1268 try { 1269 downstreamIface = createTestInterface(); 1270 1271 final String iface = mTetheredInterfaceRequester.getInterface(); 1272 assertEquals("TetheredInterfaceCallback for unexpected interface", 1273 downstreamIface.getInterfaceName(), iface); 1274 1275 final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET) 1276 .setConnectivityScope(CONNECTIVITY_SCOPE_GLOBAL).build(); 1277 tetheringEventCallback = enableTethering(iface, request, null /* any upstream */); 1278 tetheringEventCallback.awaitInterfaceTethered(); 1279 1280 // Verify NetworkCallback works accordingly. 1281 final Network network = networkCallback.expect(CallbackEntry.AVAILABLE).getNetwork(); 1282 final CallbackEntry.CapabilitiesChanged capEvent = 1283 networkCallback.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED); 1284 assertEquals(network, capEvent.getNetwork()); 1285 assertTrue(capEvent.getCaps().hasTransport(TRANSPORT_ETHERNET)); 1286 assertTrue(capEvent.getCaps().hasCapability(NET_CAPABILITY_LOCAL_NETWORK)); 1287 } finally { 1288 stopEthernetTethering(tetheringEventCallback); 1289 maybeCloseTestInterface(downstreamIface); 1290 } 1291 } 1292 } 1293