1 /* 2 * Copyright (C) 2016 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.apf; 18 19 import static android.system.OsConstants.*; 20 21 import android.os.SystemClock; 22 import android.net.LinkProperties; 23 import android.net.NetworkUtils; 24 import android.net.apf.ApfGenerator; 25 import android.net.apf.ApfGenerator.IllegalInstructionException; 26 import android.net.apf.ApfGenerator.Register; 27 import android.net.ip.IpManager; 28 import android.net.metrics.ApfProgramEvent; 29 import android.net.metrics.ApfStats; 30 import android.net.metrics.IpConnectivityLog; 31 import android.net.metrics.RaEvent; 32 import android.system.ErrnoException; 33 import android.system.Os; 34 import android.system.PacketSocketAddress; 35 import android.text.format.DateUtils; 36 import android.util.Log; 37 import android.util.Pair; 38 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.internal.util.HexDump; 42 import com.android.internal.util.IndentingPrintWriter; 43 44 import java.io.FileDescriptor; 45 import java.io.IOException; 46 import java.lang.Thread; 47 import java.net.Inet6Address; 48 import java.net.InetAddress; 49 import java.net.NetworkInterface; 50 import java.net.SocketException; 51 import java.net.UnknownHostException; 52 import java.nio.ByteBuffer; 53 import java.nio.BufferUnderflowException; 54 import java.util.ArrayList; 55 import java.util.Arrays; 56 57 import libcore.io.IoBridge; 58 59 /** 60 * For networks that support packet filtering via APF programs, {@code ApfFilter} 61 * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to 62 * filter out redundant duplicate ones. 63 * 64 * Threading model: 65 * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to 66 * know what RAs to filter for, thus generating APF programs is dependent on mRas. 67 * mRas can be accessed by multiple threads: 68 * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs. 69 * - callers of: 70 * - setMulticastFilter(), which can cause an APF program to be generated. 71 * - dump(), which dumps mRas among other things. 72 * - shutdown(), which clears mRas. 73 * So access to mRas is synchronized. 74 * 75 * @hide 76 */ 77 public class ApfFilter { 78 79 // Enums describing the outcome of receiving an RA packet. 80 private static enum ProcessRaResult { 81 MATCH, // Received RA matched a known RA 82 DROPPED, // Received RA ignored due to MAX_RAS 83 PARSE_ERROR, // Received RA could not be parsed 84 ZERO_LIFETIME, // Received RA had 0 lifetime 85 UPDATE_NEW_RA, // APF program updated for new RA 86 UPDATE_EXPIRY // APF program updated for expiry 87 } 88 89 // Thread to listen for RAs. 90 @VisibleForTesting 91 class ReceiveThread extends Thread { 92 private final byte[] mPacket = new byte[1514]; 93 private final FileDescriptor mSocket; 94 private volatile boolean mStopped; 95 96 // Starting time of the RA receiver thread. 97 private final long mStart = SystemClock.elapsedRealtime(); 98 99 private int mReceivedRas; // Number of received RAs 100 private int mMatchingRas; // Number of received RAs matching a known RA 101 private int mDroppedRas; // Number of received RAs ignored due to the MAX_RAS limit 102 private int mParseErrors; // Number of received RAs that could not be parsed 103 private int mZeroLifetimeRas; // Number of received RAs with a 0 lifetime 104 private int mProgramUpdates; // Number of APF program updates triggered by receiving a RA 105 106 public ReceiveThread(FileDescriptor socket) { 107 mSocket = socket; 108 } 109 110 public void halt() { 111 mStopped = true; 112 try { 113 // Interrupts the read() call the thread is blocked in. 114 IoBridge.closeAndSignalBlockedThreads(mSocket); 115 } catch (IOException ignored) {} 116 } 117 118 @Override 119 public void run() { 120 log("begin monitoring"); 121 while (!mStopped) { 122 try { 123 int length = Os.read(mSocket, mPacket, 0, mPacket.length); 124 updateStats(processRa(mPacket, length)); 125 } catch (IOException|ErrnoException e) { 126 if (!mStopped) { 127 Log.e(TAG, "Read error", e); 128 } 129 } 130 } 131 logStats(); 132 } 133 134 private void updateStats(ProcessRaResult result) { 135 mReceivedRas++; 136 switch(result) { 137 case MATCH: 138 mMatchingRas++; 139 return; 140 case DROPPED: 141 mDroppedRas++; 142 return; 143 case PARSE_ERROR: 144 mParseErrors++; 145 return; 146 case ZERO_LIFETIME: 147 mZeroLifetimeRas++; 148 return; 149 case UPDATE_EXPIRY: 150 mMatchingRas++; 151 mProgramUpdates++; 152 return; 153 case UPDATE_NEW_RA: 154 mProgramUpdates++; 155 return; 156 } 157 } 158 159 private void logStats() { 160 long durationMs = SystemClock.elapsedRealtime() - mStart; 161 int maxSize = mApfCapabilities.maximumApfProgramSize; 162 mMetricsLog.log(new ApfStats(durationMs, mReceivedRas, mMatchingRas, mDroppedRas, 163 mZeroLifetimeRas, mParseErrors, mProgramUpdates, maxSize)); 164 } 165 } 166 167 private static final String TAG = "ApfFilter"; 168 private static final boolean DBG = true; 169 private static final boolean VDBG = false; 170 171 private static final int ETH_HEADER_LEN = 14; 172 private static final int ETH_DEST_ADDR_OFFSET = 0; 173 private static final int ETH_ETHERTYPE_OFFSET = 12; 174 private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{ 175 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; 176 // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN. 177 private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6; 178 // Endianness is not an issue for this constant because the APF interpreter always operates in 179 // network byte order. 180 private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff; 181 private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; 182 private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; 183 private static final int IPV4_ANY_HOST_ADDRESS = 0; 184 185 private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; 186 private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; 187 private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; 188 private static final int IPV6_HEADER_LEN = 40; 189 // The IPv6 all nodes address ff02::1 190 private static final byte[] IPV6_ALL_NODES_ADDRESS = 191 new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; 192 193 private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; 194 private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136; 195 private static final int ICMP6_ROUTER_ADVERTISEMENT = 134; 196 197 // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT 198 private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2; 199 private static final int UDP_HEADER_LEN = 8; 200 201 private static final int DHCP_CLIENT_PORT = 68; 202 // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT 203 private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28; 204 205 private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN; 206 private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6; 207 private static final short ARP_OPCODE_REQUEST = 1; 208 private static final short ARP_OPCODE_REPLY = 2; 209 private static final byte[] ARP_IPV4_HEADER = new byte[]{ 210 0, 1, // Hardware type: Ethernet (1) 211 8, 0, // Protocol type: IP (0x0800) 212 6, // Hardware size: 6 213 4, // Protocol size: 4 214 }; 215 private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24; 216 217 private final ApfCapabilities mApfCapabilities; 218 private final IpManager.Callback mIpManagerCallback; 219 private final NetworkInterface mNetworkInterface; 220 private final IpConnectivityLog mMetricsLog; 221 @VisibleForTesting 222 byte[] mHardwareAddress; 223 @VisibleForTesting 224 ReceiveThread mReceiveThread; 225 @GuardedBy("this") 226 private long mUniqueCounter; 227 @GuardedBy("this") 228 private boolean mMulticastFilter; 229 // Our IPv4 address, if we have just one, otherwise null. 230 @GuardedBy("this") 231 private byte[] mIPv4Address; 232 233 @VisibleForTesting 234 ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface, 235 IpManager.Callback ipManagerCallback, boolean multicastFilter, IpConnectivityLog log) { 236 mApfCapabilities = apfCapabilities; 237 mIpManagerCallback = ipManagerCallback; 238 mNetworkInterface = networkInterface; 239 mMulticastFilter = multicastFilter; 240 mMetricsLog = log; 241 242 maybeStartFilter(); 243 } 244 245 private void log(String s) { 246 Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s); 247 } 248 249 @GuardedBy("this") 250 private long getUniqueNumberLocked() { 251 return mUniqueCounter++; 252 } 253 254 /** 255 * Attempt to start listening for RAs and, if RAs are received, generating and installing 256 * filters to ignore useless RAs. 257 */ 258 @VisibleForTesting 259 void maybeStartFilter() { 260 FileDescriptor socket; 261 try { 262 mHardwareAddress = mNetworkInterface.getHardwareAddress(); 263 synchronized(this) { 264 // Install basic filters 265 installNewProgramLocked(); 266 } 267 socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6); 268 PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, 269 mNetworkInterface.getIndex()); 270 Os.bind(socket, addr); 271 NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat); 272 } catch(SocketException|ErrnoException e) { 273 Log.e(TAG, "Error starting filter", e); 274 return; 275 } 276 mReceiveThread = new ReceiveThread(socket); 277 mReceiveThread.start(); 278 } 279 280 // Returns seconds since Unix Epoch. 281 // TODO: use SystemClock.elapsedRealtime() instead 282 private static long curTime() { 283 return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS; 284 } 285 286 // A class to hold information about an RA. 287 private class Ra { 288 // From RFC4861: 289 private static final int ICMP6_RA_HEADER_LEN = 16; 290 private static final int ICMP6_RA_CHECKSUM_OFFSET = 291 ETH_HEADER_LEN + IPV6_HEADER_LEN + 2; 292 private static final int ICMP6_RA_CHECKSUM_LEN = 2; 293 private static final int ICMP6_RA_OPTION_OFFSET = 294 ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN; 295 private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET = 296 ETH_HEADER_LEN + IPV6_HEADER_LEN + 6; 297 private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2; 298 // Prefix information option. 299 private static final int ICMP6_PREFIX_OPTION_TYPE = 3; 300 private static final int ICMP6_PREFIX_OPTION_LEN = 32; 301 private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4; 302 private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4; 303 private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8; 304 private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4; 305 306 // From RFC6106: Recursive DNS Server option 307 private static final int ICMP6_RDNSS_OPTION_TYPE = 25; 308 // From RFC6106: DNS Search List option 309 private static final int ICMP6_DNSSL_OPTION_TYPE = 31; 310 311 // From RFC4191: Route Information option 312 private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24; 313 // Above three options all have the same format: 314 private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4; 315 private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4; 316 317 // Note: mPacket's position() cannot be assumed to be reset. 318 private final ByteBuffer mPacket; 319 // List of binary ranges that include the whole packet except the lifetimes. 320 // Pairs consist of offset and length. 321 private final ArrayList<Pair<Integer, Integer>> mNonLifetimes = 322 new ArrayList<Pair<Integer, Integer>>(); 323 // Minimum lifetime in packet 324 long mMinLifetime; 325 // When the packet was last captured, in seconds since Unix Epoch 326 long mLastSeen; 327 328 // For debugging only. Offsets into the packet where PIOs are. 329 private final ArrayList<Integer> mPrefixOptionOffsets = new ArrayList<>(); 330 331 // For debugging only. Offsets into the packet where RDNSS options are. 332 private final ArrayList<Integer> mRdnssOptionOffsets = new ArrayList<>(); 333 334 // For debugging only. How many times this RA was seen. 335 int seenCount = 0; 336 337 // For debugging only. Returns the hex representation of the last matching packet. 338 String getLastMatchingPacket() { 339 return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(), 340 false /* lowercase */); 341 } 342 343 // For debugging only. Returns the string representation of the IPv6 address starting at 344 // position pos in the packet. 345 private String IPv6AddresstoString(int pos) { 346 try { 347 byte[] array = mPacket.array(); 348 // Can't just call copyOfRange() and see if it throws, because if it reads past the 349 // end it pads with zeros instead of throwing. 350 if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) { 351 return "???"; 352 } 353 byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16); 354 InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes); 355 return address.getHostAddress(); 356 } catch (UnsupportedOperationException e) { 357 // array() failed. Cannot happen, mPacket is array-backed and read-write. 358 return "???"; 359 } catch (ClassCastException | UnknownHostException e) { 360 // Cannot happen. 361 return "???"; 362 } 363 } 364 365 // Can't be static because it's in a non-static inner class. 366 // TODO: Make this static once RA is its own class. 367 private int uint8(byte b) { 368 return b & 0xff; 369 } 370 371 private int uint16(short s) { 372 return s & 0xffff; 373 } 374 375 private long uint32(int i) { 376 return i & 0xffffffffL; 377 } 378 379 private long getUint16(ByteBuffer buffer, int position) { 380 return uint16(buffer.getShort(position)); 381 } 382 383 private long getUint32(ByteBuffer buffer, int position) { 384 return uint32(buffer.getInt(position)); 385 } 386 387 private void prefixOptionToString(StringBuffer sb, int offset) { 388 String prefix = IPv6AddresstoString(offset + 16); 389 int length = uint8(mPacket.get(offset + 2)); 390 long valid = mPacket.getInt(offset + 4); 391 long preferred = mPacket.getInt(offset + 8); 392 sb.append(String.format("%s/%d %ds/%ds ", prefix, length, valid, preferred)); 393 } 394 395 private void rdnssOptionToString(StringBuffer sb, int offset) { 396 int optLen = uint8(mPacket.get(offset + 1)) * 8; 397 if (optLen < 24) return; // Malformed or empty. 398 long lifetime = uint32(mPacket.getInt(offset + 4)); 399 int numServers = (optLen - 8) / 16; 400 sb.append("DNS ").append(lifetime).append("s"); 401 for (int server = 0; server < numServers; server++) { 402 sb.append(" ").append(IPv6AddresstoString(offset + 8 + 16 * server)); 403 } 404 } 405 406 public String toString() { 407 try { 408 StringBuffer sb = new StringBuffer(); 409 sb.append(String.format("RA %s -> %s %ds ", 410 IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET), 411 IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET), 412 uint16(mPacket.getShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET)))); 413 for (int i: mPrefixOptionOffsets) { 414 prefixOptionToString(sb, i); 415 } 416 for (int i: mRdnssOptionOffsets) { 417 rdnssOptionToString(sb, i); 418 } 419 return sb.toString(); 420 } catch (BufferUnderflowException | IndexOutOfBoundsException e) { 421 return "<Malformed RA>"; 422 } 423 } 424 425 /** 426 * Add a binary range of the packet that does not include a lifetime to mNonLifetimes. 427 * Assumes mPacket.position() is as far as we've parsed the packet. 428 * @param lastNonLifetimeStart offset within packet of where the last binary range of 429 * data not including a lifetime. 430 * @param lifetimeOffset offset from mPacket.position() to the next lifetime data. 431 * @param lifetimeLength length of the next lifetime data. 432 * @return offset within packet of where the next binary range of data not including 433 * a lifetime. This can be passed into the next invocation of this function 434 * via {@code lastNonLifetimeStart}. 435 */ 436 private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset, 437 int lifetimeLength) { 438 lifetimeOffset += mPacket.position(); 439 mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart, 440 lifetimeOffset - lastNonLifetimeStart)); 441 return lifetimeOffset + lifetimeLength; 442 } 443 444 private int addNonLifetimeU32(int lastNonLifetimeStart) { 445 return addNonLifetime(lastNonLifetimeStart, 446 ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN); 447 } 448 449 // Note that this parses RA and may throw IllegalArgumentException (from 450 // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException 451 // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with 452 // specifications. 453 Ra(byte[] packet, int length) { 454 mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length)); 455 mLastSeen = curTime(); 456 457 // Sanity check packet in case a packet arrives before we attach RA filter 458 // to our packet socket. b/29586253 459 if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 || 460 uint8(mPacket.get(IPV6_NEXT_HEADER_OFFSET)) != IPPROTO_ICMPV6 || 461 uint8(mPacket.get(ICMP6_TYPE_OFFSET)) != ICMP6_ROUTER_ADVERTISEMENT) { 462 throw new IllegalArgumentException("Not an ICMP6 router advertisement"); 463 } 464 465 466 RaEvent.Builder builder = new RaEvent.Builder(); 467 468 // Ignore the checksum. 469 int lastNonLifetimeStart = addNonLifetime(0, 470 ICMP6_RA_CHECKSUM_OFFSET, 471 ICMP6_RA_CHECKSUM_LEN); 472 473 // Parse router lifetime 474 lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, 475 ICMP6_RA_ROUTER_LIFETIME_OFFSET, 476 ICMP6_RA_ROUTER_LIFETIME_LEN); 477 builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET)); 478 479 // Ensures that the RA is not truncated. 480 mPacket.position(ICMP6_RA_OPTION_OFFSET); 481 while (mPacket.hasRemaining()) { 482 final int position = mPacket.position(); 483 final int optionType = uint8(mPacket.get(position)); 484 final int optionLength = uint8(mPacket.get(position + 1)) * 8; 485 long lifetime; 486 switch (optionType) { 487 case ICMP6_PREFIX_OPTION_TYPE: 488 // Parse valid lifetime 489 lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, 490 ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, 491 ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN); 492 lifetime = getUint32(mPacket, 493 position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET); 494 builder.updatePrefixValidLifetime(lifetime); 495 // Parse preferred lifetime 496 lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, 497 ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, 498 ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN); 499 lifetime = getUint32(mPacket, 500 position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET); 501 builder.updatePrefixPreferredLifetime(lifetime); 502 mPrefixOptionOffsets.add(position); 503 break; 504 // These three options have the same lifetime offset and size, and 505 // are processed with the same specialized addNonLifetimeU32: 506 case ICMP6_RDNSS_OPTION_TYPE: 507 mRdnssOptionOffsets.add(position); 508 lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); 509 lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET); 510 builder.updateRdnssLifetime(lifetime); 511 break; 512 case ICMP6_ROUTE_INFO_OPTION_TYPE: 513 lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); 514 lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET); 515 builder.updateRouteInfoLifetime(lifetime); 516 break; 517 case ICMP6_DNSSL_OPTION_TYPE: 518 lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); 519 lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET); 520 builder.updateDnsslLifetime(lifetime); 521 break; 522 default: 523 // RFC4861 section 4.2 dictates we ignore unknown options for fowards 524 // compatibility. 525 break; 526 } 527 if (optionLength <= 0) { 528 throw new IllegalArgumentException(String.format( 529 "Invalid option length opt=%d len=%d", optionType, optionLength)); 530 } 531 mPacket.position(position + optionLength); 532 } 533 // Mark non-lifetime bytes since last lifetime. 534 addNonLifetime(lastNonLifetimeStart, 0, 0); 535 mMinLifetime = minLifetime(packet, length); 536 mMetricsLog.log(builder.build()); 537 } 538 539 // Ignoring lifetimes (which may change) does {@code packet} match this RA? 540 boolean matches(byte[] packet, int length) { 541 if (length != mPacket.capacity()) return false; 542 byte[] referencePacket = mPacket.array(); 543 for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) { 544 for (int i = nonLifetime.first; i < (nonLifetime.first + nonLifetime.second); i++) { 545 if (packet[i] != referencePacket[i]) return false; 546 } 547 } 548 return true; 549 } 550 551 // What is the minimum of all lifetimes within {@code packet} in seconds? 552 // Precondition: matches(packet, length) already returned true. 553 long minLifetime(byte[] packet, int length) { 554 long minLifetime = Long.MAX_VALUE; 555 // Wrap packet in ByteBuffer so we can read big-endian values easily 556 ByteBuffer byteBuffer = ByteBuffer.wrap(packet); 557 for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) { 558 int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second; 559 560 // The checksum is in mNonLifetimes, but it's not a lifetime. 561 if (offset == ICMP6_RA_CHECKSUM_OFFSET) { 562 continue; 563 } 564 565 final int lifetimeLength = mNonLifetimes.get(i+1).first - offset; 566 final long optionLifetime; 567 switch (lifetimeLength) { 568 case 2: 569 optionLifetime = uint16(byteBuffer.getShort(offset)); 570 break; 571 case 4: 572 optionLifetime = uint32(byteBuffer.getInt(offset)); 573 break; 574 default: 575 throw new IllegalStateException("bogus lifetime size " + lifetimeLength); 576 } 577 minLifetime = Math.min(minLifetime, optionLifetime); 578 } 579 return minLifetime; 580 } 581 582 // How many seconds does this RA's have to live, taking into account the fact 583 // that we might have seen it a while ago. 584 long currentLifetime() { 585 return mMinLifetime - (curTime() - mLastSeen); 586 } 587 588 boolean isExpired() { 589 // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll 590 // have to calculte the filter lifetime specially as a fraction of 0 is still 0. 591 return currentLifetime() <= 0; 592 } 593 594 // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped. 595 // Jump to the next filter if packet doesn't match this RA. 596 @GuardedBy("ApfFilter.this") 597 long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { 598 String nextFilterLabel = "Ra" + getUniqueNumberLocked(); 599 // Skip if packet is not the right size 600 gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); 601 gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel); 602 int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER); 603 // Skip filter if expired 604 gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); 605 gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel); 606 for (int i = 0; i < mNonLifetimes.size(); i++) { 607 // Generate code to match the packet bytes 608 Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i); 609 // Don't generate JNEBS instruction for 0 bytes as it always fails the 610 // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is 611 // the number of bytes to compare. nonLifetime is zero between the 612 // valid and preferred lifetimes in the prefix option. 613 if (nonLifetime.second != 0) { 614 gen.addLoadImmediate(Register.R0, nonLifetime.first); 615 gen.addJumpIfBytesNotEqual(Register.R0, 616 Arrays.copyOfRange(mPacket.array(), nonLifetime.first, 617 nonLifetime.first + nonLifetime.second), 618 nextFilterLabel); 619 } 620 // Generate code to test the lifetimes haven't gone down too far 621 if ((i + 1) < mNonLifetimes.size()) { 622 Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1); 623 int offset = nonLifetime.first + nonLifetime.second; 624 // Skip the checksum. 625 if (offset == ICMP6_RA_CHECKSUM_OFFSET) { 626 continue; 627 } 628 int length = nextNonLifetime.first - offset; 629 switch (length) { 630 case 4: gen.addLoad32(Register.R0, offset); break; 631 case 2: gen.addLoad16(Register.R0, offset); break; 632 default: throw new IllegalStateException("bogus lifetime size " + length); 633 } 634 gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel); 635 } 636 } 637 gen.addJump(gen.DROP_LABEL); 638 gen.defineLabel(nextFilterLabel); 639 return filterLifetime; 640 } 641 } 642 643 // Maximum number of RAs to filter for. 644 private static final int MAX_RAS = 10; 645 646 @GuardedBy("this") 647 private ArrayList<Ra> mRas = new ArrayList<Ra>(); 648 649 // There is always some marginal benefit to updating the installed APF program when an RA is 650 // seen because we can extend the program's lifetime slightly, but there is some cost to 651 // updating the program, so don't bother unless the program is going to expire soon. This 652 // constant defines "soon" in seconds. 653 private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30; 654 // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever 655 // see a refresh. Using half the lifetime might be a good idea except for the fact that 656 // packets may be dropped, so let's use 6. 657 private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6; 658 659 // When did we last install a filter program? In seconds since Unix Epoch. 660 @GuardedBy("this") 661 private long mLastTimeInstalledProgram; 662 // How long should the last installed filter program live for? In seconds. 663 @GuardedBy("this") 664 private long mLastInstalledProgramMinLifetime; 665 666 // For debugging only. The last program installed. 667 @GuardedBy("this") 668 private byte[] mLastInstalledProgram; 669 670 // For debugging only. How many times the program was updated since we started. 671 @GuardedBy("this") 672 private int mNumProgramUpdates; 673 674 /** 675 * Generate filter code to process ARP packets. Execution of this code ends in either the 676 * DROP_LABEL or PASS_LABEL and does not fall off the end. 677 * Preconditions: 678 * - Packet being filtered is ARP 679 */ 680 @GuardedBy("this") 681 private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException { 682 // Here's a basic summary of what the ARP filter program does: 683 // 684 // if not ARP IPv4 685 // pass 686 // if not ARP IPv4 reply or request 687 // pass 688 // if unicast ARP reply 689 // pass 690 // if interface has no IPv4 address 691 // if target ip is 0.0.0.0 692 // drop 693 // else 694 // if target ip is not the interface ip 695 // drop 696 // pass 697 698 final String checkTargetIPv4 = "checkTargetIPv4"; 699 700 // Pass if not ARP IPv4. 701 gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); 702 gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL); 703 704 // Pass if unknown ARP opcode. 705 gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); 706 gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check 707 gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL); 708 709 // Pass if unicast reply. 710 gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); 711 gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); 712 713 // Either a unicast request, a unicast reply, or a broadcast reply. 714 gen.defineLabel(checkTargetIPv4); 715 if (mIPv4Address == null) { 716 // When there is no IPv4 address, drop GARP replies (b/29404209). 717 gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); 718 gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL); 719 } else { 720 // When there is an IPv4 address, drop unicast/broadcast requests 721 // and broadcast replies with a different target IPv4 address. 722 gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); 723 gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL); 724 } 725 726 gen.addJump(gen.PASS_LABEL); 727 } 728 729 /** 730 * Generate filter code to process IPv4 packets. Execution of this code ends in either the 731 * DROP_LABEL or PASS_LABEL and does not fall off the end. 732 * Preconditions: 733 * - Packet being filtered is IPv4 734 */ 735 @GuardedBy("this") 736 private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException { 737 // Here's a basic summary of what the IPv4 filter program does: 738 // 739 // if filtering multicast (i.e. multicast lock not held): 740 // if it's multicast: 741 // drop 742 // if it's not broadcast: 743 // pass 744 // if it's not DHCP destined to our MAC: 745 // drop 746 // pass 747 748 if (mMulticastFilter) { 749 // Check for multicast destination address range 750 gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET); 751 gen.addAnd(0xf0); 752 gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL); 753 754 // Drop all broadcasts besides DHCP addressed to us 755 // If not a broadcast packet, pass 756 gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); 757 gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); 758 // If not UDP, drop 759 gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); 760 gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL); 761 // If fragment, drop. This matches the BPF filter installed by the DHCP client. 762 gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET); 763 gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL); 764 // If not to DHCP client port, drop 765 gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); 766 gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET); 767 gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL); 768 // If not DHCP to our MAC address, drop 769 gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET); 770 // NOTE: Relies on R1 containing IPv4 header offset. 771 gen.addAddR1(); 772 gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL); 773 } 774 775 // Otherwise, pass 776 gen.addJump(gen.PASS_LABEL); 777 } 778 779 780 /** 781 * Generate filter code to process IPv6 packets. Execution of this code ends in either the 782 * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets. 783 * Preconditions: 784 * - Packet being filtered is IPv6 785 */ 786 @GuardedBy("this") 787 private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException { 788 // Here's a basic summary of what the IPv6 filter program does: 789 // 790 // if it's not ICMPv6: 791 // if it's multicast and we're dropping multicast: 792 // drop 793 // pass 794 // if it's ICMPv6 NA to ff02::1: 795 // drop 796 797 gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); 798 799 // Drop multicast if the multicast filter is enabled. 800 if (mMulticastFilter) { 801 // Don't touch ICMPv6 multicast here, we deal with it in more detail later. 802 String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter"; 803 gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel); 804 805 // Drop all other packets sent to ff00::/8. 806 gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET); 807 gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL); 808 // Not multicast and not ICMPv6. Pass. 809 gen.addJump(gen.PASS_LABEL); 810 gen.defineLabel(skipIpv6MulticastFilterLabel); 811 } else { 812 // If not ICMPv6, pass. 813 gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL); 814 } 815 816 // Add unsolicited multicast neighbor announcements filter 817 String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA"; 818 // If not neighbor announcements, skip unsolicited multicast NA filter 819 gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); 820 gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel); 821 // If to ff02::1, drop 822 // TODO: Drop only if they don't contain the address of on-link neighbours. 823 gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); 824 gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS, 825 skipUnsolicitedMulticastNALabel); 826 gen.addJump(gen.DROP_LABEL); 827 gen.defineLabel(skipUnsolicitedMulticastNALabel); 828 } 829 830 /** 831 * Begin generating an APF program to: 832 * <ul> 833 * <li>Drop ARP requests not for us, if mIPv4Address is set, 834 * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC, 835 * <li>Drop IPv4 multicast packets, if mMulticastFilter, 836 * <li>Pass all other IPv4 packets, 837 * <li>Drop all broadcast non-IP non-ARP packets. 838 * <li>Pass all non-ICMPv6 IPv6 packets, 839 * <li>Pass all non-IPv4 and non-IPv6 packets, 840 * <li>Drop IPv6 ICMPv6 NAs to ff02::1. 841 * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows 842 * insertion of RA filters here, or if there aren't any, just passes the packets. 843 * </ul> 844 */ 845 @GuardedBy("this") 846 private ApfGenerator beginProgramLocked() throws IllegalInstructionException { 847 ApfGenerator gen = new ApfGenerator(); 848 // This is guaranteed to return true because of the check in maybeCreate. 849 gen.setApfVersion(mApfCapabilities.apfVersionSupported); 850 851 // Here's a basic summary of what the initial program does: 852 // 853 // if it's ARP: 854 // insert ARP filter to drop or pass these appropriately 855 // if it's IPv4: 856 // insert IPv4 filter to drop or pass these appropriately 857 // if it's not IPv6: 858 // if it's broadcast: 859 // drop 860 // pass 861 // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets 862 863 // Add ARP filters: 864 String skipArpFiltersLabel = "skipArpFilters"; 865 gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); 866 gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel); 867 generateArpFilterLocked(gen); 868 gen.defineLabel(skipArpFiltersLabel); 869 870 // Add IPv4 filters: 871 String skipIPv4FiltersLabel = "skipIPv4Filters"; 872 // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not 873 // execute the ARP filter, since that filter does not fall through, but either drops or 874 // passes. 875 gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel); 876 generateIPv4FilterLocked(gen); 877 gen.defineLabel(skipIPv4FiltersLabel); 878 879 // Check for IPv6: 880 // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not 881 // execute the ARP or IPv4 filters, since those filters do not fall through, but either 882 // drop or pass. 883 String ipv6FilterLabel = "IPv6Filters"; 884 gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel); 885 886 // Drop non-IP non-ARP broadcasts, pass the rest 887 gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); 888 gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); 889 gen.addJump(gen.DROP_LABEL); 890 891 // Add IPv6 filters: 892 gen.defineLabel(ipv6FilterLabel); 893 generateIPv6FilterLocked(gen); 894 return gen; 895 } 896 897 /** 898 * Generate and install a new filter program. 899 */ 900 @GuardedBy("this") 901 @VisibleForTesting 902 void installNewProgramLocked() { 903 purgeExpiredRasLocked(); 904 ArrayList<Ra> rasToFilter = new ArrayList<>(); 905 final byte[] program; 906 long programMinLifetime = Long.MAX_VALUE; 907 try { 908 // Step 1: Determine how many RA filters we can fit in the program. 909 ApfGenerator gen = beginProgramLocked(); 910 for (Ra ra : mRas) { 911 ra.generateFilterLocked(gen); 912 // Stop if we get too big. 913 if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break; 914 rasToFilter.add(ra); 915 } 916 // Step 2: Actually generate the program 917 gen = beginProgramLocked(); 918 for (Ra ra : rasToFilter) { 919 programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen)); 920 } 921 // Execution will reach the end of the program if no filters match, which will pass the 922 // packet to the AP. 923 program = gen.generate(); 924 } catch (IllegalInstructionException e) { 925 Log.e(TAG, "Program failed to generate: ", e); 926 return; 927 } 928 mLastTimeInstalledProgram = curTime(); 929 mLastInstalledProgramMinLifetime = programMinLifetime; 930 mLastInstalledProgram = program; 931 mNumProgramUpdates++; 932 933 if (VDBG) { 934 hexDump("Installing filter: ", program, program.length); 935 } 936 mIpManagerCallback.installPacketFilter(program); 937 int flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter); 938 mMetricsLog.log(new ApfProgramEvent( 939 programMinLifetime, rasToFilter.size(), mRas.size(), program.length, flags)); 940 } 941 942 /** 943 * Returns {@code true} if a new program should be installed because the current one dies soon. 944 */ 945 private boolean shouldInstallnewProgram() { 946 long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime; 947 return expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING; 948 } 949 950 private void hexDump(String msg, byte[] packet, int length) { 951 log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */)); 952 } 953 954 @GuardedBy("this") 955 private void purgeExpiredRasLocked() { 956 for (int i = 0; i < mRas.size();) { 957 if (mRas.get(i).isExpired()) { 958 log("Expiring " + mRas.get(i)); 959 mRas.remove(i); 960 } else { 961 i++; 962 } 963 } 964 } 965 966 /** 967 * Process an RA packet, updating the list of known RAs and installing a new APF program 968 * if the current APF program should be updated. 969 * @return a ProcessRaResult enum describing what action was performed. 970 */ 971 private synchronized ProcessRaResult processRa(byte[] packet, int length) { 972 if (VDBG) hexDump("Read packet = ", packet, length); 973 974 // Have we seen this RA before? 975 for (int i = 0; i < mRas.size(); i++) { 976 Ra ra = mRas.get(i); 977 if (ra.matches(packet, length)) { 978 if (VDBG) log("matched RA " + ra); 979 // Update lifetimes. 980 ra.mLastSeen = curTime(); 981 ra.mMinLifetime = ra.minLifetime(packet, length); 982 ra.seenCount++; 983 984 // Keep mRas in LRU order so as to prioritize generating filters for recently seen 985 // RAs. LRU prioritizes this because RA filters are generated in order from mRas 986 // until the filter program exceeds the maximum filter program size allowed by the 987 // chipset, so RAs appearing earlier in mRas are more likely to make it into the 988 // filter program. 989 // TODO: consider sorting the RAs in order of increasing expiry time as well. 990 // Swap to front of array. 991 mRas.add(0, mRas.remove(i)); 992 993 // If the current program doesn't expire for a while, don't update. 994 if (shouldInstallnewProgram()) { 995 installNewProgramLocked(); 996 return ProcessRaResult.UPDATE_EXPIRY; 997 } 998 return ProcessRaResult.MATCH; 999 } 1000 } 1001 purgeExpiredRasLocked(); 1002 // TODO: figure out how to proceed when we've received more then MAX_RAS RAs. 1003 if (mRas.size() >= MAX_RAS) { 1004 return ProcessRaResult.DROPPED; 1005 } 1006 final Ra ra; 1007 try { 1008 ra = new Ra(packet, length); 1009 } catch (Exception e) { 1010 Log.e(TAG, "Error parsing RA: " + e); 1011 return ProcessRaResult.PARSE_ERROR; 1012 } 1013 // Ignore 0 lifetime RAs. 1014 if (ra.isExpired()) { 1015 return ProcessRaResult.ZERO_LIFETIME; 1016 } 1017 log("Adding " + ra); 1018 mRas.add(ra); 1019 installNewProgramLocked(); 1020 return ProcessRaResult.UPDATE_NEW_RA; 1021 } 1022 1023 /** 1024 * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet 1025 * filtering using APF programs. 1026 */ 1027 public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities, 1028 NetworkInterface networkInterface, IpManager.Callback ipManagerCallback, 1029 boolean multicastFilter) { 1030 if (apfCapabilities == null || networkInterface == null) return null; 1031 if (apfCapabilities.apfVersionSupported == 0) return null; 1032 if (apfCapabilities.maximumApfProgramSize < 512) { 1033 Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize); 1034 return null; 1035 } 1036 // For now only support generating programs for Ethernet frames. If this restriction is 1037 // lifted: 1038 // 1. the program generator will need its offsets adjusted. 1039 // 2. the packet filter attached to our packet socket will need its offset adjusted. 1040 if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null; 1041 if (!new ApfGenerator().setApfVersion(apfCapabilities.apfVersionSupported)) { 1042 Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported); 1043 return null; 1044 } 1045 return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback, 1046 multicastFilter, new IpConnectivityLog()); 1047 } 1048 1049 public synchronized void shutdown() { 1050 if (mReceiveThread != null) { 1051 log("shutting down"); 1052 mReceiveThread.halt(); // Also closes socket. 1053 mReceiveThread = null; 1054 } 1055 mRas.clear(); 1056 } 1057 1058 public synchronized void setMulticastFilter(boolean enabled) { 1059 if (mMulticastFilter != enabled) { 1060 mMulticastFilter = enabled; 1061 installNewProgramLocked(); 1062 } 1063 } 1064 1065 // Find the single IPv4 address if there is one, otherwise return null. 1066 private static byte[] findIPv4Address(LinkProperties lp) { 1067 byte[] ipv4Address = null; 1068 for (InetAddress inetAddr : lp.getAddresses()) { 1069 byte[] addr = inetAddr.getAddress(); 1070 if (addr.length != 4) continue; 1071 // More than one IPv4 address, abort 1072 if (ipv4Address != null && !Arrays.equals(ipv4Address, addr)) return null; 1073 ipv4Address = addr; 1074 } 1075 return ipv4Address; 1076 } 1077 1078 public synchronized void setLinkProperties(LinkProperties lp) { 1079 // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state. 1080 byte[] ipv4Address = findIPv4Address(lp); 1081 // If ipv4Address is the same as mIPv4Address, then there's no change, just return. 1082 if (Arrays.equals(ipv4Address, mIPv4Address)) return; 1083 // Otherwise update mIPv4Address and install new program. 1084 mIPv4Address = ipv4Address; 1085 installNewProgramLocked(); 1086 } 1087 1088 public synchronized void dump(IndentingPrintWriter pw) { 1089 pw.println("Capabilities: " + mApfCapabilities); 1090 pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED")); 1091 pw.println("Multicast: " + (mMulticastFilter ? "DROP" : "ALLOW")); 1092 try { 1093 pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address).getHostAddress()); 1094 } catch (UnknownHostException|NullPointerException e) {} 1095 1096 if (mLastTimeInstalledProgram == 0) { 1097 pw.println("No program installed."); 1098 return; 1099 } 1100 pw.println("Program updates: " + mNumProgramUpdates); 1101 pw.println(String.format( 1102 "Last program length %d, installed %ds ago, lifetime %ds", 1103 mLastInstalledProgram.length, curTime() - mLastTimeInstalledProgram, 1104 mLastInstalledProgramMinLifetime)); 1105 1106 pw.println("RA filters:"); 1107 pw.increaseIndent(); 1108 for (Ra ra: mRas) { 1109 pw.println(ra); 1110 pw.increaseIndent(); 1111 pw.println(String.format( 1112 "Seen: %d, last %ds ago", ra.seenCount, curTime() - ra.mLastSeen)); 1113 if (DBG) { 1114 pw.println("Last match:"); 1115 pw.increaseIndent(); 1116 pw.println(ra.getLastMatchingPacket()); 1117 pw.decreaseIndent(); 1118 } 1119 pw.decreaseIndent(); 1120 } 1121 pw.decreaseIndent(); 1122 1123 if (DBG) { 1124 pw.println("Last program:"); 1125 pw.increaseIndent(); 1126 pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */)); 1127 pw.decreaseIndent(); 1128 } 1129 } 1130 } 1131