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