1 /*
2  * Copyright (C) 2021 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.DnsResolver.CLASS_IN;
20 import static android.net.DnsResolver.TYPE_AAAA;
21 import static android.net.InetAddresses.parseNumericAddress;
22 import static android.system.OsConstants.ICMP_ECHO;
23 import static android.system.OsConstants.ICMP_ECHOREPLY;
24 import static android.system.OsConstants.IPPROTO_ICMP;
25 import static android.system.OsConstants.IPPROTO_ICMPV6;
26 import static android.system.OsConstants.IPPROTO_IP;
27 import static android.system.OsConstants.IPPROTO_IPV6;
28 import static android.system.OsConstants.IPPROTO_TCP;
29 import static android.system.OsConstants.IPPROTO_UDP;
30 
31 import static com.android.net.module.util.DnsPacket.ANSECTION;
32 import static com.android.net.module.util.DnsPacket.DnsHeader;
33 import static com.android.net.module.util.DnsPacket.DnsRecord;
34 import static com.android.net.module.util.DnsPacket.QDSECTION;
35 import static com.android.net.module.util.HexDump.dumpHexString;
36 import static com.android.net.module.util.IpUtils.icmpChecksum;
37 import static com.android.net.module.util.IpUtils.ipChecksum;
38 import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY;
39 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST;
40 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN;
41 import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST;
42 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4;
43 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
44 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
45 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
46 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
47 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
48 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
49 import static com.android.net.module.util.NetworkStackConstants.ICMP_CHECKSUM_OFFSET;
50 import static com.android.net.module.util.NetworkStackConstants.IPV4_CHECKSUM_OFFSET;
51 import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
52 import static com.android.net.module.util.NetworkStackConstants.IPV4_LENGTH_OFFSET;
53 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST;
54 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
55 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED;
56 import static com.android.net.module.util.NetworkStackConstants.TCPHDR_SYN;
57 
58 import static org.junit.Assert.assertNotNull;
59 import static org.junit.Assert.fail;
60 
61 import android.net.dhcp.DhcpAckPacket;
62 import android.net.dhcp.DhcpOfferPacket;
63 import android.net.dhcp.DhcpPacket;
64 import android.text.TextUtils;
65 import android.util.ArrayMap;
66 import android.util.Log;
67 
68 import androidx.annotation.NonNull;
69 import androidx.annotation.Nullable;
70 
71 import com.android.net.module.util.DnsPacket;
72 import com.android.net.module.util.Ipv6Utils;
73 import com.android.net.module.util.PacketBuilder;
74 import com.android.net.module.util.Struct;
75 import com.android.net.module.util.arp.ArpPacket;
76 import com.android.net.module.util.structs.EthernetHeader;
77 import com.android.net.module.util.structs.Icmpv4Header;
78 import com.android.net.module.util.structs.Icmpv6Header;
79 import com.android.net.module.util.structs.Ipv4Header;
80 import com.android.net.module.util.structs.Ipv6Header;
81 import com.android.net.module.util.structs.LlaOption;
82 import com.android.net.module.util.structs.NsHeader;
83 import com.android.net.module.util.structs.PrefixInformationOption;
84 import com.android.net.module.util.structs.RaHeader;
85 import com.android.net.module.util.structs.TcpHeader;
86 import com.android.net.module.util.structs.UdpHeader;
87 import com.android.testutils.PollPacketReader;
88 
89 import java.net.Inet4Address;
90 import java.net.Inet6Address;
91 import java.net.InetAddress;
92 import java.nio.ByteBuffer;
93 import java.util.ArrayList;
94 import java.util.Arrays;
95 import java.util.List;
96 import java.util.Map;
97 import java.util.Random;
98 import java.util.concurrent.TimeoutException;
99 import java.util.function.Predicate;
100 
101 /**
102  * A class simulate tethered client. When caller create TetheringTester, it would connect to
103  * tethering module that do the dhcp and slaac to obtain ipv4 and ipv6 address. Then caller can
104  * send/receive packets by this class.
105  */
106 public final class TetheringTester {
107     private static final String TAG = TetheringTester.class.getSimpleName();
108     private static final int PACKET_READ_TIMEOUT_MS = 500;
109     private static final int DHCP_DISCOVER_ATTEMPTS = 10;
110     private static final int READ_RA_ATTEMPTS = 10;
111     private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
112             DhcpPacket.DHCP_SUBNET_MASK,
113             DhcpPacket.DHCP_ROUTER,
114             DhcpPacket.DHCP_DNS_SERVER,
115             DhcpPacket.DHCP_LEASE_TIME,
116     };
117     private static final InetAddress LINK_LOCAL = parseNumericAddress("fe80::1");
118     // IPv4 header definition.
119     protected static final short ID = 27149;
120     protected static final short FLAGS_AND_FRAGMENT_OFFSET = (short) 0x4000; // flags=DF, offset=0
121     protected static final byte TIME_TO_LIVE = (byte) 0x40;
122     protected static final byte TYPE_OF_SERVICE = 0;
123 
124     // IPv6 header definition.
125     private static final short HOP_LIMIT = 0x40;
126     // version=6, traffic class=0x0, flowlabel=0x0;
127     private static final int VERSION_TRAFFICCLASS_FLOWLABEL = 0x60000000;
128 
129     // UDP and TCP header definition.
130     private static final short WINDOW = (short) 0x2000;
131     private static final short URGENT_POINTER = 0;
132 
133     // ICMP definition.
134     private static final short ICMPECHO_CODE = 0x0;
135 
136     // Prefix64 discovery definition. See RFC 7050 section 8.
137     // Note that the AAAA response Pref64::WKAs consisting of Pref64::/n and WKA.
138     // Use 64:ff9b::/96 as Pref64::/n and WKA 192.0.0.17{0|1} here.
139     //
140     // Host                                          DNS64 server
141     //   |                                                |
142     //   |  "AAAA" query for "ipv4only.arpa."             |
143     //   |----------------------------------------------->|
144     //   |                                                |
145     //   |  "AAAA" response with:                         |
146     //   |  "64:ff9b::192.0.0.170"                        |
147     //   |<-----------------------------------------------|
148     //
149     private static final String PREF64_IPV4ONLY_HOSTNAME = "ipv4only.arpa";
150     private static final InetAddress PREF64_IPV4ONLY_ADDR = parseNumericAddress(
151             "64:ff9b::192.0.0.170");
152 
153     // DNS header definition.
154     private static final short FLAG = (short) 0x8100;  // qr, ra
155     private static final short TTL = (short) 0;
156 
157     public static final String DHCP_HOSTNAME = "testhostname";
158 
159     private final ArrayMap<MacAddress, TetheredDevice> mTetheredDevices;
160     private final PollPacketReader mDownstreamReader;
161     private final PollPacketReader mUpstreamReader;
162 
TetheringTester(PollPacketReader downstream)163     public TetheringTester(PollPacketReader downstream) {
164         this(downstream, null);
165     }
166 
TetheringTester(PollPacketReader downstream, PollPacketReader upstream)167     public TetheringTester(PollPacketReader downstream, PollPacketReader upstream) {
168         if (downstream == null) fail("Downstream reader could not be NULL");
169 
170         mDownstreamReader = downstream;
171         mUpstreamReader = upstream;
172         mTetheredDevices = new ArrayMap<>();
173     }
174 
createTetheredDevice(MacAddress macAddr, boolean hasIpv6)175     public TetheredDevice createTetheredDevice(MacAddress macAddr, boolean hasIpv6)
176             throws Exception {
177         if (mTetheredDevices.get(macAddr) != null) {
178             fail("Tethered device already created");
179         }
180 
181         TetheredDevice tethered = new TetheredDevice(macAddr, hasIpv6);
182         mTetheredDevices.put(macAddr, tethered);
183 
184         return tethered;
185     }
186 
187     public class TetheredDevice {
188         public final MacAddress macAddr;
189         public final MacAddress routerMacAddr;
190         public final Inet4Address ipv4Addr;
191         public final Inet4Address ipv4Gatway;
192         public final Inet6Address ipv6Addr;
193 
TetheredDevice(MacAddress mac, boolean hasIpv6)194         private TetheredDevice(MacAddress mac, boolean hasIpv6) throws Exception {
195             macAddr = mac;
196             DhcpResults dhcpResults = runDhcp(macAddr.toByteArray());
197             ipv4Addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
198             ipv4Gatway = (Inet4Address) dhcpResults.gateway;
199             routerMacAddr = getRouterMacAddressFromArp(ipv4Addr, macAddr,
200                     dhcpResults.serverAddress);
201             ipv6Addr = hasIpv6 ? runSlaac(macAddr, routerMacAddr) : null;
202         }
203     }
204 
205     /** Simulate dhcp client to obtain ipv4 address. */
runDhcp(byte[] clientMacAddr)206     public DhcpResults runDhcp(byte[] clientMacAddr)
207             throws Exception {
208         // We have to retransmit DHCP requests because IpServer declares itself to be ready before
209         // its DhcpServer is actually started. TODO: fix this race and remove this loop.
210         DhcpPacket offerPacket = null;
211         for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
212             Log.d(TAG, "Sending DHCP discover");
213             sendDhcpDiscover(clientMacAddr);
214             offerPacket = getNextDhcpPacket();
215             if (offerPacket instanceof DhcpOfferPacket) break;
216         }
217         if (!(offerPacket instanceof DhcpOfferPacket)) {
218             throw new TimeoutException("No DHCPOFFER received on interface within timeout");
219         }
220 
221         sendDhcpRequest(offerPacket, clientMacAddr);
222         DhcpPacket ackPacket = getNextDhcpPacket();
223         if (!(ackPacket instanceof DhcpAckPacket)) {
224             throw new TimeoutException("No DHCPACK received on interface within timeout");
225         }
226 
227         return ackPacket.toDhcpResults();
228     }
229 
sendDhcpDiscover(byte[] macAddress)230     private void sendDhcpDiscover(byte[] macAddress) throws Exception {
231         ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
232                 new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
233                 macAddress,  false /* unicast */, DHCP_REQUESTED_PARAMS,
234                 false /* rapid commit */,  DHCP_HOSTNAME);
235         mDownstreamReader.sendResponse(packet);
236     }
237 
sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress)238     private void sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress)
239             throws Exception {
240         DhcpResults results = offerPacket.toDhcpResults();
241         Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
242         Inet4Address serverIdentifier = results.serverAddress;
243         ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
244                 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
245                 false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
246                 serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
247         mDownstreamReader.sendResponse(packet);
248     }
249 
getNextDhcpPacket()250     private DhcpPacket getNextDhcpPacket() throws Exception {
251         final byte[] packet = getDownloadPacket((p) -> {
252             // Test whether this is DHCP packet.
253             try {
254                 DhcpPacket.decodeFullPacket(p, p.length, DhcpPacket.ENCAP_L2);
255             } catch (DhcpPacket.ParseException e) {
256                 // Not a DHCP packet.
257                 return false;
258             }
259 
260             return true;
261         });
262 
263         return packet == null ? null :
264                 DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2);
265     }
266 
267     @Nullable
parseArpPacket(final byte[] packet)268     private ArpPacket parseArpPacket(final byte[] packet) {
269         try {
270             return ArpPacket.parseArpPacket(packet, packet.length);
271         } catch (ArpPacket.ParseException e) {
272             return null;
273         }
274     }
275 
maybeReplyArp(byte[] packet)276     private void maybeReplyArp(byte[] packet) {
277         ByteBuffer buf = ByteBuffer.wrap(packet);
278 
279         final ArpPacket arpPacket = parseArpPacket(packet);
280         if (arpPacket == null || arpPacket.opCode != ARP_REQUEST) return;
281 
282         for (int i = 0; i < mTetheredDevices.size(); i++) {
283             TetheredDevice tethered = mTetheredDevices.valueAt(i);
284             if (!arpPacket.targetIp.equals(tethered.ipv4Addr)) continue;
285 
286             final ByteBuffer arpReply = ArpPacket.buildArpPacket(
287                     arpPacket.senderHwAddress.toByteArray() /* dst */,
288                     tethered.macAddr.toByteArray() /* srcMac */,
289                     arpPacket.senderIp.getAddress() /* target IP */,
290                     arpPacket.senderHwAddress.toByteArray() /* target HW address */,
291                     tethered.ipv4Addr.getAddress() /* sender IP */,
292                     (short) ARP_REPLY);
293             try {
294                 sendUploadPacket(arpReply);
295             } catch (Exception e) {
296                 fail("Failed to reply ARP for " + tethered.ipv4Addr);
297             }
298             return;
299         }
300     }
301 
getRouterMacAddressFromArp(final Inet4Address tetherIp, final MacAddress tetherMac, final Inet4Address routerIp)302     private MacAddress getRouterMacAddressFromArp(final Inet4Address tetherIp,
303             final MacAddress tetherMac, final Inet4Address routerIp) throws Exception {
304         final ByteBuffer arpProbe = ArpPacket.buildArpPacket(ETHER_BROADCAST /* dst */,
305                 tetherMac.toByteArray() /* srcMac */, routerIp.getAddress() /* target IP */,
306                 new byte[ETHER_ADDR_LEN] /* target HW address */,
307                 tetherIp.getAddress() /* sender IP */, (short) ARP_REQUEST);
308         sendUploadPacket(arpProbe);
309 
310         final byte[] packet = getDownloadPacket((p) -> {
311             final ArpPacket arpPacket = parseArpPacket(p);
312             if (arpPacket == null || arpPacket.opCode != ARP_REPLY) return false;
313             return arpPacket.targetIp.equals(tetherIp);
314         });
315 
316         if (packet != null) {
317             Log.d(TAG, "Get Mac address from ARP");
318             final ArpPacket arpReply = ArpPacket.parseArpPacket(packet, packet.length);
319             return arpReply.senderHwAddress;
320         }
321 
322         fail("Could not get ARP packet");
323         return null;
324     }
325 
getRaPrefixOptions(byte[] packet)326     private List<PrefixInformationOption> getRaPrefixOptions(byte[] packet) {
327         ByteBuffer buf = ByteBuffer.wrap(packet);
328         if (!isExpectedIcmpPacket(buf, true /* hasEth */, false /* isIpv4 */,
329                 ICMPV6_ROUTER_ADVERTISEMENT)) {
330             fail("Parsing RA packet fail");
331         }
332 
333         Struct.parse(RaHeader.class, buf);
334         final ArrayList<PrefixInformationOption> pioList = new ArrayList<>();
335         while (buf.position() < packet.length) {
336             final int currentPos = buf.position();
337             final int type = Byte.toUnsignedInt(buf.get());
338             final int length = Byte.toUnsignedInt(buf.get());
339             if (type == ICMPV6_ND_OPTION_PIO) {
340                 final ByteBuffer pioBuf = ByteBuffer.wrap(buf.array(), currentPos,
341                         Struct.getSize(PrefixInformationOption.class));
342                 final PrefixInformationOption pio =
343                         Struct.parse(PrefixInformationOption.class, pioBuf);
344                 pioList.add(pio);
345 
346                 // Move ByteBuffer position to the next option.
347                 buf.position(currentPos + Struct.getSize(PrefixInformationOption.class));
348             } else {
349                 buf.position(currentPos + (length * 8));
350             }
351         }
352         return pioList;
353     }
354 
runSlaac(MacAddress srcMac, MacAddress dstMac)355     private Inet6Address runSlaac(MacAddress srcMac, MacAddress dstMac) throws Exception {
356         sendRsPacket(srcMac, dstMac);
357 
358         final byte[] raPacket = verifyPacketNotNull("Receive RA fail", getDownloadPacket(p -> {
359             return isExpectedIcmpPacket(p, true /* hasEth */, false /* isIpv4 */,
360                     ICMPV6_ROUTER_ADVERTISEMENT);
361         }));
362 
363         final List<PrefixInformationOption> options = getRaPrefixOptions(raPacket);
364 
365         for (PrefixInformationOption pio : options) {
366             if (pio.validLifetime > 0) {
367                 final byte[] addressBytes = pio.prefix;
368                 // Random the last two bytes as suffix.
369                 // TODO: Currently do not implmement DAD in the test. Rely the gateway ipv6 address
370                 // genetrated by tethering module always has random the last byte.
371                 addressBytes[addressBytes.length - 1] = (byte) (new Random()).nextInt();
372                 addressBytes[addressBytes.length - 2] = (byte) (new Random()).nextInt();
373 
374                 return (Inet6Address) InetAddress.getByAddress(addressBytes);
375             }
376         }
377 
378         fail("No available ipv6 prefix");
379         return null;
380     }
381 
sendRsPacket(MacAddress srcMac, MacAddress dstMac)382     private void sendRsPacket(MacAddress srcMac, MacAddress dstMac) throws Exception {
383         Log.d(TAG, "Sending RS");
384         ByteBuffer slla = LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac);
385         ByteBuffer rs = Ipv6Utils.buildRsPacket(srcMac, dstMac, (Inet6Address) LINK_LOCAL,
386                 IPV6_ADDR_ALL_NODES_MULTICAST, slla);
387 
388         sendUploadPacket(rs);
389     }
390 
maybeReplyNa(byte[] packet)391     private void maybeReplyNa(byte[] packet) {
392         ByteBuffer buf = ByteBuffer.wrap(packet);
393         final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf);
394         if (ethHdr.etherType != ETHER_TYPE_IPV6) return;
395 
396         final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf);
397         if (ipv6Hdr.nextHeader != (byte) IPPROTO_ICMPV6) return;
398 
399         final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf);
400         if (icmpv6Hdr.type != (short) ICMPV6_NEIGHBOR_SOLICITATION) return;
401 
402         final NsHeader nsHdr = Struct.parse(NsHeader.class, buf);
403         for (int i = 0; i < mTetheredDevices.size(); i++) {
404             TetheredDevice tethered = mTetheredDevices.valueAt(i);
405             if (!nsHdr.target.equals(tethered.ipv6Addr)) continue;
406 
407             final ByteBuffer tlla = LlaOption.build((byte) ICMPV6_ND_OPTION_TLLA, tethered.macAddr);
408             int flags = NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED
409                     | NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE;
410             ByteBuffer ns = Ipv6Utils.buildNaPacket(tethered.macAddr, tethered.routerMacAddr,
411                     nsHdr.target, ipv6Hdr.srcIp, flags, nsHdr.target, tlla);
412             try {
413                 sendUploadPacket(ns);
414             } catch (Exception e) {
415                 fail("Failed to reply NA for " + tethered.ipv6Addr);
416             }
417 
418             return;
419         }
420     }
421 
isExpectedIcmpPacket(byte[] packet, boolean hasEth, boolean isIpv4, int type)422     public static boolean isExpectedIcmpPacket(byte[] packet, boolean hasEth, boolean isIpv4,
423             int type) {
424         final ByteBuffer buf = ByteBuffer.wrap(packet);
425         return isExpectedIcmpPacket(buf, hasEth, isIpv4, type);
426     }
427 
isExpectedIcmpPacket(ByteBuffer buf, boolean hasEth, boolean isIpv4, int type)428     private static boolean isExpectedIcmpPacket(ByteBuffer buf, boolean hasEth, boolean isIpv4,
429             int type) {
430         try {
431             if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false;
432 
433             final int ipProto = isIpv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6;
434             if (!hasExpectedIpHeader(buf, isIpv4, ipProto)) return false;
435 
436             if (isIpv4) {
437                 return Struct.parse(Icmpv4Header.class, buf).type == (short) type;
438             } else {
439                 return Struct.parse(Icmpv6Header.class, buf).type == (short) type;
440             }
441         } catch (Exception e) {
442             // Parsing packet fail means it is not icmp packet.
443         }
444 
445         return false;
446     }
447 
hasExpectedEtherHeader(@onNull final ByteBuffer buf, boolean isIpv4)448     private static boolean hasExpectedEtherHeader(@NonNull final ByteBuffer buf, boolean isIpv4)
449             throws Exception {
450         final int expected = isIpv4 ? ETHER_TYPE_IPV4 : ETHER_TYPE_IPV6;
451 
452         return Struct.parse(EthernetHeader.class, buf).etherType == expected;
453     }
454 
hasExpectedIpHeader(@onNull final ByteBuffer buf, boolean isIpv4, int ipProto)455     private static boolean hasExpectedIpHeader(@NonNull final ByteBuffer buf, boolean isIpv4,
456             int ipProto) throws Exception {
457         if (isIpv4) {
458             return Struct.parse(Ipv4Header.class, buf).protocol == (byte) ipProto;
459         } else {
460             return Struct.parse(Ipv6Header.class, buf).nextHeader == (byte) ipProto;
461         }
462     }
463 
isExpectedUdpPacket(@onNull final byte[] rawPacket, boolean hasEth, boolean isIpv4, Predicate<ByteBuffer> payloadVerifier)464     private static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
465             boolean isIpv4, Predicate<ByteBuffer> payloadVerifier) {
466         final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
467         try {
468             if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false;
469 
470             if (!hasExpectedIpHeader(buf, isIpv4, IPPROTO_UDP)) return false;
471 
472             if (Struct.parse(UdpHeader.class, buf) == null) return false;
473 
474             if (!payloadVerifier.test(buf)) return false;
475         } catch (Exception e) {
476             // Parsing packet fail means it is not udp packet.
477             return false;
478         }
479         return true;
480     }
481 
482     // Returns remaining bytes in the ByteBuffer in a new byte array of the right size. The
483     // ByteBuffer will be empty upon return. Used to avoid lint warning.
484     // See https://errorprone.info/bugpattern/ByteBufferBackingArray
getRemaining(final ByteBuffer buf)485     private static byte[] getRemaining(final ByteBuffer buf) {
486         final byte[] bytes = new byte[buf.remaining()];
487         buf.get(bytes);
488         Log.d(TAG, "Get remaining bytes: " + dumpHexString(bytes));
489         return bytes;
490     }
491 
492     // |expectedPayload| is copied as read-only because the caller may reuse it.
isExpectedUdpPacket(@onNull final byte[] rawPacket, boolean hasEth, boolean isIpv4, @NonNull final ByteBuffer expectedPayload)493     public static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
494             boolean isIpv4, @NonNull final ByteBuffer expectedPayload) {
495         return isExpectedUdpPacket(rawPacket, hasEth, isIpv4, p -> {
496             if (p.remaining() != expectedPayload.limit()) return false;
497 
498             return Arrays.equals(getRemaining(p), getRemaining(
499                     expectedPayload.asReadOnlyBuffer()));
500         });
501     }
502 
503     /**
504      * Checks if the given raw packet data represents an expected fragmented IP packet.
505      *
506      * @param rawPacket the raw packet data to check.
507      * @param id the identification field of the fragmented IP packet.
508      * @param expectedPayloads a map of fragment offsets to their corresponding payload data.
509      * @return true if the packet is a valid fragmented IP packet with matching payload fragments;
510      *         false otherwise.
511      */
isExpectedFragmentIpPacket(@onNull final byte[] rawPacket, int id, @NonNull final Map<Short, ByteBuffer> expectedPayloads)512     public static boolean isExpectedFragmentIpPacket(@NonNull final byte[] rawPacket, int id,
513             @NonNull final Map<Short, ByteBuffer> expectedPayloads) {
514         final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
515         try {
516             // Validate Ethernet header and IPv4 header.
517             if (!hasExpectedEtherHeader(buf, true /* isIpv4 */)) return false;
518             final Ipv4Header ipv4Header = Struct.parse(Ipv4Header.class, buf);
519             if (ipv4Header.protocol != (byte) IPPROTO_UDP) return false;
520             if (ipv4Header.id != id) return false;
521             // Validate payload data which expected at a specific fragment offset.
522             final ByteBuffer expectedPayload =
523                     expectedPayloads.get(ipv4Header.flagsAndFragmentOffset);
524             if (expectedPayload == null) return false;
525             if (buf.remaining() != expectedPayload.limit()) return false;
526             // Validate UDP header (which located in the 1st fragment).
527             // TODO: Validate the checksum field in UDP header. Currently, it'll be altered by NAT.
528             if ((ipv4Header.flagsAndFragmentOffset & 0x1FFF) == 0) {
529                 final UdpHeader receivedUdpHeader = Struct.parse(UdpHeader.class, buf);
530                 final UdpHeader expectedUdpHeader = Struct.parse(UdpHeader.class, expectedPayload);
531                 if (receivedUdpHeader == null || expectedUdpHeader == null) return false;
532                 if (receivedUdpHeader.srcPort != expectedUdpHeader.srcPort
533                         || receivedUdpHeader.dstPort != expectedUdpHeader.dstPort
534                         || receivedUdpHeader.length != expectedUdpHeader.length) {
535                     return false;
536                 }
537                 return true;
538             }
539             // Check the contents of the remaining payload.
540             return Arrays.equals(getRemaining(buf),
541                     getRemaining(expectedPayload.asReadOnlyBuffer()));
542         } catch (Exception e) {
543             // A failed packet parsing indicates that the packet is not a fragmented IPv4 packet.
544             return false;
545         }
546     }
547 
548     // |expectedPayload| is copied as read-only because the caller may reuse it.
549     // See hasExpectedDnsMessage.
isExpectedUdpDnsPacket(@onNull final byte[] rawPacket, boolean hasEth, boolean isIpv4, @NonNull final ByteBuffer expectedPayload)550     public static boolean isExpectedUdpDnsPacket(@NonNull final byte[] rawPacket, boolean hasEth,
551             boolean isIpv4, @NonNull final ByteBuffer expectedPayload) {
552         return isExpectedUdpPacket(rawPacket, hasEth, isIpv4, p -> {
553             return hasExpectedDnsMessage(p, expectedPayload);
554         });
555     }
556 
557     public static class TestDnsPacket extends DnsPacket {
558         TestDnsPacket(byte[] data) throws DnsPacket.ParseException {
559             super(data);
560         }
561 
562         TestDnsPacket(@NonNull DnsHeader header, @Nullable ArrayList<DnsRecord> qd,
563                 @Nullable ArrayList<DnsRecord> an) {
564             super(header, qd, an);
565         }
566 
567         @Nullable
568         public static TestDnsPacket getTestDnsPacket(final ByteBuffer buf) {
569             try {
570                 // The ByteBuffer will be empty upon return.
571                 return new TestDnsPacket(getRemaining(buf));
572             } catch (DnsPacket.ParseException e) {
573                 return null;
574             }
575         }
576 
577         public DnsHeader getHeader() {
578             return mHeader;
579         }
580 
581         public List<DnsRecord> getRecordList(int secType) {
582             return mRecords[secType];
583         }
584 
585         public int getANCount() {
586             return mHeader.getRecordCount(ANSECTION);
587         }
588 
589         public int getQDCount() {
590             return mHeader.getRecordCount(QDSECTION);
591         }
592 
593         public int getNSCount() {
594             return mHeader.getRecordCount(NSSECTION);
595         }
596 
597         public int getARCount() {
598             return mHeader.getRecordCount(ARSECTION);
599         }
600 
601         private boolean isRecordsEquals(int type, @NonNull final TestDnsPacket other) {
602             List<DnsRecord> records = getRecordList(type);
603             List<DnsRecord> otherRecords = other.getRecordList(type);
604 
605             if (records.size() != otherRecords.size()) return false;
606 
607             // Expect that two compared resource records are in the same order. For current tests
608             // in EthernetTetheringTest, it is okay because dnsmasq doesn't reorder the forwarded
609             // resource records.
610             // TODO: consider allowing that compare records out of order.
611             for (int i = 0; i < records.size(); i++) {
612                 // TODO: use DnsRecord.equals once aosp/1387135 is merged.
613                 if (!TextUtils.equals(records.get(i).dName, otherRecords.get(i).dName)
614                         || records.get(i).nsType != otherRecords.get(i).nsType
615                         || records.get(i).nsClass != otherRecords.get(i).nsClass
616                         || records.get(i).ttl != otherRecords.get(i).ttl
617                         || !Arrays.equals(records.get(i).getRR(), otherRecords.get(i).getRR())) {
618                     return false;
619                 }
620             }
621             return true;
622         }
623 
624         public boolean isQDRecordsEquals(@NonNull final TestDnsPacket other) {
625             return isRecordsEquals(QDSECTION, other);
626         }
627 
628         public boolean isANRecordsEquals(@NonNull final TestDnsPacket other) {
629             return isRecordsEquals(ANSECTION, other);
630         }
631     }
632 
633     // The ByteBuffer |actual| will be empty upon return. The ByteBuffer |excepted| will be copied
634     // as read-only because the caller may reuse it.
635     private static boolean hasExpectedDnsMessage(@NonNull final ByteBuffer actual,
636             @NonNull final ByteBuffer excepted) {
637         // Forwarded DNS message is extracted from remaining received packet buffer which has
638         // already parsed ethernet header, if any, IP header and UDP header.
639         final TestDnsPacket forwardedDns = TestDnsPacket.getTestDnsPacket(actual);
640         if (forwardedDns == null) return false;
641 
642         // Original DNS message is the payload of the sending test UDP packet. It is used to check
643         // that the forwarded DNS query and reply have corresponding contents.
644         final TestDnsPacket originalDns = TestDnsPacket.getTestDnsPacket(
645                 excepted.asReadOnlyBuffer());
646         assertNotNull(originalDns);
647 
648         // Compare original DNS message which is sent to dnsmasq and forwarded DNS message which
649         // is forwarded by dnsmasq. The original message and forwarded message may be not identical
650         // because dnsmasq may change the header flags or even recreate the DNS query message and
651         // so on. We only simple check on forwarded packet and monitor if test will be broken by
652         // vendor dnsmasq customization. See forward_query() in external/dnsmasq/src/forward.c.
653         //
654         // DNS message format. See rfc1035 section 4.1.
655         // +---------------------+
656         // |        Header       |
657         // +---------------------+
658         // |       Question      | the question for the name server
659         // +---------------------+
660         // |        Answer       | RRs answering the question
661         // +---------------------+
662         // |      Authority      | RRs pointing toward an authority
663         // +---------------------+
664         // |      Additional     | RRs holding additional information
665         // +---------------------+
666 
667         // [1] Header section. See rfc1035 section 4.1.1.
668         // Verify QR flag bit, QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT.
669         if (originalDns.getHeader().isResponse() != forwardedDns.getHeader().isResponse()) {
670             return false;
671         }
672         if (originalDns.getQDCount() != forwardedDns.getQDCount()) return false;
673         if (originalDns.getANCount() != forwardedDns.getANCount()) return false;
674         if (originalDns.getNSCount() != forwardedDns.getNSCount()) return false;
675         if (originalDns.getARCount() != forwardedDns.getARCount()) return false;
676 
677         // [2] Question section. See rfc1035 section 4.1.2.
678         // Question section has at least one entry either DNS query or DNS reply.
679         if (forwardedDns.getRecordList(QDSECTION).isEmpty()) return false;
680         // Expect that original and forwarded message have the same question records (usually 1).
681         if (!originalDns.isQDRecordsEquals(forwardedDns)) return false;
682 
683         // [3] Answer section. See rfc1035 section 4.1.3.
684         if (forwardedDns.getHeader().isResponse()) {
685             // DNS reply has at least have one answer in our tests.
686             // See EthernetTetheringTest#testTetherUdpV4Dns.
687             if (forwardedDns.getRecordList(ANSECTION).isEmpty()) return false;
688             // Expect that original and forwarded message have the same answer records.
689             if (!originalDns.isANRecordsEquals(forwardedDns)) return false;
690         }
691 
692         // Ignore checking {Authority, Additional} sections because they are not tested
693         // in EthernetTetheringTest.
694         return true;
695     }
696 
697 
698     private static boolean isTcpSynPacket(@NonNull final TcpHeader tcpHeader) {
699         return (tcpHeader.dataOffsetAndControlBits & TCPHDR_SYN) != 0;
700     }
701 
702     public static boolean isExpectedTcpPacket(@NonNull final byte[] rawPacket, boolean hasEth,
703             boolean isIpv4, int seq, @NonNull final ByteBuffer payload) {
704         final ByteBuffer buf = ByteBuffer.wrap(rawPacket);
705         try {
706             if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false;
707 
708             if (!hasExpectedIpHeader(buf, isIpv4, IPPROTO_TCP)) return false;
709 
710             final TcpHeader tcpHeader = Struct.parse(TcpHeader.class, buf);
711             if (tcpHeader.seq != seq) return false;
712 
713             // Don't try to parse the payload if it is a TCP SYN segment because additional TCP
714             // option MSS may be added in the SYN segment. Currently, TetherController uses
715             // iptables to limit downstream MSS for IPv4. The additional TCP options will be
716             // misunderstood as payload because parsing TCP options are not supported by class
717             // TcpHeader for now. See TetherController::setupIptablesHooks.
718             // TODO: remove once TcpHeader supports parsing TCP options.
719             if (isTcpSynPacket(tcpHeader)) {
720                 Log.d(TAG, "Found SYN segment. Ignore parsing the remaining part of packet.");
721                 return true;
722             }
723 
724             if (payload.limit() != buf.remaining()) return false;
725             return Arrays.equals(getRemaining(buf), getRemaining(payload.asReadOnlyBuffer()));
726         } catch (Exception e) {
727             // Parsing packet fail means it is not tcp packet.
728         }
729 
730         return false;
731     }
732 
733     @NonNull
734     public static List<ByteBuffer> buildUdpPackets(
735             @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
736             @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
737             short srcPort, short dstPort, @Nullable final ByteBuffer payload, int l2mtu)
738             throws Exception {
739         final int ipProto = getIpProto(srcIp, dstIp);
740         final boolean hasEther = (srcMac != null && dstMac != null);
741         final int payloadLen = (payload == null) ? 0 : payload.limit();
742         final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_UDP,
743                 payloadLen);
744         final PacketBuilder packetBuilder = new PacketBuilder(buffer);
745 
746         // [1] Ethernet header
747         if (hasEther) {
748             packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
749         }
750 
751         // [2] IP header
752         if (ipProto == IPPROTO_IP) {
753             packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
754                     TIME_TO_LIVE, (byte) IPPROTO_UDP, (Inet4Address) srcIp, (Inet4Address) dstIp);
755         } else {
756             packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_UDP,
757                     HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
758         }
759 
760         // [3] UDP header
761         packetBuilder.writeUdpHeader(srcPort, dstPort);
762 
763         // [4] Payload
764         if (payload != null) {
765             buffer.put(payload);
766             // in case data might be reused by caller, restore the position and
767             // limit of bytebuffer.
768             payload.clear();
769         }
770 
771         return l2mtu == 0
772                 ? Arrays.asList(packetBuilder.finalizePacket())
773                 : packetBuilder.finalizePacket(l2mtu);
774     }
775 
776     /**
777      * Builds a UDP packet.
778      *
779      * @param srcMac the source MAC address.
780      * @param dstMac the destination MAC address.
781      * @param srcIp the source IP address.
782      * @param dstIp the destination IP address.
783      * @param srcPort the source port number.
784      * @param dstPort the destination port number.
785      * @param payload the optional payload data to be included in the packet.
786      * @return a ByteBuffer containing the constructed UDP packet.
787      */
788     @NonNull
789     public static ByteBuffer buildUdpPacket(
790             @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
791             @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
792             short srcPort, short dstPort, @Nullable final ByteBuffer payload)
793             throws Exception {
794         return buildUdpPackets(srcMac, dstMac, srcIp, dstIp, srcPort, dstPort, payload, 0).get(0);
795     }
796 
797     @NonNull
798     public static ByteBuffer buildUdpPacket(@NonNull final InetAddress srcIp,
799             @NonNull final InetAddress dstIp, short srcPort, short dstPort,
800             @Nullable final ByteBuffer payload) throws Exception {
801         return buildUdpPacket(null /* srcMac */, null /* dstMac */, srcIp, dstIp, srcPort,
802                 dstPort, payload);
803     }
804 
805     @NonNull
806     public static ByteBuffer buildTcpPacket(
807             @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
808             @NonNull final InetAddress srcIp, @NonNull final InetAddress dstIp,
809             short srcPort, short dstPort, final short seq, final short ack,
810             final byte tcpFlags, @NonNull final ByteBuffer payload) throws Exception {
811         final int ipProto = getIpProto(srcIp, dstIp);
812         final boolean hasEther = (srcMac != null && dstMac != null);
813         final ByteBuffer buffer = PacketBuilder.allocate(hasEther, ipProto, IPPROTO_TCP,
814                 payload.limit());
815         final PacketBuilder packetBuilder = new PacketBuilder(buffer);
816 
817         // [1] Ethernet header
818         if (hasEther) {
819             packetBuilder.writeL2Header(srcMac, dstMac, getEthType(srcIp, dstIp));
820         }
821 
822         // [2] IP header
823         if (ipProto == IPPROTO_IP) {
824             packetBuilder.writeIpv4Header(TYPE_OF_SERVICE, ID, FLAGS_AND_FRAGMENT_OFFSET,
825                     TIME_TO_LIVE, (byte) IPPROTO_TCP, (Inet4Address) srcIp, (Inet4Address) dstIp);
826         } else {
827             packetBuilder.writeIpv6Header(VERSION_TRAFFICCLASS_FLOWLABEL, (byte) IPPROTO_TCP,
828                     HOP_LIMIT, (Inet6Address) srcIp, (Inet6Address) dstIp);
829         }
830 
831         // [3] TCP header
832         packetBuilder.writeTcpHeader(srcPort, dstPort, seq, ack, tcpFlags, WINDOW, URGENT_POINTER);
833 
834         // [4] Payload
835         buffer.put(payload);
836         // in case data might be reused by caller, restore the position and
837         // limit of bytebuffer.
838         payload.clear();
839 
840         return packetBuilder.finalizePacket();
841     }
842 
843     // PacketBuilder doesn't support IPv4 ICMP packet. It may need to refactor PacketBuilder first
844     // because ICMP is a specific layer 3 protocol for PacketBuilder which expects packets always
845     // have layer 3 (IP) and layer 4 (TCP, UDP) for now. Since we don't use IPv4 ICMP packet too
846     // much in this test, we just write a ICMP packet builder here.
847     @NonNull
848     public static ByteBuffer buildIcmpEchoPacketV4(
849             @Nullable final MacAddress srcMac, @Nullable final MacAddress dstMac,
850             @NonNull final Inet4Address srcIp, @NonNull final Inet4Address dstIp,
851             int type, short id, short seq) throws Exception {
852         if (type != ICMP_ECHO && type != ICMP_ECHOREPLY) {
853             fail("Unsupported ICMP type: " + type);
854         }
855 
856         // Build ICMP echo id and seq fields as payload. Ignore the data field.
857         final ByteBuffer payload = ByteBuffer.allocate(4);
858         payload.putShort(id);
859         payload.putShort(seq);
860         payload.rewind();
861 
862         final boolean hasEther = (srcMac != null && dstMac != null);
863         final int etherHeaderLen = hasEther ? Struct.getSize(EthernetHeader.class) : 0;
864         final int ipv4HeaderLen = Struct.getSize(Ipv4Header.class);
865         final int Icmpv4HeaderLen = Struct.getSize(Icmpv4Header.class);
866         final int payloadLen = payload.limit();
867         final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv4HeaderLen
868                 + Icmpv4HeaderLen + payloadLen);
869 
870         // [1] Ethernet header
871         if (hasEther) {
872             final EthernetHeader ethHeader = new EthernetHeader(dstMac, srcMac, ETHER_TYPE_IPV4);
873             ethHeader.writeToByteBuffer(packet);
874         }
875 
876         // [2] IP header
877         final Ipv4Header ipv4Header = new Ipv4Header(TYPE_OF_SERVICE,
878                 (short) 0 /* totalLength, calculate later */, ID,
879                 FLAGS_AND_FRAGMENT_OFFSET, TIME_TO_LIVE, (byte) IPPROTO_ICMP,
880                 (short) 0 /* checksum, calculate later */, srcIp, dstIp);
881         ipv4Header.writeToByteBuffer(packet);
882 
883         // [3] ICMP header
884         final Icmpv4Header icmpv4Header = new Icmpv4Header((byte) type, ICMPECHO_CODE,
885                 (short) 0 /* checksum, calculate later */);
886         icmpv4Header.writeToByteBuffer(packet);
887 
888         // [4] Payload
889         packet.put(payload);
890         packet.flip();
891 
892         // [5] Finalize packet
893         // Used for updating IP header fields. If there is Ehternet header, IPv4 header offset
894         // in buffer equals ethernet header length because IPv4 header is located next to ethernet
895         // header. Otherwise, IPv4 header offset is 0.
896         final int ipv4HeaderOffset = hasEther ? etherHeaderLen : 0;
897 
898         // Populate the IPv4 totalLength field.
899         packet.putShort(ipv4HeaderOffset + IPV4_LENGTH_OFFSET,
900                 (short) (ipv4HeaderLen + Icmpv4HeaderLen + payloadLen));
901 
902         // Populate the IPv4 header checksum field.
903         packet.putShort(ipv4HeaderOffset + IPV4_CHECKSUM_OFFSET,
904                 ipChecksum(packet, ipv4HeaderOffset /* headerOffset */));
905 
906         // Populate the ICMP checksum field.
907         packet.putShort(ipv4HeaderOffset + IPV4_HEADER_MIN_LEN + ICMP_CHECKSUM_OFFSET,
908                 icmpChecksum(packet, ipv4HeaderOffset + IPV4_HEADER_MIN_LEN,
909                         Icmpv4HeaderLen + payloadLen));
910         return packet;
911     }
912 
913     @NonNull
914     public static ByteBuffer buildIcmpEchoPacketV4(@NonNull final Inet4Address srcIp,
915             @NonNull final Inet4Address dstIp, int type, short id, short seq)
916             throws Exception {
917         return buildIcmpEchoPacketV4(null /* srcMac */, null /* dstMac */, srcIp, dstIp,
918                 type, id, seq);
919     }
920 
921     private static short getEthType(@NonNull final InetAddress srcIp,
922             @NonNull final InetAddress dstIp) {
923         return isAddressIpv4(srcIp, dstIp) ? (short) ETHER_TYPE_IPV4 : (short) ETHER_TYPE_IPV6;
924     }
925 
926     private static int getIpProto(@NonNull final InetAddress srcIp,
927             @NonNull final InetAddress dstIp) {
928         return isAddressIpv4(srcIp, dstIp) ? IPPROTO_IP : IPPROTO_IPV6;
929     }
930 
931     public static boolean isAddressIpv4(@NonNull final  InetAddress srcIp,
932             @NonNull final InetAddress dstIp) {
933         if (srcIp instanceof Inet4Address && dstIp instanceof Inet4Address) return true;
934         if (srcIp instanceof Inet6Address && dstIp instanceof Inet6Address) return false;
935 
936         fail("Unsupported conditions: srcIp " + srcIp + ", dstIp " + dstIp);
937         return false;  // unreachable
938     }
939 
940     public void sendUploadPacket(ByteBuffer packet) throws Exception {
941         mDownstreamReader.sendResponse(packet);
942     }
943 
944     private void sendDownloadPacket(ByteBuffer packet) throws Exception {
945         assertNotNull("Can't deal with upstream interface in local only mode", mUpstreamReader);
946 
947         mUpstreamReader.sendResponse(packet);
948     }
949 
950     private byte[] getDownloadPacket(Predicate<byte[]> filter) {
951         byte[] packet;
952         while ((packet = mDownstreamReader.poll(PACKET_READ_TIMEOUT_MS)) != null) {
953             if (filter.test(packet)) return packet;
954 
955             maybeReplyArp(packet);
956             maybeReplyNa(packet);
957         }
958 
959         return null;
960     }
961 
962     @NonNull
963     private ByteBuffer buildUdpDnsPrefix64ReplyPacket(int dnsId, @NonNull final Inet6Address srcIp,
964             @NonNull final Inet6Address dstIp, short srcPort, short dstPort) throws Exception {
965         // [1] Build prefix64 DNS message.
966         final ArrayList<DnsRecord> qlist = new ArrayList<>();
967         // Fill QD section.
968         qlist.add(DnsRecord.makeQuestion(PREF64_IPV4ONLY_HOSTNAME, TYPE_AAAA, CLASS_IN));
969         final ArrayList<DnsRecord> alist = new ArrayList<>();
970         // Fill AN sections.
971         alist.add(DnsRecord.makeAOrAAAARecord(ANSECTION, PREF64_IPV4ONLY_HOSTNAME, CLASS_IN, TTL,
972                 PREF64_IPV4ONLY_ADDR));
973         final TestDnsPacket dns = new TestDnsPacket(
974                 new DnsHeader(dnsId, FLAG, qlist.size(), alist.size()), qlist, alist);
975 
976         // [2] Build IPv6 UDP DNS packet.
977         return buildUdpPacket(srcIp, dstIp, srcPort, dstPort, ByteBuffer.wrap(dns.getBytes()));
978     }
979 
980     private void maybeReplyUdpDnsPrefix64Discovery(@NonNull byte[] packet) {
981         final ByteBuffer buf = ByteBuffer.wrap(packet);
982 
983         // [1] Parse the prefix64 discovery DNS query for hostname ipv4only.arpa.
984         // Parse IPv6 and UDP header.
985         Ipv6Header ipv6Header = null;
986         try {
987             ipv6Header = Struct.parse(Ipv6Header.class, buf);
988             if (ipv6Header == null || ipv6Header.nextHeader != IPPROTO_UDP) return;
989         } catch (Exception e) {
990             // Parsing packet fail means it is not IPv6 UDP packet.
991             return;
992         }
993         final UdpHeader udpHeader = Struct.parse(UdpHeader.class, buf);
994 
995         // Parse DNS message.
996         final TestDnsPacket pref64Query = TestDnsPacket.getTestDnsPacket(buf);
997         if (pref64Query == null) return;
998         if (pref64Query.getHeader().isResponse()) return;
999         if (pref64Query.getQDCount() != 1) return;
1000         if (pref64Query.getANCount() != 0) return;
1001         if (pref64Query.getNSCount() != 0) return;
1002         if (pref64Query.getARCount() != 0) return;
1003 
1004         final List<DnsRecord> qdRecordList = pref64Query.getRecordList(QDSECTION);
1005         if (qdRecordList.size() != 1) return;
1006         if (!qdRecordList.get(0).dName.equals(PREF64_IPV4ONLY_HOSTNAME)) return;
1007 
1008         // [2] Build prefix64 DNS discovery reply from received query.
1009         // DNS response transaction id must be copied from DNS query. Used by the requester
1010         // to match up replies to outstanding queries. See RFC 1035 section 4.1.1. Also reverse
1011         // the source/destination address/port of query packet for building reply packet.
1012         final ByteBuffer replyPacket;
1013         try {
1014             replyPacket = buildUdpDnsPrefix64ReplyPacket(pref64Query.getHeader().getId(),
1015                     ipv6Header.dstIp /* srcIp */, ipv6Header.srcIp /* dstIp */,
1016                     (short) udpHeader.dstPort /* srcPort */,
1017                     (short) udpHeader.srcPort /* dstPort */);
1018         } catch (Exception e) {
1019             fail("Failed to build prefix64 discovery reply for " + ipv6Header.srcIp + ": " + e);
1020             return;
1021         }
1022 
1023         Log.d(TAG, "Sending prefix64 discovery reply");
1024         try {
1025             sendDownloadPacket(replyPacket);
1026         } catch (Exception e) {
1027             fail("Failed to reply prefix64 discovery for " + ipv6Header.srcIp + ": " + e);
1028         }
1029     }
1030 
1031     private byte[] getUploadPacket(Predicate<byte[]> filter) {
1032         assertNotNull("Can't deal with upstream interface in local only mode", mUpstreamReader);
1033 
1034         byte[] packet;
1035         while ((packet = mUpstreamReader.poll(PACKET_READ_TIMEOUT_MS)) != null) {
1036             if (filter.test(packet)) return packet;
1037 
1038             maybeReplyUdpDnsPrefix64Discovery(packet);
1039         }
1040         return null;
1041     }
1042 
1043     private @NonNull byte[] verifyPacketNotNull(String message, @Nullable byte[] packet) {
1044         assertNotNull(message, packet);
1045 
1046         return packet;
1047     }
1048 
1049     public byte[] testUpload(final ByteBuffer packet, final Predicate<byte[]> filter)
1050             throws Exception {
1051         sendUploadPacket(packet);
1052 
1053         return getUploadPacket(filter);
1054     }
1055 
1056     public byte[] verifyUpload(final ByteBuffer packet, final Predicate<byte[]> filter)
1057             throws Exception {
1058         return verifyPacketNotNull("Upload fail", testUpload(packet, filter));
1059     }
1060 
1061     public byte[] verifyDownload(final ByteBuffer packet, final Predicate<byte[]> filter)
1062             throws Exception {
1063         sendDownloadPacket(packet);
1064 
1065         return verifyPacketNotNull("Download fail", getDownloadPacket(filter));
1066     }
1067 
1068     /**
1069      * Sends a batch of download packets and verifies against a specified filtering condition.
1070      *
1071      * This method is designed for testing fragmented packets. All packets are sent before
1072      * verification because the kernel buffers fragments until the last one is received.
1073      * Captured packets are then verified against the provided filter.
1074      *
1075      * @param packets the list of ByteBuffers containing the packets to send.
1076      * @param filter a Predicate that defines the filtering condition to apply to each received
1077      *               packet. If the filter returns true for a packet's data, it is considered to
1078      *               meet the verification criteria.
1079      */
1080     public void verifyDownloadBatch(final List<ByteBuffer> packets, final Predicate<byte[]> filter)
1081             throws Exception {
1082         for (ByteBuffer packet : packets) {
1083             sendDownloadPacket(packet);
1084         }
1085         for (int i = 0; i < packets.size(); ++i) {
1086             verifyPacketNotNull("Download fail", getDownloadPacket(filter));
1087         }
1088     }
1089 
1090     // Send DHCPDISCOVER to DHCP server to see if DHCP server is still alive to handle
1091     // the upcoming DHCP packets. This method should be only used when we know the DHCP
1092     // server has been created successfully before.
1093     public boolean testDhcpServerAlive(final MacAddress mac) throws Exception {
1094         sendDhcpDiscover(mac.toByteArray());
1095         return getNextDhcpPacket() != null;
1096     }
1097 }
1098