aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorevitayan <evitayan@google.com>2020-03-12 21:32:56 -0700
committerevitayan <evitayan@google.com>2020-04-01 15:13:15 -0700
commitebdb40bd6c55a424bd276afe3a036c87d552ce6c (patch)
tree19086b7b9517cd42115a8a3982fe250ea345c805
parent2f82a5075004b0c73a2c0d597e7863d495d7a77b (diff)
downloadike-ebdb40bd6c55a424bd276afe3a036c87d552ce6c.tar.gz
Create IkeNattKeepalive to manage hardware and software keepalive
This commit adds support for starting NATT keepalives to ensure that dynamic NAT tables are kept updated. If hardware keepalives are supported, the IKE library will attempt to use it. If not, the IKE library will fallback to doing keepalives in software. Bug: 148794150 Test: FrameworksIkeTests Test: Done in the following CL Change-Id: Icdd55dd26989791bca00dfcb604e84942c10a2a5
-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);
+ }
+}