blob: 48e4ac0df63485a243db4cc4ed34659a291a0332 [file] [log] [blame]
Ari Saha89831742015-06-26 10:31:48 -07001/*
Amit Ghoshc9ac1e52017-07-28 12:31:18 +01002 * Copyright 2017-present Open Networking Foundation
Ari Saha89831742015-06-26 10:31:48 -07003 *
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 */
17
Matteo Scandolocf847b82019-04-26 15:00:00 -070018package org.opencord.aaa.impl;
Ari Saha89831742015-06-26 10:31:48 -070019
Shubham Sharma4900ce62019-06-19 14:18:50 +000020import com.google.common.collect.Maps;
Ari Saha89831742015-06-26 10:31:48 -070021import org.onlab.packet.MacAddress;
22import org.onosproject.net.ConnectPoint;
Matteo Scandolocf847b82019-04-26 15:00:00 -070023import org.opencord.aaa.AuthenticationEvent;
24import org.opencord.aaa.StateMachineDelegate;
Ari Saha89831742015-06-26 10:31:48 -070025import org.slf4j.Logger;
Thomas Vachuskae9894202015-07-30 11:59:07 -070026
Shubham Sharma4900ce62019-06-19 14:18:50 +000027import java.util.HashSet;
28import java.util.Map;
29import java.util.Set;
30
31import static org.slf4j.LoggerFactory.getLogger;
Ari Saha89831742015-06-26 10:31:48 -070032
33/**
34 * AAA Finite State Machine.
35 */
36
Carmelo Cascone58b53292019-09-30 12:35:31 -070037public class StateMachine {
Ari Saha89831742015-06-26 10:31:48 -070038 //INDEX to identify the state in the transition table
39 static final int STATE_IDLE = 0;
40 static final int STATE_STARTED = 1;
41 static final int STATE_PENDING = 2;
42 static final int STATE_AUTHORIZED = 3;
43 static final int STATE_UNAUTHORIZED = 4;
44
Shubham Sharma1e43c562019-06-19 14:18:12 +000045 // Defining the states where timeout can happen
46 static final Set<Integer> TIMEOUT_ELIGIBLE_STATES = new HashSet();
47 static {
48 TIMEOUT_ELIGIBLE_STATES.add(STATE_STARTED);
49 TIMEOUT_ELIGIBLE_STATES.add(STATE_PENDING);
50 }
51 // INDEX to identify the transition in the transition table
Ari Saha89831742015-06-26 10:31:48 -070052 static final int TRANSITION_START = 0; // --> started
53 static final int TRANSITION_REQUEST_ACCESS = 1;
54 static final int TRANSITION_AUTHORIZE_ACCESS = 2;
55 static final int TRANSITION_DENY_ACCESS = 3;
56 static final int TRANSITION_LOGOFF = 4;
57
Shubham Sharma4900ce62019-06-19 14:18:50 +000058 private static int identifier = 1;
Ari Saha89831742015-06-26 10:31:48 -070059 private byte challengeIdentifier;
60 private byte[] challengeState;
61 private byte[] username;
62 private byte[] requestAuthenticator;
63
64 // Supplicant connectivity info
Ray Milkeyf61a24e2015-09-24 16:34:02 -070065 private ConnectPoint supplicantConnectpoint;
66 private MacAddress supplicantAddress;
67 private short vlanId;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +010068 private byte priorityCode;
Ari Saha89831742015-06-26 10:31:48 -070069
Shubham Sharma1e43c562019-06-19 14:18:12 +000070 // Boolean flag indicating whether response is pending from AAA Server.
71 // Used for counting timeout happening for AAA Sessions due to no response.
72 private boolean waitingForRadiusResponse;
73
74 private static int cleanupTimerTimeOutInMins;
75
Ari Saha89831742015-06-26 10:31:48 -070076 private String sessionId = null;
77
78 private final Logger log = getLogger(getClass());
79
Shubham Sharma1e43c562019-06-19 14:18:12 +000080 private State[] states = {new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized() };
Ari Saha89831742015-06-26 10:31:48 -070081
Shubham Sharma1e43c562019-06-19 14:18:12 +000082 // Cleanup Timer instance created for this session
83 private java.util.concurrent.ScheduledFuture<?> cleanupTimer = null;
Ari Saha89831742015-06-26 10:31:48 -070084
Shubham Sharma1e43c562019-06-19 14:18:12 +000085 // TimeStamp of last EAPOL or RADIUS message received.
86 private long lastPacketReceivedTime = 0;
87
88 // State transition table
Ari Saha89831742015-06-26 10:31:48 -070089 /*
Shubham Sharma1e43c562019-06-19 14:18:12 +000090 *
91 * state IDLE | STARTED | PENDING | AUTHORIZED | UNAUTHORIZED //// input
92 * -----------------------------------------------------------------------------
93 * -----------------------
94 *
95 * START STARTED | _ | _ | STARTED | STARTED
96 *
97 * REQUEST_ACCESS _ | PENDING | _ | _ | _
98 *
99 * AUTHORIZE_ACCESS _ | _ | AUTHORIZED | _ | _
100 *
101 * DENY_ACCESS _ | - | UNAUTHORIZED | _ | _
102 *
103 * LOGOFF _ | _ | _ | IDLE | IDLE
Ari Saha89831742015-06-26 10:31:48 -0700104 */
105
Shubham Sharma1e43c562019-06-19 14:18:12 +0000106 private int[] idleTransition = {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE };
107 private int[] startedTransition = {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED };
108 private int[] pendingTransition = {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED,
109 STATE_PENDING };
110 private int[] authorizedTransition = {STATE_STARTED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED,
111 STATE_IDLE };
112 private int[] unauthorizedTransition = {STATE_STARTED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED,
113 STATE_IDLE };
Ari Saha89831742015-06-26 10:31:48 -0700114
Shubham Sharma1e43c562019-06-19 14:18:12 +0000115 // THE TRANSITION TABLE
116 private int[][] transition = {idleTransition, startedTransition, pendingTransition, authorizedTransition,
117 unauthorizedTransition };
Ari Saha89831742015-06-26 10:31:48 -0700118
119 private int currentState = STATE_IDLE;
120
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700121 // Maps of state machines. Each state machine is represented by an
122 // unique identifier on the switch: dpid + port number
123 private static Map<String, StateMachine> sessionIdMap;
124 private static Map<Integer, StateMachine> identifierMap;
Ari Saha89831742015-06-26 10:31:48 -0700125
Jonathan Hart5db44532018-07-12 18:13:54 -0700126 private static StateMachineDelegate delegate;
127
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700128 public static void initializeMaps() {
129 sessionIdMap = Maps.newConcurrentMap();
130 identifierMap = Maps.newConcurrentMap();
Shubham Sharma4900ce62019-06-19 14:18:50 +0000131 identifier = 1;
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700132 }
133
134 public static void destroyMaps() {
135 sessionIdMap = null;
136 identifierMap = null;
137 }
138
Matteo Scandolocf847b82019-04-26 15:00:00 -0700139 public static void setDelegate(StateMachineDelegate delegate) {
140 StateMachine.delegate = delegate;
Jonathan Hart5db44532018-07-12 18:13:54 -0700141 }
142
Shubham Sharma1e43c562019-06-19 14:18:12 +0000143 public static void setcleanupTimerTimeOutInMins(int cleanupTimerTimeoutInMins) {
144 cleanupTimerTimeOutInMins = cleanupTimerTimeoutInMins;
145 }
146
Jonathan Hart5db44532018-07-12 18:13:54 -0700147 public static void unsetDelegate(StateMachineDelegate delegate) {
148 if (StateMachine.delegate == delegate) {
149 StateMachine.delegate = null;
150 }
151 }
152
Qianqian Hu61a6a402016-02-16 15:18:05 +0800153 public static Map<String, StateMachine> sessionIdMap() {
154 return sessionIdMap;
155 }
156
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700157 public static StateMachine lookupStateMachineById(byte identifier) {
158 return identifierMap.get((int) identifier);
159 }
160
161 public static StateMachine lookupStateMachineBySessionId(String sessionId) {
162 return sessionIdMap.get(sessionId);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100163 }
164
Shubham Sharma1e43c562019-06-19 14:18:12 +0000165 public static void deleteStateMachineId(String sessionId) {
166 sessionIdMap.remove(sessionId);
167 }
168
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100169 public static void deleteStateMachineMapping(StateMachine machine) {
170 identifierMap.entrySet().removeIf(e -> e.getValue().equals(machine));
Shubham Sharma1e43c562019-06-19 14:18:12 +0000171 if (machine.cleanupTimer != null) {
172 machine.cleanupTimer.cancel(false);
173 machine.cleanupTimer = null;
174 }
175 }
176
177 public java.util.concurrent.ScheduledFuture<?> getCleanupTimer() {
178 return cleanupTimer;
179 }
180
181 public boolean isWaitingForRadiusResponse() {
182 return waitingForRadiusResponse;
183 }
184
185 public void setWaitingForRadiusResponse(boolean waitingForRadiusResponse) {
186 this.waitingForRadiusResponse = waitingForRadiusResponse;
187 }
188
189 public void setCleanupTimer(java.util.concurrent.ScheduledFuture<?> cleanupTimer) {
190 this.cleanupTimer = cleanupTimer;
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100191 }
192
193 /**
David K. Bainbridge62019492017-07-28 17:02:10 -0700194 * Deletes authentication state machine records for a given MAC address.
Shubham Sharma1e43c562019-06-19 14:18:12 +0000195 *
David K. Bainbridge62019492017-07-28 17:02:10 -0700196 * @param mac mac address of the suppliant who's state machine should be removed
197 */
198 public static void deleteByMac(MacAddress mac) {
199
200 // Walk the map from session IDs to state machines looking for a MAC match
201 for (Map.Entry<String, StateMachine> e : sessionIdMap.entrySet()) {
202
203 // If a MAC match is found then delete the entry from the session ID
204 // and identifier map as well as call delete identifier to clean up
205 // the identifier bit set.
Shubham Sharma1e43c562019-06-19 14:18:12 +0000206 if (e.getValue() != null && e.getValue().supplicantAddress != null
207 && e.getValue().supplicantAddress.equals(mac)) {
David K. Bainbridge62019492017-07-28 17:02:10 -0700208 sessionIdMap.remove(e.getValue().sessionId);
Shubham Sharma4900ce62019-06-19 14:18:50 +0000209 if (e.getValue().identifier != 1) {
David K. Bainbridge62019492017-07-28 17:02:10 -0700210 deleteStateMachineMapping(e.getValue());
211 }
212 break;
213 }
214 }
215 }
216
217 /**
Jonathan Hart932bedc2018-07-12 13:46:09 -0700218 * Creates a new StateMachine with the given session ID.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700219 *
Shubham Sharma1e43c562019-06-19 14:18:12 +0000220 * @param sessionId session Id represented by the switch dpid + port number
Ari Saha89831742015-06-26 10:31:48 -0700221 */
Jonathan Hart932bedc2018-07-12 13:46:09 -0700222 public StateMachine(String sessionId) {
223 log.info("Creating a new state machine for {}", sessionId);
Ari Saha89831742015-06-26 10:31:48 -0700224 this.sessionId = sessionId;
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700225 sessionIdMap.put(sessionId, this);
Ari Saha89831742015-06-26 10:31:48 -0700226 }
227
228 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700229 * Gets the connect point for the supplicant side.
230 *
231 * @return supplicant connect point
232 */
233 public ConnectPoint supplicantConnectpoint() {
234 return supplicantConnectpoint;
235 }
236
237 /**
238 * Sets the supplicant side connect point.
239 *
240 * @param supplicantConnectpoint supplicant select point.
241 */
242 public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) {
243 this.supplicantConnectpoint = supplicantConnectpoint;
244 }
245
246 /**
247 * Gets the MAC address of the supplicant.
248 *
249 * @return supplicant MAC address
250 */
251 public MacAddress supplicantAddress() {
252 return supplicantAddress;
253 }
254
255 /**
256 * Sets the supplicant MAC address.
257 *
258 * @param supplicantAddress new supplicant MAC address
259 */
260 public void setSupplicantAddress(MacAddress supplicantAddress) {
261 this.supplicantAddress = supplicantAddress;
262 }
263
264 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000265 * Sets the lastPacketReceivedTime.
266 *
267 * @param lastPacketReceivedTime timelastPacket was received
268 */
269 public void setLastPacketReceivedTime(long lastPacketReceivedTime) {
270 this.lastPacketReceivedTime = lastPacketReceivedTime;
271 }
272
273 /**
274 * Gets the lastPacketReceivedTime.
275 *
276 * @return lastPacketReceivedTime
277 */
278 public long getLastPacketReceivedTime() {
279 return lastPacketReceivedTime;
280 }
281
282 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700283 * Gets the client's Vlan ID.
284 *
285 * @return client vlan ID
286 */
287 public short vlanId() {
288 return vlanId;
289 }
290
291 /**
292 * Sets the client's vlan ID.
293 *
294 * @param vlanId new client vlan ID
295 */
296 public void setVlanId(short vlanId) {
297 this.vlanId = vlanId;
298 }
299
300 /**
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100301 * Gets the client's priority Code.
302 *
303 * @return client Priority code
304 */
305 public byte priorityCode() {
306 return priorityCode;
307 }
308
309 /**
310 * Sets the client's priority Code.
311 *
312 * @param priorityCode new client priority Code
313 */
314 public void setPriorityCode(byte priorityCode) {
315 this.priorityCode = priorityCode;
316 }
317
318 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700319 * Gets the client id that is requesting for access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700320 *
Ari Saha89831742015-06-26 10:31:48 -0700321 * @return The client id.
322 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700323 public String sessionId() {
Ari Saha89831742015-06-26 10:31:48 -0700324 return this.sessionId;
325 }
326
327 /**
Ari Saha89831742015-06-26 10:31:48 -0700328 * Set the challenge identifier and the state issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700329 *
Shubham Sharma1e43c562019-06-19 14:18:12 +0000330 * @param challengeIdentifier The challenge identifier set into the EAP packet
331 * from the RADIUS message.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700332 * @param challengeState The challenge state from the RADIUS.
Ari Saha89831742015-06-26 10:31:48 -0700333 */
334 protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
335 this.challengeIdentifier = challengeIdentifier;
336 this.challengeState = challengeState;
337 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700338
Ari Saha89831742015-06-26 10:31:48 -0700339 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000340 * Set the challenge identifier issued by the RADIUS on the access challenge
341 * request.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700342 *
Shubham Sharma1e43c562019-06-19 14:18:12 +0000343 * @param challengeIdentifier The challenge identifier set into the EAP packet
344 * from the RADIUS message.
Ari Saha89831742015-06-26 10:31:48 -0700345 */
346 protected void setChallengeIdentifier(byte challengeIdentifier) {
347 log.info("Set Challenge Identifier to {}", challengeIdentifier);
348 this.challengeIdentifier = challengeIdentifier;
349 }
350
351 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700352 * Gets the challenge EAP identifier set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700353 *
Ari Saha89831742015-06-26 10:31:48 -0700354 * @return The challenge EAP identifier.
355 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700356 protected byte challengeIdentifier() {
Ari Saha89831742015-06-26 10:31:48 -0700357 return this.challengeIdentifier;
358 }
359
Ari Saha89831742015-06-26 10:31:48 -0700360 /**
361 * Set the challenge state info issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700362 *
Ari Saha89831742015-06-26 10:31:48 -0700363 * @param challengeState The challenge state from the RADIUS.
364 */
365 protected void setChallengeState(byte[] challengeState) {
366 log.info("Set Challenge State");
367 this.challengeState = challengeState;
368 }
369
370 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700371 * Gets the challenge state set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700372 *
Ari Saha89831742015-06-26 10:31:48 -0700373 * @return The challenge state.
374 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700375 protected byte[] challengeState() {
Ari Saha89831742015-06-26 10:31:48 -0700376 return this.challengeState;
377 }
378
379 /**
380 * Set the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700381 *
Ari Saha89831742015-06-26 10:31:48 -0700382 * @param username The username sent to the RADIUS upon access request.
383 */
384 protected void setUsername(byte[] username) {
385 this.username = username;
386 }
387
Ari Saha89831742015-06-26 10:31:48 -0700388 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700389 * Gets the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700390 *
Ari Saha89831742015-06-26 10:31:48 -0700391 * @return The requestAuthenticator.
392 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700393 protected byte[] requestAuthenticator() {
Ari Saha89831742015-06-26 10:31:48 -0700394 return this.requestAuthenticator;
395 }
396
397 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700398 * Sets the authenticator.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700399 *
Ari Saha89831742015-06-26 10:31:48 -0700400 * @param authenticator The username sent to the RADIUS upon access request.
401 */
402 protected void setRequestAuthenticator(byte[] authenticator) {
403 this.requestAuthenticator = authenticator;
404 }
405
Ari Saha89831742015-06-26 10:31:48 -0700406 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700407 * Gets the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700408 *
Ari Saha89831742015-06-26 10:31:48 -0700409 * @return The username.
410 */
Carmelo Cascone58b53292019-09-30 12:35:31 -0700411 public byte[] username() {
Ari Saha89831742015-06-26 10:31:48 -0700412 return this.username;
413 }
414
415 /**
416 * Return the identifier of the state machine.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700417 *
Ari Saha89831742015-06-26 10:31:48 -0700418 * @return The state machine identifier.
419 */
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100420 public synchronized byte identifier() {
Shubham Sharma4900ce62019-06-19 14:18:50 +0000421 //identifier 0 is for statusServerrequest
422 //identifier 1 is for fake accessRequest
423 identifier = (identifier + 1) % 253;
424 identifierMap.put((identifier + 2), this);
425 return (byte) (identifier + 2);
Ari Saha89831742015-06-26 10:31:48 -0700426 }
427
Ari Saha89831742015-06-26 10:31:48 -0700428 /**
429 * Move to the next state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700430 *
Ray Milkey78e95a42015-09-24 08:36:45 -0700431 * @param msg message
Ari Saha89831742015-06-26 10:31:48 -0700432 */
Thomas Vachuskae9894202015-07-30 11:59:07 -0700433 private void next(int msg) {
Ari Saha89831742015-06-26 10:31:48 -0700434 currentState = transition[currentState][msg];
435 log.info("Current State " + currentState);
436 }
437
438 /**
439 * Client has requested the start action to allow network access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700440 *
441 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700442 */
443 public void start() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700444 states[currentState].start();
Jonathan Hart5db44532018-07-12 18:13:54 -0700445
Shubham Sharma1e43c562019-06-19 14:18:12 +0000446 delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.STARTED, supplicantConnectpoint));
Jonathan Hart5db44532018-07-12 18:13:54 -0700447
Shubham Sharma1e43c562019-06-19 14:18:12 +0000448 // move to the next state
Ray Milkey78e95a42015-09-24 08:36:45 -0700449 next(TRANSITION_START);
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100450 identifier = this.identifier();
Ari Saha89831742015-06-26 10:31:48 -0700451 }
452
453 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000454 * An Identification information has been sent by the supplicant. Move to the
455 * next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700456 *
457 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700458 */
459 public void requestAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700460 states[currentState].requestAccess();
Jonathan Hart5db44532018-07-12 18:13:54 -0700461
Shubham Sharma1e43c562019-06-19 14:18:12 +0000462 delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.REQUESTED, supplicantConnectpoint));
Jonathan Hart5db44532018-07-12 18:13:54 -0700463
Shubham Sharma1e43c562019-06-19 14:18:12 +0000464 // move to the next state
Ray Milkey78e95a42015-09-24 08:36:45 -0700465 next(TRANSITION_REQUEST_ACCESS);
Ari Saha89831742015-06-26 10:31:48 -0700466 }
467
468 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000469 * RADIUS has accepted the identification. Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700470 *
471 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700472 */
473 public void authorizeAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700474 states[currentState].radiusAccepted();
Shubham Sharma1e43c562019-06-19 14:18:12 +0000475 // move to the next state
Ray Milkey78e95a42015-09-24 08:36:45 -0700476 next(TRANSITION_AUTHORIZE_ACCESS);
Ari Saha89831742015-06-26 10:31:48 -0700477
Shubham Sharma1e43c562019-06-19 14:18:12 +0000478 delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.APPROVED, supplicantConnectpoint));
Ari Saha89831742015-06-26 10:31:48 -0700479
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100480 // Clear mapping
481 deleteStateMachineMapping(this);
Ari Saha89831742015-06-26 10:31:48 -0700482 }
483
484 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000485 * RADIUS has denied the identification. Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700486 *
487 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700488 */
489 public void denyAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700490 states[currentState].radiusDenied();
Shubham Sharma1e43c562019-06-19 14:18:12 +0000491 // move to the next state
Ray Milkey78e95a42015-09-24 08:36:45 -0700492 next(TRANSITION_DENY_ACCESS);
Jonathan Hart5db44532018-07-12 18:13:54 -0700493
Shubham Sharma1e43c562019-06-19 14:18:12 +0000494 delegate.notify(new AuthenticationEvent(AuthenticationEvent.Type.DENIED, supplicantConnectpoint));
Jonathan Hart5db44532018-07-12 18:13:54 -0700495
Amit Ghoshc9ac1e52017-07-28 12:31:18 +0100496 // Clear mappings
497 deleteStateMachineMapping(this);
Ari Saha89831742015-06-26 10:31:48 -0700498 }
499
500 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000501 * Logoff request has been requested. Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700502 *
503 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700504 */
505 public void logoff() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700506 states[currentState].logoff();
Shubham Sharma1e43c562019-06-19 14:18:12 +0000507 // move to the next state
Ray Milkey78e95a42015-09-24 08:36:45 -0700508 next(TRANSITION_LOGOFF);
Ari Saha89831742015-06-26 10:31:48 -0700509 }
510
511 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700512 * Gets the current state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700513 *
Shubham Sharma1e43c562019-06-19 14:18:12 +0000514 * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING,
515 * STATE_AUTHORIZED, STATE_UNAUTHORIZED.
Ari Saha89831742015-06-26 10:31:48 -0700516 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700517 public int state() {
Ari Saha89831742015-06-26 10:31:48 -0700518 return currentState;
519 }
520
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700521 @Override
Ari Saha89831742015-06-26 10:31:48 -0700522 public String toString() {
Shubham Sharma1e43c562019-06-19 14:18:12 +0000523 return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t"
524 + ("state: " + this.currentState);
Ari Saha89831742015-06-26 10:31:48 -0700525 }
Ari Saha89831742015-06-26 10:31:48 -0700526
Ray Milkey78e95a42015-09-24 08:36:45 -0700527 abstract class State {
528 private final Logger log = getLogger(getClass());
Thomas Vachuskae9894202015-07-30 11:59:07 -0700529
Ray Milkey78e95a42015-09-24 08:36:45 -0700530 private String name = "State";
Ari Saha89831742015-06-26 10:31:48 -0700531
Ray Milkey78e95a42015-09-24 08:36:45 -0700532 public void start() throws StateMachineInvalidTransitionException {
533 log.warn("START transition from this state is not allowed.");
534 }
Ari Saha89831742015-06-26 10:31:48 -0700535
Ray Milkey78e95a42015-09-24 08:36:45 -0700536 public void requestAccess() throws StateMachineInvalidTransitionException {
537 log.warn("REQUEST ACCESS transition from this state is not allowed.");
538 }
539
540 public void radiusAccepted() throws StateMachineInvalidTransitionException {
541 log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
542 }
543
544 public void radiusDenied() throws StateMachineInvalidTransitionException {
545 log.warn("DENY ACCESS transition from this state is not allowed.");
546 }
547
548 public void logoff() throws StateMachineInvalidTransitionException {
549 log.warn("LOGOFF transition from this state is not allowed.");
550 }
Ari Saha89831742015-06-26 10:31:48 -0700551 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700552
Ray Milkey78e95a42015-09-24 08:36:45 -0700553 /**
554 * Idle state: supplicant is logged of from the network.
555 */
556 class Idle extends State {
557 private final Logger log = getLogger(getClass());
558 private String name = "IDLE_STATE";
559
Shubham Sharma1e43c562019-06-19 14:18:12 +0000560 @Override
Ray Milkey78e95a42015-09-24 08:36:45 -0700561 public void start() {
562 log.info("Moving from IDLE state to STARTED state.");
563 }
Ari Saha89831742015-06-26 10:31:48 -0700564 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700565
Ray Milkey78e95a42015-09-24 08:36:45 -0700566 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000567 * Started state: supplicant has entered the network and informed the
568 * authenticator.
Ray Milkey78e95a42015-09-24 08:36:45 -0700569 */
570 class Started extends State {
571 private final Logger log = getLogger(getClass());
572 private String name = "STARTED_STATE";
573
Shubham Sharma1e43c562019-06-19 14:18:12 +0000574 @Override
Ray Milkey78e95a42015-09-24 08:36:45 -0700575 public void requestAccess() {
576 log.info("Moving from STARTED state to PENDING state.");
577 }
Ari Saha89831742015-06-26 10:31:48 -0700578 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700579
Ray Milkey78e95a42015-09-24 08:36:45 -0700580 /**
Shubham Sharma1e43c562019-06-19 14:18:12 +0000581 * Pending state: supplicant has been identified by the authenticator but has
582 * not access yet.
Ray Milkey78e95a42015-09-24 08:36:45 -0700583 */
584 class Pending extends State {
585 private final Logger log = getLogger(getClass());
586 private String name = "PENDING_STATE";
587
Shubham Sharma1e43c562019-06-19 14:18:12 +0000588 @Override
Ray Milkey78e95a42015-09-24 08:36:45 -0700589 public void radiusAccepted() {
590 log.info("Moving from PENDING state to AUTHORIZED state.");
591 }
592
Shubham Sharma1e43c562019-06-19 14:18:12 +0000593 @Override
Ray Milkey78e95a42015-09-24 08:36:45 -0700594 public void radiusDenied() {
595 log.info("Moving from PENDING state to UNAUTHORIZED state.");
596 }
Ari Saha89831742015-06-26 10:31:48 -0700597 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700598
Ray Milkey78e95a42015-09-24 08:36:45 -0700599 /**
600 * Authorized state: supplicant port has been accepted, access is granted.
601 */
602 class Authorized extends State {
603 private final Logger log = getLogger(getClass());
604 private String name = "AUTHORIZED_STATE";
Ari Saha89831742015-06-26 10:31:48 -0700605
Shubham Sharma1e43c562019-06-19 14:18:12 +0000606 @Override
Qianqian Hu0c349812016-02-15 17:25:22 +0800607 public void start() {
608 log.info("Moving from AUTHORIZED state to STARTED state.");
609 }
610
Shubham Sharma1e43c562019-06-19 14:18:12 +0000611 @Override
Ray Milkey78e95a42015-09-24 08:36:45 -0700612 public void logoff() {
Ari Saha89831742015-06-26 10:31:48 -0700613
Ray Milkey78e95a42015-09-24 08:36:45 -0700614 log.info("Moving from AUTHORIZED state to IDLE state.");
615 }
Ari Saha89831742015-06-26 10:31:48 -0700616 }
617
Ray Milkey78e95a42015-09-24 08:36:45 -0700618 /**
619 * Unauthorized state: supplicant port has been rejected, access is denied.
620 */
621 class Unauthorized extends State {
622 private final Logger log = getLogger(getClass());
623 private String name = "UNAUTHORIZED_STATE";
624
Shubham Sharma1e43c562019-06-19 14:18:12 +0000625 @Override
ke han04e47f32016-10-28 14:15:43 +0800626 public void start() {
627 log.info("Moving from UNAUTHORIZED state to STARTED state.");
628 }
629
Shubham Sharma1e43c562019-06-19 14:18:12 +0000630 @Override
Ray Milkey78e95a42015-09-24 08:36:45 -0700631 public void logoff() {
632 log.info("Moving from UNAUTHORIZED state to IDLE state.");
633 }
Ari Saha89831742015-06-26 10:31:48 -0700634 }
Ari Saha89831742015-06-26 10:31:48 -0700635
Shubham Sharma1e43c562019-06-19 14:18:12 +0000636 /**
637 * Class for cleaning the StateMachine for those session for which no response
638 * is coming--implementing timeout.
639 */
640 class CleanupTimerTask implements Runnable {
641 private final Logger log = getLogger(getClass());
642 private String sessionId;
643 private AaaManager aaaManager;
644
645 CleanupTimerTask(String sessionId, AaaManager aaaManager) {
646 this.sessionId = sessionId;
647 this.aaaManager = aaaManager;
648 }
649
650 @Override
651 public void run() {
652 StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId);
653 if (null != stateMachine) {
654 // Asserting if last packet received for this stateMachine session was beyond half of timeout period.
655 // StateMachine is considered eligible for cleanup when no packets has been exchanged by it with AAA
656 // Server or RG during a long period (half of timeout period). For example, when cleanup timer has
657 // been configured as 10 minutes, StateMachine would be cleaned up at the end of 10 minutes if
658 // the authentication is still pending and no packet was exchanged for this session during last 5
659 // minutes.
660
661 boolean noTrafficWithinThreshold = (System.currentTimeMillis()
662 - stateMachine.getLastPacketReceivedTime()) > ((cleanupTimerTimeOutInMins * 60 * 1000) / 2);
663
664 if ((TIMEOUT_ELIGIBLE_STATES.contains(stateMachine.state())) && noTrafficWithinThreshold) {
665 log.info("Deleting StateMachineMapping for sessionId: {}", sessionId);
666 cleanupTimer = null;
667 if (stateMachine.state() == STATE_PENDING && stateMachine.isWaitingForRadiusResponse()) {
668 aaaManager.aaaStatisticsManager.getAaaStats().increaseTimedOutPackets();
669 }
670 deleteStateMachineId(sessionId);
671 deleteStateMachineMapping(stateMachine);
672
673 // If StateMachine is not eligible for cleanup yet, reschedule cleanupTimer further.
674 } else {
675 aaaManager.scheduleStateMachineCleanupTimer(sessionId, stateMachine);
676 }
677 } else {
678 // This statement should not be logged; cleanupTimer should be cancelled for stateMachine
679 // instances which have been authenticated successfully.
680 log.warn("state-machine not found for sessionId: {}", sessionId);
681 }
682
683 }
684 }
Ari Saha89831742015-06-26 10:31:48 -0700685
Ari Saha89831742015-06-26 10:31:48 -0700686}