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