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.InetAddresses.parseNumericAddress; 20 import static android.system.OsConstants.IPPROTO_ICMP; 21 import static android.system.OsConstants.IPPROTO_ICMPV6; 22 import static android.system.OsConstants.IPPROTO_TCP; 23 import static android.system.OsConstants.IPPROTO_UDP; 24 25 import static com.android.net.module.util.DnsPacket.ANSECTION; 26 import static com.android.net.module.util.DnsPacket.ARSECTION; 27 import static com.android.net.module.util.DnsPacket.NSSECTION; 28 import static com.android.net.module.util.DnsPacket.QDSECTION; 29 import static com.android.net.module.util.HexDump.dumpHexString; 30 import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY; 31 import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST; 32 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN; 33 import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST; 34 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV4; 35 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6; 36 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO; 37 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; 38 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA; 39 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION; 40 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 41 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST; 42 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE; 43 import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED; 44 import static com.android.net.module.util.NetworkStackConstants.TCPHDR_SYN; 45 46 import static org.junit.Assert.assertNotNull; 47 import static org.junit.Assert.fail; 48 49 import android.net.dhcp.DhcpAckPacket; 50 import android.net.dhcp.DhcpOfferPacket; 51 import android.net.dhcp.DhcpPacket; 52 import android.text.TextUtils; 53 import android.util.ArrayMap; 54 import android.util.Log; 55 56 import androidx.annotation.NonNull; 57 import androidx.annotation.Nullable; 58 59 import com.android.net.module.util.DnsPacket; 60 import com.android.net.module.util.Ipv6Utils; 61 import com.android.net.module.util.Struct; 62 import com.android.net.module.util.structs.EthernetHeader; 63 import com.android.net.module.util.structs.Icmpv4Header; 64 import com.android.net.module.util.structs.Icmpv6Header; 65 import com.android.net.module.util.structs.Ipv4Header; 66 import com.android.net.module.util.structs.Ipv6Header; 67 import com.android.net.module.util.structs.LlaOption; 68 import com.android.net.module.util.structs.NsHeader; 69 import com.android.net.module.util.structs.PrefixInformationOption; 70 import com.android.net.module.util.structs.RaHeader; 71 import com.android.net.module.util.structs.TcpHeader; 72 import com.android.net.module.util.structs.UdpHeader; 73 import com.android.networkstack.arp.ArpPacket; 74 import com.android.testutils.TapPacketReader; 75 76 import java.net.Inet4Address; 77 import java.net.Inet6Address; 78 import java.net.InetAddress; 79 import java.nio.ByteBuffer; 80 import java.util.ArrayList; 81 import java.util.Arrays; 82 import java.util.List; 83 import java.util.Random; 84 import java.util.concurrent.TimeoutException; 85 import java.util.function.Predicate; 86 87 /** 88 * A class simulate tethered client. When caller create TetheringTester, it would connect to 89 * tethering module that do the dhcp and slaac to obtain ipv4 and ipv6 address. Then caller can 90 * send/receive packets by this class. 91 */ 92 public final class TetheringTester { 93 private static final String TAG = TetheringTester.class.getSimpleName(); 94 private static final int PACKET_READ_TIMEOUT_MS = 500; 95 private static final int DHCP_DISCOVER_ATTEMPTS = 10; 96 private static final int READ_RA_ATTEMPTS = 10; 97 private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] { 98 DhcpPacket.DHCP_SUBNET_MASK, 99 DhcpPacket.DHCP_ROUTER, 100 DhcpPacket.DHCP_DNS_SERVER, 101 DhcpPacket.DHCP_LEASE_TIME, 102 }; 103 private static final InetAddress LINK_LOCAL = parseNumericAddress("fe80::1"); 104 105 public static final String DHCP_HOSTNAME = "testhostname"; 106 107 private final ArrayMap<MacAddress, TetheredDevice> mTetheredDevices; 108 private final TapPacketReader mDownstreamReader; 109 private final TapPacketReader mUpstreamReader; 110 TetheringTester(TapPacketReader downstream)111 public TetheringTester(TapPacketReader downstream) { 112 this(downstream, null); 113 } 114 TetheringTester(TapPacketReader downstream, TapPacketReader upstream)115 public TetheringTester(TapPacketReader downstream, TapPacketReader upstream) { 116 if (downstream == null) fail("Downstream reader could not be NULL"); 117 118 mDownstreamReader = downstream; 119 mUpstreamReader = upstream; 120 mTetheredDevices = new ArrayMap<>(); 121 } 122 createTetheredDevice(MacAddress macAddr, boolean hasIpv6)123 public TetheredDevice createTetheredDevice(MacAddress macAddr, boolean hasIpv6) 124 throws Exception { 125 if (mTetheredDevices.get(macAddr) != null) { 126 fail("Tethered device already created"); 127 } 128 129 TetheredDevice tethered = new TetheredDevice(macAddr, hasIpv6); 130 mTetheredDevices.put(macAddr, tethered); 131 132 return tethered; 133 } 134 135 public class TetheredDevice { 136 public final MacAddress macAddr; 137 public final MacAddress routerMacAddr; 138 public final Inet4Address ipv4Addr; 139 public final Inet4Address ipv4Gatway; 140 public final Inet6Address ipv6Addr; 141 TetheredDevice(MacAddress mac, boolean hasIpv6)142 private TetheredDevice(MacAddress mac, boolean hasIpv6) throws Exception { 143 macAddr = mac; 144 DhcpResults dhcpResults = runDhcp(macAddr.toByteArray()); 145 ipv4Addr = (Inet4Address) dhcpResults.ipAddress.getAddress(); 146 ipv4Gatway = (Inet4Address) dhcpResults.gateway; 147 routerMacAddr = getRouterMacAddressFromArp(ipv4Addr, macAddr, 148 dhcpResults.serverAddress); 149 ipv6Addr = hasIpv6 ? runSlaac(macAddr, routerMacAddr) : null; 150 } 151 } 152 153 /** Simulate dhcp client to obtain ipv4 address. */ runDhcp(byte[] clientMacAddr)154 public DhcpResults runDhcp(byte[] clientMacAddr) 155 throws Exception { 156 // We have to retransmit DHCP requests because IpServer declares itself to be ready before 157 // its DhcpServer is actually started. TODO: fix this race and remove this loop. 158 DhcpPacket offerPacket = null; 159 for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) { 160 Log.d(TAG, "Sending DHCP discover"); 161 sendDhcpDiscover(clientMacAddr); 162 offerPacket = getNextDhcpPacket(); 163 if (offerPacket instanceof DhcpOfferPacket) break; 164 } 165 if (!(offerPacket instanceof DhcpOfferPacket)) { 166 throw new TimeoutException("No DHCPOFFER received on interface within timeout"); 167 } 168 169 sendDhcpRequest(offerPacket, clientMacAddr); 170 DhcpPacket ackPacket = getNextDhcpPacket(); 171 if (!(ackPacket instanceof DhcpAckPacket)) { 172 throw new TimeoutException("No DHCPACK received on interface within timeout"); 173 } 174 175 return ackPacket.toDhcpResults(); 176 } 177 sendDhcpDiscover(byte[] macAddress)178 private void sendDhcpDiscover(byte[] macAddress) throws Exception { 179 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2, 180 new Random().nextInt() /* transactionId */, (short) 0 /* secs */, 181 macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS, 182 false /* rapid commit */, DHCP_HOSTNAME); 183 mDownstreamReader.sendResponse(packet); 184 } 185 sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress)186 private void sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress) 187 throws Exception { 188 DhcpResults results = offerPacket.toDhcpResults(); 189 Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress(); 190 Inet4Address serverIdentifier = results.serverAddress; 191 ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2, 192 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */, 193 false /* broadcast */, macAddress, clientIp /* requestedIpAddress */, 194 serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME); 195 mDownstreamReader.sendResponse(packet); 196 } 197 getNextDhcpPacket()198 private DhcpPacket getNextDhcpPacket() throws Exception { 199 final byte[] packet = getDownloadPacket((p) -> { 200 // Test whether this is DHCP packet. 201 try { 202 DhcpPacket.decodeFullPacket(p, p.length, DhcpPacket.ENCAP_L2); 203 } catch (DhcpPacket.ParseException e) { 204 // Not a DHCP packet. 205 return false; 206 } 207 208 return true; 209 }); 210 211 return packet == null ? null : 212 DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2); 213 } 214 215 @Nullable parseArpPacket(final byte[] packet)216 private ArpPacket parseArpPacket(final byte[] packet) { 217 try { 218 return ArpPacket.parseArpPacket(packet, packet.length); 219 } catch (ArpPacket.ParseException e) { 220 return null; 221 } 222 } 223 maybeReplyArp(byte[] packet)224 private void maybeReplyArp(byte[] packet) { 225 ByteBuffer buf = ByteBuffer.wrap(packet); 226 227 final ArpPacket arpPacket = parseArpPacket(packet); 228 if (arpPacket == null || arpPacket.opCode != ARP_REQUEST) return; 229 230 for (int i = 0; i < mTetheredDevices.size(); i++) { 231 TetheredDevice tethered = mTetheredDevices.valueAt(i); 232 if (!arpPacket.targetIp.equals(tethered.ipv4Addr)) continue; 233 234 final ByteBuffer arpReply = ArpPacket.buildArpPacket( 235 arpPacket.senderHwAddress.toByteArray() /* dst */, 236 tethered.macAddr.toByteArray() /* srcMac */, 237 arpPacket.senderIp.getAddress() /* target IP */, 238 arpPacket.senderHwAddress.toByteArray() /* target HW address */, 239 tethered.ipv4Addr.getAddress() /* sender IP */, 240 (short) ARP_REPLY); 241 try { 242 sendUploadPacket(arpReply); 243 } catch (Exception e) { 244 fail("Failed to reply ARP for " + tethered.ipv4Addr); 245 } 246 return; 247 } 248 } 249 getRouterMacAddressFromArp(final Inet4Address tetherIp, final MacAddress tetherMac, final Inet4Address routerIp)250 private MacAddress getRouterMacAddressFromArp(final Inet4Address tetherIp, 251 final MacAddress tetherMac, final Inet4Address routerIp) throws Exception { 252 final ByteBuffer arpProbe = ArpPacket.buildArpPacket(ETHER_BROADCAST /* dst */, 253 tetherMac.toByteArray() /* srcMac */, routerIp.getAddress() /* target IP */, 254 new byte[ETHER_ADDR_LEN] /* target HW address */, 255 tetherIp.getAddress() /* sender IP */, (short) ARP_REQUEST); 256 sendUploadPacket(arpProbe); 257 258 final byte[] packet = getDownloadPacket((p) -> { 259 final ArpPacket arpPacket = parseArpPacket(p); 260 if (arpPacket == null || arpPacket.opCode != ARP_REPLY) return false; 261 return arpPacket.targetIp.equals(tetherIp); 262 }); 263 264 if (packet != null) { 265 Log.d(TAG, "Get Mac address from ARP"); 266 final ArpPacket arpReply = ArpPacket.parseArpPacket(packet, packet.length); 267 return arpReply.senderHwAddress; 268 } 269 270 fail("Could not get ARP packet"); 271 return null; 272 } 273 getRaPrefixOptions(byte[] packet)274 private List<PrefixInformationOption> getRaPrefixOptions(byte[] packet) { 275 ByteBuffer buf = ByteBuffer.wrap(packet); 276 if (!isExpectedIcmpPacket(buf, true /* hasEth */, false /* isIpv4 */, 277 ICMPV6_ROUTER_ADVERTISEMENT)) { 278 fail("Parsing RA packet fail"); 279 } 280 281 Struct.parse(RaHeader.class, buf); 282 final ArrayList<PrefixInformationOption> pioList = new ArrayList<>(); 283 while (buf.position() < packet.length) { 284 final int currentPos = buf.position(); 285 final int type = Byte.toUnsignedInt(buf.get()); 286 final int length = Byte.toUnsignedInt(buf.get()); 287 if (type == ICMPV6_ND_OPTION_PIO) { 288 final ByteBuffer pioBuf = ByteBuffer.wrap(buf.array(), currentPos, 289 Struct.getSize(PrefixInformationOption.class)); 290 final PrefixInformationOption pio = 291 Struct.parse(PrefixInformationOption.class, pioBuf); 292 pioList.add(pio); 293 294 // Move ByteBuffer position to the next option. 295 buf.position(currentPos + Struct.getSize(PrefixInformationOption.class)); 296 } else { 297 buf.position(currentPos + (length * 8)); 298 } 299 } 300 return pioList; 301 } 302 runSlaac(MacAddress srcMac, MacAddress dstMac)303 private Inet6Address runSlaac(MacAddress srcMac, MacAddress dstMac) throws Exception { 304 sendRsPacket(srcMac, dstMac); 305 306 final byte[] raPacket = verifyPacketNotNull("Receive RA fail", getDownloadPacket(p -> { 307 return isExpectedIcmpPacket(p, true /* hasEth */, false /* isIpv4 */, 308 ICMPV6_ROUTER_ADVERTISEMENT); 309 })); 310 311 final List<PrefixInformationOption> options = getRaPrefixOptions(raPacket); 312 313 for (PrefixInformationOption pio : options) { 314 if (pio.validLifetime > 0) { 315 final byte[] addressBytes = pio.prefix; 316 // Random the last two bytes as suffix. 317 // TODO: Currently do not implmement DAD in the test. Rely the gateway ipv6 address 318 // genetrated by tethering module always has random the last byte. 319 addressBytes[addressBytes.length - 1] = (byte) (new Random()).nextInt(); 320 addressBytes[addressBytes.length - 2] = (byte) (new Random()).nextInt(); 321 322 return (Inet6Address) InetAddress.getByAddress(addressBytes); 323 } 324 } 325 326 fail("No available ipv6 prefix"); 327 return null; 328 } 329 sendRsPacket(MacAddress srcMac, MacAddress dstMac)330 private void sendRsPacket(MacAddress srcMac, MacAddress dstMac) throws Exception { 331 Log.d(TAG, "Sending RS"); 332 ByteBuffer slla = LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac); 333 ByteBuffer rs = Ipv6Utils.buildRsPacket(srcMac, dstMac, (Inet6Address) LINK_LOCAL, 334 IPV6_ADDR_ALL_NODES_MULTICAST, slla); 335 336 sendUploadPacket(rs); 337 } 338 maybeReplyNa(byte[] packet)339 private void maybeReplyNa(byte[] packet) { 340 ByteBuffer buf = ByteBuffer.wrap(packet); 341 final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf); 342 if (ethHdr.etherType != ETHER_TYPE_IPV6) return; 343 344 final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf); 345 if (ipv6Hdr.nextHeader != (byte) IPPROTO_ICMPV6) return; 346 347 final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf); 348 if (icmpv6Hdr.type != (short) ICMPV6_NEIGHBOR_SOLICITATION) return; 349 350 final NsHeader nsHdr = Struct.parse(NsHeader.class, buf); 351 for (int i = 0; i < mTetheredDevices.size(); i++) { 352 TetheredDevice tethered = mTetheredDevices.valueAt(i); 353 if (!nsHdr.target.equals(tethered.ipv6Addr)) continue; 354 355 final ByteBuffer tlla = LlaOption.build((byte) ICMPV6_ND_OPTION_TLLA, tethered.macAddr); 356 int flags = NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED 357 | NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE; 358 ByteBuffer ns = Ipv6Utils.buildNaPacket(tethered.macAddr, tethered.routerMacAddr, 359 nsHdr.target, ipv6Hdr.srcIp, flags, nsHdr.target, tlla); 360 try { 361 sendUploadPacket(ns); 362 } catch (Exception e) { 363 fail("Failed to reply NA for " + tethered.ipv6Addr); 364 } 365 366 return; 367 } 368 } 369 isExpectedIcmpPacket(byte[] packet, boolean hasEth, boolean isIpv4, int type)370 public static boolean isExpectedIcmpPacket(byte[] packet, boolean hasEth, boolean isIpv4, 371 int type) { 372 final ByteBuffer buf = ByteBuffer.wrap(packet); 373 return isExpectedIcmpPacket(buf, hasEth, isIpv4, type); 374 } 375 isExpectedIcmpPacket(ByteBuffer buf, boolean hasEth, boolean isIpv4, int type)376 private static boolean isExpectedIcmpPacket(ByteBuffer buf, boolean hasEth, boolean isIpv4, 377 int type) { 378 try { 379 if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false; 380 381 final int ipProto = isIpv4 ? IPPROTO_ICMP : IPPROTO_ICMPV6; 382 if (!hasExpectedIpHeader(buf, isIpv4, ipProto)) return false; 383 384 if (isIpv4) { 385 return Struct.parse(Icmpv4Header.class, buf).type == (short) type; 386 } else { 387 return Struct.parse(Icmpv6Header.class, buf).type == (short) type; 388 } 389 } catch (Exception e) { 390 // Parsing packet fail means it is not icmp packet. 391 } 392 393 return false; 394 } 395 hasExpectedEtherHeader(@onNull final ByteBuffer buf, boolean isIpv4)396 private static boolean hasExpectedEtherHeader(@NonNull final ByteBuffer buf, boolean isIpv4) 397 throws Exception { 398 final int expected = isIpv4 ? ETHER_TYPE_IPV4 : ETHER_TYPE_IPV6; 399 400 return Struct.parse(EthernetHeader.class, buf).etherType == expected; 401 } 402 hasExpectedIpHeader(@onNull final ByteBuffer buf, boolean isIpv4, int ipProto)403 private static boolean hasExpectedIpHeader(@NonNull final ByteBuffer buf, boolean isIpv4, 404 int ipProto) throws Exception { 405 if (isIpv4) { 406 return Struct.parse(Ipv4Header.class, buf).protocol == (byte) ipProto; 407 } else { 408 return Struct.parse(Ipv6Header.class, buf).nextHeader == (byte) ipProto; 409 } 410 } 411 isExpectedUdpPacket(@onNull final byte[] rawPacket, boolean hasEth, boolean isIpv4, Predicate<ByteBuffer> payloadVerifier)412 private static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth, 413 boolean isIpv4, Predicate<ByteBuffer> payloadVerifier) { 414 final ByteBuffer buf = ByteBuffer.wrap(rawPacket); 415 try { 416 if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false; 417 418 if (!hasExpectedIpHeader(buf, isIpv4, IPPROTO_UDP)) return false; 419 420 if (Struct.parse(UdpHeader.class, buf) == null) return false; 421 422 if (!payloadVerifier.test(buf)) return false; 423 } catch (Exception e) { 424 // Parsing packet fail means it is not udp packet. 425 return false; 426 } 427 return true; 428 } 429 430 // Returns remaining bytes in the ByteBuffer in a new byte array of the right size. The 431 // ByteBuffer will be empty upon return. Used to avoid lint warning. 432 // See https://errorprone.info/bugpattern/ByteBufferBackingArray getRemaining(final ByteBuffer buf)433 private static byte[] getRemaining(final ByteBuffer buf) { 434 final byte[] bytes = new byte[buf.remaining()]; 435 buf.get(bytes); 436 Log.d(TAG, "Get remaining bytes: " + dumpHexString(bytes)); 437 return bytes; 438 } 439 440 // |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)441 public static boolean isExpectedUdpPacket(@NonNull final byte[] rawPacket, boolean hasEth, 442 boolean isIpv4, @NonNull final ByteBuffer expectedPayload) { 443 return isExpectedUdpPacket(rawPacket, hasEth, isIpv4, p -> { 444 if (p.remaining() != expectedPayload.limit()) return false; 445 446 return Arrays.equals(getRemaining(p), getRemaining( 447 expectedPayload.asReadOnlyBuffer())); 448 }); 449 } 450 451 // |expectedPayload| is copied as read-only because the caller may reuse it. 452 // See hasExpectedDnsMessage. isExpectedUdpDnsPacket(@onNull final byte[] rawPacket, boolean hasEth, boolean isIpv4, @NonNull final ByteBuffer expectedPayload)453 public static boolean isExpectedUdpDnsPacket(@NonNull final byte[] rawPacket, boolean hasEth, 454 boolean isIpv4, @NonNull final ByteBuffer expectedPayload) { 455 return isExpectedUdpPacket(rawPacket, hasEth, isIpv4, p -> { 456 return hasExpectedDnsMessage(p, expectedPayload); 457 }); 458 } 459 460 public static class TestDnsPacket extends DnsPacket { 461 TestDnsPacket(byte[] data) throws DnsPacket.ParseException { 462 super(data); 463 } 464 465 @Nullable 466 public static TestDnsPacket getTestDnsPacket(final ByteBuffer buf) { 467 try { 468 // The ByteBuffer will be empty upon return. 469 return new TestDnsPacket(getRemaining(buf)); 470 } catch (DnsPacket.ParseException e) { 471 return null; 472 } 473 } 474 475 public DnsHeader getHeader() { 476 return mHeader; 477 } 478 479 public List<DnsRecord> getRecordList(int secType) { 480 return mRecords[secType]; 481 } 482 483 public int getANCount() { 484 return mHeader.getRecordCount(ANSECTION); 485 } 486 487 public int getQDCount() { 488 return mHeader.getRecordCount(QDSECTION); 489 } 490 491 public int getNSCount() { 492 return mHeader.getRecordCount(NSSECTION); 493 } 494 495 public int getARCount() { 496 return mHeader.getRecordCount(ARSECTION); 497 } 498 499 private boolean isRecordsEquals(int type, @NonNull final TestDnsPacket other) { 500 List<DnsRecord> records = getRecordList(type); 501 List<DnsRecord> otherRecords = other.getRecordList(type); 502 503 if (records.size() != otherRecords.size()) return false; 504 505 // Expect that two compared resource records are in the same order. For current tests 506 // in EthernetTetheringTest, it is okay because dnsmasq doesn't reorder the forwarded 507 // resource records. 508 // TODO: consider allowing that compare records out of order. 509 for (int i = 0; i < records.size(); i++) { 510 // TODO: use DnsRecord.equals once aosp/1387135 is merged. 511 if (!TextUtils.equals(records.get(i).dName, otherRecords.get(i).dName) 512 || records.get(i).nsType != otherRecords.get(i).nsType 513 || records.get(i).nsClass != otherRecords.get(i).nsClass 514 || records.get(i).ttl != otherRecords.get(i).ttl 515 || !Arrays.equals(records.get(i).getRR(), otherRecords.get(i).getRR())) { 516 return false; 517 } 518 } 519 return true; 520 } 521 522 public boolean isQDRecordsEquals(@NonNull final TestDnsPacket other) { 523 return isRecordsEquals(QDSECTION, other); 524 } 525 526 public boolean isANRecordsEquals(@NonNull final TestDnsPacket other) { 527 return isRecordsEquals(ANSECTION, other); 528 } 529 } 530 531 // The ByteBuffer |actual| will be empty upon return. The ByteBuffer |excepted| will be copied 532 // as read-only because the caller may reuse it. 533 private static boolean hasExpectedDnsMessage(@NonNull final ByteBuffer actual, 534 @NonNull final ByteBuffer excepted) { 535 // Forwarded DNS message is extracted from remaining received packet buffer which has 536 // already parsed ethernet header, if any, IP header and UDP header. 537 final TestDnsPacket forwardedDns = TestDnsPacket.getTestDnsPacket(actual); 538 if (forwardedDns == null) return false; 539 540 // Original DNS message is the payload of the sending test UDP packet. It is used to check 541 // that the forwarded DNS query and reply have corresponding contents. 542 final TestDnsPacket originalDns = TestDnsPacket.getTestDnsPacket( 543 excepted.asReadOnlyBuffer()); 544 assertNotNull(originalDns); 545 546 // Compare original DNS message which is sent to dnsmasq and forwarded DNS message which 547 // is forwarded by dnsmasq. The original message and forwarded message may be not identical 548 // because dnsmasq may change the header flags or even recreate the DNS query message and 549 // so on. We only simple check on forwarded packet and monitor if test will be broken by 550 // vendor dnsmasq customization. See forward_query() in external/dnsmasq/src/forward.c. 551 // 552 // DNS message format. See rfc1035 section 4.1. 553 // +---------------------+ 554 // | Header | 555 // +---------------------+ 556 // | Question | the question for the name server 557 // +---------------------+ 558 // | Answer | RRs answering the question 559 // +---------------------+ 560 // | Authority | RRs pointing toward an authority 561 // +---------------------+ 562 // | Additional | RRs holding additional information 563 // +---------------------+ 564 565 // [1] Header section. See rfc1035 section 4.1.1. 566 // Verify QR flag bit, QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT. 567 if (originalDns.getHeader().isResponse() != forwardedDns.getHeader().isResponse()) { 568 return false; 569 } 570 if (originalDns.getQDCount() != forwardedDns.getQDCount()) return false; 571 if (originalDns.getANCount() != forwardedDns.getANCount()) return false; 572 if (originalDns.getNSCount() != forwardedDns.getNSCount()) return false; 573 if (originalDns.getARCount() != forwardedDns.getARCount()) return false; 574 575 // [2] Question section. See rfc1035 section 4.1.2. 576 // Question section has at least one entry either DNS query or DNS reply. 577 if (forwardedDns.getRecordList(QDSECTION).isEmpty()) return false; 578 // Expect that original and forwarded message have the same question records (usually 1). 579 if (!originalDns.isQDRecordsEquals(forwardedDns)) return false; 580 581 // [3] Answer section. See rfc1035 section 4.1.3. 582 if (forwardedDns.getHeader().isResponse()) { 583 // DNS reply has at least have one answer in our tests. 584 // See EthernetTetheringTest#testTetherUdpV4Dns. 585 if (forwardedDns.getRecordList(ANSECTION).isEmpty()) return false; 586 // Expect that original and forwarded message have the same answer records. 587 if (!originalDns.isANRecordsEquals(forwardedDns)) return false; 588 } 589 590 // Ignore checking {Authority, Additional} sections because they are not tested 591 // in EthernetTetheringTest. 592 return true; 593 } 594 595 596 private static boolean isTcpSynPacket(@NonNull final TcpHeader tcpHeader) { 597 return (tcpHeader.dataOffsetAndControlBits & TCPHDR_SYN) != 0; 598 } 599 600 public static boolean isExpectedTcpPacket(@NonNull final byte[] rawPacket, boolean hasEth, 601 boolean isIpv4, int seq, @NonNull final ByteBuffer payload) { 602 final ByteBuffer buf = ByteBuffer.wrap(rawPacket); 603 try { 604 if (hasEth && !hasExpectedEtherHeader(buf, isIpv4)) return false; 605 606 if (!hasExpectedIpHeader(buf, isIpv4, IPPROTO_TCP)) return false; 607 608 final TcpHeader tcpHeader = Struct.parse(TcpHeader.class, buf); 609 if (tcpHeader.seq != seq) return false; 610 611 // Don't try to parse the payload if it is a TCP SYN segment because additional TCP 612 // option MSS may be added in the SYN segment. Currently, TetherController uses 613 // iptables to limit downstream MSS for IPv4. The additional TCP options will be 614 // misunderstood as payload because parsing TCP options are not supported by class 615 // TcpHeader for now. See TetherController::setupIptablesHooks. 616 // TODO: remove once TcpHeader supports parsing TCP options. 617 if (isTcpSynPacket(tcpHeader)) { 618 Log.d(TAG, "Found SYN segment. Ignore parsing the remaining part of packet."); 619 return true; 620 } 621 622 if (payload.limit() != buf.remaining()) return false; 623 return Arrays.equals(getRemaining(buf), getRemaining(payload.asReadOnlyBuffer())); 624 } catch (Exception e) { 625 // Parsing packet fail means it is not tcp packet. 626 } 627 628 return false; 629 } 630 631 private void sendUploadPacket(ByteBuffer packet) throws Exception { 632 mDownstreamReader.sendResponse(packet); 633 } 634 635 private void sendDownloadPacket(ByteBuffer packet) throws Exception { 636 assertNotNull("Can't deal with upstream interface in local only mode", mUpstreamReader); 637 638 mUpstreamReader.sendResponse(packet); 639 } 640 641 private byte[] getDownloadPacket(Predicate<byte[]> filter) { 642 byte[] packet; 643 while ((packet = mDownstreamReader.poll(PACKET_READ_TIMEOUT_MS)) != null) { 644 if (filter.test(packet)) return packet; 645 646 maybeReplyArp(packet); 647 maybeReplyNa(packet); 648 } 649 650 return null; 651 } 652 653 private byte[] getUploadPacket(Predicate<byte[]> filter) { 654 assertNotNull("Can't deal with upstream interface in local only mode", mUpstreamReader); 655 656 return mUpstreamReader.poll(PACKET_READ_TIMEOUT_MS, filter); 657 } 658 659 private @NonNull byte[] verifyPacketNotNull(String message, @Nullable byte[] packet) { 660 assertNotNull(message, packet); 661 662 return packet; 663 } 664 665 public byte[] testUpload(final ByteBuffer packet, final Predicate<byte[]> filter) 666 throws Exception { 667 sendUploadPacket(packet); 668 669 return getUploadPacket(filter); 670 } 671 672 public byte[] verifyUpload(final ByteBuffer packet, final Predicate<byte[]> filter) 673 throws Exception { 674 return verifyPacketNotNull("Upload fail", testUpload(packet, filter)); 675 } 676 677 public byte[] verifyDownload(final ByteBuffer packet, final Predicate<byte[]> filter) 678 throws Exception { 679 sendDownloadPacket(packet); 680 681 return verifyPacketNotNull("Download fail", getDownloadPacket(filter)); 682 } 683 } 684