Merge cherrypicks of [17911860] into sparse-8547507-L12600000954433473. Change-Id: If98b99824c1a441e6ea4332481459e6a4796f061
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index 3ed6c0c..4ada67f 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java
@@ -493,10 +493,6 @@ private static final boolean NO_CALLBACKS = false; private static final boolean SEND_CALLBACKS = true; - // This must match the interface prefix in clatd.c. - // TODO: Revert this hack once IpClient and Nat464Xlat work in concert. - private static final String CLAT_PREFIX = "v4-"; - private static final int IMMEDIATE_FAILURE_DURATION = 0; private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1; @@ -544,7 +540,6 @@ private final String mTag; private final Context mContext; private final String mInterfaceName; - private final String mClatInterfaceName; @VisibleForTesting protected final IpClientCallbacksWrapper mCallback; private final Dependencies mDependencies; @@ -712,7 +707,6 @@ mContext = context; mInterfaceName = ifName; - mClatInterfaceName = CLAT_PREFIX + ifName; mDependencies = deps; mMetricsLog = deps.getIpConnectivityLog(); mNetworkQuirkMetrics = deps.getNetworkQuirkMetrics(); @@ -762,44 +756,19 @@ updateGratuitousNaTargetSet(targetIp, false /* remove address */); }); } + + @Override + public void onClatInterfaceStateUpdate(boolean add) { + // TODO: when clat interface was removed, consider sending a message to + // the IpClient main StateMachine thread, in case "NDO enabled" state + // becomes tied to more things that 464xlat operation. + getHandler().post(() -> { + mCallback.setNeighborDiscoveryOffload(add ? false : true); + }); + } }, - config, mLog, mDependencies) { - @Override - public void onInterfaceAdded(String iface) { - super.onInterfaceAdded(iface); - if (mClatInterfaceName.equals(iface)) { - mCallback.setNeighborDiscoveryOffload(false); - } else if (!mInterfaceName.equals(iface)) { - return; - } - - final String msg = "interfaceAdded(" + iface + ")"; - logMsg(msg); - } - - @Override - public void onInterfaceRemoved(String iface) { - super.onInterfaceRemoved(iface); - // TODO: Also observe mInterfaceName going down and take some - // kind of appropriate action. - if (mClatInterfaceName.equals(iface)) { - // TODO: consider sending a message to the IpClient main - // StateMachine thread, in case "NDO enabled" state becomes - // tied to more things that 464xlat operation. - mCallback.setNeighborDiscoveryOffload(true); - } else if (!mInterfaceName.equals(iface)) { - return; - } - - final String msg = "interfaceRemoved(" + iface + ")"; - logMsg(msg); - } - - private void logMsg(String msg) { - Log.d(mTag, msg); - getHandler().post(() -> mLog.log("OBSERVED " + msg)); - } - }; + config, mLog, mDependencies + ); mLinkProperties = new LinkProperties(); mLinkProperties.setInterfaceName(mInterfaceName);
diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java index 8a5ed2e..b1dbabd 100644 --- a/src/android/net/ip/IpClientLinkObserver.java +++ b/src/android/net/ip/IpClientLinkObserver.java
@@ -42,6 +42,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.netlink.NduseroptMessage; @@ -121,6 +122,14 @@ * @param addr The removed IPv6 address. */ void onIpv6AddressRemoved(Inet6Address addr); + + /** + * Called when the clat interface was added/removed. + * + * @param add True: clat interface was added. + * False: clat interface was removed. + */ + void onClatInterfaceStateUpdate(boolean add); } /** Configuration parameters for IpClientLinkObserver. */ @@ -142,15 +151,21 @@ private final Configuration mConfig; private final Handler mHandler; private final IpClient.Dependencies mDependencies; - + private final String mClatInterfaceName; private final MyNetlinkMonitor mNetlinkMonitor; + private boolean mClatInterfaceExists; + + // This must match the interface prefix in clatd.c. + // TODO: Revert this hack once IpClient and Nat464Xlat work in concert. + protected static final String CLAT_PREFIX = "v4-"; private static final boolean DBG = false; public IpClientLinkObserver(Context context, Handler h, String iface, Callback callback, Configuration config, SharedLog log, IpClient.Dependencies deps) { mContext = context; mInterfaceName = iface; + mClatInterfaceName = CLAT_PREFIX + iface; mTag = "NetlinkTracker/" + mInterfaceName; mCallback = callback; mLinkProperties = new LinkProperties(); @@ -192,19 +207,22 @@ } @Override + public void onInterfaceAdded(String iface) { + if (isNetlinkEventParsingEnabled()) return; + maybeLog("interfaceAdded", iface); + if (mClatInterfaceName.equals(iface)) { + mCallback.onClatInterfaceStateUpdate(true /* add interface */); + } + } + + @Override public void onInterfaceRemoved(String iface) { + if (isNetlinkEventParsingEnabled()) return; maybeLog("interfaceRemoved", iface); - if (mInterfaceName.equals(iface)) { - // Our interface was removed. Clear our LinkProperties and tell our owner that they are - // now empty. Note that from the moment that the interface is removed, any further - // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd - // code that parses them will not be able to resolve the ifindex to an interface name. - final boolean linkState; - synchronized (this) { - clearLinkProperties(); - linkState = getInterfaceLinkStateLocked(); - } - mCallback.update(linkState); + if (mClatInterfaceName.equals(iface)) { + mCallback.onClatInterfaceStateUpdate(false /* remove interface */); + } else if (mInterfaceName.equals(iface)) { + updateInterfaceRemoved(); } } @@ -308,6 +326,19 @@ } } + private void updateInterfaceRemoved() { + // Our interface was removed. Clear our LinkProperties and tell our owner that they are + // now empty. Note that from the moment that the interface is removed, any further + // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd + // code that parses them will not be able to resolve the ifindex to an interface name. + final boolean linkState; + synchronized (this) { + clearLinkProperties(); + linkState = getInterfaceLinkStateLocked(); + } + mCallback.update(linkState); + } + /** * Returns a copy of this object's LinkProperties. */ @@ -503,14 +534,45 @@ } } + private void updateClatInterfaceLinkState(@NonNull final StructIfinfoMsg ifinfoMsg, + @Nullable final String ifname, short nlMsgType) { + switch (nlMsgType) { + case NetlinkConstants.RTM_NEWLINK: + if (mClatInterfaceExists) break; + maybeLog("clatInterfaceAdded", ifname); + mCallback.onClatInterfaceStateUpdate(true /* add interface */); + mClatInterfaceExists = true; + break; + + case NetlinkConstants.RTM_DELLINK: + if (!mClatInterfaceExists) break; + maybeLog("clatInterfaceRemoved", ifname); + mCallback.onClatInterfaceStateUpdate(false /* remove interface */); + mClatInterfaceExists = false; + break; + + default: + Log.e(mTag, "unsupported rtnetlink link msg type " + nlMsgType); + break; + } + } + private void processRtNetlinkLinkMessage(RtNetlinkLinkMessage msg) { if (!isNetlinkEventParsingEnabled()) return; + // Check if receiving netlink link state update for clat interface. + final String ifname = msg.getInterfaceName(); + final short nlMsgType = msg.getHeader().nlmsg_type; final StructIfinfoMsg ifinfoMsg = msg.getIfinfoHeader(); + if (mClatInterfaceName.equals(ifname)) { + updateClatInterfaceLinkState(ifinfoMsg, ifname, nlMsgType); + return; + } + if (ifinfoMsg.family != AF_UNSPEC || ifinfoMsg.index != mIfindex) return; if ((ifinfoMsg.flags & IFF_LOOPBACK) != 0) return; - switch (msg.getHeader().nlmsg_type) { + switch (nlMsgType) { case NetlinkConstants.RTM_NEWLINK: final boolean state = (ifinfoMsg.flags & IFF_LOWER_UP) != 0; maybeLog("interfaceLinkStateChanged", "ifindex " + mIfindex @@ -519,10 +581,12 @@ break; case NetlinkConstants.RTM_DELLINK: + maybeLog("interfaceRemoved", ifname); + updateInterfaceRemoved(); break; default: - Log.e(mTag, "Unknown rtnetlink link msg type " + msg.getHeader().nlmsg_type); + Log.e(mTag, "Unknown rtnetlink link msg type " + nlMsgType); break; } }
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp index 36c6162..9984a54 100644 --- a/tests/integration/Android.bp +++ b/tests/integration/Android.bp
@@ -34,6 +34,7 @@ java_defaults { name: "NetworkStackIntegrationTestsDefaults", + defaults: ["framework-connectivity-test-defaults"], srcs: [ "src/**/*.java", "src/**/*.kt",
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java index f423862..78d0bca 100644 --- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -28,6 +28,7 @@ import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; 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.IpReachabilityMonitor.MIN_NUD_SOLICIT_NUM; import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM; import static android.net.ip.IpReachabilityMonitor.nudEventTypeToInt; @@ -670,6 +671,16 @@ mHandler.post(() -> mPacketReader.start()); } + private TestNetworkInterface setUpClatInterface(@NonNull String baseIface) throws Exception { + final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + final TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () -> { + final TestNetworkManager tnm = + inst.getContext().getSystemService(TestNetworkManager.class); + return tnm.createTapInterface(false /* bringUp */, CLAT_PREFIX + baseIface); + }); + return iface; + } + private void teardownTapInterface() { if (mPacketReader != null) { mHandler.post(() -> mPacketReader.stop()); // Also closes the socket @@ -1077,7 +1088,7 @@ return getNextDhcpPacket(); } - private void removeTapInterface(final FileDescriptor fd) { + private void removeTestInterface(final FileDescriptor fd) { try { Os.close(fd); } catch (ErrnoException e) { @@ -1109,7 +1120,7 @@ } private void doRestoreInitialMtuTest(final boolean shouldChangeMtu, - final boolean shouldRemoveTapInterface) throws Exception { + final boolean shouldRemoveTestInterface) throws Exception { final long currentTime = System.currentTimeMillis(); int mtu = TEST_DEFAULT_MTU; @@ -1132,11 +1143,11 @@ // empty LinkProperties changes instead of one. reset(mCb); - if (shouldRemoveTapInterface) removeTapInterface(mTapFd); + if (shouldRemoveTestInterface) removeTestInterface(mTapFd); try { mIpc.shutdown(); awaitIpClientShutdown(); - if (shouldRemoveTapInterface) { + if (shouldRemoveTestInterface) { verify(mNetd, never()).interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); } else { // Verify that MTU indeed has been restored or not. @@ -1504,12 +1515,12 @@ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testRestoreInitialInterfaceMtu() throws Exception { - doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); + doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTestInterface */); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testRestoreInitialInterfaceMtu_WithoutMtuChange() throws Exception { - doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); + doRestoreInitialMtuTest(false /* shouldChangeMtu */, false /* shouldRemoveTestInterface */); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") @@ -1517,19 +1528,19 @@ doThrow(new RemoteException("NetdNativeService::interfaceSetMtu")).when(mNetd) .interfaceSetMtu(mIfaceName, TEST_DEFAULT_MTU); - doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTapInterface */); + doRestoreInitialMtuTest(true /* shouldChangeMtu */, false /* shouldRemoveTestInterface */); assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), TEST_MIN_MTU); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStopping() throws Exception { - doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTapInterface */); + doRestoreInitialMtuTest(true /* shouldChangeMtu */, true /* shouldRemoveTestInterface */); } @Test public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning() throws Exception { - removeTapInterface(mTapFd); + removeTestInterface(mTapFd); ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() .withoutIPv6() @@ -1588,7 +1599,7 @@ // Intend to remove the tap interface and force IpClient throw provisioning failure // due to that interface is not found. - removeTapInterface(mTapFd); + removeTestInterface(mTapFd); assertNull(InterfaceParams.getByName(mIfaceName)); startIpClientProvisioning(config); @@ -3752,4 +3763,25 @@ .build() ); } + + @Test + public void testIpClientLinkObserver_onClatInterfaceStateUpdate() throws Exception { + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIPv4() + .build(); + startIpClientProvisioning(config); + doIpv6OnlyProvisioning(); + + reset(mCb); + + // Add the clat interface and check the callback. + final TestNetworkInterface clatIface = setUpClatInterface(mIfaceName); + assertNotNull(clatIface); + assertTrue(clatIface.getInterfaceName().equals(CLAT_PREFIX + mIfaceName)); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setNeighborDiscoveryOffload(false); + + // Remove the clat interface and check the callback. + removeTestInterface(clatIface.getFileDescriptor().getFileDescriptor()); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setNeighborDiscoveryOffload(true); + } }