blob: 6b8ebcbc8d77e2fb84c4ea931fd33004cc88fc9e [file] [log] [blame]
Ari Saha79d7c252015-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
19package org.onosproject.aaa;
20
Ray Milkey75879ef2015-09-24 16:34:02 -070021import java.util.BitSet;
22import java.util.Map;
23
Ari Saha79d7c252015-06-26 10:31:48 -070024import org.onlab.packet.MacAddress;
25import org.onosproject.net.ConnectPoint;
Ari Saha79d7c252015-06-26 10:31:48 -070026import org.slf4j.Logger;
Thomas Vachuskad894b5d2015-07-30 11:59:07 -070027
Ray Milkey75879ef2015-09-24 16:34:02 -070028import com.google.common.collect.Maps;
Ari Saha79d7c252015-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 Saha79d7c252015-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 Milkey75879ef2015-09-24 16:34:02 -070061 private ConnectPoint supplicantConnectpoint;
62 private MacAddress supplicantAddress;
63 private short vlanId;
Ari Saha79d7c252015-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
Qianqian Hu5f374cd2016-02-15 17:25:22 +080083 START STARTED | _ | _ | STARTED | _
Ari Saha79d7c252015-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 Hu5f374cd2016-02-15 17:25:22 +0800101 {STATE_STARTED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
Ari Saha79d7c252015-06-26 10:31:48 -0700102 private int[] unauthorizedTransition =
103 {STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_IDLE};
104
105 //THE TRANSITION TABLE
106 private int[][] transition =
107 {idleTransition, startedTransition, pendingTransition, authorizedTransition,
108 unauthorizedTransition};
109
110 private int currentState = STATE_IDLE;
111
Ray Milkey75879ef2015-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 Saha79d7c252015-06-26 10:31:48 -0700116
Ray Milkey75879ef2015-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
127 public static StateMachine lookupStateMachineById(byte identifier) {
128 return identifierMap.get((int) identifier);
129 }
130
131 public static StateMachine lookupStateMachineBySessionId(String sessionId) {
132 return sessionIdMap.get(sessionId);
133 } /**
Ari Saha79d7c252015-06-26 10:31:48 -0700134 * State Machine Constructor.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700135 *
136 * @param sessionId session Id represented by the switch dpid + port number
Ari Saha79d7c252015-06-26 10:31:48 -0700137 */
Ray Milkey3c6c2f92016-02-16 14:23:26 -0800138 public StateMachine(String sessionId) {
Ari Saha79d7c252015-06-26 10:31:48 -0700139 log.info("Creating a new state machine for {}", sessionId);
140 this.sessionId = sessionId;
Ray Milkey75879ef2015-09-24 16:34:02 -0700141 sessionIdMap.put(sessionId, this);
Ari Saha79d7c252015-06-26 10:31:48 -0700142 }
143
144 /**
Ray Milkey75879ef2015-09-24 16:34:02 -0700145 * Gets the connect point for the supplicant side.
146 *
147 * @return supplicant connect point
148 */
149 public ConnectPoint supplicantConnectpoint() {
150 return supplicantConnectpoint;
151 }
152
153 /**
154 * Sets the supplicant side connect point.
155 *
156 * @param supplicantConnectpoint supplicant select point.
157 */
158 public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) {
159 this.supplicantConnectpoint = supplicantConnectpoint;
160 }
161
162 /**
163 * Gets the MAC address of the supplicant.
164 *
165 * @return supplicant MAC address
166 */
167 public MacAddress supplicantAddress() {
168 return supplicantAddress;
169 }
170
171 /**
172 * Sets the supplicant MAC address.
173 *
174 * @param supplicantAddress new supplicant MAC address
175 */
176 public void setSupplicantAddress(MacAddress supplicantAddress) {
177 this.supplicantAddress = supplicantAddress;
178 }
179
180 /**
181 * Gets the client's Vlan ID.
182 *
183 * @return client vlan ID
184 */
185 public short vlanId() {
186 return vlanId;
187 }
188
189 /**
190 * Sets the client's vlan ID.
191 *
192 * @param vlanId new client vlan ID
193 */
194 public void setVlanId(short vlanId) {
195 this.vlanId = vlanId;
196 }
197
198 /**
199 * Gets the client id that is requesting for access.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700200 *
Ari Saha79d7c252015-06-26 10:31:48 -0700201 * @return The client id.
202 */
Ray Milkey75879ef2015-09-24 16:34:02 -0700203 public String sessionId() {
Ari Saha79d7c252015-06-26 10:31:48 -0700204 return this.sessionId;
205 }
206
207 /**
208 * Create the identifier for the state machine (happens when goes to STARTED state).
209 */
210 private void createIdentifier() throws StateMachineException {
211 log.debug("Creating Identifier.");
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700212 int index;
Ari Saha79d7c252015-06-26 10:31:48 -0700213
214 try {
215 //find the first available spot for identifier assignment
216 index = StateMachine.bitSet.nextClearBit(0);
217
218 //there is a limit of 256 identifiers
219 if (index == 256) {
220 throw new StateMachineException("Cannot handle any new identifier. Limit is 256.");
221 }
222 } catch (IndexOutOfBoundsException e) {
223 throw new StateMachineException(e.getMessage());
224 }
225
226 log.info("Assigning identifier {}", index);
227 StateMachine.bitSet.set(index);
228 this.identifier = index;
229 }
230
231 /**
232 * Set the challenge identifier and the state issued by the RADIUS.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700233 *
Ari Saha79d7c252015-06-26 10:31:48 -0700234 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700235 * @param challengeState The challenge state from the RADIUS.
Ari Saha79d7c252015-06-26 10:31:48 -0700236 */
237 protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
238 this.challengeIdentifier = challengeIdentifier;
239 this.challengeState = challengeState;
240 }
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700241
Ari Saha79d7c252015-06-26 10:31:48 -0700242 /**
243 * Set the challenge identifier issued by the RADIUS on the access challenge request.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700244 *
Ari Saha79d7c252015-06-26 10:31:48 -0700245 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
246 */
247 protected void setChallengeIdentifier(byte challengeIdentifier) {
248 log.info("Set Challenge Identifier to {}", challengeIdentifier);
249 this.challengeIdentifier = challengeIdentifier;
250 }
251
252 /**
Ray Milkey75879ef2015-09-24 16:34:02 -0700253 * Gets the challenge EAP identifier set by the RADIUS.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700254 *
Ari Saha79d7c252015-06-26 10:31:48 -0700255 * @return The challenge EAP identifier.
256 */
Ray Milkey75879ef2015-09-24 16:34:02 -0700257 protected byte challengeIdentifier() {
Ari Saha79d7c252015-06-26 10:31:48 -0700258 return this.challengeIdentifier;
259 }
260
261
262 /**
263 * Set the challenge state info issued by the RADIUS.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700264 *
Ari Saha79d7c252015-06-26 10:31:48 -0700265 * @param challengeState The challenge state from the RADIUS.
266 */
267 protected void setChallengeState(byte[] challengeState) {
268 log.info("Set Challenge State");
269 this.challengeState = challengeState;
270 }
271
272 /**
Ray Milkey75879ef2015-09-24 16:34:02 -0700273 * Gets the challenge state set by the RADIUS.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700274 *
Ari Saha79d7c252015-06-26 10:31:48 -0700275 * @return The challenge state.
276 */
Ray Milkey75879ef2015-09-24 16:34:02 -0700277 protected byte[] challengeState() {
Ari Saha79d7c252015-06-26 10:31:48 -0700278 return this.challengeState;
279 }
280
281 /**
282 * Set the username.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700283 *
Ari Saha79d7c252015-06-26 10:31:48 -0700284 * @param username The username sent to the RADIUS upon access request.
285 */
286 protected void setUsername(byte[] username) {
287 this.username = username;
288 }
289
290
291 /**
Ray Milkey75879ef2015-09-24 16:34:02 -0700292 * Gets the username.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700293 *
Ari Saha79d7c252015-06-26 10:31:48 -0700294 * @return The requestAuthenticator.
295 */
Ray Milkey75879ef2015-09-24 16:34:02 -0700296 protected byte[] requestAuthenticator() {
Ari Saha79d7c252015-06-26 10:31:48 -0700297 return this.requestAuthenticator;
298 }
299
300 /**
Ray Milkey75879ef2015-09-24 16:34:02 -0700301 * Sets the authenticator.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700302 *
Ari Saha79d7c252015-06-26 10:31:48 -0700303 * @param authenticator The username sent to the RADIUS upon access request.
304 */
305 protected void setRequestAuthenticator(byte[] authenticator) {
306 this.requestAuthenticator = authenticator;
307 }
308
309
310 /**
Ray Milkey75879ef2015-09-24 16:34:02 -0700311 * Gets the username.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700312 *
Ari Saha79d7c252015-06-26 10:31:48 -0700313 * @return The username.
314 */
Ray Milkey75879ef2015-09-24 16:34:02 -0700315 protected byte[] username() {
Ari Saha79d7c252015-06-26 10:31:48 -0700316 return this.username;
317 }
318
319 /**
320 * Return the identifier of the state machine.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700321 *
Ari Saha79d7c252015-06-26 10:31:48 -0700322 * @return The state machine identifier.
323 */
Ray Milkey75879ef2015-09-24 16:34:02 -0700324 public byte identifier() {
Ari Saha79d7c252015-06-26 10:31:48 -0700325 return (byte) this.identifier;
326 }
327
328
329 protected void deleteIdentifier() {
330 if (this.identifier != -1) {
331 log.info("Freeing up " + this.identifier);
332 //this state machine should be deleted and free up the identifier
333 StateMachine.bitSet.clear(this.identifier);
334 this.identifier = -1;
335 }
336 }
337
338
339 /**
340 * Move to the next state.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700341 *
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700342 * @param msg message
Ari Saha79d7c252015-06-26 10:31:48 -0700343 */
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700344 private void next(int msg) {
Ari Saha79d7c252015-06-26 10:31:48 -0700345 currentState = transition[currentState][msg];
346 log.info("Current State " + currentState);
347 }
348
349 /**
350 * Client has requested the start action to allow network access.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700351 *
352 * @throws StateMachineException if authentication protocol is violated
Ari Saha79d7c252015-06-26 10:31:48 -0700353 */
354 public void start() throws StateMachineException {
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700355 states[currentState].start();
356 //move to the next state
357 next(TRANSITION_START);
358 createIdentifier();
Ray Milkey75879ef2015-09-24 16:34:02 -0700359 identifierMap.put(identifier, this);
Ari Saha79d7c252015-06-26 10:31:48 -0700360 }
361
362 /**
363 * An Identification information has been sent by the supplicant.
364 * Move to the next state if possible.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700365 *
366 * @throws StateMachineException if authentication protocol is violated
Ari Saha79d7c252015-06-26 10:31:48 -0700367 */
368 public void requestAccess() throws StateMachineException {
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700369 states[currentState].requestAccess();
370 //move to the next state
371 next(TRANSITION_REQUEST_ACCESS);
Ari Saha79d7c252015-06-26 10:31:48 -0700372 }
373
374 /**
375 * RADIUS has accepted the identification.
376 * Move to the next state if possible.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700377 *
378 * @throws StateMachineException if authentication protocol is violated
Ari Saha79d7c252015-06-26 10:31:48 -0700379 */
380 public void authorizeAccess() throws StateMachineException {
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700381 states[currentState].radiusAccepted();
382 //move to the next state
383 next(TRANSITION_AUTHORIZE_ACCESS);
Ari Saha79d7c252015-06-26 10:31:48 -0700384
Ray Milkey3c6c2f92016-02-16 14:23:26 -0800385 // TODO: put in calls to launch vSG here
Ari Saha79d7c252015-06-26 10:31:48 -0700386
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700387 deleteIdentifier();
Ari Saha79d7c252015-06-26 10:31:48 -0700388 }
389
390 /**
391 * RADIUS has denied the identification.
392 * Move to the next state if possible.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700393 *
394 * @throws StateMachineException if authentication protocol is violated
Ari Saha79d7c252015-06-26 10:31:48 -0700395 */
396 public void denyAccess() throws StateMachineException {
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700397 states[currentState].radiusDenied();
398 //move to the next state
399 next(TRANSITION_DENY_ACCESS);
400 deleteIdentifier();
Ari Saha79d7c252015-06-26 10:31:48 -0700401 }
402
403 /**
404 * Logoff request has been requested.
405 * Move to the next state if possible.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700406 *
407 * @throws StateMachineException if authentication protocol is violated
Ari Saha79d7c252015-06-26 10:31:48 -0700408 */
409 public void logoff() throws StateMachineException {
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700410 states[currentState].logoff();
411 //move to the next state
412 next(TRANSITION_LOGOFF);
Ari Saha79d7c252015-06-26 10:31:48 -0700413 }
414
415 /**
Ray Milkey75879ef2015-09-24 16:34:02 -0700416 * Gets the current state.
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700417 *
Ari Saha79d7c252015-06-26 10:31:48 -0700418 * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
419 * STATE_UNAUTHORIZED.
420 */
Ray Milkey75879ef2015-09-24 16:34:02 -0700421 public int state() {
Ari Saha79d7c252015-06-26 10:31:48 -0700422 return currentState;
423 }
424
Ray Milkey75879ef2015-09-24 16:34:02 -0700425 @Override
Ari Saha79d7c252015-06-26 10:31:48 -0700426 public String toString() {
427 return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
428 ("state: " + this.currentState);
429 }
Ari Saha79d7c252015-06-26 10:31:48 -0700430
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700431 abstract class State {
432 private final Logger log = getLogger(getClass());
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700433
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700434 private String name = "State";
Ari Saha79d7c252015-06-26 10:31:48 -0700435
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700436 public void start() throws StateMachineInvalidTransitionException {
437 log.warn("START transition from this state is not allowed.");
438 }
Ari Saha79d7c252015-06-26 10:31:48 -0700439
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700440 public void requestAccess() throws StateMachineInvalidTransitionException {
441 log.warn("REQUEST ACCESS transition from this state is not allowed.");
442 }
443
444 public void radiusAccepted() throws StateMachineInvalidTransitionException {
445 log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
446 }
447
448 public void radiusDenied() throws StateMachineInvalidTransitionException {
449 log.warn("DENY ACCESS transition from this state is not allowed.");
450 }
451
452 public void logoff() throws StateMachineInvalidTransitionException {
453 log.warn("LOGOFF transition from this state is not allowed.");
454 }
Ari Saha79d7c252015-06-26 10:31:48 -0700455 }
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700456
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700457 /**
458 * Idle state: supplicant is logged of from the network.
459 */
460 class Idle extends State {
461 private final Logger log = getLogger(getClass());
462 private String name = "IDLE_STATE";
463
464 public void start() {
465 log.info("Moving from IDLE state to STARTED state.");
466 }
Ari Saha79d7c252015-06-26 10:31:48 -0700467 }
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700468
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700469 /**
470 * Started state: supplicant has entered the network and informed the authenticator.
471 */
472 class Started extends State {
473 private final Logger log = getLogger(getClass());
474 private String name = "STARTED_STATE";
475
476 public void requestAccess() {
477 log.info("Moving from STARTED state to PENDING state.");
478 }
Ari Saha79d7c252015-06-26 10:31:48 -0700479 }
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700480
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700481 /**
482 * Pending state: supplicant has been identified by the authenticator but has not access yet.
483 */
484 class Pending extends State {
485 private final Logger log = getLogger(getClass());
486 private String name = "PENDING_STATE";
487
488 public void radiusAccepted() {
489 log.info("Moving from PENDING state to AUTHORIZED state.");
490 }
491
492 public void radiusDenied() {
493 log.info("Moving from PENDING state to UNAUTHORIZED state.");
494 }
Ari Saha79d7c252015-06-26 10:31:48 -0700495 }
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700496
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700497 /**
498 * Authorized state: supplicant port has been accepted, access is granted.
499 */
500 class Authorized extends State {
501 private final Logger log = getLogger(getClass());
502 private String name = "AUTHORIZED_STATE";
Ari Saha79d7c252015-06-26 10:31:48 -0700503
Qianqian Hu5f374cd2016-02-15 17:25:22 +0800504 public void start() {
505 log.info("Moving from AUTHORIZED state to STARTED state.");
506 }
507
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700508 public void logoff() {
Ari Saha79d7c252015-06-26 10:31:48 -0700509
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700510 log.info("Moving from AUTHORIZED state to IDLE state.");
511 }
Ari Saha79d7c252015-06-26 10:31:48 -0700512 }
513
Ray Milkeyc3fca7f2015-09-24 08:36:45 -0700514 /**
515 * Unauthorized state: supplicant port has been rejected, access is denied.
516 */
517 class Unauthorized extends State {
518 private final Logger log = getLogger(getClass());
519 private String name = "UNAUTHORIZED_STATE";
520
521 public void logoff() {
522 log.info("Moving from UNAUTHORIZED state to IDLE state.");
523 }
Ari Saha79d7c252015-06-26 10:31:48 -0700524 }
Ari Saha79d7c252015-06-26 10:31:48 -0700525
526
Ari Saha79d7c252015-06-26 10:31:48 -0700527}