aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorevitayan <evitayan@google.com>2020-03-30 15:55:53 -0700
committerevitayan <evitayan@google.com>2020-04-01 15:14:15 -0700
commitd9ab1ad8df645b1c5d8972d307a110d66fe0b690 (patch)
treef406c8e4b6f41f9e28a29825f57806147a214a6c
parentebdb40bd6c55a424bd276afe3a036c87d552ce6c (diff)
downloadike-d9ab1ad8df645b1c5d8972d307a110d66fe0b690.tar.gz
Start NATT keepalive from IkeSessionStateMachine
This commit supports IkeSessionStateMachine to do NAT-T Keepalive when a NAT is detected. IKE libray will first try using hardware offload if available. If it is not available, a software keepalive will be attempted Bug: 148794150 Test: FrameworksIkeTests Test: Manually tested against Strongswan sever, saw receiving isakmp-nat-keep-alive in tcpdump Change-Id: I5407da239b8958828ff03681d2014e3b015b0d79
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java45
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/utils/IkeAlarmReceiver.java4
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java50
3 files changed, 87 insertions, 12 deletions
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index 8b931ffa..cd76d41a 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -41,6 +41,7 @@ import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_CHILD;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DELETE_IKE;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_DPD;
+import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_KEEPALIVE;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_REKEY_CHILD;
import static com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver.ACTION_REKEY_IKE;
@@ -98,6 +99,7 @@ import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedExcepti
import com.android.internal.net.ipsec.ike.exceptions.InvalidKeException;
import com.android.internal.net.ipsec.ike.exceptions.InvalidSyntaxException;
import com.android.internal.net.ipsec.ike.exceptions.NoValidProposalChosenException;
+import com.android.internal.net.ipsec.ike.keepalive.IkeNattKeepalive;
import com.android.internal.net.ipsec.ike.message.IkeAuthDigitalSignPayload;
import com.android.internal.net.ipsec.ike.message.IkeAuthPayload;
import com.android.internal.net.ipsec.ike.message.IkeAuthPskPayload;
@@ -195,6 +197,7 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
sIntentFilter.addAction(ACTION_DPD);
sIntentFilter.addAction(ACTION_REKEY_CHILD);
sIntentFilter.addAction(ACTION_REKEY_IKE);
+ sIntentFilter.addAction(ACTION_KEEPALIVE);
}
private static final AtomicInteger sIkeSessionIdGenerator = new AtomicInteger();
@@ -216,6 +219,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
@VisibleForTesting
static final long TEMP_FAILURE_RETRY_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(5L);
+ @VisibleForTesting static final int NATT_KEEPALIVE_DELAY_SECONDS = 10;
+
// Package private IKE exchange subtypes describe the specific function of a IKE
// request/response exchange. It helps IkeSessionStateMachine to do message validation according
// to the subtype specific rules.
@@ -288,6 +293,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
static final int CMD_EAP_FINISH_EAP_AUTH = CMD_GENERAL_BASE + 14;
/** Alarm goes off for a scheduled event, check {@link Message.arg2} for event type */
static final int CMD_ALARM_FIRED = CMD_GENERAL_BASE + 15;
+ /** Send keepalive packet */
+ static final int CMD_SEND_KEEPALIVE = CMD_GENERAL_BASE + 16;
/** Force state machine to a target state for testing purposes. */
static final int CMD_FORCE_TRANSITION = CMD_GENERAL_BASE + 99;
@@ -370,6 +377,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
@VisibleForTesting boolean mIsLocalBehindNat;
/** Indicates if remote node is behind a NAT. */
@VisibleForTesting boolean mIsRemoteBehindNat;
+ /** NATT keepalive scheduler. Initialized when a NAT is detected */
+ @VisibleForTesting IkeNattKeepalive mIkeNattKeepalive;
/** Indicates if both sides support fragmentation. Set in IKE INIT */
@VisibleForTesting boolean mSupportFragment;
@@ -1008,8 +1017,13 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
}
}
- if (mIkeSocket == null) return;
- mIkeSocket.releaseReference(this);
+ if (mIkeNattKeepalive != null) {
+ mIkeNattKeepalive.stop();
+ }
+
+ if (mIkeSocket != null) {
+ mIkeSocket.releaseReference(this);
+ }
sIkeAlarmReceiver.unregisterIkeSession(mIkeSessionId);
@@ -1244,6 +1258,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
}
}
+ private String getIntentIdentifier() {
+ return TAG + "_" + mIkeSessionId;
+ }
+
private String getIntentIdentifier(long remoteIkeSpi) {
return TAG + "_" + mIkeSessionId + "_" + remoteIkeSpi;
}
@@ -1521,6 +1539,10 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
protected void handleFiredAlarm(Message message) {
switch (message.arg2) {
+ case CMD_SEND_KEEPALIVE:
+ // Software keepalive alarm is fired
+ mIkeNattKeepalive.onAlarmFired();
+ return;
case CMD_LOCAL_REQUEST_DELETE_CHILD:
// Child SA (identified by remoteChildSpi) has hit its hard lifetime
enqueueChildLocalRequest(message);
@@ -2987,9 +3009,28 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
} catch (ErrnoException | IOException | ResourceUnavailableException e) {
handleIkeFatalError(e);
}
+
+ mIkeNattKeepalive =
+ new IkeNattKeepalive(
+ mContext,
+ NATT_KEEPALIVE_DELAY_SECONDS,
+ (Inet4Address) mLocalAddress,
+ (Inet4Address) mRemoteAddress,
+ ((IkeUdpEncapSocket) mIkeSocket).getUdpEncapsulationSocket(),
+ mIkeSocket.getNetwork(),
+ buildKeepaliveIntent());
+ mIkeNattKeepalive.start();
}
}
+ private PendingIntent buildKeepaliveIntent() {
+ return buildIkeAlarmIntent(
+ mContext,
+ ACTION_KEEPALIVE,
+ getIntentIdentifier(),
+ obtainMessage(CMD_ALARM_FIRED, mIkeSessionId, CMD_SEND_KEEPALIVE));
+ }
+
@Override
public void exitState() {
super.exitState();
diff --git a/src/java/com/android/internal/net/ipsec/ike/utils/IkeAlarmReceiver.java b/src/java/com/android/internal/net/ipsec/ike/utils/IkeAlarmReceiver.java
index 7a12ddd2..c37c8daf 100644
--- a/src/java/com/android/internal/net/ipsec/ike/utils/IkeAlarmReceiver.java
+++ b/src/java/com/android/internal/net/ipsec/ike/utils/IkeAlarmReceiver.java
@@ -41,6 +41,7 @@ public class IkeAlarmReceiver extends BroadcastReceiver {
public static final String ACTION_DELETE_IKE = "IkeAlarmReceiver.ACTION_DELETE_IKE";
public static final String ACTION_REKEY_IKE = "IkeAlarmReceiver.ACTION_REKEY_IKE";
public static final String ACTION_DPD = "IkeAlarmReceiver.ACTION_DPD";
+ public static final String ACTION_KEEPALIVE = "IkeAlarmReceiver.ACTION_KEEPALIVE";
private static final HashSet<String> sIkeSessionActionsSet = new HashSet<>();
@@ -76,7 +77,8 @@ public class IkeAlarmReceiver extends BroadcastReceiver {
case ACTION_REKEY_CHILD: // fallthrough
case ACTION_DELETE_IKE: // fallthrough
case ACTION_REKEY_IKE: // fallthrough
- case ACTION_DPD:
+ case ACTION_DPD: // fallthrough
+ case ACTION_KEEPALIVE:
// This Message has lost its target information after being sent as a Broadcast
Message message =
(Message) intent.getExtras().getParcelable(PARCELABLE_NAME_IKE_SESSION_MSG);
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
index 73b6c7e7..f7ca9db7 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
@@ -73,10 +73,13 @@ import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.Context;
+import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpSecManager;
+import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.Network;
+import android.net.SocketKeepalive;
import android.net.eap.EapSessionConfig;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -93,6 +96,7 @@ import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.os.Handler;
import android.os.test.TestLooper;
import android.telephony.TelephonyManager;
@@ -150,6 +154,7 @@ import com.android.internal.net.ipsec.ike.message.IkeTestUtils;
import com.android.internal.net.ipsec.ike.message.IkeTsPayload;
import com.android.internal.net.ipsec.ike.testutils.CertUtils;
import com.android.internal.net.ipsec.ike.testutils.MockIpSecTestUtils;
+import com.android.internal.net.ipsec.ike.utils.IkeAlarmReceiver;
import com.android.internal.net.ipsec.ike.utils.Retransmitter;
import com.android.internal.net.ipsec.ike.utils.Retransmitter.IBackoffTimeoutCalculator;
import com.android.internal.net.ipsec.ike.utils.State;
@@ -288,11 +293,12 @@ public final class IkeSessionStateMachineTest {
private static final long RETRANSMIT_BACKOFF_TIMEOUT_MS = 5000L;
private MockIpSecTestUtils mMockIpSecTestUtils;
- private Context mContext;
+ private Context mSpyContext;
private IpSecManager mIpSecManager;
private ConnectivityManager mMockConnectManager;
private Network mMockDefaultNetwork;
+ private SocketKeepalive mMockSocketKeepalive;
private IkeUdpEncapSocket mSpyIkeUdpEncapSocket;
private IkeUdp4Socket mSpyIkeUdp4Socket;
private IkeUdp6Socket mSpyIkeUdp6Socket;
@@ -656,7 +662,16 @@ public final class IkeSessionStateMachineTest {
mMockIpSecTestUtils = MockIpSecTestUtils.setUpMockIpSec();
mIpSecManager = mMockIpSecTestUtils.getIpSecManager();
- mContext = mMockIpSecTestUtils.getContext();
+
+ mSpyContext = spy(mMockIpSecTestUtils.getContext());
+ doReturn(null)
+ .when(mSpyContext)
+ .registerReceiver(
+ any(IkeAlarmReceiver.class),
+ any(IntentFilter.class),
+ any(),
+ any(Handler.class));
+ doNothing().when(mSpyContext).unregisterReceiver(any(IkeAlarmReceiver.class));
mMockConnectManager = mock(ConnectivityManager.class);
mMockDefaultNetwork = mock(Network.class);
@@ -666,6 +681,20 @@ public final class IkeSessionStateMachineTest {
.when(mMockDefaultNetwork)
.getByName(REMOTE_ADDRESS.getHostAddress());
+ mMockSocketKeepalive = mock(SocketKeepalive.class);
+ doReturn(mMockSocketKeepalive)
+ .when(mMockConnectManager)
+ .createSocketKeepalive(
+ any(Network.class),
+ any(UdpEncapsulationSocket.class),
+ any(Inet4Address.class),
+ any(Inet4Address.class),
+ any(Executor.class),
+ any(SocketKeepalive.Callback.class));
+ doReturn(mMockConnectManager)
+ .when(mSpyContext)
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+
mEapSessionConfig =
new EapSessionConfig.Builder()
.setEapSimConfig(EAP_SIM_SUB_ID, TelephonyManager.APPTYPE_USIM)
@@ -758,7 +787,7 @@ public final class IkeSessionStateMachineTest {
IkeSessionStateMachine ikeSession =
new IkeSessionStateMachine(
mLooper.getLooper(),
- mContext,
+ mSpyContext,
mIpSecManager,
ikeParams,
mChildSessionParams,
@@ -1319,6 +1348,9 @@ public final class IkeSessionStateMachineTest {
// Validate socket switched
assertTrue(mIkeSessionStateMachine.mIkeSocket instanceof IkeUdpEncapSocket);
verify(mSpyIkeUdp4Socket).unregisterIke(anyLong());
+
+ // Validate keepalive has started
+ verify(mMockSocketKeepalive).start(anyInt());
}
@Ignore
@@ -1462,7 +1494,7 @@ public final class IkeSessionStateMachineTest {
// After state machine start, add to the callback->statemachine map
when(mMockChildSessionFactoryHelper.makeChildSessionStateMachine(
eq(mLooper.getLooper()),
- eq(mContext),
+ eq(mSpyContext),
anyInt(),
any(AlarmManager.class),
eq(mChildSessionParams),
@@ -1519,7 +1551,7 @@ public final class IkeSessionStateMachineTest {
verify(mMockChildSessionFactoryHelper)
.makeChildSessionStateMachine(
eq(mLooper.getLooper()),
- eq(mContext),
+ eq(mSpyContext),
anyInt(),
any(AlarmManager.class),
eq(mChildSessionParams),
@@ -2261,7 +2293,7 @@ public final class IkeSessionStateMachineTest {
verify(mMockChildSessionFactoryHelper)
.makeChildSessionStateMachine(
eq(mLooper.getLooper()),
- eq(mContext),
+ eq(mSpyContext),
anyInt(),
any(AlarmManager.class),
eq(mChildSessionParams),
@@ -2532,7 +2564,7 @@ public final class IkeSessionStateMachineTest {
.newEapAuthenticator(
eq(mIkeSessionStateMachine.getHandler().getLooper()),
captor.capture(),
- eq(mContext),
+ eq(mSpyContext),
eq(mEapSessionConfig));
return captor.getValue();
@@ -4100,7 +4132,7 @@ public final class IkeSessionStateMachineTest {
verify(mMockChildSessionFactoryHelper)
.makeChildSessionStateMachine(
eq(mLooper.getLooper()),
- eq(mContext),
+ eq(mSpyContext),
anyInt(),
any(AlarmManager.class),
eq(mChildSessionParams),
@@ -4183,7 +4215,7 @@ public final class IkeSessionStateMachineTest {
IkeSessionStateMachine ikeSession =
new IkeSessionStateMachine(
mLooper.getLooper(),
- mContext,
+ mSpyContext,
mIpSecManager,
mockSessionParams,
mChildSessionParams,