blob: 323313f15703f8f9d0daa8efe801127ebd6a3d12 [file] [log] [blame]
Ari Saha89831742015-06-26 10:31:48 -07001/*
2 *
3 * Copyright 2015 AT&T Foundry
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
alshabib6d527452016-06-01 18:00:47 -070019package org.opencord.aaa;
Ari Saha89831742015-06-26 10:31:48 -070020
Ray Milkeyf61a24e2015-09-24 16:34:02 -070021import java.util.BitSet;
22import java.util.Map;
23
Ari Saha89831742015-06-26 10:31:48 -070024import org.onlab.packet.MacAddress;
25import org.onosproject.net.ConnectPoint;
Ari Saha89831742015-06-26 10:31:48 -070026import org.slf4j.Logger;
Thomas Vachuskae9894202015-07-30 11:59:07 -070027
Ray Milkeyf61a24e2015-09-24 16:34:02 -070028import com.google.common.collect.Maps;
Ari Saha89831742015-06-26 10:31:48 -070029
30import static org.slf4j.LoggerFactory.getLogger;
31
32/**
33 * AAA Finite State Machine.
34 */
35
36class StateMachine {
37 //INDEX to identify the state in the transition table
38 static final int STATE_IDLE = 0;
39 static final int STATE_STARTED = 1;
40 static final int STATE_PENDING = 2;
41 static final int STATE_AUTHORIZED = 3;
42 static final int STATE_UNAUTHORIZED = 4;
43
44 //INDEX to identify the transition in the transition table
45 static final int TRANSITION_START = 0; // --> started
46 static final int TRANSITION_REQUEST_ACCESS = 1;
47 static final int TRANSITION_AUTHORIZE_ACCESS = 2;
48 static final int TRANSITION_DENY_ACCESS = 3;
49 static final int TRANSITION_LOGOFF = 4;
50
51 //map of access identifiers (issued at EAPOL START)
52 static BitSet bitSet = new BitSet();
Ari Saha89831742015-06-26 10:31:48 -070053
54 private int identifier = -1;
55 private byte challengeIdentifier;
56 private byte[] challengeState;
57 private byte[] username;
58 private byte[] requestAuthenticator;
59
60 // Supplicant connectivity info
Ray Milkeyf61a24e2015-09-24 16:34:02 -070061 private ConnectPoint supplicantConnectpoint;
62 private MacAddress supplicantAddress;
63 private short vlanId;
Ari Saha89831742015-06-26 10:31:48 -070064
65 private String sessionId = null;
66
67 private final Logger log = getLogger(getClass());
68
69
70 private State[] states = {
71 new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized()
72 };
73
74
75 //State transition table
76 /*
77
78 state IDLE | STARTED | PENDING | AUTHORIZED | UNAUTHORIZED
79 ////
80 input
81 ----------------------------------------------------------------------------------------------------
82
ke han04e47f32016-10-28 14:15:43 +080083 START STARTED | _ | _ | STARTED | STARTED
Ari Saha89831742015-06-26 10:31:48 -070084
85 REQUEST_ACCESS _ | PENDING | _ | _ | _
86
87 AUTHORIZE_ACCESS _ | _ | AUTHORIZED | _ | _
88
89 DENY_ACCESS _ | - | UNAUTHORIZED | _ | _
90
91 LOGOFF _ | _ | _ | IDLE | IDLE
92 */
93
94 private int[] idleTransition =
95 {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE};
96 private int[] startedTransition =
97 {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED};
98 private int[] pendingTransition =
99 {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED, STATE_PENDING};
100 private int[] authorizedTransition =
Qianqian Hu0c349812016-02-15 17:25:22 +0800101 {STATE_STARTED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
Ari Saha89831742015-06-26 10:31:48 -0700102 private int[] unauthorizedTransition =
ke han04e47f32016-10-28 14:15:43 +0800103 {STATE_STARTED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_IDLE};
Ari Saha89831742015-06-26 10:31:48 -0700104
105 //THE TRANSITION TABLE
106 private int[][] transition =
107 {idleTransition, startedTransition, pendingTransition, authorizedTransition,
108 unauthorizedTransition};
109
110 private int currentState = STATE_IDLE;
111
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700112 // Maps of state machines. Each state machine is represented by an
113 // unique identifier on the switch: dpid + port number
114 private static Map<String, StateMachine> sessionIdMap;
115 private static Map<Integer, StateMachine> identifierMap;
Ari Saha89831742015-06-26 10:31:48 -0700116
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700117 public static void initializeMaps() {
118 sessionIdMap = Maps.newConcurrentMap();
119 identifierMap = Maps.newConcurrentMap();
120 }
121
122 public static void destroyMaps() {
123 sessionIdMap = null;
124 identifierMap = null;
125 }
126
Qianqian Hu61a6a402016-02-16 15:18:05 +0800127 public static Map<String, StateMachine> sessionIdMap() {
128 return sessionIdMap;
129 }
130
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700131 public static StateMachine lookupStateMachineById(byte identifier) {
132 return identifierMap.get((int) identifier);
133 }
134
135 public static StateMachine lookupStateMachineBySessionId(String sessionId) {
136 return sessionIdMap.get(sessionId);
137 } /**
Ari Saha89831742015-06-26 10:31:48 -0700138 * State Machine Constructor.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700139 *
140 * @param sessionId session Id represented by the switch dpid + port number
Ari Saha89831742015-06-26 10:31:48 -0700141 */
Ray Milkey508cec52016-02-16 14:23:26 -0800142 public StateMachine(String sessionId) {
Ari Saha89831742015-06-26 10:31:48 -0700143 log.info("Creating a new state machine for {}", sessionId);
144 this.sessionId = sessionId;
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700145 sessionIdMap.put(sessionId, this);
Ari Saha89831742015-06-26 10:31:48 -0700146 }
147
148 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700149 * Gets the connect point for the supplicant side.
150 *
151 * @return supplicant connect point
152 */
153 public ConnectPoint supplicantConnectpoint() {
154 return supplicantConnectpoint;
155 }
156
157 /**
158 * Sets the supplicant side connect point.
159 *
160 * @param supplicantConnectpoint supplicant select point.
161 */
162 public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) {
163 this.supplicantConnectpoint = supplicantConnectpoint;
164 }
165
166 /**
167 * Gets the MAC address of the supplicant.
168 *
169 * @return supplicant MAC address
170 */
171 public MacAddress supplicantAddress() {
172 return supplicantAddress;
173 }
174
175 /**
176 * Sets the supplicant MAC address.
177 *
178 * @param supplicantAddress new supplicant MAC address
179 */
180 public void setSupplicantAddress(MacAddress supplicantAddress) {
181 this.supplicantAddress = supplicantAddress;
182 }
183
184 /**
185 * Gets the client's Vlan ID.
186 *
187 * @return client vlan ID
188 */
189 public short vlanId() {
190 return vlanId;
191 }
192
193 /**
194 * Sets the client's vlan ID.
195 *
196 * @param vlanId new client vlan ID
197 */
198 public void setVlanId(short vlanId) {
199 this.vlanId = vlanId;
200 }
201
202 /**
203 * Gets the client id that is requesting for access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700204 *
Ari Saha89831742015-06-26 10:31:48 -0700205 * @return The client id.
206 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700207 public String sessionId() {
Ari Saha89831742015-06-26 10:31:48 -0700208 return this.sessionId;
209 }
210
211 /**
212 * Create the identifier for the state machine (happens when goes to STARTED state).
213 */
214 private void createIdentifier() throws StateMachineException {
215 log.debug("Creating Identifier.");
Ray Milkey78e95a42015-09-24 08:36:45 -0700216 int index;
Ari Saha89831742015-06-26 10:31:48 -0700217
218 try {
219 //find the first available spot for identifier assignment
220 index = StateMachine.bitSet.nextClearBit(0);
221
222 //there is a limit of 256 identifiers
223 if (index == 256) {
224 throw new StateMachineException("Cannot handle any new identifier. Limit is 256.");
225 }
226 } catch (IndexOutOfBoundsException e) {
227 throw new StateMachineException(e.getMessage());
228 }
229
230 log.info("Assigning identifier {}", index);
231 StateMachine.bitSet.set(index);
232 this.identifier = index;
233 }
234
235 /**
236 * Set the challenge identifier and the state issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700237 *
Ari Saha89831742015-06-26 10:31:48 -0700238 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700239 * @param challengeState The challenge state from the RADIUS.
Ari Saha89831742015-06-26 10:31:48 -0700240 */
241 protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
242 this.challengeIdentifier = challengeIdentifier;
243 this.challengeState = challengeState;
244 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700245
Ari Saha89831742015-06-26 10:31:48 -0700246 /**
247 * Set the challenge identifier issued by the RADIUS on the access challenge request.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700248 *
Ari Saha89831742015-06-26 10:31:48 -0700249 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
250 */
251 protected void setChallengeIdentifier(byte challengeIdentifier) {
252 log.info("Set Challenge Identifier to {}", challengeIdentifier);
253 this.challengeIdentifier = challengeIdentifier;
254 }
255
256 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700257 * Gets the challenge EAP identifier set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700258 *
Ari Saha89831742015-06-26 10:31:48 -0700259 * @return The challenge EAP identifier.
260 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700261 protected byte challengeIdentifier() {
Ari Saha89831742015-06-26 10:31:48 -0700262 return this.challengeIdentifier;
263 }
264
265
266 /**
267 * Set the challenge state info issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700268 *
Ari Saha89831742015-06-26 10:31:48 -0700269 * @param challengeState The challenge state from the RADIUS.
270 */
271 protected void setChallengeState(byte[] challengeState) {
272 log.info("Set Challenge State");
273 this.challengeState = challengeState;
274 }
275
276 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700277 * Gets the challenge state set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700278 *
Ari Saha89831742015-06-26 10:31:48 -0700279 * @return The challenge state.
280 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700281 protected byte[] challengeState() {
Ari Saha89831742015-06-26 10:31:48 -0700282 return this.challengeState;
283 }
284
285 /**
286 * Set the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700287 *
Ari Saha89831742015-06-26 10:31:48 -0700288 * @param username The username sent to the RADIUS upon access request.
289 */
290 protected void setUsername(byte[] username) {
291 this.username = username;
292 }
293
294
295 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700296 * Gets the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700297 *
Ari Saha89831742015-06-26 10:31:48 -0700298 * @return The requestAuthenticator.
299 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700300 protected byte[] requestAuthenticator() {
Ari Saha89831742015-06-26 10:31:48 -0700301 return this.requestAuthenticator;
302 }
303
304 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700305 * Sets the authenticator.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700306 *
Ari Saha89831742015-06-26 10:31:48 -0700307 * @param authenticator The username sent to the RADIUS upon access request.
308 */
309 protected void setRequestAuthenticator(byte[] authenticator) {
310 this.requestAuthenticator = authenticator;
311 }
312
313
314 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700315 * Gets the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700316 *
Ari Saha89831742015-06-26 10:31:48 -0700317 * @return The username.
318 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700319 protected byte[] username() {
Ari Saha89831742015-06-26 10:31:48 -0700320 return this.username;
321 }
322
323 /**
324 * Return the identifier of the state machine.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700325 *
Ari Saha89831742015-06-26 10:31:48 -0700326 * @return The state machine identifier.
327 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700328 public byte identifier() {
Ari Saha89831742015-06-26 10:31:48 -0700329 return (byte) this.identifier;
330 }
331
332
333 protected void deleteIdentifier() {
334 if (this.identifier != -1) {
335 log.info("Freeing up " + this.identifier);
336 //this state machine should be deleted and free up the identifier
337 StateMachine.bitSet.clear(this.identifier);
338 this.identifier = -1;
339 }
340 }
341
342
343 /**
344 * Move to the next state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700345 *
Ray Milkey78e95a42015-09-24 08:36:45 -0700346 * @param msg message
Ari Saha89831742015-06-26 10:31:48 -0700347 */
Thomas Vachuskae9894202015-07-30 11:59:07 -0700348 private void next(int msg) {
Ari Saha89831742015-06-26 10:31:48 -0700349 currentState = transition[currentState][msg];
350 log.info("Current State " + currentState);
351 }
352
353 /**
354 * Client has requested the start action to allow network access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700355 *
356 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700357 */
358 public void start() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700359 states[currentState].start();
360 //move to the next state
361 next(TRANSITION_START);
362 createIdentifier();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700363 identifierMap.put(identifier, this);
Ari Saha89831742015-06-26 10:31:48 -0700364 }
365
366 /**
367 * An Identification information has been sent by the supplicant.
368 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700369 *
370 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700371 */
372 public void requestAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700373 states[currentState].requestAccess();
374 //move to the next state
375 next(TRANSITION_REQUEST_ACCESS);
Ari Saha89831742015-06-26 10:31:48 -0700376 }
377
378 /**
379 * RADIUS has accepted the identification.
380 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700381 *
382 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700383 */
384 public void authorizeAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700385 states[currentState].radiusAccepted();
386 //move to the next state
387 next(TRANSITION_AUTHORIZE_ACCESS);
Ari Saha89831742015-06-26 10:31:48 -0700388
Ray Milkey508cec52016-02-16 14:23:26 -0800389 // TODO: put in calls to launch vSG here
Ari Saha89831742015-06-26 10:31:48 -0700390
Ray Milkey78e95a42015-09-24 08:36:45 -0700391 deleteIdentifier();
Ari Saha89831742015-06-26 10:31:48 -0700392 }
393
394 /**
395 * RADIUS has denied the identification.
396 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700397 *
398 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700399 */
400 public void denyAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700401 states[currentState].radiusDenied();
402 //move to the next state
403 next(TRANSITION_DENY_ACCESS);
404 deleteIdentifier();
Ari Saha89831742015-06-26 10:31:48 -0700405 }
406
407 /**
408 * Logoff request has been requested.
409 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700410 *
411 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700412 */
413 public void logoff() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700414 states[currentState].logoff();
415 //move to the next state
416 next(TRANSITION_LOGOFF);
Ari Saha89831742015-06-26 10:31:48 -0700417 }
418
419 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700420 * Gets the current state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700421 *
Ari Saha89831742015-06-26 10:31:48 -0700422 * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
423 * STATE_UNAUTHORIZED.
424 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700425 public int state() {
Ari Saha89831742015-06-26 10:31:48 -0700426 return currentState;
427 }
428
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700429 @Override
Ari Saha89831742015-06-26 10:31:48 -0700430 public String toString() {
431 return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
432 ("state: " + this.currentState);
433 }
Ari Saha89831742015-06-26 10:31:48 -0700434
Ray Milkey78e95a42015-09-24 08:36:45 -0700435 abstract class State {
436 private final Logger log = getLogger(getClass());
Thomas Vachuskae9894202015-07-30 11:59:07 -0700437
Ray Milkey78e95a42015-09-24 08:36:45 -0700438 private String name = "State";
Ari Saha89831742015-06-26 10:31:48 -0700439
Ray Milkey78e95a42015-09-24 08:36:45 -0700440 public void start() throws StateMachineInvalidTransitionException {
441 log.warn("START transition from this state is not allowed.");
442 }
Ari Saha89831742015-06-26 10:31:48 -0700443
Ray Milkey78e95a42015-09-24 08:36:45 -0700444 public void requestAccess() throws StateMachineInvalidTransitionException {
445 log.warn("REQUEST ACCESS transition from this state is not allowed.");
446 }
447
448 public void radiusAccepted() throws StateMachineInvalidTransitionException {
449 log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
450 }
451
452 public void radiusDenied() throws StateMachineInvalidTransitionException {
453 log.warn("DENY ACCESS transition from this state is not allowed.");
454 }
455
456 public void logoff() throws StateMachineInvalidTransitionException {
457 log.warn("LOGOFF transition from this state is not allowed.");
458 }
Ari Saha89831742015-06-26 10:31:48 -0700459 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700460
Ray Milkey78e95a42015-09-24 08:36:45 -0700461 /**
462 * Idle state: supplicant is logged of from the network.
463 */
464 class Idle extends State {
465 private final Logger log = getLogger(getClass());
466 private String name = "IDLE_STATE";
467
468 public void start() {
469 log.info("Moving from IDLE state to STARTED state.");
470 }
Ari Saha89831742015-06-26 10:31:48 -0700471 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700472
Ray Milkey78e95a42015-09-24 08:36:45 -0700473 /**
474 * Started state: supplicant has entered the network and informed the authenticator.
475 */
476 class Started extends State {
477 private final Logger log = getLogger(getClass());
478 private String name = "STARTED_STATE";
479
480 public void requestAccess() {
481 log.info("Moving from STARTED state to PENDING state.");
482 }
Ari Saha89831742015-06-26 10:31:48 -0700483 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700484
Ray Milkey78e95a42015-09-24 08:36:45 -0700485 /**
486 * Pending state: supplicant has been identified by the authenticator but has not access yet.
487 */
488 class Pending extends State {
489 private final Logger log = getLogger(getClass());
490 private String name = "PENDING_STATE";
491
492 public void radiusAccepted() {
493 log.info("Moving from PENDING state to AUTHORIZED state.");
494 }
495
496 public void radiusDenied() {
497 log.info("Moving from PENDING state to UNAUTHORIZED state.");
498 }
Ari Saha89831742015-06-26 10:31:48 -0700499 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700500
Ray Milkey78e95a42015-09-24 08:36:45 -0700501 /**
502 * Authorized state: supplicant port has been accepted, access is granted.
503 */
504 class Authorized extends State {
505 private final Logger log = getLogger(getClass());
506 private String name = "AUTHORIZED_STATE";
Ari Saha89831742015-06-26 10:31:48 -0700507
Qianqian Hu0c349812016-02-15 17:25:22 +0800508 public void start() {
509 log.info("Moving from AUTHORIZED state to STARTED state.");
510 }
511
Ray Milkey78e95a42015-09-24 08:36:45 -0700512 public void logoff() {
Ari Saha89831742015-06-26 10:31:48 -0700513
Ray Milkey78e95a42015-09-24 08:36:45 -0700514 log.info("Moving from AUTHORIZED state to IDLE state.");
515 }
Ari Saha89831742015-06-26 10:31:48 -0700516 }
517
Ray Milkey78e95a42015-09-24 08:36:45 -0700518 /**
519 * Unauthorized state: supplicant port has been rejected, access is denied.
520 */
521 class Unauthorized extends State {
522 private final Logger log = getLogger(getClass());
523 private String name = "UNAUTHORIZED_STATE";
524
ke han04e47f32016-10-28 14:15:43 +0800525 public void start() {
526 log.info("Moving from UNAUTHORIZED state to STARTED state.");
527 }
528
Ray Milkey78e95a42015-09-24 08:36:45 -0700529 public void logoff() {
530 log.info("Moving from UNAUTHORIZED state to IDLE state.");
531 }
Ari Saha89831742015-06-26 10:31:48 -0700532 }
Ari Saha89831742015-06-26 10:31:48 -0700533
534
Ari Saha89831742015-06-26 10:31:48 -0700535}