diff options
| author | 2018-05-21 15:33:28 +0000 | |
|---|---|---|
| committer | 2018-05-21 15:33:28 +0000 | |
| commit | b5dda0e316a26936a7834373215fbf08fb27a8c3 (patch) | |
| tree | 43372aecae8ceb4058e87a68264f5e2bc859ee8c | |
| parent | b1b864cee20ebe34c2ec7638b51da1318f4f5984 (diff) | |
| parent | ab30db7072968016c1be0bb384385fd7235ae815 (diff) | |
Merge changes from topic "apf-read-ram"
* changes:
apf: Add counters for dropped / passed packets
Add support for reading a snapshot of the APF data
| -rw-r--r-- | services/net/java/android/net/apf/ApfCapabilities.java | 10 | ||||
| -rw-r--r-- | services/net/java/android/net/apf/ApfFilter.java | 276 | ||||
| -rw-r--r-- | services/net/java/android/net/apf/ApfGenerator.java | 5 | ||||
| -rw-r--r-- | services/net/java/android/net/ip/IpClient.java | 43 |
4 files changed, 301 insertions, 33 deletions
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java index 703b41560290..dec8ca207343 100644 --- a/services/net/java/android/net/apf/ApfCapabilities.java +++ b/services/net/java/android/net/apf/ApfCapabilities.java @@ -49,4 +49,14 @@ public class ApfCapabilities { return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(), apfVersionSupported, maximumApfProgramSize, apfPacketFormat); } + + /** + * Returns true if the APF interpreter advertises support for the data buffer access opcodes + * LDDW and STDW. + * + * Full LDDW and STDW support is present from APFv4 on. + */ + public boolean hasDataAccess() { + return apfVersionSupported >= 4; + } } diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index 92d37096d5a6..8ac6ede03529 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -24,6 +24,7 @@ import static com.android.internal.util.BitUtils.getUint32; import static com.android.internal.util.BitUtils.getUint8; import static com.android.internal.util.BitUtils.uint32; +import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -102,6 +103,70 @@ public class ApfFilter { UPDATE_EXPIRY // APF program updated for expiry } + /** + * APF packet counters. + * + * Packet counters are 32bit big-endian values, and allocated near the end of the APF data + * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4, + * the last writable 32bit word. + */ + @VisibleForTesting + private static enum Counter { + RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds) + TOTAL_PACKETS, + PASSED_ARP, + PASSED_DHCP, + PASSED_IPV4, + PASSED_IPV6_NON_ICMP, + PASSED_IPV4_UNICAST, + PASSED_IPV6_ICMP, + PASSED_IPV6_UNICAST_NON_ICMP, + PASSED_ARP_NON_IPV4, + PASSED_ARP_UNKNOWN, + PASSED_ARP_UNICAST_REPLY, + PASSED_NON_IP_UNICAST, + DROPPED_ETH_BROADCAST, + DROPPED_RA, + DROPPED_GARP_REPLY, + DROPPED_ARP_OTHER_HOST, + DROPPED_IPV4_L2_BROADCAST, + DROPPED_IPV4_BROADCAST_ADDR, + DROPPED_IPV4_BROADCAST_NET, + DROPPED_IPV4_MULTICAST, + DROPPED_IPV6_ROUTER_SOLICITATION, + DROPPED_IPV6_MULTICAST_NA, + DROPPED_IPV6_MULTICAST, + DROPPED_IPV6_MULTICAST_PING, + DROPPED_IPV6_NON_ICMP_MULTICAST, + DROPPED_802_3_FRAME, + DROPPED_ETHERTYPE_BLACKLISTED; + + // Returns the negative byte offset from the end of the APF data segment for + // a given counter. + public int offset() { + return - this.ordinal() * 4; // Currently, all counters are 32bit long. + } + + // Returns the total size of the data segment in bytes. + public static int totalSize() { + return (Counter.class.getEnumConstants().length - 1) * 4; + } + } + + /** + * When APFv4 is supported, loads R1 with the offset of the specified counter. + */ + private void maybeSetCounter(ApfGenerator gen, Counter c) { + if (mApfCapabilities.hasDataAccess()) { + gen.addLoadImmediate(Register.R1, c.offset()); + } + } + + // When APFv4 is supported, these point to the trampolines generated by emitEpilogue(). + // Otherwise, they're just aliases for PASS_LABEL and DROP_LABEL. + private final String mCountAndPassLabel; + private final String mCountAndDropLabel; + // Thread to listen for RAs. @VisibleForTesting class ReceiveThread extends Thread { @@ -289,6 +354,16 @@ public class ApfFilter { mDrop802_3Frames = config.ieee802_3Filter; mContext = context; + if (mApfCapabilities.hasDataAccess()) { + mCountAndPassLabel = "countAndPass"; + mCountAndDropLabel = "countAndDrop"; + } else { + // APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP, + // preserving the original pre-APFv4 behavior. + mCountAndPassLabel = ApfGenerator.PASS_LABEL; + mCountAndDropLabel = ApfGenerator.DROP_LABEL; + } + // Now fill the black list from the passed array mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList); @@ -302,6 +377,10 @@ public class ApfFilter { new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); } + public synchronized void setDataSnapshot(byte[] data) { + mDataSnapshot = data; + } + private void log(String s) { Log.d(TAG, "(" + mInterfaceParams.name + "): " + s); } @@ -350,6 +429,10 @@ public class ApfFilter { try { mHardwareAddress = mInterfaceParams.macAddr.toByteArray(); synchronized(this) { + // Clear APF memory. + byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize]; + mIpClientCallback.installPacketFilter(zeroes); + // Install basic filters installNewProgramLocked(); } @@ -729,7 +812,8 @@ public class ApfFilter { gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel); } } - gen.addJump(gen.DROP_LABEL); + maybeSetCounter(gen, Counter.DROPPED_RA); + gen.addJump(mCountAndDropLabel); gen.defineLabel(nextFilterLabel); return filterLifetime; } @@ -764,6 +848,16 @@ public class ApfFilter { @GuardedBy("this") private byte[] mLastInstalledProgram; + /** + * For debugging only. Contains the latest APF buffer snapshot captured from the firmware. + * + * A typical size for this buffer is 4KB. It is present only if the WiFi HAL supports + * IWifiStaIface#readApfPacketFilterData(), and the APF interpreter advertised support for + * the opcodes to access the data buffer (LDDW and STDW). + */ + @GuardedBy("this") @Nullable + private byte[] mDataSnapshot; + // How many times the program was updated since we started. @GuardedBy("this") private int mNumProgramUpdates = 0; @@ -799,31 +893,37 @@ public class ApfFilter { // Pass if not ARP IPv4. gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_ARP_NON_IPV4); + gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel); // Pass if unknown ARP opcode. gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check - gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_ARP_UNKNOWN); + gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel); // Pass if unicast reply. gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY); + gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); // Either a unicast request, a unicast reply, or a broadcast reply. gen.defineLabel(checkTargetIPv4); if (mIPv4Address == null) { // When there is no IPv4 address, drop GARP replies (b/29404209). gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); - gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL); + maybeSetCounter(gen, Counter.DROPPED_GARP_REPLY); + gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); } else { // When there is an IPv4 address, drop unicast/broadcast requests // and broadcast replies with a different target IPv4 address. gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL); + maybeSetCounter(gen, Counter.DROPPED_ARP_OTHER_HOST); + gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel); } - gen.addJump(gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_ARP); + gen.addJump(mCountAndPassLabel); } /** @@ -866,7 +966,8 @@ public class ApfFilter { // NOTE: Relies on R1 containing IPv4 header offset. gen.addAddR1(); gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter); - gen.addJump(gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_DHCP); + gen.addJump(mCountAndPassLabel); // Drop all multicasts/broadcasts. gen.defineLabel(skipDhcpv4Filter); @@ -874,24 +975,31 @@ public class ApfFilter { // If IPv4 destination address is in multicast range, drop. gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET); gen.addAnd(0xf0); - gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL); + maybeSetCounter(gen, Counter.DROPPED_IPV4_MULTICAST); + gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel); // If IPv4 broadcast packet, drop regardless of L2 (b/30231088). + maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR); gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET); - gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, gen.DROP_LABEL); + gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel); if (mIPv4Address != null && mIPv4PrefixLength < 31) { + maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET); int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength); - gen.addJumpIfR0Equals(broadcastAddr, gen.DROP_LABEL); + gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel); } // If L2 broadcast packet, drop. + // TODO: can we invert this condition to fall through to the common pass case below? + maybeSetCounter(gen, Counter.PASSED_IPV4_UNICAST); gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); - gen.addJump(gen.DROP_LABEL); + gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); + maybeSetCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST); + gen.addJump(mCountAndDropLabel); } // Otherwise, pass - gen.addJump(gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_IPV4); + gen.addJump(mCountAndPassLabel); } @@ -938,14 +1046,17 @@ public class ApfFilter { // Drop all other packets sent to ff00::/8 (multicast prefix). gen.defineLabel(dropAllIPv6MulticastsLabel); + maybeSetCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST); gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET); - gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL); + gen.addJumpIfR0Equals(0xff, mCountAndDropLabel); // Not multicast. Pass. - gen.addJump(gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP); + gen.addJump(mCountAndPassLabel); gen.defineLabel(skipIPv6MulticastFilterLabel); } else { // If not ICMPv6, pass. - gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL); + maybeSetCounter(gen, Counter.PASSED_IPV6_NON_ICMP); + gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel); } // If we got this far, the packet is ICMPv6. Drop some specific types. @@ -954,7 +1065,8 @@ public class ApfFilter { String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA"; gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); // Drop all router solicitations (b/32833400) - gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL); + maybeSetCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION); + gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel); // If not neighbor announcements, skip filter. gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel); // If to ff02::1, drop. @@ -962,7 +1074,8 @@ public class ApfFilter { gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS, skipUnsolicitedMulticastNALabel); - gen.addJump(gen.DROP_LABEL); + maybeSetCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA); + gen.addJump(mCountAndDropLabel); gen.defineLabel(skipUnsolicitedMulticastNALabel); } @@ -985,10 +1098,18 @@ public class ApfFilter { * </ul> */ @GuardedBy("this") - private ApfGenerator beginProgramLocked() throws IllegalInstructionException { + private ApfGenerator emitPrologueLocked() throws IllegalInstructionException { // This is guaranteed to succeed because of the check in maybeCreate. ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported); + if (mApfCapabilities.hasDataAccess()) { + // Increment TOTAL_PACKETS + maybeSetCounter(gen, Counter.TOTAL_PACKETS); + gen.addLoadData(Register.R0, 0); // load counter + gen.addAdd(1); + gen.addStoreData(Register.R0, 0); // write-back counter + } + // Here's a basic summary of what the initial program does: // // if it's a 802.3 Frame (ethtype < 0x0600): @@ -1009,12 +1130,14 @@ public class ApfFilter { if (mDrop802_3Frames) { // drop 802.3 frames (ethtype < 0x0600) - gen.addJumpIfR0LessThan(ETH_TYPE_MIN, gen.DROP_LABEL); + maybeSetCounter(gen, Counter.DROPPED_802_3_FRAME); + gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel); } // Handle ether-type black list + maybeSetCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED); for (int p : mEthTypeBlackList) { - gen.addJumpIfR0Equals(p, gen.DROP_LABEL); + gen.addJumpIfR0Equals(p, mCountAndDropLabel); } // Add ARP filters: @@ -1041,8 +1164,10 @@ public class ApfFilter { // Drop non-IP non-ARP broadcasts, pass the rest gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL); - gen.addJump(gen.DROP_LABEL); + maybeSetCounter(gen, Counter.PASSED_NON_IP_UNICAST); + gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); + maybeSetCounter(gen, Counter.DROPPED_ETH_BROADCAST); + gen.addJump(mCountAndDropLabel); // Add IPv6 filters: gen.defineLabel(ipv6FilterLabel); @@ -1051,6 +1176,39 @@ public class ApfFilter { } /** + * Append packet counting epilogue to the APF program. + * + * Currently, the epilogue consists of two trampolines which count passed and dropped packets + * before jumping to the actual PASS and DROP labels. + */ + @GuardedBy("this") + private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException { + // If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it + // will just fall-through to the PASS label. + if (!mApfCapabilities.hasDataAccess()) return; + + // Execution will reach the bottom of the program if none of the filters match, + // which will pass the packet to the application processor. + maybeSetCounter(gen, Counter.PASSED_IPV6_ICMP); + + // Append the count & pass trampoline, which increments the counter at the data address + // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting + // the entire sequence inline for every counter. + gen.defineLabel(mCountAndPassLabel); + gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) + gen.addAdd(1); // R0++ + gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 + gen.addJump(gen.PASS_LABEL); + + // Same as above for the count & drop trampoline. + gen.defineLabel(mCountAndDropLabel); + gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) + gen.addAdd(1); // R0++ + gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 + gen.addJump(gen.DROP_LABEL); + } + + /** * Generate and install a new filter program. */ @GuardedBy("this") @@ -1060,22 +1218,39 @@ public class ApfFilter { ArrayList<Ra> rasToFilter = new ArrayList<>(); final byte[] program; long programMinLifetime = Long.MAX_VALUE; + long maximumApfProgramSize = mApfCapabilities.maximumApfProgramSize; + if (mApfCapabilities.hasDataAccess()) { + // Reserve space for the counters. + maximumApfProgramSize -= Counter.totalSize(); + } + try { // Step 1: Determine how many RA filters we can fit in the program. - ApfGenerator gen = beginProgramLocked(); + ApfGenerator gen = emitPrologueLocked(); + + // The epilogue normally goes after the RA filters, but add it early to include its + // length when estimating the total. + emitEpilogue(gen); + + // Can't fit the program even without any RA filters? + if (gen.programLengthOverEstimate() > maximumApfProgramSize) { + Log.e(TAG, "Program exceeds maximum size " + maximumApfProgramSize); + return; + } + for (Ra ra : mRas) { ra.generateFilterLocked(gen); // Stop if we get too big. - if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break; + if (gen.programLengthOverEstimate() > maximumApfProgramSize) break; rasToFilter.add(ra); } + // Step 2: Actually generate the program - gen = beginProgramLocked(); + gen = emitPrologueLocked(); for (Ra ra : rasToFilter) { programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen)); } - // Execution will reach the end of the program if no filters match, which will pass the - // packet to the AP. + emitEpilogue(gen); program = gen.generate(); } catch (IllegalInstructionException|IllegalStateException e) { Log.e(TAG, "Failed to generate APF program.", e); @@ -1277,6 +1452,23 @@ public class ApfFilter { installNewProgramLocked(); } + static public long counterValue(byte[] data, Counter counter) + throws ArrayIndexOutOfBoundsException { + // Follow the same wrap-around addressing scheme of the interpreter. + int offset = counter.offset(); + if (offset < 0) { + offset = data.length + offset; + } + + // Decode 32bit big-endian integer into a long so we can count up beyond 2^31. + long value = 0; + for (int i = 0; i < 4; i++) { + value = value << 8 | (data[offset] & 0xFF); + offset++; + } + return value; + } + public synchronized void dump(IndentingPrintWriter pw) { pw.println("Capabilities: " + mApfCapabilities); pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED")); @@ -1318,6 +1510,32 @@ public class ApfFilter { pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */)); pw.decreaseIndent(); } + + pw.println("APF packet counters: "); + pw.increaseIndent(); + if (!mApfCapabilities.hasDataAccess()) { + pw.println("APF counters not supported"); + } else if (mDataSnapshot == null) { + pw.println("No last snapshot."); + } else { + try { + Counter[] counters = Counter.class.getEnumConstants(); + for (Counter c : Arrays.asList(counters).subList(1, counters.length)) { + long value = counterValue(mDataSnapshot, c); + // Only print non-zero counters + if (value != 0) { + pw.println(c.toString() + ": " + value); + } + } + } catch (ArrayIndexOutOfBoundsException e) { + pw.println("Uh-oh: " + e); + } + if (VDBG) { + pw.println("Raw data dump: "); + pw.println(HexDump.dumpHexString(mDataSnapshot)); + } + } + pw.decreaseIndent(); } // TODO: move to android.net.NetworkUtils diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java index 99b2fc6db472..87a1b5ea8b4d 100644 --- a/services/net/java/android/net/apf/ApfGenerator.java +++ b/services/net/java/android/net/apf/ApfGenerator.java @@ -378,8 +378,7 @@ public class ApfGenerator { } /** - * Returns true if the specified {@code version} is supported by the ApfGenerator, otherwise - * false. + * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false. */ public static boolean supportsVersion(int version) { return version >= MIN_APF_VERSION; @@ -753,7 +752,7 @@ public class ApfGenerator { /** * Add an instruction to the end of the program to jump to {@code target} if the bytes of the - * packet at, an offset specified by {@code register}, match {@code bytes}. + * packet at an offset specified by {@code register} match {@code bytes}. */ public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target) throws IllegalInstructionException { diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java index 37bb2ad8f6c1..63ae09a79379 100644 --- a/services/net/java/android/net/ip/IpClient.java +++ b/services/net/java/android/net/ip/IpClient.java @@ -16,6 +16,7 @@ package android.net.ip; +import com.android.internal.util.HexDump; import com.android.internal.util.MessageUtils; import com.android.internal.util.WakeupMessage; @@ -174,6 +175,12 @@ public class IpClient extends StateMachine { // Install an APF program to filter incoming packets. public void installPacketFilter(byte[] filter) {} + // Asynchronously read back the APF program & data buffer from the wifi driver. + // Due to Wifi HAL limitations, the current implementation only supports dumping the entire + // buffer. In response to this request, the driver returns the data buffer asynchronously + // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. + public void startReadPacketFilter() {} + // If multicast filtering cannot be accomplished with APF, this function will be called to // actuate multicast filtering using another means. public void setFallbackMulticastFilter(boolean enabled) {} @@ -280,6 +287,11 @@ public class IpClient extends StateMachine { log("installPacketFilter(byte[" + filter.length + "])"); } @Override + public void startReadPacketFilter() { + mCallback.startReadPacketFilter(); + log("startReadPacketFilter()"); + } + @Override public void setFallbackMulticastFilter(boolean enabled) { mCallback.setFallbackMulticastFilter(enabled); log("setFallbackMulticastFilter(" + enabled + ")"); @@ -591,6 +603,7 @@ public class IpClient extends StateMachine { private static final int CMD_SET_MULTICAST_FILTER = 9; private static final int EVENT_PROVISIONING_TIMEOUT = 10; private static final int EVENT_DHCPACTION_TIMEOUT = 11; + private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12; private static final int MAX_LOG_RECORDS = 500; private static final int MAX_PACKET_RECORDS = 100; @@ -644,6 +657,14 @@ public class IpClient extends StateMachine { private boolean mMulticastFiltering; private long mStartTimeMillis; + /** + * Reading the snapshot is an asynchronous operation initiated by invoking + * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an + * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable + * signals when a new snapshot is ready. + */ + private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable(); + public static class Dependencies { public INetworkManagementService getNMS() { return INetworkManagementService.Stub.asInterface( @@ -857,6 +878,10 @@ public class IpClient extends StateMachine { sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); } + public void readPacketFilterComplete(byte[] data) { + sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data); + } + /** * Set the TCP buffer sizes to use. * @@ -902,7 +927,16 @@ public class IpClient extends StateMachine { pw.println(mTag + " APF dump:"); pw.increaseIndent(); if (apfFilter != null) { + if (apfCapabilities.hasDataAccess()) { + // Request a new snapshot, then wait for it. + mApfDataSnapshotComplete.close(); + mCallback.startReadPacketFilter(); + if (!mApfDataSnapshotComplete.block(1000)) { + pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT"); + } + } apfFilter.dump(pw); + } else { pw.print("No active ApfFilter; "); if (provisioningConfig == null) { @@ -914,7 +948,6 @@ public class IpClient extends StateMachine { } } pw.decreaseIndent(); - pw.println(); pw.println(mTag + " current ProvisioningConfiguration:"); pw.increaseIndent(); @@ -1710,6 +1743,14 @@ public class IpClient extends StateMachine { break; } + case EVENT_READ_PACKET_FILTER_COMPLETE: { + if (mApfFilter != null) { + mApfFilter.setDataSnapshot((byte[]) msg.obj); + } + mApfDataSnapshotComplete.open(); + break; + } + case EVENT_DHCPACTION_TIMEOUT: stopDhcpAction(); break; |