Merge TP1A.220425.001
Change-Id: Ida0a124540b8b5a19dc215b3a648dd67ace78969
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);
+ }
}