ApfFilter: more precise IPv4 broadcast filter
This patch refines the Apf IPv4 filter for interfaces with Apf
capabilities to drop packets when:
- the multicast lock is not held
- the packet is an IPv4 packet
- the packet is not a DHCP packet addressed to us
- the packet is L2 broadcast, or IPv4 multicast, or IPv4 broadcast
- caveat: subnet broadcast address is not checked.
This allows to drop IPv4 broadcast packets whose MAC destination
address is not the L2 broadcast address but the current address of the
interface. Such packets can be received on network that rewrite L2
addresses and can put significant pressure on battery by waking up the
phone unnecessarily.
Bug: 30231088
Change-Id: I8b1785fc5ceadaa1f2881765983e502135dcbc46
This commit is contained in:
@@ -181,6 +181,7 @@ public class ApfFilter {
|
||||
private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
|
||||
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
|
||||
private static final int IPV4_ANY_HOST_ADDRESS = 0;
|
||||
private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
|
||||
|
||||
private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
|
||||
private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
|
||||
@@ -737,39 +738,54 @@ public class ApfFilter {
|
||||
// Here's a basic summary of what the IPv4 filter program does:
|
||||
//
|
||||
// if filtering multicast (i.e. multicast lock not held):
|
||||
// if it's multicast:
|
||||
// drop
|
||||
// if it's not broadcast:
|
||||
// if it's DHCP destined to our MAC:
|
||||
// pass
|
||||
// if it's not DHCP destined to our MAC:
|
||||
// if it's L2 broadcast:
|
||||
// drop
|
||||
// if it's IPv4 multicast:
|
||||
// drop
|
||||
// if it's IPv4 broadcast:
|
||||
// drop
|
||||
// pass
|
||||
|
||||
if (mMulticastFilter) {
|
||||
// Check for multicast destination address range
|
||||
final String skipDhcpv4Filter = "skip_dhcp_v4_filter";
|
||||
|
||||
// Pass DHCP addressed to us.
|
||||
// Check it's UDP.
|
||||
gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
|
||||
gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter);
|
||||
// Check it's not a fragment. This matches the BPF filter installed by the DHCP client.
|
||||
gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
|
||||
gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter);
|
||||
// Check it's addressed to DHCP client port.
|
||||
gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
|
||||
gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
|
||||
gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter);
|
||||
// Check it's DHCP to our MAC address.
|
||||
gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
|
||||
// NOTE: Relies on R1 containing IPv4 header offset.
|
||||
gen.addAddR1();
|
||||
gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
|
||||
gen.addJump(gen.PASS_LABEL);
|
||||
|
||||
// Drop all multicasts/broadcasts.
|
||||
gen.defineLabel(skipDhcpv4Filter);
|
||||
|
||||
// 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);
|
||||
|
||||
// Drop all broadcasts besides DHCP addressed to us
|
||||
// If not a broadcast packet, pass
|
||||
// If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
|
||||
gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
|
||||
gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, gen.DROP_LABEL);
|
||||
// TODO: also filter subnet broadcast address.
|
||||
|
||||
// If L2 broadcast packet, drop.
|
||||
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);
|
||||
gen.addJump(gen.DROP_LABEL);
|
||||
}
|
||||
|
||||
// Otherwise, pass
|
||||
|
||||
@@ -604,7 +604,7 @@ public class ApfTest extends AndroidTestCase {
|
||||
|
||||
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
|
||||
IpConnectivityLog log) throws Exception {
|
||||
super(new ApfCapabilities(2, 1536, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
|
||||
super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
|
||||
ipManagerCallback, multicastFilter, log);
|
||||
}
|
||||
|
||||
@@ -721,7 +721,9 @@ public class ApfTest extends AndroidTestCase {
|
||||
@LargeTest
|
||||
public void testApfFilterIPv4() throws Exception {
|
||||
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
|
||||
|
||||
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
|
||||
|
||||
byte[] program = ipManagerCallback.getApfProgram();
|
||||
|
||||
// Verify empty packet of 100 zero bytes is passed
|
||||
@@ -734,9 +736,9 @@ public class ApfTest extends AndroidTestCase {
|
||||
put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_IPV4_ADDR);
|
||||
assertPass(program, packet.array());
|
||||
|
||||
// Verify L2 unicast to IPv4 broadcast addresses is passed (b/30231088)
|
||||
// Verify L2 unicast to IPv4 broadcast addresses is dropped (b/30231088)
|
||||
put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
|
||||
assertPass(program, packet.array());
|
||||
assertDrop(program, packet.array());
|
||||
put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
|
||||
assertPass(program, packet.array());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user