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