Merge "Translate v4 keepalive packet on clat started network to v6"
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 6ba2033..368860e 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -291,6 +291,18 @@
}
}
+ /**
+ * Construct a new AutomaticOnOffKeepalive from existing AutomaticOnOffKeepalive with a
+ * new KeepaliveInfo.
+ */
+ public AutomaticOnOffKeepalive withKeepaliveInfo(KeepaliveTracker.KeepaliveInfo ki)
+ throws InvalidSocketException {
+ return new AutomaticOnOffKeepalive(
+ ki,
+ mAutomaticOnOffState != STATE_ALWAYS_ON /* autoOnOff */,
+ mUnderpinnedNetwork);
+ }
+
@Override
public String toString() {
return "AutomaticOnOffKeepalive [ "
@@ -470,13 +482,26 @@
* The message is expected to contain a KeepaliveTracker.KeepaliveInfo.
*/
public void handleStartKeepalive(Message message) {
- final AutomaticOnOffKeepalive autoKi = (AutomaticOnOffKeepalive) message.obj;
- final int error = mKeepaliveTracker.handleStartKeepalive(autoKi.mKi);
+ final AutomaticOnOffKeepalive target = (AutomaticOnOffKeepalive) message.obj;
+ final Pair<Integer, KeepaliveTracker.KeepaliveInfo> res =
+ mKeepaliveTracker.handleStartKeepalive(target.mKi);
+ final int error = res.first;
if (error != SUCCESS) {
- mEventLog.log("Failed to start keepalive " + autoKi.mCallback + " on "
- + autoKi.getNetwork() + " with error " + error);
+ mEventLog.log("Failed to start keepalive " + target.mCallback + " on "
+ + target.getNetwork() + " with error " + error);
return;
}
+ // Generate a new auto ki with the started keepalive info.
+ final AutomaticOnOffKeepalive autoKi;
+ try {
+ autoKi = target.withKeepaliveInfo(res.second);
+ // Close the duplicated fd.
+ target.close();
+ } catch (InvalidSocketException e) {
+ Log.wtf(TAG, "Fail to create AutomaticOnOffKeepalive", e);
+ return;
+ }
+
mEventLog.log("Start keepalive " + autoKi.mCallback + " on " + autoKi.getNetwork());
mKeepaliveStatsTracker.onStartKeepalive(
autoKi.getNetwork(),
@@ -506,14 +531,19 @@
* @return SUCCESS if the keepalive is successfully starting and the error reason otherwise.
*/
private int handleResumeKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki) {
- final int error = mKeepaliveTracker.handleStartKeepalive(ki);
+ final Pair<Integer, KeepaliveTracker.KeepaliveInfo> res =
+ mKeepaliveTracker.handleStartKeepalive(ki);
+ final KeepaliveTracker.KeepaliveInfo startedKi = res.second;
+ final int error = res.first;
if (error != SUCCESS) {
- mEventLog.log("Failed to resume keepalive " + ki.mCallback + " on " + ki.mNai
- + " with error " + error);
+ mEventLog.log("Failed to resume keepalive " + startedKi.mCallback + " on "
+ + startedKi.mNai + " with error " + error);
return error;
}
- mKeepaliveStatsTracker.onResumeKeepalive(ki.getNai().network(), ki.getSlot());
- mEventLog.log("Resumed successfully keepalive " + ki.mCallback + " on " + ki.mNai);
+
+ mKeepaliveStatsTracker.onResumeKeepalive(startedKi.getNai().network(), startedKi.getSlot());
+ mEventLog.log("Resumed successfully keepalive " + startedKi.mCallback
+ + " on " + startedKi.mNai);
return SUCCESS;
}
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 76e97e2..125c269 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -54,6 +54,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import android.util.Pair;
import com.android.connectivity.resources.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -62,6 +63,8 @@
import com.android.net.module.util.IpUtils;
import java.io.FileDescriptor;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -292,11 +295,15 @@
private int checkSourceAddress() {
// Check that we have the source address.
- for (InetAddress address : mNai.linkProperties.getAddresses()) {
+ for (InetAddress address : mNai.linkProperties.getAllAddresses()) {
if (address.equals(mPacket.getSrcAddress())) {
return SUCCESS;
}
}
+ // Or the address is the clat source address.
+ if (mPacket.getSrcAddress().equals(mNai.getClatv6SrcAddress())) {
+ return SUCCESS;
+ }
return ERROR_INVALID_IP_ADDRESS;
}
@@ -479,6 +486,15 @@
return new KeepaliveInfo(mCallback, mNai, mPacket, mPid, mUid, mInterval, mType,
fd, mSlot, true /* resumed */);
}
+
+ /**
+ * Construct a new KeepaliveInfo from existing KeepaliveInfo with a new KeepalivePacketData.
+ */
+ public KeepaliveInfo withPacketData(@NonNull KeepalivePacketData packet)
+ throws InvalidSocketException {
+ return new KeepaliveInfo(mCallback, mNai, packet, mPid, mUid, mInterval, mType,
+ mFd, mSlot, mResumed);
+ }
}
void notifyErrorCallback(ISocketKeepaliveCallback cb, int error) {
@@ -512,15 +528,47 @@
* Handle start keepalives with the message.
*
* @param ki the keepalive to start.
- * @return SUCCESS if the keepalive is successfully starting and the error reason otherwise.
+ * @return Pair of (SUCCESS if the keepalive is successfully starting and the error reason
+ * otherwise, the started KeepaliveInfo object)
*/
- public int handleStartKeepalive(KeepaliveInfo ki) {
- NetworkAgentInfo nai = ki.getNai();
+ public Pair<Integer, KeepaliveInfo> handleStartKeepalive(KeepaliveInfo ki) {
+ final KeepaliveInfo newKi;
+ try {
+ newKi = handleUpdateKeepaliveForClat(ki);
+ } catch (InvalidSocketException | InvalidPacketException e) {
+ Log.e(TAG, "Fail to construct keepalive packet");
+ notifyErrorCallback(ki.mCallback, ERROR_INVALID_IP_ADDRESS);
+ // Fail to create new keepalive packet for clat. Return the original keepalive info.
+ return new Pair<>(ERROR_INVALID_IP_ADDRESS, ki);
+ }
+
+ final NetworkAgentInfo nai = newKi.getNai();
// If this was a paused keepalive, then reuse the same slot that was kept for it. Otherwise,
// use the first free slot for this network agent.
- final int slot = NO_KEEPALIVE != ki.mSlot ? ki.mSlot : findFirstFreeSlot(nai);
- mKeepalives.get(nai).put(slot, ki);
- return ki.start(slot);
+ final int slot = NO_KEEPALIVE != newKi.mSlot ? newKi.mSlot : findFirstFreeSlot(nai);
+ mKeepalives.get(nai).put(slot, newKi);
+
+ return new Pair<>(newKi.start(slot), newKi);
+ }
+
+ private KeepaliveInfo handleUpdateKeepaliveForClat(KeepaliveInfo ki)
+ throws InvalidSocketException, InvalidPacketException {
+ // Only try to translate address if the packet source address is the clat's source address.
+ if (!ki.mPacket.getSrcAddress().equals(ki.getNai().getClatv4SrcAddress())) return ki;
+
+ final InetAddress dstAddr = ki.mPacket.getDstAddress();
+ // Do not perform translation for a v6 dst address.
+ if (!(dstAddr instanceof Inet4Address)) return ki;
+
+ final Inet6Address address = ki.getNai().translateV4toClatV6((Inet4Address) dstAddr);
+
+ if (address == null) return ki;
+
+ final int srcPort = ki.mPacket.getSrcPort();
+ final KeepaliveInfo newInfo = ki.withPacketData(NattKeepalivePacketData.nattKeepalivePacket(
+ ki.getNai().getClatv6SrcAddress(), srcPort, address, NATT_PORT));
+ Log.d(TAG, "Src is clat v4 address. Convert from " + ki + " to " + newInfo);
+ return newInfo;
}
public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 9406edd..bbde9b4 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -6857,17 +6857,19 @@
@Test
public void testPacketKeepalives() throws Exception {
- InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
+ final LinkAddress v4Addr = new LinkAddress("192.0.2.129/24");
+ final InetAddress myIPv4 = v4Addr.getAddress();
InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
InetAddress myIPv6 = InetAddress.getByName("2001:db8::1");
InetAddress dstIPv4 = InetAddress.getByName("8.8.8.8");
InetAddress dstIPv6 = InetAddress.getByName("2001:4860:4860::8888");
-
+ doReturn(getClatInterfaceConfigParcel(v4Addr)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
final int validKaInterval = 15;
final int invalidKaInterval = 9;
LinkProperties lp = new LinkProperties();
- lp.setInterfaceName("wlan12");
+ lp.setInterfaceName(MOBILE_IFNAME);
lp.addLinkAddress(new LinkAddress(myIPv6, 64));
lp.addLinkAddress(new LinkAddress(myIPv4, 25));
lp.addRoute(new RouteInfo(InetAddress.getByName("fe80::1234")));
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index 9e604e3..eeffbe1 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -52,6 +52,7 @@
import android.content.res.Resources;
import android.net.INetd;
import android.net.ISocketKeepaliveCallback;
+import android.net.InetAddresses;
import android.net.KeepalivePacketData;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -116,7 +117,8 @@
private static final int MOCK_RESOURCE_ID = 5;
private static final int TEST_KEEPALIVE_INTERVAL_SEC = 10;
private static final int TEST_KEEPALIVE_INVALID_INTERVAL_SEC = 9;
-
+ private static final byte[] V4_SRC_ADDR = new byte[] { (byte) 192, 0, 0, (byte) 129 };
+ private static final String TEST_V4_IFACE = "v4-testIface";
private AutomaticOnOffKeepaliveTracker mAOOKeepaliveTracker;
private HandlerThread mHandlerThread;
@@ -327,6 +329,8 @@
NetworkInfo.DetailedState.CONNECTED, "test reason", "test extra info");
doReturn(new Network(TEST_NETID)).when(mNai).network();
mNai.linkProperties = new LinkProperties();
+ doReturn(null).when(mNai).translateV4toClatV6(any());
+ doReturn(null).when(mNai).getClatv6SrcAddress();
doReturn(PERMISSION_GRANTED).when(mCtx).checkPermission(any() /* permission */,
anyInt() /* pid */, anyInt() /* uid */);
@@ -429,8 +433,7 @@
}
private TestKeepaliveInfo doStartNattKeepalive(int intervalSeconds) throws Exception {
- final InetAddress srcAddress = InetAddress.getByAddress(
- new byte[] { (byte) 192, 0, 0, (byte) 129 });
+ final InetAddress srcAddress = InetAddress.getByAddress(V4_SRC_ADDR);
final int srcPort = 12345;
final InetAddress dstAddress = InetAddress.getByAddress(new byte[] {8, 8, 8, 8});
final int dstPort = 12345;
@@ -610,6 +613,42 @@
}
@Test
+ public void testStartNattKeepalive_addressTranslationOnClat() throws Exception {
+ final InetAddress v6AddrSrc = InetAddresses.parseNumericAddress("2001:db8::1");
+ final InetAddress v6AddrDst = InetAddresses.parseNumericAddress("2001:db8::2");
+ doReturn(v6AddrDst).when(mNai).translateV4toClatV6(any());
+ doReturn(v6AddrSrc).when(mNai).getClatv6SrcAddress();
+ doReturn(InetAddress.getByAddress(V4_SRC_ADDR)).when(mNai).getClatv4SrcAddress();
+ // Setup nai to add clat address
+ final LinkProperties stacked = new LinkProperties();
+ stacked.setInterfaceName(TEST_V4_IFACE);
+ mNai.linkProperties.addStackedLink(stacked);
+
+ final TestKeepaliveInfo testInfo = doStartNattKeepalive();
+ final ArgumentCaptor<NattKeepalivePacketData> kpdCaptor =
+ ArgumentCaptor.forClass(NattKeepalivePacketData.class);
+ verify(mNai).onStartNattSocketKeepalive(
+ eq(TEST_SLOT), eq(TEST_KEEPALIVE_INTERVAL_SEC), kpdCaptor.capture());
+ final NattKeepalivePacketData kpd = kpdCaptor.getValue();
+ // Verify the addresses are updated to v6 when clat is started.
+ assertEquals(v6AddrSrc, kpd.getSrcAddress());
+ assertEquals(v6AddrDst, kpd.getDstAddress());
+
+ triggerEventKeepalive(TEST_SLOT, SocketKeepalive.SUCCESS);
+ verify(testInfo.socketKeepaliveCallback).onStarted();
+
+ // Remove clat address should stop the keepalive.
+ doReturn(null).when(mNai).getClatv6SrcAddress();
+ visibleOnHandlerThread(
+ mTestHandler, () -> mAOOKeepaliveTracker.handleCheckKeepalivesStillValid(mNai));
+ checkAndProcessKeepaliveStop();
+ assertNull(getAutoKiForBinder(testInfo.binder));
+
+ verify(testInfo.socketKeepaliveCallback).onError(SocketKeepalive.ERROR_INVALID_IP_ADDRESS);
+ verifyNoMoreInteractions(ignoreStubs(testInfo.socketKeepaliveCallback));
+ }
+
+ @Test
public void testHandleEventSocketKeepalive_startingFailureHardwareError() throws Exception {
final TestKeepaliveInfo testInfo = doStartNattKeepalive();