Snap for 8710048 from d1c5545015fce4cf098349a0640a7b14a53b8ea2 to tm-release

Change-Id: I6594e006cd06ffbf239eb8533aab47e9789d604e
diff --git a/common/moduleutils/src/android/net/ip/NetlinkMonitor.java b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java
index 4efc480..e58b44e 100644
--- a/common/moduleutils/src/android/net/ip/NetlinkMonitor.java
+++ b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java
@@ -18,6 +18,7 @@
 
 import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
 import static android.system.OsConstants.AF_NETLINK;
+import static android.system.OsConstants.ENOBUFS;
 import static android.system.OsConstants.SOCK_DGRAM;
 import static android.system.OsConstants.SOCK_NONBLOCK;
 import static android.system.OsConstants.SOL_SOCKET;
@@ -101,7 +102,11 @@
         try {
             fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily);
             if (mSockRcvbufSize != DEFAULT_SOCKET_RECV_BUFSIZE) {
-                Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, mSockRcvbufSize);
+                try {
+                    Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, mSockRcvbufSize);
+                } catch (ErrnoException e) {
+                    Log.wtf(mTag, "Failed to set SO_RCVBUF to " + mSockRcvbufSize, e);
+                }
             }
             Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups));
             NetlinkSocket.connectToKernel(fd);
@@ -152,6 +157,27 @@
         mLog.e(msg, e);
     }
 
+    // Ignoring ENOBUFS may miss any important netlink messages, there are some messages which
+    // cannot be recovered by dumping current state once missed since kernel doesn't keep state
+    // for it. In addition, dumping current state will not result in any RTM_DELxxx messages, so
+    // reconstructing current state from a dump will be difficult. However, for those netlink
+    // messages don't cause any state changes, e.g. RTM_NEWLINK with current link state, maybe
+    // it's okay to ignore them, because these netlink messages won't cause any changes on the
+    // LinkProperties. Given the above trade-offs, try to ignore ENOBUFS and that's similar to
+    // what netd does today.
+    //
+    // TODO: log metrics when ENOBUFS occurs, or even force a disconnect, it will help see how
+    // often this error occurs on fields with the associated socket receive buffer size.
+    @Override
+    protected boolean handleReadError(ErrnoException e) {
+        logError("readPacket error: ", e);
+        if (e.errno == ENOBUFS) {
+            Log.wtf(mTag, "Errno: ENOBUFS");
+            return false;
+        }
+        return true;
+    }
+
     // TODO: move NetworkStackUtils to frameworks/libs/net for NetworkStackUtils#closeSocketQuietly.
     private void closeSocketQuietly(FileDescriptor fd) {
         try {
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index 407a7af..bebe7c9 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -45,6 +45,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.net.module.util.InterfaceParams;
 import com.android.net.module.util.netlink.NduseroptMessage;
 import com.android.net.module.util.netlink.NetlinkConstants;
@@ -162,6 +163,13 @@
     protected static final String CLAT_PREFIX = "v4-";
     private static final boolean DBG = true;
 
+    // The default socket receive buffer size in bytes(4MB). If too many netlink messages are
+    // sent too quickly, those messages can overflow the socket receive buffer. Set a large-enough
+    // recv buffer size to avoid the ENOBUFS as much as possible.
+    @VisibleForTesting
+    static final String CONFIG_SOCKET_RECV_BUFSIZE = "ipclient_netlink_sock_recv_buf_size";
+    private static final int SOCKET_RECV_BUFSIZE = 4 * 1024 * 1024;
+
     public IpClientLinkObserver(Context context, Handler h, String iface, Callback callback,
             Configuration config, SharedLog log, IpClient.Dependencies deps) {
         mContext = context;
@@ -211,6 +219,15 @@
                 isAtLeastT() /* default value */);
     }
 
+    private int getSocketReceiveBufferSize() {
+        final int size = mDependencies.getDeviceConfigPropertyInt(CONFIG_SOCKET_RECV_BUFSIZE,
+                SOCKET_RECV_BUFSIZE /* default value */);
+        if (size < 0) {
+            throw new IllegalArgumentException("Invalid SO_RCVBUF " + size);
+        }
+        return size;
+    }
+
     @Override
     public void onInterfaceAdded(String iface) {
         if (isNetlinkEventParsingEnabled()) return;
@@ -406,11 +423,12 @@
         MyNetlinkMonitor(Handler h, SharedLog log, String tag) {
             super(h, log, tag, OsConstants.NETLINK_ROUTE,
                     !isNetlinkEventParsingEnabled()
-                    ? NetlinkConstants.RTMGRP_ND_USEROPT
-                    : (NetlinkConstants.RTMGRP_ND_USEROPT | NetlinkConstants.RTMGRP_LINK
-                            | NetlinkConstants.RTMGRP_IPV4_IFADDR
-                            | NetlinkConstants.RTMGRP_IPV6_IFADDR
-                            | NetlinkConstants.RTMGRP_IPV6_ROUTE));
+                        ? NetlinkConstants.RTMGRP_ND_USEROPT
+                        : (NetlinkConstants.RTMGRP_ND_USEROPT | NetlinkConstants.RTMGRP_LINK
+                                | NetlinkConstants.RTMGRP_IPV4_IFADDR
+                                | NetlinkConstants.RTMGRP_IPV6_IFADDR
+                                | NetlinkConstants.RTMGRP_IPV6_ROUTE),
+                    getSocketReceiveBufferSize());
 
             mHandler = h;
         }
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
index 78d0bca..2f3987c 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -29,6 +29,7 @@
 import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS;
 import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable;
 import static android.net.ip.IpClientLinkObserver.CLAT_PREFIX;
+import static android.net.ip.IpClientLinkObserver.CONFIG_SOCKET_RECV_BUFSIZE;
 import static android.net.ip.IpReachabilityMonitor.MIN_NUD_SOLICIT_NUM;
 import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM;
 import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt;
@@ -185,6 +186,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestName;
@@ -632,6 +634,11 @@
         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_PROBE_MAX_MS, 20);
         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_FIRST_ANNOUNCE_DELAY_MS, 10);
         mDependencies.setDeviceConfigProperty(DhcpClient.ARP_ANNOUNCE_INTERVAL_MS, 10);
