aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYan Yan <evitayan@google.com>2020-04-02 01:00:54 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-04-02 01:00:54 +0000
commitb22a56fd3db12293b0db12313fc5552ae2fea346 (patch)
tree300342a9ce6065b887b927e788f9aced9779b365
parentfc562f6f1d986fcef2217ddaff956f8a8f6ea7b5 (diff)
parentebdb40bd6c55a424bd276afe3a036c87d552ce6c (diff)
downloadike-b22a56fd3db12293b0db12313fc5552ae2fea346.tar.gz
Merge "Create IkeNattKeepalive to manage hardware and software keepalive"
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/keepalive/HardwareKeepaliveImpl.java127
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/keepalive/IkeNattKeepalive.java140
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/keepalive/SoftwareKeepaliveImpl.java107
3 files changed, 374 insertions, 0 deletions
diff --git a/src/java/com/android/internal/net/ipsec/ike/keepalive/HardwareKeepaliveImpl.java b/src/java/com/android/internal/net/ipsec/ike/keepalive/HardwareKeepaliveImpl.java
new file mode 100644
index 00000000..bb0abe58
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/keepalive/HardwareKeepaliveImpl.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net.ipsec.ike.keepalive;
+
+import static android.net.SocketKeepalive.ERROR_HARDWARE_ERROR;
+import static android.net.SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES;
+import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.SocketKeepalive.ERROR_INVALID_LENGTH;
+import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
+import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE;
+import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
+import static android.net.ipsec.ike.IkeManager.getIkeLog;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.Network;
+import android.net.SocketKeepalive;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.util.concurrent.Executors;
+
+/** This class provides methods to manage hardware offload NAT-T keepalive. */
+public class HardwareKeepaliveImpl implements IkeNattKeepalive.NattKeepalive {
+ private static final String TAG = "HardwareKeepaliveImpl";
+
+ private final int mKeepaliveDelaySeconds;
+ private final SocketKeepalive mSocketKeepalive;
+ private final HardwareKeepaliveCallback mHardwareKeepaliveCb;
+
+ /** Construct an instance of HardwareKeepaliveImpl */
+ public HardwareKeepaliveImpl(
+ Context context,
+ int keepaliveDelaySeconds,
+ Inet4Address src,
+ Inet4Address dest,
+ UdpEncapsulationSocket socket,
+ Network network,
+ HardwareKeepaliveCallback hardwareKeepaliveCb)
+ throws IOException {
+ // Setup for hardware offload keepalive. Fail to create mSocketKeepalive will cause
+ // MySocketKeepaliveCb#onError to be fired
+ mKeepaliveDelaySeconds = keepaliveDelaySeconds;
+ mHardwareKeepaliveCb = hardwareKeepaliveCb;
+
+ ConnectivityManager connMgr =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mSocketKeepalive =
+ connMgr.createSocketKeepalive(
+ network,
+ socket,
+ src,
+ dest,
+ Executors.newSingleThreadExecutor(),
+ new MySocketKeepaliveCb());
+ }
+
+ @Override
+ public void start() {
+ mSocketKeepalive.start(mKeepaliveDelaySeconds);
+ }
+
+ @Override
+ public void stop() {
+ mSocketKeepalive.stop();
+ }
+
+ @Override
+ public void onAlarmFired() {
+ // Do thing. Should never be called
+ }
+
+ /** Callback interface to receive states change of hardware keepalive */
+ public interface HardwareKeepaliveCallback {
+ /** Called when there is a hardware error for keepalive. */
+ void onHardwareOffloadError();
+
+ /**
+ * Called when there is a network or configuration error which cause sending keepalive
+ * packet to fail
+ */
+ void onNetworkError();
+ }
+
+ private class MySocketKeepaliveCb extends SocketKeepalive.Callback {
+ @Override
+ public void onError(int error) {
+ getIkeLog().d(TAG, "Hardware offload failed on error: " + error);
+ switch (error) {
+ case ERROR_INVALID_NETWORK: // fallthrough
+ case ERROR_INVALID_IP_ADDRESS: // fallthrough
+ case ERROR_INVALID_PORT: // fallthrough
+ case ERROR_INVALID_LENGTH: // fallthrough
+ case ERROR_INVALID_INTERVAL: // fallthrough
+ case ERROR_INVALID_SOCKET: // fallthrough
+ case ERROR_SOCKET_NOT_IDLE: // fallthrough
+ mHardwareKeepaliveCb.onNetworkError();
+ return;
+ case ERROR_UNSUPPORTED: // fallthrough
+ case ERROR_HARDWARE_ERROR: // fallthrough
+ case ERROR_INSUFFICIENT_RESOURCES:
+ mHardwareKeepaliveCb.onHardwareOffloadError();
+ return;
+ default:
+ mHardwareKeepaliveCb.onNetworkError();
+ }
+ }
+ }
+}
diff --git a/src/java/com/android/internal/net/ipsec/ike/keepalive/IkeNattKeepalive.java b/src/java/com/android/internal/net/ipsec/ike/keepalive/IkeNattKeepalive.java
new file mode 100644
index 00000000..129a1f05
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/keepalive/IkeNattKeepalive.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net.ipsec.ike.keepalive;
+
+import static android.net.ipsec.ike.IkeManager.getIkeLog;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.Network;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+
+/**
+ * This class provides methods to manage NAT-T keepalive for a UdpEncapsulationSocket.
+ *
+ * <p>Upon calling {@link start()}, this class will start a NAT-T keepalive, using hardware offload
+ * if available. If hardware offload is not available, a software keepalive will be attempted.
+ */
+public class IkeNattKeepalive {
+ private static final String TAG = "IkeNattKeepalive";
+
+ private NattKeepalive mNattKeepalive;
+
+ /** Construct an instance of IkeNattKeepalive */
+ public IkeNattKeepalive(
+ Context context,
+ int keepaliveDelaySeconds,
+ Inet4Address src,
+ Inet4Address dest,
+ UdpEncapsulationSocket socket,
+ Network network,
+ PendingIntent keepAliveAlarmIntent)
+ throws IOException {
+ mNattKeepalive =
+ new HardwareKeepaliveImpl(
+ context,
+ keepaliveDelaySeconds,
+ src,
+ dest,
+ socket,
+ network,
+ new HardwareKeepaliveCb(
+ context,
+ keepaliveDelaySeconds,
+ dest,
+ socket,
+ keepAliveAlarmIntent));
+ }
+
+ /** Start keepalive */
+ public void start() {
+ // Try keepalive using hardware offload first
+ getIkeLog().d(TAG, "Start NAT-T keepalive");
+ mNattKeepalive.start();
+ }
+
+ /** Stop keepalive */
+ public void stop() {
+ getIkeLog().d(TAG, "Stop NAT-T keepalive");
+
+ mNattKeepalive.stop();
+ }
+
+ /** Receive a keepalive alarm */
+ public void onAlarmFired() {
+ mNattKeepalive.onAlarmFired();
+ }
+
+ /** Interface that a keepalive implementation MUST provide to support NAT-T keepalive for IKE */
+ public interface NattKeepalive {
+ /** Start keepalive */
+ void start();
+ /** Stop keepalive */
+ void stop();
+ /** Receive a keepalive alarm */
+ void onAlarmFired();
+ }
+
+ private class HardwareKeepaliveCb implements HardwareKeepaliveImpl.HardwareKeepaliveCallback {
+ private final Context mContext;
+ private final int mKeepaliveDelaySeconds;
+ private final Inet4Address mDest;
+ private final UdpEncapsulationSocket mSocket;
+ private final PendingIntent mKeepAliveAlarmIntent;
+
+ HardwareKeepaliveCb(
+ Context context,
+ int keepaliveDelaySeconds,
+ Inet4Address dest,
+ UdpEncapsulationSocket socket,
+ PendingIntent keepAliveAlarmIntent) {
+ mContext = context;
+ mKeepaliveDelaySeconds = keepaliveDelaySeconds;
+ mDest = dest;
+ mSocket = socket;
+ mKeepAliveAlarmIntent = keepAliveAlarmIntent;
+ }
+
+ @Override
+ public void onHardwareOffloadError() {
+ getIkeLog().d(TAG, "Switch to software keepalive");
+ mNattKeepalive.stop();
+
+ mNattKeepalive =
+ new SoftwareKeepaliveImpl(
+ mContext,
+ mKeepaliveDelaySeconds,
+ mDest,
+ mSocket,
+ mKeepAliveAlarmIntent);
+ mNattKeepalive.start();
+ }
+
+ @Override
+ public void onNetworkError() {
+ // Stop doing keepalive when getting network error since it will also fail software
+ // keepalive. Considering the only user of IkeNattKeepalive is IkeSessionStateMachine,
+ // not notifying user this error won't bring user extral risk. When there is a network
+ // error, IkeSessionStateMachine will eventually hit the max request retransmission
+ // times and be terminated anyway.
+ stop();
+ }
+ }
+}
diff --git a/src/java/com/android/internal/net/ipsec/ike/keepalive/SoftwareKeepaliveImpl.java b/src/java/com/android/internal/net/ipsec/ike/keepalive/SoftwareKeepaliveImpl.java
new file mode 100644
index 00000000..af39d6e3
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/keepalive/SoftwareKeepaliveImpl.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net.ipsec.ike.keepalive;
+
+import static android.net.ipsec.ike.IkeManager.getIkeLog;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import com.android.internal.net.ipsec.ike.IkeSocket;
+
+import java.net.Inet4Address;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+
+/** This class provides methods to schedule and send keepalive packet. */
+public final class SoftwareKeepaliveImpl implements IkeNattKeepalive.NattKeepalive {
+ private static final String TAG = "SoftwareKeepaliveImpl";
+
+ // NAT-Keepalive packet payload as per RFC 3948
+ private static final byte[] NATT_KEEPALIVE_PAYLOAD = new byte[] {(byte) 0xff};
+
+ private final long mKeepaliveDelayMs;
+ private final UdpEncapsulationSocket mSocket;
+ private final Inet4Address mDestAddress;
+ private final AlarmManager mAlarmMgr;
+ private final PendingIntent mKeepaliveIntent;
+
+ /**
+ * Construct an instance of SoftwareKeepaliveImpl
+ *
+ * <p>Caller that provides keepAliveAlarmIntent is responsible for handling the alarm.
+ */
+ public SoftwareKeepaliveImpl(
+ Context context,
+ int keepaliveDelaySeconds,
+ Inet4Address dest,
+ UdpEncapsulationSocket socket,
+ PendingIntent keepAliveAlarmIntent) {
+ mKeepaliveDelayMs = TimeUnit.SECONDS.toMillis(keepaliveDelaySeconds);
+ mSocket = socket;
+
+ mAlarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mDestAddress = dest;
+ mKeepaliveIntent = keepAliveAlarmIntent;
+ }
+
+ @Override
+ public void start() {
+ sendKeepaliveAndScheduleNext();
+ }
+
+ @Override
+ public void stop() {
+ mAlarmMgr.cancel(mKeepaliveIntent);
+ mKeepaliveIntent.cancel();
+ }
+
+ @Override
+ public void onAlarmFired() {
+ sendKeepaliveAndScheduleNext();
+ }
+
+ /** Send out keepalive packet and schedule next keepalive event */
+ private void sendKeepaliveAndScheduleNext() {
+ try {
+ Os.sendto(
+ mSocket.getFileDescriptor(),
+ ByteBuffer.wrap(NATT_KEEPALIVE_PAYLOAD),
+ 0,
+ mDestAddress,
+ IkeSocket.SERVER_PORT_UDP_ENCAPSULATED);
+
+ } catch (ErrnoException | SocketException e) {
+ getIkeLog().i(TAG, "Failed to keepalive packet to " + mDestAddress.getHostAddress(), e);
+ }
+
+ // It is time-critical to send packets periodically to keep the dynamic NAT mapping
+ // alive. Thus, the alarm has to be "setExact" to avoid batching delay (can be at most 75%)
+ // and allowed to goes off when the device is in doze mode. There will still be a rate limit
+ // on firing alarms. Please check AlarmManager#setExactAndAllowWhileIdle for more details.
+ mAlarmMgr.setExactAndAllowWhileIdle(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + mKeepaliveDelayMs,
+ mKeepaliveIntent);
+ }
+}