Loading services/net/java/android/net/apf/ApfFilter.java +74 −34 Original line number Diff line number Diff line Loading @@ -16,21 +16,21 @@ package android.net.apf; import static android.net.util.NetworkConstants.*; import static android.system.OsConstants.*; import static com.android.internal.util.BitUtils.bytesToBEInt; import static com.android.internal.util.BitUtils.getUint16; import static com.android.internal.util.BitUtils.getUint32; import static com.android.internal.util.BitUtils.getUint8; import static com.android.internal.util.BitUtils.uint16; import static com.android.internal.util.BitUtils.uint32; import static com.android.internal.util.BitUtils.uint8; import android.os.SystemClock; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkUtils; import android.net.apf.ApfGenerator; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; import android.net.ip.IpClient; Loading @@ -39,31 +39,29 @@ import android.net.metrics.ApfStats; import android.net.metrics.IpConnectivityLog; import android.net.metrics.RaEvent; import android.net.util.InterfaceParams; import android.os.PowerManager; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; import android.system.PacketSocketAddress; import android.text.format.DateUtils; import android.util.Log; import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; 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.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import libcore.io.IoBridge; /** Loading Loading @@ -215,10 +213,6 @@ public class ApfFilter { { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; private static final int ICMP6_ROUTER_SOLICITATION = 133; private static final int ICMP6_ROUTER_ADVERTISEMENT = 134; private static final int ICMP6_NEIGHBOR_SOLICITATION = 135; private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136; // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2; Loading Loading @@ -258,9 +252,26 @@ public class ApfFilter { private long mUniqueCounter; @GuardedBy("this") private boolean mMulticastFilter; @GuardedBy("this") private boolean mInDozeMode; private final boolean mDrop802_3Frames; private final int[] mEthTypeBlackList; // Detects doze mode state transitions. private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); final boolean deviceIdle = powerManager.isDeviceIdleMode(); setDozeMode(deviceIdle); } } }; private final Context mContext; // Our IPv4 address, if we have just one, otherwise null. @GuardedBy("this") private byte[] mIPv4Address; Loading @@ -269,13 +280,14 @@ public class ApfFilter { private int mIPv4PrefixLength; @VisibleForTesting ApfFilter(ApfConfiguration config, InterfaceParams ifParams, ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams, IpClient.Callback ipClientCallback, IpConnectivityLog log) { mApfCapabilities = config.apfCapabilities; mIpClientCallback = ipClientCallback; mInterfaceParams = ifParams; mMulticastFilter = config.multicastFilter; mDrop802_3Frames = config.ieee802_3Filter; mContext = context; // Now fill the black list from the passed array mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList); Loading @@ -284,6 +296,10 @@ public class ApfFilter { // TODO: ApfFilter should not generate programs until IpClient sends provisioning success. maybeStartFilter(); // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter. mContext.registerReceiver(mDeviceIdleReceiver, new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); } private void log(String s) { Loading Loading @@ -522,7 +538,7 @@ public class ApfFilter { // to our packet socket. b/29586253 if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 || getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 || getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMP6_ROUTER_ADVERTISEMENT) { getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) { throw new InvalidRaException("Not an ICMP6 router advertisement"); } Loading Loading @@ -889,8 +905,9 @@ public class ApfFilter { private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException { // Here's a basic summary of what the IPv6 filter program does: // // if it's not ICMPv6: // if it's multicast and we're dropping multicast: // if we're dropping multicast // if it's not IPCMv6 or it's ICMPv6 but we're in doze mode: // if it's multicast: // drop // pass // if it's ICMPv6 RS to any: Loading @@ -902,28 +919,44 @@ public class ApfFilter { // Drop multicast if the multicast filter is enabled. if (mMulticastFilter) { // Don't touch ICMPv6 multicast here, we deal with it in more detail later. String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter"; gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel); final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter"; final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast"; // While in doze mode, drop ICMPv6 multicast pings, let the others pass. // While awake, let all ICMPv6 multicasts through. if (mInDozeMode) { // Not ICMPv6? -> Proceed to multicast filtering gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel); // Drop all other packets sent to ff00::/8. // ICMPv6 but not ECHO? -> Skip the multicast filter. // (ICMPv6 ECHO requests will go through the multicast filter below). gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel); } else { gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel); } // Drop all other packets sent to ff00::/8 (multicast prefix). gen.defineLabel(dropAllIPv6MulticastsLabel); gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET); gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL); // Not multicast and not ICMPv6. Pass. // Not multicast. Pass. gen.addJump(gen.PASS_LABEL); gen.defineLabel(skipIpv6MulticastFilterLabel); gen.defineLabel(skipIPv6MulticastFilterLabel); } else { // If not ICMPv6, pass. gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL); } // If we got this far, the packet is ICMPv6. Drop some specific types. // Add unsolicited multicast neighbor announcements filter String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA"; gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); // Drop all router solicitations (b/32833400) gen.addJumpIfR0Equals(ICMP6_ROUTER_SOLICITATION, gen.DROP_LABEL); gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL); // If not neighbor announcements, skip filter. gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel); gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel); // If to ff02::1, drop. // TODO: Drop only if they don't contain the address of on-link neighbours. gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); Loading Loading @@ -1168,9 +1201,9 @@ public class ApfFilter { * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet * filtering using APF programs. */ public static ApfFilter maybeCreate(ApfConfiguration config, public static ApfFilter maybeCreate(Context context, ApfConfiguration config, InterfaceParams ifParams, IpClient.Callback ipClientCallback) { if (config == null || ifParams == null) return null; if (context == null || config == null || ifParams == null) return null; ApfCapabilities apfCapabilities = config.apfCapabilities; if (apfCapabilities == null) return null; if (apfCapabilities.apfVersionSupported == 0) return null; Loading @@ -1187,7 +1220,8 @@ public class ApfFilter { Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported); return null; } return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog()); return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog()); } public synchronized void shutdown() { Loading @@ -1197,12 +1231,11 @@ public class ApfFilter { mReceiveThread = null; } mRas.clear(); mContext.unregisterReceiver(mDeviceIdleReceiver); } public synchronized void setMulticastFilter(boolean isEnabled) { if (mMulticastFilter == isEnabled) { return; } if (mMulticastFilter == isEnabled) return; mMulticastFilter = isEnabled; if (!isEnabled) { mNumProgramUpdatesAllowingMulticast++; Loading @@ -1210,6 +1243,13 @@ public class ApfFilter { installNewProgramLocked(); } @VisibleForTesting public synchronized void setDozeMode(boolean isEnabled) { if (mInDozeMode == isEnabled) return; mInDozeMode = isEnabled; installNewProgramLocked(); } /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */ private static LinkAddress findIPv4LinkAddress(LinkProperties lp) { LinkAddress ipv4Address = null; Loading services/net/java/android/net/ip/IpClient.java +1 −1 Original line number Diff line number Diff line Loading @@ -1490,7 +1490,7 @@ public class IpClient extends StateMachine { mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames); apfConfig.ethTypeBlackList = mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList); mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback); mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback); // TODO: investigate the effects of any multicast filtering racing/interfering with the // rest of this IP configuration startup. if (mApfFilter == null) { Loading services/net/java/android/net/util/NetworkConstants.java +2 −1 Original line number Diff line number Diff line Loading @@ -136,6 +136,8 @@ public final class NetworkConstants { * - https://tools.ietf.org/html/rfc4861 */ public static final int ICMPV6_HEADER_MIN_LEN = 4; public static final int ICMPV6_ECHO_REQUEST_TYPE = 128; public static final int ICMPV6_ECHO_REPLY_TYPE = 129; public static final int ICMPV6_ROUTER_SOLICITATION = 133; public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134; public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; Loading @@ -147,7 +149,6 @@ public final class NetworkConstants { public static final int ICMPV6_ND_OPTION_TLLA = 2; public static final int ICMPV6_ND_OPTION_MTU = 5; public static final int ICMPV6_ECHO_REQUEST_TYPE = 128; /** * UDP constants. Loading tests/net/java/android/net/apf/ApfTest.java +68 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.net.apf; import static android.net.util.NetworkConstants.*; import static android.system.OsConstants.*; import static com.android.internal.util.BitUtils.bytesToBEInt; import static com.android.internal.util.BitUtils.put; Loading @@ -26,6 +27,7 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkUtils; Loading Loading @@ -82,6 +84,7 @@ public class ApfTest { private static final int TIMEOUT_MS = 500; @Mock IpConnectivityLog mLog; @Mock Context mContext; @Before public void setUp() throws Exception { Loading Loading @@ -633,9 +636,9 @@ public class ApfTest { private FileDescriptor mWriteSocket; private final long mFixedTimeMs = SystemClock.elapsedRealtime(); public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback, IpConnectivityLog log) throws Exception { super(config, InterfaceParams.getByName("lo"), ipManagerCallback, log); public TestApfFilter(Context context, ApfConfiguration config, IpManager.Callback ipManagerCallback, IpConnectivityLog log) throws Exception { super(context, config, InterfaceParams.getByName("lo"), ipManagerCallback, log); } // Pretend an RA packet has been received and show it to ApfFilter. Loading Loading @@ -757,6 +760,17 @@ public class ApfTest { private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2}; private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0}; // Helper to initialize a default apfFilter. private ApfFilter setupApfFilter(IpManager.Callback ipManagerCallback, ApfConfiguration config) throws Exception { LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); LinkProperties lp = new LinkProperties(); lp.addLinkAddress(link); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); return apfFilter; } @Test public void testApfFilterIPv4() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); Loading @@ -766,7 +780,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); byte[] program = ipManagerCallback.getApfProgram(); Loading Loading @@ -818,7 +832,7 @@ public class ApfTest { public void testApfFilterIPv6() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfConfiguration config = getDefaultConfig(); TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); byte[] program = ipManagerCallback.getApfProgram(); // Verify empty IPv6 packet is passed Loading Loading @@ -861,7 +875,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); byte[] program = ipManagerCallback.getApfProgram(); Loading Loading @@ -925,7 +939,7 @@ public class ApfTest { apfFilter.shutdown(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); program = ipManagerCallback.getApfProgram(); assertDrop(program, mcastv4packet.array()); Loading @@ -941,16 +955,47 @@ public class ApfTest { } @Test public void testApfFilter802_3() throws Exception { public void testApfFilterMulticastPingWhileDozing() 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 = setupApfFilter(ipManagerCallback, getDefaultConfig()); ApfConfiguration config = getDefaultConfig(); TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); // Construct a multicast ICMPv6 ECHO request. final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb}; ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6); packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE); put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr); // Normally, we let multicast pings alone... assertPass(ipManagerCallback.getApfProgram(), packet.array()); // ...and even while dozing... apfFilter.setDozeMode(true); assertPass(ipManagerCallback.getApfProgram(), packet.array()); // ...but when the multicast filter is also enabled, drop the multicast pings to save power. apfFilter.setMulticastFilter(true); assertDrop(ipManagerCallback.getApfProgram(), packet.array()); // However, we should still let through all other ICMPv6 types. ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone()); raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT); assertPass(ipManagerCallback.getApfProgram(), raPacket.array()); // Now wake up from doze mode to ensure that we no longer drop the packets. // (The multicast filter is still enabled at this point). apfFilter.setDozeMode(false); assertPass(ipManagerCallback.getApfProgram(), packet.array()); apfFilter.shutdown(); } @Test public void testApfFilter802_3() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfConfiguration config = getDefaultConfig(); ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config); byte[] program = ipManagerCallback.getApfProgram(); // Verify empty packet of 100 zero bytes is passed Loading @@ -970,8 +1015,7 @@ public class ApfTest { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ieee802_3Filter = DROP_802_3_FRAMES; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); apfFilter = setupApfFilter(ipManagerCallback, config); program = ipManagerCallback.getApfProgram(); // Verify that IEEE802.3 frame is dropped Loading @@ -992,18 +1036,13 @@ public class ApfTest { @Test public void testApfFilterEthTypeBL() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); LinkProperties lp = new LinkProperties(); lp.addLinkAddress(link); final int[] emptyBlackList = {}; final int[] ipv4BlackList = {ETH_P_IP}; final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6}; MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfConfiguration config = getDefaultConfig(); TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config); byte[] program = ipManagerCallback.getApfProgram(); // Verify empty packet of 100 zero bytes is passed Loading @@ -1023,8 +1062,7 @@ public class ApfTest { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ethTypeBlackList = ipv4BlackList; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); apfFilter = setupApfFilter(ipManagerCallback, config); program = ipManagerCallback.getApfProgram(); // Verify that IPv4 frame will be dropped Loading @@ -1039,8 +1077,7 @@ public class ApfTest { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ethTypeBlackList = ipv4Ipv6BlackList; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); apfFilter = setupApfFilter(ipManagerCallback, config); program = ipManagerCallback.getApfProgram(); // Verify that IPv4 frame will be dropped Loading Loading @@ -1081,7 +1118,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); // Verify initially ARP request filter is off, and GARP filter is on. verifyArpFilter(ipManagerCallback.getApfProgram(), PASS); Loading Loading @@ -1205,7 +1242,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); byte[] program = ipManagerCallback.getApfProgram(); final int ROUTER_LIFETIME = 1000; Loading Loading @@ -1351,7 +1388,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); for (int i = 0; i < 1000; i++) { byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; r.nextBytes(packet); Loading @@ -1372,7 +1409,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); for (int i = 0; i < 1000; i++) { byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; r.nextBytes(packet); Loading Loading
services/net/java/android/net/apf/ApfFilter.java +74 −34 Original line number Diff line number Diff line Loading @@ -16,21 +16,21 @@ package android.net.apf; import static android.net.util.NetworkConstants.*; import static android.system.OsConstants.*; import static com.android.internal.util.BitUtils.bytesToBEInt; import static com.android.internal.util.BitUtils.getUint16; import static com.android.internal.util.BitUtils.getUint32; import static com.android.internal.util.BitUtils.getUint8; import static com.android.internal.util.BitUtils.uint16; import static com.android.internal.util.BitUtils.uint32; import static com.android.internal.util.BitUtils.uint8; import android.os.SystemClock; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkUtils; import android.net.apf.ApfGenerator; import android.net.apf.ApfGenerator.IllegalInstructionException; import android.net.apf.ApfGenerator.Register; import android.net.ip.IpClient; Loading @@ -39,31 +39,29 @@ import android.net.metrics.ApfStats; import android.net.metrics.IpConnectivityLog; import android.net.metrics.RaEvent; import android.net.util.InterfaceParams; import android.os.PowerManager; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; import android.system.PacketSocketAddress; import android.text.format.DateUtils; import android.util.Log; import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; 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.SocketException; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import libcore.io.IoBridge; /** Loading Loading @@ -215,10 +213,6 @@ public class ApfFilter { { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; private static final int ICMP6_ROUTER_SOLICITATION = 133; private static final int ICMP6_ROUTER_ADVERTISEMENT = 134; private static final int ICMP6_NEIGHBOR_SOLICITATION = 135; private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136; // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2; Loading Loading @@ -258,9 +252,26 @@ public class ApfFilter { private long mUniqueCounter; @GuardedBy("this") private boolean mMulticastFilter; @GuardedBy("this") private boolean mInDozeMode; private final boolean mDrop802_3Frames; private final int[] mEthTypeBlackList; // Detects doze mode state transitions. private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); final boolean deviceIdle = powerManager.isDeviceIdleMode(); setDozeMode(deviceIdle); } } }; private final Context mContext; // Our IPv4 address, if we have just one, otherwise null. @GuardedBy("this") private byte[] mIPv4Address; Loading @@ -269,13 +280,14 @@ public class ApfFilter { private int mIPv4PrefixLength; @VisibleForTesting ApfFilter(ApfConfiguration config, InterfaceParams ifParams, ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams, IpClient.Callback ipClientCallback, IpConnectivityLog log) { mApfCapabilities = config.apfCapabilities; mIpClientCallback = ipClientCallback; mInterfaceParams = ifParams; mMulticastFilter = config.multicastFilter; mDrop802_3Frames = config.ieee802_3Filter; mContext = context; // Now fill the black list from the passed array mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList); Loading @@ -284,6 +296,10 @@ public class ApfFilter { // TODO: ApfFilter should not generate programs until IpClient sends provisioning success. maybeStartFilter(); // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter. mContext.registerReceiver(mDeviceIdleReceiver, new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); } private void log(String s) { Loading Loading @@ -522,7 +538,7 @@ public class ApfFilter { // to our packet socket. b/29586253 if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 || getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 || getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMP6_ROUTER_ADVERTISEMENT) { getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) { throw new InvalidRaException("Not an ICMP6 router advertisement"); } Loading Loading @@ -889,8 +905,9 @@ public class ApfFilter { private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException { // Here's a basic summary of what the IPv6 filter program does: // // if it's not ICMPv6: // if it's multicast and we're dropping multicast: // if we're dropping multicast // if it's not IPCMv6 or it's ICMPv6 but we're in doze mode: // if it's multicast: // drop // pass // if it's ICMPv6 RS to any: Loading @@ -902,28 +919,44 @@ public class ApfFilter { // Drop multicast if the multicast filter is enabled. if (mMulticastFilter) { // Don't touch ICMPv6 multicast here, we deal with it in more detail later. String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter"; gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel); final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter"; final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast"; // While in doze mode, drop ICMPv6 multicast pings, let the others pass. // While awake, let all ICMPv6 multicasts through. if (mInDozeMode) { // Not ICMPv6? -> Proceed to multicast filtering gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel); // Drop all other packets sent to ff00::/8. // ICMPv6 but not ECHO? -> Skip the multicast filter. // (ICMPv6 ECHO requests will go through the multicast filter below). gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel); } else { gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel); } // Drop all other packets sent to ff00::/8 (multicast prefix). gen.defineLabel(dropAllIPv6MulticastsLabel); gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET); gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL); // Not multicast and not ICMPv6. Pass. // Not multicast. Pass. gen.addJump(gen.PASS_LABEL); gen.defineLabel(skipIpv6MulticastFilterLabel); gen.defineLabel(skipIPv6MulticastFilterLabel); } else { // If not ICMPv6, pass. gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL); } // If we got this far, the packet is ICMPv6. Drop some specific types. // Add unsolicited multicast neighbor announcements filter String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA"; gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); // Drop all router solicitations (b/32833400) gen.addJumpIfR0Equals(ICMP6_ROUTER_SOLICITATION, gen.DROP_LABEL); gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL); // If not neighbor announcements, skip filter. gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel); gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel); // If to ff02::1, drop. // TODO: Drop only if they don't contain the address of on-link neighbours. gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); Loading Loading @@ -1168,9 +1201,9 @@ public class ApfFilter { * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet * filtering using APF programs. */ public static ApfFilter maybeCreate(ApfConfiguration config, public static ApfFilter maybeCreate(Context context, ApfConfiguration config, InterfaceParams ifParams, IpClient.Callback ipClientCallback) { if (config == null || ifParams == null) return null; if (context == null || config == null || ifParams == null) return null; ApfCapabilities apfCapabilities = config.apfCapabilities; if (apfCapabilities == null) return null; if (apfCapabilities.apfVersionSupported == 0) return null; Loading @@ -1187,7 +1220,8 @@ public class ApfFilter { Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported); return null; } return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog()); return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog()); } public synchronized void shutdown() { Loading @@ -1197,12 +1231,11 @@ public class ApfFilter { mReceiveThread = null; } mRas.clear(); mContext.unregisterReceiver(mDeviceIdleReceiver); } public synchronized void setMulticastFilter(boolean isEnabled) { if (mMulticastFilter == isEnabled) { return; } if (mMulticastFilter == isEnabled) return; mMulticastFilter = isEnabled; if (!isEnabled) { mNumProgramUpdatesAllowingMulticast++; Loading @@ -1210,6 +1243,13 @@ public class ApfFilter { installNewProgramLocked(); } @VisibleForTesting public synchronized void setDozeMode(boolean isEnabled) { if (mInDozeMode == isEnabled) return; mInDozeMode = isEnabled; installNewProgramLocked(); } /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */ private static LinkAddress findIPv4LinkAddress(LinkProperties lp) { LinkAddress ipv4Address = null; Loading
services/net/java/android/net/ip/IpClient.java +1 −1 Original line number Diff line number Diff line Loading @@ -1490,7 +1490,7 @@ public class IpClient extends StateMachine { mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames); apfConfig.ethTypeBlackList = mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList); mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback); mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback); // TODO: investigate the effects of any multicast filtering racing/interfering with the // rest of this IP configuration startup. if (mApfFilter == null) { Loading
services/net/java/android/net/util/NetworkConstants.java +2 −1 Original line number Diff line number Diff line Loading @@ -136,6 +136,8 @@ public final class NetworkConstants { * - https://tools.ietf.org/html/rfc4861 */ public static final int ICMPV6_HEADER_MIN_LEN = 4; public static final int ICMPV6_ECHO_REQUEST_TYPE = 128; public static final int ICMPV6_ECHO_REPLY_TYPE = 129; public static final int ICMPV6_ROUTER_SOLICITATION = 133; public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134; public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; Loading @@ -147,7 +149,6 @@ public final class NetworkConstants { public static final int ICMPV6_ND_OPTION_TLLA = 2; public static final int ICMPV6_ND_OPTION_MTU = 5; public static final int ICMPV6_ECHO_REQUEST_TYPE = 128; /** * UDP constants. Loading
tests/net/java/android/net/apf/ApfTest.java +68 −31 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.net.apf; import static android.net.util.NetworkConstants.*; import static android.system.OsConstants.*; import static com.android.internal.util.BitUtils.bytesToBEInt; import static com.android.internal.util.BitUtils.put; Loading @@ -26,6 +27,7 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkUtils; Loading Loading @@ -82,6 +84,7 @@ public class ApfTest { private static final int TIMEOUT_MS = 500; @Mock IpConnectivityLog mLog; @Mock Context mContext; @Before public void setUp() throws Exception { Loading Loading @@ -633,9 +636,9 @@ public class ApfTest { private FileDescriptor mWriteSocket; private final long mFixedTimeMs = SystemClock.elapsedRealtime(); public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback, IpConnectivityLog log) throws Exception { super(config, InterfaceParams.getByName("lo"), ipManagerCallback, log); public TestApfFilter(Context context, ApfConfiguration config, IpManager.Callback ipManagerCallback, IpConnectivityLog log) throws Exception { super(context, config, InterfaceParams.getByName("lo"), ipManagerCallback, log); } // Pretend an RA packet has been received and show it to ApfFilter. Loading Loading @@ -757,6 +760,17 @@ public class ApfTest { private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2}; private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0}; // Helper to initialize a default apfFilter. private ApfFilter setupApfFilter(IpManager.Callback ipManagerCallback, ApfConfiguration config) throws Exception { LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); LinkProperties lp = new LinkProperties(); lp.addLinkAddress(link); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); return apfFilter; } @Test public void testApfFilterIPv4() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); Loading @@ -766,7 +780,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); byte[] program = ipManagerCallback.getApfProgram(); Loading Loading @@ -818,7 +832,7 @@ public class ApfTest { public void testApfFilterIPv6() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfConfiguration config = getDefaultConfig(); TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); byte[] program = ipManagerCallback.getApfProgram(); // Verify empty IPv6 packet is passed Loading Loading @@ -861,7 +875,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); byte[] program = ipManagerCallback.getApfProgram(); Loading Loading @@ -925,7 +939,7 @@ public class ApfTest { apfFilter.shutdown(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); program = ipManagerCallback.getApfProgram(); assertDrop(program, mcastv4packet.array()); Loading @@ -941,16 +955,47 @@ public class ApfTest { } @Test public void testApfFilter802_3() throws Exception { public void testApfFilterMulticastPingWhileDozing() 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 = setupApfFilter(ipManagerCallback, getDefaultConfig()); ApfConfiguration config = getDefaultConfig(); TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); // Construct a multicast ICMPv6 ECHO request. final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb}; ByteBuffer packet = ByteBuffer.wrap(new byte[100]); packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6); packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE); put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr); // Normally, we let multicast pings alone... assertPass(ipManagerCallback.getApfProgram(), packet.array()); // ...and even while dozing... apfFilter.setDozeMode(true); assertPass(ipManagerCallback.getApfProgram(), packet.array()); // ...but when the multicast filter is also enabled, drop the multicast pings to save power. apfFilter.setMulticastFilter(true); assertDrop(ipManagerCallback.getApfProgram(), packet.array()); // However, we should still let through all other ICMPv6 types. ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone()); raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT); assertPass(ipManagerCallback.getApfProgram(), raPacket.array()); // Now wake up from doze mode to ensure that we no longer drop the packets. // (The multicast filter is still enabled at this point). apfFilter.setDozeMode(false); assertPass(ipManagerCallback.getApfProgram(), packet.array()); apfFilter.shutdown(); } @Test public void testApfFilter802_3() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfConfiguration config = getDefaultConfig(); ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config); byte[] program = ipManagerCallback.getApfProgram(); // Verify empty packet of 100 zero bytes is passed Loading @@ -970,8 +1015,7 @@ public class ApfTest { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ieee802_3Filter = DROP_802_3_FRAMES; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); apfFilter = setupApfFilter(ipManagerCallback, config); program = ipManagerCallback.getApfProgram(); // Verify that IEEE802.3 frame is dropped Loading @@ -992,18 +1036,13 @@ public class ApfTest { @Test public void testApfFilterEthTypeBL() throws Exception { MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); LinkProperties lp = new LinkProperties(); lp.addLinkAddress(link); final int[] emptyBlackList = {}; final int[] ipv4BlackList = {ETH_P_IP}; final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6}; MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback(); ApfConfiguration config = getDefaultConfig(); TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config); byte[] program = ipManagerCallback.getApfProgram(); // Verify empty packet of 100 zero bytes is passed Loading @@ -1023,8 +1062,7 @@ public class ApfTest { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ethTypeBlackList = ipv4BlackList; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); apfFilter = setupApfFilter(ipManagerCallback, config); program = ipManagerCallback.getApfProgram(); // Verify that IPv4 frame will be dropped Loading @@ -1039,8 +1077,7 @@ public class ApfTest { ipManagerCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ethTypeBlackList = ipv4Ipv6BlackList; apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); apfFilter.setLinkProperties(lp); apfFilter = setupApfFilter(ipManagerCallback, config); program = ipManagerCallback.getApfProgram(); // Verify that IPv4 frame will be dropped Loading Loading @@ -1081,7 +1118,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); // Verify initially ARP request filter is off, and GARP filter is on. verifyArpFilter(ipManagerCallback.getApfProgram(), PASS); Loading Loading @@ -1205,7 +1242,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog); byte[] program = ipManagerCallback.getApfProgram(); final int ROUTER_LIFETIME = 1000; Loading Loading @@ -1351,7 +1388,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); for (int i = 0; i < 1000; i++) { byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; r.nextBytes(packet); Loading @@ -1372,7 +1409,7 @@ public class ApfTest { ApfConfiguration config = getDefaultConfig(); config.multicastFilter = DROP_MULTICAST; config.ieee802_3Filter = DROP_802_3_FRAMES; TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog); TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); for (int i = 0; i < 1000; i++) { byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; r.nextBytes(packet); Loading