blob: 60959ada8d512f771fe2cb0e50fc47871c0a1df2 [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
19package org.onosproject.aaa;
20
21import org.onlab.packet.MacAddress;
22import org.onosproject.net.ConnectPoint;
23import org.onosproject.xosintegration.VoltTenant;
24import org.onosproject.xosintegration.VoltTenantService;
25import org.slf4j.Logger;
Thomas Vachuskae9894202015-07-30 11:59:07 -070026
Ari Saha89831742015-06-26 10:31:48 -070027import java.util.BitSet;
28
29import static org.slf4j.LoggerFactory.getLogger;
30
31/**
32 * AAA Finite State Machine.
33 */
34
35class StateMachine {
36 //INDEX to identify the state in the transition table
37 static final int STATE_IDLE = 0;
38 static final int STATE_STARTED = 1;
39 static final int STATE_PENDING = 2;
40 static final int STATE_AUTHORIZED = 3;
41 static final int STATE_UNAUTHORIZED = 4;
42
43 //INDEX to identify the transition in the transition table
44 static final int TRANSITION_START = 0; // --> started
45 static final int TRANSITION_REQUEST_ACCESS = 1;
46 static final int TRANSITION_AUTHORIZE_ACCESS = 2;
47 static final int TRANSITION_DENY_ACCESS = 3;
48 static final int TRANSITION_LOGOFF = 4;
49
50 //map of access identifiers (issued at EAPOL START)
51 static BitSet bitSet = new BitSet();
52 private final VoltTenantService voltService;
53
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
61 protected ConnectPoint supplicantConnectpoint;
62 protected MacAddress supplicantAddress;
63 protected short vlanId;
64
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
83 START STARTED | _ | _ | _ | _
84
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 =
101 {STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
102 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
112
113 /**
114 * State Machine Constructor.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700115 *
116 * @param sessionId session Id represented by the switch dpid + port number
117 * @param voltService volt service reference
Ari Saha89831742015-06-26 10:31:48 -0700118 */
119 public StateMachine(String sessionId, VoltTenantService voltService) {
120 log.info("Creating a new state machine for {}", sessionId);
121 this.sessionId = sessionId;
122 this.voltService = voltService;
123
124 }
125
126 /**
127 * Get the client id that is requesting for access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700128 *
Ari Saha89831742015-06-26 10:31:48 -0700129 * @return The client id.
130 */
131 public String getSessionId() {
132 return this.sessionId;
133 }
134
135 /**
136 * Create the identifier for the state machine (happens when goes to STARTED state).
137 */
138 private void createIdentifier() throws StateMachineException {
139 log.debug("Creating Identifier.");
140 int index = -1;
141
142 try {
143 //find the first available spot for identifier assignment
144 index = StateMachine.bitSet.nextClearBit(0);
145
146 //there is a limit of 256 identifiers
147 if (index == 256) {
148 throw new StateMachineException("Cannot handle any new identifier. Limit is 256.");
149 }
150 } catch (IndexOutOfBoundsException e) {
151 throw new StateMachineException(e.getMessage());
152 }
153
154 log.info("Assigning identifier {}", index);
155 StateMachine.bitSet.set(index);
156 this.identifier = index;
157 }
158
159 /**
160 * Set the challenge identifier and the state issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700161 *
Ari Saha89831742015-06-26 10:31:48 -0700162 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700163 * @param challengeState The challenge state from the RADIUS.
Ari Saha89831742015-06-26 10:31:48 -0700164 */
165 protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
166 this.challengeIdentifier = challengeIdentifier;
167 this.challengeState = challengeState;
168 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700169
Ari Saha89831742015-06-26 10:31:48 -0700170 /**
171 * Set the challenge identifier issued by the RADIUS on the access challenge request.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700172 *
Ari Saha89831742015-06-26 10:31:48 -0700173 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
174 */
175 protected void setChallengeIdentifier(byte challengeIdentifier) {
176 log.info("Set Challenge Identifier to {}", challengeIdentifier);
177 this.challengeIdentifier = challengeIdentifier;
178 }
179
180 /**
181 * Get the challenge EAP identifier set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700182 *
Ari Saha89831742015-06-26 10:31:48 -0700183 * @return The challenge EAP identifier.
184 */
185 protected byte getChallengeIdentifier() {
186 return this.challengeIdentifier;
187 }
188
189
190 /**
191 * Set the challenge state info issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700192 *
Ari Saha89831742015-06-26 10:31:48 -0700193 * @param challengeState The challenge state from the RADIUS.
194 */
195 protected void setChallengeState(byte[] challengeState) {
196 log.info("Set Challenge State");
197 this.challengeState = challengeState;
198 }
199
200 /**
201 * Get the challenge state set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700202 *
Ari Saha89831742015-06-26 10:31:48 -0700203 * @return The challenge state.
204 */
205 protected byte[] getChallengeState() {
206 return this.challengeState;
207 }
208
209 /**
210 * Set the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700211 *
Ari Saha89831742015-06-26 10:31:48 -0700212 * @param username The username sent to the RADIUS upon access request.
213 */
214 protected void setUsername(byte[] username) {
215 this.username = username;
216 }
217
218
219 /**
220 * Get the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700221 *
Ari Saha89831742015-06-26 10:31:48 -0700222 * @return The requestAuthenticator.
223 */
224 protected byte[] getReqeustAuthenticator() {
225 return this.requestAuthenticator;
226 }
227
228 /**
229 * Set the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700230 *
Ari Saha89831742015-06-26 10:31:48 -0700231 * @param authenticator The username sent to the RADIUS upon access request.
232 */
233 protected void setRequestAuthenticator(byte[] authenticator) {
234 this.requestAuthenticator = authenticator;
235 }
236
237
238 /**
239 * Get the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700240 *
Ari Saha89831742015-06-26 10:31:48 -0700241 * @return The username.
242 */
243 protected byte[] getUsername() {
244 return this.username;
245 }
246
247 /**
248 * Return the identifier of the state machine.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700249 *
Ari Saha89831742015-06-26 10:31:48 -0700250 * @return The state machine identifier.
251 */
252 public byte getIdentifier() {
253 return (byte) this.identifier;
254 }
255
256
257 protected void deleteIdentifier() {
258 if (this.identifier != -1) {
259 log.info("Freeing up " + this.identifier);
260 //this state machine should be deleted and free up the identifier
261 StateMachine.bitSet.clear(this.identifier);
262 this.identifier = -1;
263 }
264 }
265
266
267 /**
268 * Move to the next state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700269 *
Ari Saha89831742015-06-26 10:31:48 -0700270 * @param msg
271 */
Thomas Vachuskae9894202015-07-30 11:59:07 -0700272 private void next(int msg) {
Ari Saha89831742015-06-26 10:31:48 -0700273 currentState = transition[currentState][msg];
274 log.info("Current State " + currentState);
275 }
276
277 /**
278 * Client has requested the start action to allow network access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700279 *
280 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700281 */
282 public void start() throws StateMachineException {
283 try {
284 states[currentState].start();
285 //move to the next state
286 next(TRANSITION_START);
287 createIdentifier();
288 } catch (StateMachineInvalidTransitionException e) {
289 e.printStackTrace();
290 }
291 }
292
293 /**
294 * An Identification information has been sent by the supplicant.
295 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700296 *
297 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700298 */
299 public void requestAccess() throws StateMachineException {
300 try {
301 states[currentState].requestAccess();
302 //move to the next state
303 next(TRANSITION_REQUEST_ACCESS);
304 } catch (StateMachineInvalidTransitionException e) {
305 e.printStackTrace();
306 }
307 }
308
309 /**
310 * RADIUS has accepted the identification.
311 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700312 *
313 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700314 */
315 public void authorizeAccess() throws StateMachineException {
316 try {
317 states[currentState].radiusAccepted();
318 //move to the next state
319 next(TRANSITION_AUTHORIZE_ACCESS);
320
321 if (voltService != null) {
322 voltService.addTenant(
323 VoltTenant.builder()
324 .withHumanReadableName("VCPE-" + this.identifier)
325 .withId(this.identifier)
326 .withProviderService(1)
327 .withServiceSpecificId(String.valueOf(this.identifier))
328 .withPort(this.supplicantConnectpoint)
329 .withVlanId(String.valueOf(this.vlanId)).build());
330 }
331
332 deleteIdentifier();
333 } catch (StateMachineInvalidTransitionException e) {
334 e.printStackTrace();
335 }
336
337 }
338
339 /**
340 * RADIUS has denied the identification.
341 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700342 *
343 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700344 */
345 public void denyAccess() throws StateMachineException {
346 try {
347 states[currentState].radiusDenied();
348 //move to the next state
349 next(TRANSITION_DENY_ACCESS);
350 deleteIdentifier();
351 } catch (StateMachineInvalidTransitionException e) {
352 e.printStackTrace();
353 }
354 }
355
356 /**
357 * Logoff request has been requested.
358 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700359 *
360 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700361 */
362 public void logoff() throws StateMachineException {
363 try {
364 states[currentState].logoff();
365 //move to the next state
366 next(TRANSITION_LOGOFF);
367 } catch (StateMachineInvalidTransitionException e) {
368 e.printStackTrace();
369 }
370 }
371
372 /**
373 * Get the current state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700374 *
Ari Saha89831742015-06-26 10:31:48 -0700375 * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
376 * STATE_UNAUTHORIZED.
377 */
378 public int getState() {
379 return currentState;
380 }
381
382
Ari Saha89831742015-06-26 10:31:48 -0700383 public String toString() {
384 return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
385 ("state: " + this.currentState);
386 }
387}
388
Thomas Vachuskae9894202015-07-30 11:59:07 -0700389// FIXME: A source file should contain no more than one top-level entity!
390
Ari Saha89831742015-06-26 10:31:48 -0700391abstract class State {
392 private final Logger log = getLogger(getClass());
393
394 private String name = "State";
395
396 public void start() throws StateMachineInvalidTransitionException {
397 log.warn("START transition from this state is not allowed.");
398 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700399
Ari Saha89831742015-06-26 10:31:48 -0700400 public void requestAccess() throws StateMachineInvalidTransitionException {
401 log.warn("REQUEST ACCESS transition from this state is not allowed.");
402 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700403
Ari Saha89831742015-06-26 10:31:48 -0700404 public void radiusAccepted() throws StateMachineInvalidTransitionException {
405 log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
406 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700407
Ari Saha89831742015-06-26 10:31:48 -0700408 public void radiusDenied() throws StateMachineInvalidTransitionException {
409 log.warn("DENY ACCESS transition from this state is not allowed.");
410 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700411
Ari Saha89831742015-06-26 10:31:48 -0700412 public void logoff() throws StateMachineInvalidTransitionException {
413 log.warn("LOGOFF transition from this state is not allowed.");
414 }
415}
416
417/**
418 * Idle state: supplicant is logged of from the network.
419 */
420class Idle extends State {
421 private final Logger log = getLogger(getClass());
422 private String name = "IDLE_STATE";
423
424 public void start() {
425 log.info("Moving from IDLE state to STARTED state.");
426 }
427}
428
429/**
430 * Started state: supplicant has entered the network and informed the authenticator.
431 */
432class Started extends State {
433 private final Logger log = getLogger(getClass());
434 private String name = "STARTED_STATE";
435
436 public void requestAccess() {
437 log.info("Moving from STARTED state to PENDING state.");
438 }
439}
440
441/**
442 * Pending state: supplicant has been identified by the authenticator but has not access yet.
443 */
444class Pending extends State {
445 private final Logger log = getLogger(getClass());
446 private String name = "PENDING_STATE";
447
448 public void radiusAccepted() {
449 log.info("Moving from PENDING state to AUTHORIZED state.");
450 }
451
452 public void radiusDenied() {
453 log.info("Moving from PENDING state to UNAUTHORIZED state.");
454 }
455}
456
457/**
458 * Authorized state: supplicant port has been accepted, access is granted.
459 */
460class Authorized extends State {
461 private final Logger log = getLogger(getClass());
462 private String name = "AUTHORIZED_STATE";
463
464 public void logoff() {
465
466 log.info("Moving from AUTHORIZED state to IDLE state.");
467 }
468}
469
470/**
471 * Unauthorized state: supplicant port has been rejected, access is denied.
472 */
473class Unauthorized extends State {
474 private final Logger log = getLogger(getClass());
475 private String name = "UNAUTHORIZED_STATE";
476
477 public void logoff() {
478 log.info("Moving from UNAUTHORIZED state to IDLE state.");
479 }
480}
481
482
483/**
484 * Exception for the State Machine.
485 */
486class StateMachineException extends Exception {
487 public StateMachineException(String message) {
488 super(message);
489
490 }
491}
Thomas Vachuskae9894202015-07-30 11:59:07 -0700492
Ari Saha89831742015-06-26 10:31:48 -0700493/**
494 * Exception raised when the transition from one state to another is invalid.
495 */
496class StateMachineInvalidTransitionException extends StateMachineException {
497 public StateMachineInvalidTransitionException(String message) {
498 super(message);
499 }
500}