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