blob: 4c595ed3f751b9ded6b17163735cdae6aa12a484 [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
21import org.onlab.packet.MacAddress;
22import org.onosproject.net.ConnectPoint;
23import org.onosproject.xosintegration.VoltTenant;
24import org.onosproject.xosintegration.VoltTenantService;
25import org.slf4j.Logger;
26import java.util.BitSet;
27
28import static org.slf4j.LoggerFactory.getLogger;
29
30/**
31 * AAA Finite State Machine.
32 */
33
34class StateMachine {
35 //INDEX to identify the state in the transition table
36 static final int STATE_IDLE = 0;
37 static final int STATE_STARTED = 1;
38 static final int STATE_PENDING = 2;
39 static final int STATE_AUTHORIZED = 3;
40 static final int STATE_UNAUTHORIZED = 4;
41
42 //INDEX to identify the transition in the transition table
43 static final int TRANSITION_START = 0; // --> started
44 static final int TRANSITION_REQUEST_ACCESS = 1;
45 static final int TRANSITION_AUTHORIZE_ACCESS = 2;
46 static final int TRANSITION_DENY_ACCESS = 3;
47 static final int TRANSITION_LOGOFF = 4;
48
49 //map of access identifiers (issued at EAPOL START)
50 static BitSet bitSet = new BitSet();
51 private final VoltTenantService voltService;
52
53 private int identifier = -1;
54 private byte challengeIdentifier;
55 private byte[] challengeState;
56 private byte[] username;
57 private byte[] requestAuthenticator;
58
59 // Supplicant connectivity info
60 protected ConnectPoint supplicantConnectpoint;
61 protected MacAddress supplicantAddress;
62 protected short vlanId;
63
64 private String sessionId = null;
65
66 private final Logger log = getLogger(getClass());
67
68
69 private State[] states = {
70 new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized()
71 };
72
73
74 //State transition table
75 /*
76
77 state IDLE | STARTED | PENDING | AUTHORIZED | UNAUTHORIZED
78 ////
79 input
80 ----------------------------------------------------------------------------------------------------
81
82 START STARTED | _ | _ | _ | _
83
84 REQUEST_ACCESS _ | PENDING | _ | _ | _
85
86 AUTHORIZE_ACCESS _ | _ | AUTHORIZED | _ | _
87
88 DENY_ACCESS _ | - | UNAUTHORIZED | _ | _
89
90 LOGOFF _ | _ | _ | IDLE | IDLE
91 */
92
93 private int[] idleTransition =
94 {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE};
95 private int[] startedTransition =
96 {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED};
97 private int[] pendingTransition =
98 {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED, STATE_PENDING};
99 private int[] authorizedTransition =
100 {STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
101 private int[] unauthorizedTransition =
102 {STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_IDLE};
103
104 //THE TRANSITION TABLE
105 private int[][] transition =
106 {idleTransition, startedTransition, pendingTransition, authorizedTransition,
107 unauthorizedTransition};
108
109 private int currentState = STATE_IDLE;
110
111
112 /**
113 * State Machine Constructor.
114 * @param sessionId Session Id represented by the switch dpid + port number
115 */
116 public StateMachine(String sessionId, VoltTenantService voltService) {
117 log.info("Creating a new state machine for {}", sessionId);
118 this.sessionId = sessionId;
119 this.voltService = voltService;
120
121 }
122
123 /**
124 * Get the client id that is requesting for access.
125 * @return The client id.
126 */
127 public String getSessionId() {
128 return this.sessionId;
129 }
130
131 /**
132 * Create the identifier for the state machine (happens when goes to STARTED state).
133 */
134 private void createIdentifier() throws StateMachineException {
135 log.debug("Creating Identifier.");
136 int index = -1;
137
138 try {
139 //find the first available spot for identifier assignment
140 index = StateMachine.bitSet.nextClearBit(0);
141
142 //there is a limit of 256 identifiers
143 if (index == 256) {
144 throw new StateMachineException("Cannot handle any new identifier. Limit is 256.");
145 }
146 } catch (IndexOutOfBoundsException e) {
147 throw new StateMachineException(e.getMessage());
148 }
149
150 log.info("Assigning identifier {}", index);
151 StateMachine.bitSet.set(index);
152 this.identifier = index;
153 }
154
155 /**
156 * Set the challenge identifier and the state issued by the RADIUS.
157 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
158 * @param challengeState The challenge state from the RADIUS.
159 */
160 protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
161 this.challengeIdentifier = challengeIdentifier;
162 this.challengeState = challengeState;
163 }
164 /**
165 * Set the challenge identifier issued by the RADIUS on the access challenge request.
166 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
167 */
168 protected void setChallengeIdentifier(byte challengeIdentifier) {
169 log.info("Set Challenge Identifier to {}", challengeIdentifier);
170 this.challengeIdentifier = challengeIdentifier;
171 }
172
173 /**
174 * Get the challenge EAP identifier set by the RADIUS.
175 * @return The challenge EAP identifier.
176 */
177 protected byte getChallengeIdentifier() {
178 return this.challengeIdentifier;
179 }
180
181
182 /**
183 * Set the challenge state info issued by the RADIUS.
184 * @param challengeState The challenge state from the RADIUS.
185 */
186 protected void setChallengeState(byte[] challengeState) {
187 log.info("Set Challenge State");
188 this.challengeState = challengeState;
189 }
190
191 /**
192 * Get the challenge state set by the RADIUS.
193 * @return The challenge state.
194 */
195 protected byte[] getChallengeState() {
196 return this.challengeState;
197 }
198
199 /**
200 * Set the username.
201 * @param username The username sent to the RADIUS upon access request.
202 */
203 protected void setUsername(byte[] username) {
204 this.username = username;
205 }
206
207
208 /**
209 * Get the username.
210 * @return The requestAuthenticator.
211 */
212 protected byte[] getReqeustAuthenticator() {
213 return this.requestAuthenticator;
214 }
215
216 /**
217 * Set the username.
218 * @param authenticator The username sent to the RADIUS upon access request.
219 */
220 protected void setRequestAuthenticator(byte[] authenticator) {
221 this.requestAuthenticator = authenticator;
222 }
223
224
225 /**
226 * Get the username.
227 * @return The username.
228 */
229 protected byte[] getUsername() {
230 return this.username;
231 }
232
233 /**
234 * Return the identifier of the state machine.
235 * @return The state machine identifier.
236 */
237 public byte getIdentifier() {
238 return (byte) this.identifier;
239 }
240
241
242 protected void deleteIdentifier() {
243 if (this.identifier != -1) {
244 log.info("Freeing up " + this.identifier);
245 //this state machine should be deleted and free up the identifier
246 StateMachine.bitSet.clear(this.identifier);
247 this.identifier = -1;
248 }
249 }
250
251
252 /**
253 * Move to the next state.
254 * @param msg
255 */
256 private void next(int msg) {
257 currentState = transition[currentState][msg];
258 log.info("Current State " + currentState);
259 }
260
261 /**
262 * Client has requested the start action to allow network access.
263 */
264 public void start() throws StateMachineException {
265 try {
266 states[currentState].start();
267 //move to the next state
268 next(TRANSITION_START);
269 createIdentifier();
270 } catch (StateMachineInvalidTransitionException e) {
271 e.printStackTrace();
272 }
273 }
274
275 /**
276 * An Identification information has been sent by the supplicant.
277 * Move to the next state if possible.
278 */
279 public void requestAccess() throws StateMachineException {
280 try {
281 states[currentState].requestAccess();
282 //move to the next state
283 next(TRANSITION_REQUEST_ACCESS);
284 } catch (StateMachineInvalidTransitionException e) {
285 e.printStackTrace();
286 }
287 }
288
289 /**
290 * RADIUS has accepted the identification.
291 * Move to the next state if possible.
292 */
293 public void authorizeAccess() throws StateMachineException {
294 try {
295 states[currentState].radiusAccepted();
296 //move to the next state
297 next(TRANSITION_AUTHORIZE_ACCESS);
298
299 if (voltService != null) {
300 voltService.addTenant(
301 VoltTenant.builder()
302 .withHumanReadableName("VCPE-" + this.identifier)
303 .withId(this.identifier)
304 .withProviderService(1)
305 .withServiceSpecificId(String.valueOf(this.identifier))
306 .withPort(this.supplicantConnectpoint)
307 .withVlanId(String.valueOf(this.vlanId)).build());
308 }
309
310 deleteIdentifier();
311 } catch (StateMachineInvalidTransitionException e) {
312 e.printStackTrace();
313 }
314
315 }
316
317 /**
318 * RADIUS has denied the identification.
319 * Move to the next state if possible.
320 */
321 public void denyAccess() throws StateMachineException {
322 try {
323 states[currentState].radiusDenied();
324 //move to the next state
325 next(TRANSITION_DENY_ACCESS);
326 deleteIdentifier();
327 } catch (StateMachineInvalidTransitionException e) {
328 e.printStackTrace();
329 }
330 }
331
332 /**
333 * Logoff request has been requested.
334 * Move to the next state if possible.
335 */
336 public void logoff() throws StateMachineException {
337 try {
338 states[currentState].logoff();
339 //move to the next state
340 next(TRANSITION_LOGOFF);
341 } catch (StateMachineInvalidTransitionException e) {
342 e.printStackTrace();
343 }
344 }
345
346 /**
347 * Get the current state.
348 * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
349 * STATE_UNAUTHORIZED.
350 */
351 public int getState() {
352 return currentState;
353 }
354
355
356
357 public String toString() {
358 return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
359 ("state: " + this.currentState);
360 }
361}
362
363abstract class State {
364 private final Logger log = getLogger(getClass());
365
366 private String name = "State";
367
368 public void start() throws StateMachineInvalidTransitionException {
369 log.warn("START transition from this state is not allowed.");
370 }
371 public void requestAccess() throws StateMachineInvalidTransitionException {
372 log.warn("REQUEST ACCESS transition from this state is not allowed.");
373 }
374 public void radiusAccepted() throws StateMachineInvalidTransitionException {
375 log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
376 }
377 public void radiusDenied() throws StateMachineInvalidTransitionException {
378 log.warn("DENY ACCESS transition from this state is not allowed.");
379 }
380 public void logoff() throws StateMachineInvalidTransitionException {
381 log.warn("LOGOFF transition from this state is not allowed.");
382 }
383}
384
385/**
386 * Idle state: supplicant is logged of from the network.
387 */
388class Idle extends State {
389 private final Logger log = getLogger(getClass());
390 private String name = "IDLE_STATE";
391
392 public void start() {
393 log.info("Moving from IDLE state to STARTED state.");
394 }
395}
396
397/**
398 * Started state: supplicant has entered the network and informed the authenticator.
399 */
400class Started extends State {
401 private final Logger log = getLogger(getClass());
402 private String name = "STARTED_STATE";
403
404 public void requestAccess() {
405 log.info("Moving from STARTED state to PENDING state.");
406 }
407}
408
409/**
410 * Pending state: supplicant has been identified by the authenticator but has not access yet.
411 */
412class Pending extends State {
413 private final Logger log = getLogger(getClass());
414 private String name = "PENDING_STATE";
415
416 public void radiusAccepted() {
417 log.info("Moving from PENDING state to AUTHORIZED state.");
418 }
419
420 public void radiusDenied() {
421 log.info("Moving from PENDING state to UNAUTHORIZED state.");
422 }
423}
424
425/**
426 * Authorized state: supplicant port has been accepted, access is granted.
427 */
428class Authorized extends State {
429 private final Logger log = getLogger(getClass());
430 private String name = "AUTHORIZED_STATE";
431
432 public void logoff() {
433
434 log.info("Moving from AUTHORIZED state to IDLE state.");
435 }
436}
437
438/**
439 * Unauthorized state: supplicant port has been rejected, access is denied.
440 */
441class Unauthorized extends State {
442 private final Logger log = getLogger(getClass());
443 private String name = "UNAUTHORIZED_STATE";
444
445 public void logoff() {
446 log.info("Moving from UNAUTHORIZED state to IDLE state.");
447 }
448}
449
450
451/**
452 * Exception for the State Machine.
453 */
454class StateMachineException extends Exception {
455 public StateMachineException(String message) {
456 super(message);
457
458 }
459}
460/**
461 * Exception raised when the transition from one state to another is invalid.
462 */
463class StateMachineInvalidTransitionException extends StateMachineException {
464 public StateMachineInvalidTransitionException(String message) {
465 super(message);
466 }
467}