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