From f8a01e84317fcb9d27a294e95603b846143c7fcb Mon Sep 17 00:00:00 2001 From: Paul Jensen Date: Thu, 26 May 2016 09:16:11 -0400 Subject: [PATCH] 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 --- .../net/java/android/net/apf/ApfFilter.java | 53 ++++++++++--------- .../src/android/net/apf/ApfTest.java | 51 +++++++++++------- 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index 538e8f879a3db..485f2f5318f49 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -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); diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java index 9e04d2301ad91..fae82ca4e7a35 100644 --- a/services/tests/servicestests/src/android/net/apf/ApfTest.java +++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java @@ -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(); }