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:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user