diff options
| -rw-r--r-- | services/net/java/android/net/apf/ApfFilter.java | 98 | ||||
| -rw-r--r-- | services/tests/servicestests/src/android/net/apf/ApfTest.java | 60 |
2 files changed, 112 insertions, 46 deletions
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index 1e2c92caa250..4c7545240c23 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -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])); + } } diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java index 5c712ea7bdae..f7c61d15bb5f 100644 --- a/services/tests/servicestests/src/android/net/apf/ApfTest.java +++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java @@ -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); + } } |