1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.internal.net.ipsec.ike; 17 18 import static android.net.ipsec.ike.IkeManager.getIkeLog; 19 import static android.net.ipsec.ike.IkeManager.getIkeMetrics; 20 21 import android.net.ipsec.ike.exceptions.IkeException; 22 import android.os.Message; 23 import android.util.SparseArray; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 import com.android.internal.net.ipsec.ike.net.IkeConnectionController; 27 import com.android.internal.net.ipsec.ike.utils.IkeMetrics; 28 import com.android.internal.util.IState; 29 import com.android.internal.util.State; 30 import com.android.internal.util.StateMachine; 31 32 import java.util.concurrent.Executor; 33 import java.util.concurrent.TimeUnit; 34 35 /** 36 * This class represents the common information of both IkeSessionStateMachine and 37 * ChildSessionStateMachine 38 */ 39 abstract class AbstractSessionStateMachine extends StateMachine { 40 private static final int CMD_SHARED_BASE = 0; 41 protected static final int CMD_CATEGORY_SIZE = 100; 42 43 /** 44 * Commands of Child local request that will be used in both IkeSessionStateMachine and 45 * ChildSessionStateMachine. 46 */ 47 protected static final int CMD_CHILD_LOCAL_REQUEST_BASE = CMD_SHARED_BASE; 48 49 @VisibleForTesting 50 static final int CMD_LOCAL_REQUEST_CREATE_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 1; 51 52 @VisibleForTesting 53 static final int CMD_LOCAL_REQUEST_DELETE_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 2; 54 55 @VisibleForTesting 56 static final int CMD_LOCAL_REQUEST_REKEY_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 3; 57 58 @VisibleForTesting 59 static final int CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE = CMD_CHILD_LOCAL_REQUEST_BASE + 4; 60 61 @VisibleForTesting 62 static final int CMD_LOCAL_REQUEST_MIGRATE_CHILD = CMD_CHILD_LOCAL_REQUEST_BASE + 5; 63 64 static final int CMD_LOCAL_REQUEST_MIN = CMD_LOCAL_REQUEST_CREATE_CHILD; 65 static final int CMD_LOCAL_REQUEST_MAX = CMD_LOCAL_REQUEST_MIGRATE_CHILD; 66 67 /** Timeout commands. */ 68 protected static final int CMD_TIMEOUT_BASE = CMD_SHARED_BASE + CMD_CATEGORY_SIZE; 69 /** Timeout when the remote side fails to send a Rekey-Delete request. */ 70 @VisibleForTesting static final int TIMEOUT_REKEY_REMOTE_DELETE = CMD_TIMEOUT_BASE + 1; 71 72 /** Commands for generic usages */ 73 protected static final int CMD_GENERIC_BASE = CMD_SHARED_BASE + 2 * CMD_CATEGORY_SIZE; 74 /** Force state machine to a target state for testing purposes. */ 75 @VisibleForTesting static final int CMD_FORCE_TRANSITION = CMD_GENERIC_BASE + 1; 76 /** Force close the session. */ 77 @VisibleForTesting static final int CMD_KILL_SESSION = CMD_GENERIC_BASE + 2; 78 79 /** Private commands for subclasses */ 80 protected static final int CMD_PRIVATE_BASE = CMD_SHARED_BASE + 3 * CMD_CATEGORY_SIZE; 81 82 protected static final SparseArray<String> SHARED_CMD_TO_STR; 83 84 static { 85 SHARED_CMD_TO_STR = new SparseArray<>(); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_CREATE_CHILD, "Create Child")86 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_CREATE_CHILD, "Create Child"); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_DELETE_CHILD, "Delete Child")87 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_DELETE_CHILD, "Delete Child"); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD, "Rekey Child")88 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD, "Rekey Child"); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_MIGRATE_CHILD, "Migrate Child SA")89 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_MIGRATE_CHILD, "Migrate Child SA"); SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE, "Rekey Child (MOBIKE)")90 SHARED_CMD_TO_STR.put(CMD_LOCAL_REQUEST_REKEY_CHILD_MOBIKE, "Rekey Child (MOBIKE)"); SHARED_CMD_TO_STR.put(CMD_KILL_SESSION, "Kill session")91 SHARED_CMD_TO_STR.put(CMD_KILL_SESSION, "Kill session"); SHARED_CMD_TO_STR.put(TIMEOUT_REKEY_REMOTE_DELETE, "Timout rekey remote delete")92 SHARED_CMD_TO_STR.put(TIMEOUT_REKEY_REMOTE_DELETE, "Timout rekey remote delete"); SHARED_CMD_TO_STR.put(CMD_FORCE_TRANSITION, "Force transition")93 SHARED_CMD_TO_STR.put(CMD_FORCE_TRANSITION, "Force transition"); 94 } 95 96 // Use a value greater than the retransmit-failure timeout. 97 static final long REKEY_DELETE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(180L); 98 99 // Default delay time for retrying a request 100 static final long RETRY_INTERVAL_MS = TimeUnit.SECONDS.toMillis(15L); 101 102 @VisibleForTesting final IkeContext mIkeContext; 103 104 protected final Executor mUserCbExecutor; 105 private final String mLogTag; 106 107 protected volatile boolean mIsClosing = false; 108 AbstractSessionStateMachine( String name, IkeContext ikeContext, Executor userCbExecutor)109 protected AbstractSessionStateMachine( 110 String name, IkeContext ikeContext, Executor userCbExecutor) { 111 super(name, ikeContext.getLooper()); 112 113 mIkeContext = ikeContext; 114 mLogTag = name; 115 mUserCbExecutor = userCbExecutor; 116 } 117 118 /** 119 * Top level state for handling uncaught exceptions for all subclasses. 120 * 121 * <p>All other state in SessionStateMachine MUST extend this state. 122 * 123 * <p>Only errors this state should catch are unexpected internal failures. Since this may be 124 * run in critical processes, it must never take down the process if it fails 125 */ 126 protected abstract class ExceptionHandlerBase extends State { 127 @Override enter()128 public final void enter() { 129 try { 130 enterState(); 131 } catch (RuntimeException e) { 132 cleanUpAndQuit(e); 133 } 134 } 135 getCmdStr(int cmd)136 private String getCmdStr(int cmd) { 137 String cmdName = SHARED_CMD_TO_STR.get(cmd); 138 if (cmdName != null) { 139 return cmdName; 140 } 141 142 cmdName = getCmdString(cmd); 143 if (cmdName != null) { 144 return cmdName; 145 } 146 147 // Unrecognized message 148 return Integer.toString(cmd); 149 } 150 151 @Override processMessage(Message message)152 public final boolean processMessage(Message message) { 153 try { 154 if (mIsClosing && message.what != CMD_KILL_SESSION) { 155 logd( 156 "Ignore " 157 + getCmdStr(message.what) 158 + " since this session is going to be closed"); 159 return HANDLED; 160 } else { 161 logd("processStateMessage: " + getCmdStr(message.what)); 162 return processStateMessage(message); 163 } 164 } catch (RuntimeException e) { 165 cleanUpAndQuit(e); 166 return HANDLED; 167 } 168 } 169 170 @Override exit()171 public final void exit() { 172 try { 173 exitState(); 174 } catch (RuntimeException e) { 175 cleanUpAndQuit(e); 176 } 177 } 178 enterState()179 protected void enterState() { 180 // Do nothing. Subclasses MUST override it if they care. 181 } 182 processStateMessage(Message message)183 protected boolean processStateMessage(Message message) { 184 return NOT_HANDLED; 185 } 186 exitState()187 protected void exitState() { 188 // Do nothing. Subclasses MUST override it if they care. 189 } 190 cleanUpAndQuit(RuntimeException e)191 protected abstract void cleanUpAndQuit(RuntimeException e); 192 getCmdString(int cmd)193 protected abstract String getCmdString(int cmd); 194 getMetricsStateCode()195 protected abstract @IkeMetrics.IkeState int getMetricsStateCode(); 196 } 197 executeUserCallback(Runnable r)198 protected void executeUserCallback(Runnable r) { 199 try { 200 mUserCbExecutor.execute(r); 201 } catch (Exception e) { 202 logd("Callback execution failed", e); 203 } 204 } 205 206 /** Forcibly close this session. */ killSession()207 public void killSession() { 208 log("killSession"); 209 210 mIsClosing = true; 211 sendMessage(CMD_KILL_SESSION); 212 } 213 214 /** 215 * Quit SessionStateMachine immediately. 216 * 217 * <p>This method pushes SM_QUIT_CMD in front of the message queue and mark mIsClosing as true. 218 * All currently queued messages will be discarded. 219 * 220 * <p>Subclasses MUST call this method instead of quitNow() 221 */ 222 // quitNow() is a public final method in the base class. Thus there is no good way to prevent 223 // caller from calling it within the current inheritance structure. quitSessionNow()224 protected void quitSessionNow() { 225 mIsClosing = true; 226 quitNow(); 227 } 228 getCurrentStateName()229 protected String getCurrentStateName() { 230 final IState state = getCurrentState(); 231 if (state != null) { 232 return state.getName(); 233 } 234 235 return "Null State"; 236 } 237 getMetricsIkeStateCode()238 private @IkeMetrics.IkeState int getMetricsIkeStateCode() { 239 final IState currentState = getCurrentState(); 240 return currentState instanceof ExceptionHandlerBase 241 ? ((ExceptionHandlerBase) currentState).getMetricsStateCode() 242 : IkeMetrics.IKE_STATE_UNKNOWN; 243 } 244 recordMetricsEvent_sessionTerminated(IkeException exception)245 protected void recordMetricsEvent_sessionTerminated(IkeException exception) { 246 final @IkeMetrics.IkeError int exceptionCode = 247 exception == null ? IkeMetrics.IKE_ERROR_NONE : exception.getMetricsErrorCode(); 248 249 getIkeMetrics() 250 .logSessionTerminated( 251 mIkeContext.getIkeCaller(), 252 getMetricsSessionType(), 253 getMetricsIkeStateCode(), 254 exceptionCode); 255 } 256 recordMetricsEvent_LivenssCheckCompletion( IkeConnectionController connectionController, int elapsedTimeInMillis, int numberOfOnGoing, boolean resultSuccess)257 protected void recordMetricsEvent_LivenssCheckCompletion( 258 IkeConnectionController connectionController, 259 int elapsedTimeInMillis, 260 int numberOfOnGoing, 261 boolean resultSuccess) { 262 getIkeMetrics() 263 .logLivenessCheckCompleted( 264 mIkeContext.getIkeCaller(), 265 getMetricsIkeStateCode(), 266 connectionController.getMetricsNetworkType(), 267 elapsedTimeInMillis, 268 numberOfOnGoing, 269 resultSuccess); 270 } 271 recordMetricsEvent_SaNegotiation( int dhGroup, int encryptionAlgorithm, int keyLength, int integrityAlgorithm, int prfAlgorithm, IkeException exception)272 protected void recordMetricsEvent_SaNegotiation( 273 int dhGroup, 274 int encryptionAlgorithm, 275 int keyLength, 276 int integrityAlgorithm, 277 int prfAlgorithm, 278 IkeException exception) { 279 final @IkeMetrics.IkeError int exceptionCode = 280 exception == null ? IkeMetrics.IKE_ERROR_NONE : exception.getMetricsErrorCode(); 281 getIkeMetrics() 282 .logSaNegotiation( 283 mIkeContext.getIkeCaller(), 284 getMetricsSessionType(), 285 getMetricsIkeStateCode(), 286 dhGroup, 287 encryptionAlgorithm, 288 keyLength, 289 integrityAlgorithm, 290 prfAlgorithm, 291 exceptionCode); 292 } 293 getMetricsSessionType()294 protected abstract @IkeMetrics.IkeSessionType int getMetricsSessionType(); 295 296 @Override log(String s)297 protected void log(String s) { 298 getIkeLog().d(mLogTag, s); 299 } 300 301 @Override logd(String s)302 protected void logd(String s) { 303 getIkeLog().d(mLogTag, s); 304 } 305 logd(String s, Throwable e)306 protected void logd(String s, Throwable e) { 307 getIkeLog().d(mLogTag, s, e); 308 } 309 310 @Override logv(String s)311 protected void logv(String s) { 312 getIkeLog().v(mLogTag, s); 313 } 314 315 @Override logi(String s)316 protected void logi(String s) { 317 getIkeLog().i(mLogTag, s); 318 } 319 logi(String s, Throwable cause)320 protected void logi(String s, Throwable cause) { 321 getIkeLog().i(mLogTag, s, cause); 322 } 323 324 @Override logw(String s)325 protected void logw(String s) { 326 getIkeLog().w(mLogTag, s); 327 } 328 329 @Override loge(String s)330 protected void loge(String s) { 331 getIkeLog().e(mLogTag, s); 332 } 333 334 @Override loge(String s, Throwable e)335 protected void loge(String s, Throwable e) { 336 getIkeLog().e(mLogTag, s, e); 337 } 338 logWtf(String s)339 protected void logWtf(String s) { 340 getIkeLog().wtf(mLogTag, s); 341 } 342 logWtf(String s, Throwable e)343 protected void logWtf(String s, Throwable e) { 344 getIkeLog().wtf(mLogTag, s, e); 345 } 346 } 347