Don't drop IPv4 broadcast packets when WiFi multicast lock is held

IPv4 broadcast packets can be very common (e.g. every 2s) so they
need to be dropped in the general case.  They also may be critical
for certain discovery protocols, so allow them through with APF
when the WiFi multicast lock is held.

Bug: 26238573
Change-Id: I03e09a2b9c779da5da775e78b95e9e0339720eaf
This commit is contained in:
Paul Jensen
2016-05-26 09:16:11 -04:00
parent 19d34821b9
commit f8a01e8431
2 changed files with 58 additions and 46 deletions

View File

@@ -597,12 +597,13 @@ public class ApfFilter {
private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv4 filter program does:
//
// if it's multicast and we're dropping multicast:
// drop
// if it's not broadcast:
// pass
// if it's not DHCP destined to our MAC:
// drop
// if filtering multicast (i.e. multicast lock not held):
// if it's multicast:
// drop
// if it's not broadcast:
// pass
// if it's not DHCP destined to our MAC:
// drop
// pass
if (mMulticastFilter) {
@@ -610,27 +611,27 @@ public class ApfFilter {
gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
gen.addAnd(0xf0);
gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
}
// Drop all broadcasts besides DHCP addressed to us
// If not a broadcast packet, pass
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
// If not UDP, drop
gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
// If fragment, drop. This matches the BPF filter installed by the DHCP client.
gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
// If not to DHCP client port, drop
gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
// If not DHCP to our MAC address, drop
gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
// NOTE: Relies on R1 containing IPv4 header offset.
gen.addAddR1();
gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
// Drop all broadcasts besides DHCP addressed to us
// If not a broadcast packet, pass
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
// If not UDP, drop
gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
// If fragment, drop. This matches the BPF filter installed by the DHCP client.
gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
// If not to DHCP client port, drop
gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
// If not DHCP to our MAC address, drop
gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
// NOTE: Relies on R1 containing IPv4 header offset.
gen.addAddR1();
gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
}
// Otherwise, pass
gen.addJump(gen.PASS_LABEL);

View File

@@ -663,7 +663,7 @@ public class ApfTest extends AndroidTestCase {
@LargeTest
public void testApfFilterIPv4() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
@@ -726,46 +726,57 @@ public class ApfTest extends AndroidTestCase {
byte[] program = ipManagerCallback.getApfProgram();
// Construct IPv4 and IPv6 multicast packets.
ByteBuffer v4packet = ByteBuffer.wrap(new byte[100]);
v4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
v4packet.position(IPV4_DEST_ADDR_OFFSET);
v4packet.put(new byte[]{(byte)224,0,0,1});
ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
mcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
mcastv4packet.put(new byte[]{(byte)224,0,0,1});
ByteBuffer v6packet = ByteBuffer.wrap(new byte[100]);
v6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
v6packet.position(IPV6_DEST_ADDR_OFFSET);
v6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]);
mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
mcastv6packet.position(IPV6_DEST_ADDR_OFFSET);
mcastv6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
// Construct IPv4 broadcast packet.
ByteBuffer bcastv4packet = ByteBuffer.wrap(new byte[100]);
bcastv4packet.put(ETH_BROADCAST_MAC_ADDRESS);
bcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
bcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
bcastv4packet.put(new byte[]{(byte)192,(byte)0,(byte)2,(byte)63});
// Verify initially disabled multicast filter is off
assertPass(program, v4packet.array(), 0);
assertPass(program, v6packet.array(), 0);
assertPass(program, bcastv4packet.array(), 0);
assertPass(program, mcastv4packet.array(), 0);
assertPass(program, mcastv6packet.array(), 0);
// Turn on multicast filter and verify it works
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(true);
program = ipManagerCallback.getApfProgram();
assertDrop(program, v4packet.array(), 0);
assertDrop(program, v6packet.array(), 0);
assertDrop(program, bcastv4packet.array(), 0);
assertDrop(program, mcastv4packet.array(), 0);
assertDrop(program, mcastv6packet.array(), 0);
// Turn off multicast filter and verify it's off
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(false);
program = ipManagerCallback.getApfProgram();
assertPass(program, v4packet.array(), 0);
assertPass(program, v6packet.array(), 0);
assertPass(program, bcastv4packet.array(), 0);
assertPass(program, mcastv4packet.array(), 0);
assertPass(program, mcastv6packet.array(), 0);
// Verify it can be initialized to on
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
program = ipManagerCallback.getApfProgram();
assertDrop(program, v4packet.array(), 0);
assertDrop(program, v6packet.array(), 0);
assertDrop(program, bcastv4packet.array(), 0);
assertDrop(program, mcastv4packet.array(), 0);
assertDrop(program, mcastv6packet.array(), 0);
// Verify that ICMPv6 multicast is not dropped.
v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
assertPass(program, v6packet.array(), 0);
mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
assertPass(program, mcastv6packet.array(), 0);
apfFilter.shutdown();
}