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