|
|
|
|
@@ -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);
|
|
|
|
|
@@ -1050,6 +1175,39 @@ public class ApfFilter {
|
|
|
|
|
return gen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
@@ -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
|
|
|
|
|
|