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