Add a feature flag to control if accepting the IPv6 link-local DNS.

The default feature flag is true which doesn't change the current
behavior, just providing a function method which allows us to push
a different flag value from g3 and does not accpet the IPv6 link-local
dns, then we can do A/B testing on beta devices to see if the fix does
work as expected.

Bug: 163492391
Test: atest NetworkStackIntegrationTests
Test: atest NetworkStackRootTests
(cherry picked from https://android-review.googlesource.com/q/commit:eedf05b688aa3d910089b2305312dd488e7150d8)
Merged-In: I405d023711ad0c4c71541675eeda8cd2582e1bdc
Change-Id: I405d023711ad0c4c71541675eeda8cd2582e1bdc
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java
index f1fb3da..052cfae 100644
--- a/src/android/net/ip/IpClientLinkObserver.java
+++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -28,6 +28,7 @@
 import static com.android.net.module.util.netlink.NetlinkConstants.RTPROT_KERNEL;
 import static com.android.net.module.util.netlink.NetlinkConstants.RTPROT_RA;
 import static com.android.net.module.util.netlink.NetlinkConstants.RT_SCOPE_UNIVERSE;
+import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_ACCEPT_IPV6_LINK_LOCAL_DNS_VERSION;
 import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_PARSE_NETLINK_EVENTS_VERSION;
 
 import android.app.AlarmManager;
@@ -203,6 +204,11 @@
         mHandler.post(mNetlinkMonitor::stop);
     }
 
+    private boolean isIpv6LinkLocalDnsAccepted() {
+        return mDependencies.isFeatureEnabled(mContext,
+                IPCLIENT_ACCEPT_IPV6_LINK_LOCAL_DNS_VERSION, true /* default value */);
+    }
+
     private void maybeLog(String operation, String iface, LinkAddress address) {
         if (DBG) {
             Log.d(mTag, operation + ": " + address + " on " + iface
@@ -540,8 +546,11 @@
             if (!mNetlinkEventParsingEnabled) return;
             final String[] addresses = new String[opt.servers.length];
             for (int i = 0; i < opt.servers.length; i++) {
-                final Inet6Address addr = opt.servers[i];
-                addresses[i] = InetAddressUtils.withScopeId(addr, mIfindex).getHostAddress();
+                final Inet6Address addr = InetAddressUtils.withScopeId(opt.servers[i], mIfindex);
+                if (!addr.isLinkLocalAddress()
+                        || (addr.isLinkLocalAddress() && isIpv6LinkLocalDnsAccepted())) {
+                    addresses[i] = addr.getHostAddress();
+                }
             }
             updateInterfaceDnsServerInfo(opt.header.lifetime, addresses);
         }
diff --git a/src/com/android/networkstack/util/NetworkStackUtils.java b/src/com/android/networkstack/util/NetworkStackUtils.java
index 147c33a..e87b824 100755
--- a/src/com/android/networkstack/util/NetworkStackUtils.java
+++ b/src/com/android/networkstack/util/NetworkStackUtils.java
@@ -240,6 +240,14 @@
             "ipclient_parse_netlink_events_version";
 
     /**
+     * Experiment flag to check if an on-link IPv6 link local DNS is acceptable. The default flag
+     * value is true, just add this flag for A/B testing to see if this fix works as expected via
+     * experiment rollout.
+     */
+    public static final String IPCLIENT_ACCEPT_IPV6_LINK_LOCAL_DNS_VERSION =
+            "ipclient_accept_ipv6_link_local_dns_version";
+
+    /**
      * Experiment flag to disable accept_ra parameter when IPv6 provisioning loss happens due to
      * the default route has gone.
      */
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
index 2fb5ac8..69e7b69 100644
--- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
+++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -2046,12 +2046,14 @@
         reset(mCb);
     }
 
-    @Test
-    public void testRaRdnss_Ipv6LinkLocalDns() throws Exception {
+    private void runRaRdnssIpv6LinkLocalDnsTest(boolean isIpv6LinkLocalDnsAccepted)
+            throws Exception {
         ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                 .withoutIpReachabilityMonitor()
                 .withoutIPv4()
                 .build();
+        setFeatureEnabled(NetworkStackUtils.IPCLIENT_ACCEPT_IPV6_LINK_LOCAL_DNS_VERSION,
+                isIpv6LinkLocalDnsAccepted /* default value */);
         startIpClientProvisioning(config);
 
         final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64");
@@ -2063,7 +2065,11 @@
 
         waitForRouterSolicitation();
         mPacketReader.sendResponse(ra);
+    }
 
+    @Test
+    public void testRaRdnss_Ipv6LinkLocalDns() throws Exception {
+        runRaRdnssIpv6LinkLocalDnsTest(true /* isIpv6LinkLocalDnsAccepted */);
         final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
         verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
         final LinkProperties lp = captor.getValue();
@@ -2073,6 +2079,20 @@
         assertTrue(lp.isIpv6Provisioned());
     }
 
+    @Test
+    public void testRaRdnss_disableIpv6LinkLocalDns() throws Exception {
+        // Only run the test when the flag of parsing netlink events is enabled, feature flag
+        // "ipclient_accept_ipv6_link_local_dns" doesn't affect the legacy code.
+        assumeTrue(mIsNetlinkEventParseEnabled);
+        runRaRdnssIpv6LinkLocalDnsTest(false /* isIpv6LinkLocalDnsAccepted */);
+        verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(argThat(lp -> {
+            return lp.hasGlobalIpv6Address()
+                    && lp.hasIpv6DefaultRoute()
+                    && !lp.hasIpv6DnsServer();
+        }));
+        verify(mCb, never()).onProvisioningSuccess(any());
+    }
+
     private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception {
         inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(
                 argThat(lp -> Objects.equals(expected, lp.getNat64Prefix())));