Merge "ApfFilter: take into account IPv4 subnet prefix" into nyc-mr1-dev

This commit is contained in:
TreeHugger Robot
2016-09-13 16:28:24 +00:00
committed by Android (Google) Code Review
2 changed files with 112 additions and 46 deletions

View File

@@ -19,6 +19,7 @@ package android.net.apf;
import static android.system.OsConstants.*;
import android.os.SystemClock;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.apf.ApfGenerator;
@@ -44,6 +45,7 @@ import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.Thread;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
@@ -230,6 +232,9 @@ public class ApfFilter {
// Our IPv4 address, if we have just one, otherwise null.
@GuardedBy("this")
private byte[] mIPv4Address;
// The subnet prefix length of our IPv4 network. Only valid if mIPv4Address is not null.
@GuardedBy("this")
private int mIPv4PrefixLength;
@VisibleForTesting
ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
@@ -365,26 +370,6 @@ public class ApfFilter {
// Can't be static because it's in a non-static inner class.
// TODO: Make this static once RA is its own class.
private int uint8(byte b) {
return b & 0xff;
}
private int uint16(short s) {
return s & 0xffff;
}
private long uint32(int i) {
return i & 0xffffffffL;
}
private long getUint16(ByteBuffer buffer, int position) {
return uint16(buffer.getShort(position));
}
private long getUint32(ByteBuffer buffer, int position) {
return uint32(buffer.getInt(position));
}
private void prefixOptionToString(StringBuffer sb, int offset) {
String prefix = IPv6AddresstoString(offset + 16);
int length = uint8(mPacket.get(offset + 2));
@@ -780,7 +765,10 @@ public class ApfFilter {
// 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 (mIPv4Address != null && mIPv4PrefixLength < 31) {
int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
gen.addJumpIfR0Equals(broadcastAddr, gen.DROP_LABEL);
}
// If L2 broadcast packet, drop.
gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
@@ -1078,26 +1066,32 @@ public class ApfFilter {
}
}
// Find the single IPv4 address if there is one, otherwise return null.
private static byte[] findIPv4Address(LinkProperties lp) {
byte[] ipv4Address = null;
for (InetAddress inetAddr : lp.getAddresses()) {
byte[] addr = inetAddr.getAddress();
if (addr.length != 4) continue;
// More than one IPv4 address, abort
if (ipv4Address != null && !Arrays.equals(ipv4Address, addr)) return null;
ipv4Address = addr;
/** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
LinkAddress ipv4Address = null;
for (LinkAddress address : lp.getLinkAddresses()) {
if (!(address.getAddress() instanceof Inet4Address)) {
continue;
}
if (ipv4Address != null && !ipv4Address.isSameAddressAs(address)) {
// More than one IPv4 address, abort.
return null;
}
ipv4Address = address;
}
return ipv4Address;
}
public synchronized void setLinkProperties(LinkProperties lp) {
// NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
byte[] ipv4Address = findIPv4Address(lp);
// If ipv4Address is the same as mIPv4Address, then there's no change, just return.
if (Arrays.equals(ipv4Address, mIPv4Address)) return;
// Otherwise update mIPv4Address and install new program.
mIPv4Address = ipv4Address;
final LinkAddress ipv4Address = findIPv4LinkAddress(lp);
final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null;
final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) {
return;
}
mIPv4Address = addr;
mIPv4PrefixLength = prefix;
installNewProgramLocked();
}
@@ -1143,4 +1137,38 @@ public class ApfFilter {
pw.decreaseIndent();
}
}
private static int uint8(byte b) {
return b & 0xff;
}
private static int uint16(short s) {
return s & 0xffff;
}
private static long uint32(int i) {
return i & 0xffffffffL;
}
private static long getUint16(ByteBuffer buffer, int position) {
return uint16(buffer.getShort(position));
}
private static long getUint32(ByteBuffer buffer, int position) {
return uint32(buffer.getInt(position));
}
// TODO: move to android.net.NetworkUtils
@VisibleForTesting
public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) {
return bytesToInt(addrBytes) | (int) (uint32(-1) >>> prefixLength);
}
@VisibleForTesting
public static int bytesToInt(byte[] addrBytes) {
return (uint8(addrBytes[0]) << 24)
+ (uint8(addrBytes[1]) << 16)
+ (uint8(addrBytes[2]) << 8)
+ (uint8(addrBytes[3]));
}
}

View File

@@ -20,6 +20,9 @@ import static android.system.OsConstants.*;
import com.android.frameworks.servicestests.R;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.apf.ApfCapabilities;
import android.net.apf.ApfFilter;
import android.net.apf.ApfGenerator;
@@ -28,8 +31,6 @@ import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpManager;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.os.ConditionVariable;
import android.os.Parcelable;
import android.system.ErrnoException;
@@ -713,7 +714,7 @@ public class ApfTest extends AndroidTestCase {
private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1};
private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, (byte) 255, (byte) 255};
private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, 31, (byte) 255}; // prefix = 19
private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1};
private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2};
private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
@@ -721,8 +722,12 @@ public class ApfTest extends AndroidTestCase {
@LargeTest
public void testApfFilterIPv4() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -740,7 +745,7 @@ public class ApfTest extends AndroidTestCase {
put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
assertDrop(program, packet.array());
put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
assertPass(program, packet.array());
assertDrop(program, packet.array());
// Verify multicast/broadcast IPv4, not DHCP to us, is dropped
put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
@@ -797,15 +802,21 @@ public class ApfTest extends AndroidTestCase {
@LargeTest
public void testApfFilterMulticast() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
byte[] program = ipManagerCallback.getApfProgram();
final byte[] unicastIpv4Addr = {(byte)192,0,2,63};
final byte[] broadcastIpv4Addr = {(byte)192,0,(byte)255,(byte)255};
final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
// Construct IPv4 and IPv6 multicast packets.
ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
@@ -848,7 +859,7 @@ public class ApfTest extends AndroidTestCase {
assertDrop(program, mcastv6packet.array());
assertDrop(program, bcastv4packet1.array());
assertDrop(program, bcastv4packet2.array());
assertPass(program, bcastv4unicastl2packet.array());
assertDrop(program, bcastv4unicastl2packet.array());
// Turn off multicast filter and verify it's off
ipManagerCallback.resetApfProgramWait();
@@ -864,11 +875,12 @@ public class ApfTest extends AndroidTestCase {
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
assertDrop(program, mcastv6packet.array());
assertDrop(program, bcastv4packet1.array());
assertPass(program, bcastv4unicastl2packet.array());
assertDrop(program, bcastv4unicastl2packet.array());
// Verify that ICMPv6 multicast is not dropped.
mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
@@ -1154,4 +1166,30 @@ public class ApfTest extends AndroidTestCase {
*/
private native static boolean compareBpfApf(String filter, String pcap_filename,
byte[] apf_program);
public void testBytesToInt() {
assertEquals(0x00000000, ApfFilter.bytesToInt(IPV4_ANY_HOST_ADDR));
assertEquals(0xffffffff, ApfFilter.bytesToInt(IPV4_BROADCAST_ADDRESS));
assertEquals(0x0a000001, ApfFilter.bytesToInt(MOCK_IPV4_ADDR));
assertEquals(0x0a000002, ApfFilter.bytesToInt(ANOTHER_IPV4_ADDR));
assertEquals(0x0a001fff, ApfFilter.bytesToInt(MOCK_BROADCAST_IPV4_ADDR));
assertEquals(0xe0000001, ApfFilter.bytesToInt(MOCK_MULTICAST_IPV4_ADDR));
}
public void testBroadcastAddress() throws Exception {
assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0));
assertEqualsIp("0.0.0.0", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 32));
assertEqualsIp("0.0.3.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 22));
assertEqualsIp("0.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 8));
assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 0));
assertEqualsIp("10.0.0.1", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 32));
assertEqualsIp("10.0.0.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 24));
assertEqualsIp("10.0.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 16));
}
public void assertEqualsIp(String expected, int got) throws Exception {
int want = ApfFilter.bytesToInt(InetAddress.getByName(expected).getAddress());
assertEquals(want, got);
}
}