+
+        // Set the initial netlink socket receive buffer size to a minimum of 10KB to ensure test
+        // cases are still working, meanwhile in order to easily overflow the receive buffer by
+        // sending as few RAs as possible for test case where it's used to verify ENOBUFS.
+        mDependencies.setDeviceConfigProperty(CONFIG_SOCKET_RECV_BUFSIZE, 10 * 1024);
     }
 
     private void awaitIpClientShutdown() throws Exception {
@@ -3784,4 +3791,47 @@
         removeTestInterface(clatIface.getFileDescriptor().getFileDescriptor());
         verify(mCb, timeout(TEST_TIMEOUT_MS)).setNeighborDiscoveryOffload(true);
     }
+
+    @Ignore("TODO: temporarily ignore tests until prebuilts are updated")
+    @Test @SignatureRequiredTest(reason = "requires mock callback object")
+    public void testNetlinkSocketReceiveENOBUFS() throws Exception {
+        if (!mIsNetlinkEventParseEnabled) return;
+
+        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+                .withoutIPv4()
+                .build();
+        startIpClientProvisioning(config);
+        doIpv6OnlyProvisioning();
+        HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+
+        // Block IpClient handler.
+        final CountDownLatch latch = new CountDownLatch(1);
+        mIpc.getHandler().post(() -> {
+            try {
+                latch.await(10_000L /* 10s */, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        });
+
+        // Send large amount of RAs to overflow the netlink socket receive buffer.
+        for (int i = 0; i < 100; i++) {
+            sendBasicRouterAdvertisement(false /* waitRs */);
+        }
+
+        // Unblock the IpClient handler.
+        latch.countDown();
+        HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS);
+
+        reset(mCb);
+
+        // Send RA with 0 router lifetime to see if IpClient can see the loss of IPv6 default route.
+        // Due to ignoring the ENOBUFS and wait until handler gets idle, IpClient should be still
+        // able to see the RA with 0 router lifetime and the IPv6 default route will be removed.
+        sendRouterAdvertisementWithZeroLifetime();
+        final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
+        verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
+        final LinkProperties lp = captor.getValue();
+        assertFalse(lp.hasIpv6DefaultRoute());
+    }
 }