blob: 84f69241f93e28f6452ffaefac3901815670cddb [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
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;
26import org.onosproject.xosintegration.VoltTenant;
27import org.onosproject.xosintegration.VoltTenantService;
28import org.slf4j.Logger;
Thomas Vachuskae9894202015-07-30 11:59:07 -070029
Ray Milkeyf61a24e2015-09-24 16:34:02 -070030import com.google.common.collect.Maps;
Ari Saha89831742015-06-26 10:31:48 -070031
32import static org.slf4j.LoggerFactory.getLogger;
33
34/**
35 * AAA Finite State Machine.
36 */
37
38class StateMachine {
39 //INDEX to identify the state in the transition table
40 static final int STATE_IDLE = 0;
41 static final int STATE_STARTED = 1;
42 static final int STATE_PENDING = 2;
43 static final int STATE_AUTHORIZED = 3;
44 static final int STATE_UNAUTHORIZED = 4;
45
46 //INDEX to identify the transition in the transition table
47 static final int TRANSITION_START = 0; // --> started
48 static final int TRANSITION_REQUEST_ACCESS = 1;
49 static final int TRANSITION_AUTHORIZE_ACCESS = 2;
50 static final int TRANSITION_DENY_ACCESS = 3;
51 static final int TRANSITION_LOGOFF = 4;
52
53 //map of access identifiers (issued at EAPOL START)
54 static BitSet bitSet = new BitSet();
55 private final VoltTenantService voltService;
56
57 private int identifier = -1;
58 private byte challengeIdentifier;
59 private byte[] challengeState;
60 private byte[] username;
61 private byte[] requestAuthenticator;
62
63 // Supplicant connectivity info
Ray Milkeyf61a24e2015-09-24 16:34:02 -070064 private ConnectPoint supplicantConnectpoint;
65 private MacAddress supplicantAddress;
66 private short vlanId;
Ari Saha89831742015-06-26 10:31:48 -070067
68 private String sessionId = null;
69
70 private final Logger log = getLogger(getClass());
71
72
73 private State[] states = {
74 new Idle(), new Started(), new Pending(), new Authorized(), new Unauthorized()
75 };
76
77
78 //State transition table
79 /*
80
81 state IDLE | STARTED | PENDING | AUTHORIZED | UNAUTHORIZED
82 ////
83 input
84 ----------------------------------------------------------------------------------------------------
85
86 START STARTED | _ | _ | _ | _
87
88 REQUEST_ACCESS _ | PENDING | _ | _ | _
89
90 AUTHORIZE_ACCESS _ | _ | AUTHORIZED | _ | _
91
92 DENY_ACCESS _ | - | UNAUTHORIZED | _ | _
93
94 LOGOFF _ | _ | _ | IDLE | IDLE
95 */
96
97 private int[] idleTransition =
98 {STATE_STARTED, STATE_IDLE, STATE_IDLE, STATE_IDLE, STATE_IDLE};
99 private int[] startedTransition =
100 {STATE_STARTED, STATE_PENDING, STATE_STARTED, STATE_STARTED, STATE_STARTED};
101 private int[] pendingTransition =
102 {STATE_PENDING, STATE_PENDING, STATE_AUTHORIZED, STATE_UNAUTHORIZED, STATE_PENDING};
103 private int[] authorizedTransition =
104 {STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_AUTHORIZED, STATE_IDLE};
105 private int[] unauthorizedTransition =
106 {STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_UNAUTHORIZED, STATE_IDLE};
107
108 //THE TRANSITION TABLE
109 private int[][] transition =
110 {idleTransition, startedTransition, pendingTransition, authorizedTransition,
111 unauthorizedTransition};
112
113 private int currentState = STATE_IDLE;
114
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700115 // Maps of state machines. Each state machine is represented by an
116 // unique identifier on the switch: dpid + port number
117 private static Map<String, StateMachine> sessionIdMap;
118 private static Map<Integer, StateMachine> identifierMap;
Ari Saha89831742015-06-26 10:31:48 -0700119
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700120 public static void initializeMaps() {
121 sessionIdMap = Maps.newConcurrentMap();
122 identifierMap = Maps.newConcurrentMap();
123 }
124
125 public static void destroyMaps() {
126 sessionIdMap = null;
127 identifierMap = null;
128 }
129
130 public static StateMachine lookupStateMachineById(byte identifier) {
131 return identifierMap.get((int) identifier);
132 }
133
134 public static StateMachine lookupStateMachineBySessionId(String sessionId) {
135 return sessionIdMap.get(sessionId);
136 } /**
Ari Saha89831742015-06-26 10:31:48 -0700137 * State Machine Constructor.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700138 *
139 * @param sessionId session Id represented by the switch dpid + port number
140 * @param voltService volt service reference
Ari Saha89831742015-06-26 10:31:48 -0700141 */
142 public StateMachine(String sessionId, VoltTenantService voltService) {
143 log.info("Creating a new state machine for {}", sessionId);
144 this.sessionId = sessionId;
145 this.voltService = voltService;
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700146 sessionIdMap.put(sessionId, this);
Ari Saha89831742015-06-26 10:31:48 -0700147 }
148
149 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700150 * Gets the connect point for the supplicant side.
151 *
152 * @return supplicant connect point
153 */
154 public ConnectPoint supplicantConnectpoint() {
155 return supplicantConnectpoint;
156 }
157
158 /**
159 * Sets the supplicant side connect point.
160 *
161 * @param supplicantConnectpoint supplicant select point.
162 */
163 public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) {
164 this.supplicantConnectpoint = supplicantConnectpoint;
165 }
166
167 /**
168 * Gets the MAC address of the supplicant.
169 *
170 * @return supplicant MAC address
171 */
172 public MacAddress supplicantAddress() {
173 return supplicantAddress;
174 }
175
176 /**
177 * Sets the supplicant MAC address.
178 *
179 * @param supplicantAddress new supplicant MAC address
180 */
181 public void setSupplicantAddress(MacAddress supplicantAddress) {
182 this.supplicantAddress = supplicantAddress;
183 }
184
185 /**
186 * Gets the client's Vlan ID.
187 *
188 * @return client vlan ID
189 */
190 public short vlanId() {
191 return vlanId;
192 }
193
194 /**
195 * Sets the client's vlan ID.
196 *
197 * @param vlanId new client vlan ID
198 */
199 public void setVlanId(short vlanId) {
200 this.vlanId = vlanId;
201 }
202
203 /**
204 * Gets the client id that is requesting for access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700205 *
Ari Saha89831742015-06-26 10:31:48 -0700206 * @return The client id.
207 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700208 public String sessionId() {
Ari Saha89831742015-06-26 10:31:48 -0700209 return this.sessionId;
210 }
211
212 /**
213 * Create the identifier for the state machine (happens when goes to STARTED state).
214 */
215 private void createIdentifier() throws StateMachineException {
216 log.debug("Creating Identifier.");
Ray Milkey78e95a42015-09-24 08:36:45 -0700217 int index;
Ari Saha89831742015-06-26 10:31:48 -0700218
219 try {
220 //find the first available spot for identifier assignment
221 index = StateMachine.bitSet.nextClearBit(0);
222
223 //there is a limit of 256 identifiers
224 if (index == 256) {
225 throw new StateMachineException("Cannot handle any new identifier. Limit is 256.");
226 }
227 } catch (IndexOutOfBoundsException e) {
228 throw new StateMachineException(e.getMessage());
229 }
230
231 log.info("Assigning identifier {}", index);
232 StateMachine.bitSet.set(index);
233 this.identifier = index;
234 }
235
236 /**
237 * Set the challenge identifier and the state issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700238 *
Ari Saha89831742015-06-26 10:31:48 -0700239 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700240 * @param challengeState The challenge state from the RADIUS.
Ari Saha89831742015-06-26 10:31:48 -0700241 */
242 protected void setChallengeInfo(byte challengeIdentifier, byte[] challengeState) {
243 this.challengeIdentifier = challengeIdentifier;
244 this.challengeState = challengeState;
245 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700246
Ari Saha89831742015-06-26 10:31:48 -0700247 /**
248 * Set the challenge identifier issued by the RADIUS on the access challenge request.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700249 *
Ari Saha89831742015-06-26 10:31:48 -0700250 * @param challengeIdentifier The challenge identifier set into the EAP packet from the RADIUS message.
251 */
252 protected void setChallengeIdentifier(byte challengeIdentifier) {
253 log.info("Set Challenge Identifier to {}", challengeIdentifier);
254 this.challengeIdentifier = challengeIdentifier;
255 }
256
257 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700258 * Gets the challenge EAP identifier set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700259 *
Ari Saha89831742015-06-26 10:31:48 -0700260 * @return The challenge EAP identifier.
261 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700262 protected byte challengeIdentifier() {
Ari Saha89831742015-06-26 10:31:48 -0700263 return this.challengeIdentifier;
264 }
265
266
267 /**
268 * Set the challenge state info issued by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700269 *
Ari Saha89831742015-06-26 10:31:48 -0700270 * @param challengeState The challenge state from the RADIUS.
271 */
272 protected void setChallengeState(byte[] challengeState) {
273 log.info("Set Challenge State");
274 this.challengeState = challengeState;
275 }
276
277 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700278 * Gets the challenge state set by the RADIUS.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700279 *
Ari Saha89831742015-06-26 10:31:48 -0700280 * @return The challenge state.
281 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700282 protected byte[] challengeState() {
Ari Saha89831742015-06-26 10:31:48 -0700283 return this.challengeState;
284 }
285
286 /**
287 * Set the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700288 *
Ari Saha89831742015-06-26 10:31:48 -0700289 * @param username The username sent to the RADIUS upon access request.
290 */
291 protected void setUsername(byte[] username) {
292 this.username = username;
293 }
294
295
296 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700297 * Gets the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700298 *
Ari Saha89831742015-06-26 10:31:48 -0700299 * @return The requestAuthenticator.
300 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700301 protected byte[] requestAuthenticator() {
Ari Saha89831742015-06-26 10:31:48 -0700302 return this.requestAuthenticator;
303 }
304
305 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700306 * Sets the authenticator.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700307 *
Ari Saha89831742015-06-26 10:31:48 -0700308 * @param authenticator The username sent to the RADIUS upon access request.
309 */
310 protected void setRequestAuthenticator(byte[] authenticator) {
311 this.requestAuthenticator = authenticator;
312 }
313
314
315 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700316 * Gets the username.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700317 *
Ari Saha89831742015-06-26 10:31:48 -0700318 * @return The username.
319 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700320 protected byte[] username() {
Ari Saha89831742015-06-26 10:31:48 -0700321 return this.username;
322 }
323
324 /**
325 * Return the identifier of the state machine.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700326 *
Ari Saha89831742015-06-26 10:31:48 -0700327 * @return The state machine identifier.
328 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700329 public byte identifier() {
Ari Saha89831742015-06-26 10:31:48 -0700330 return (byte) this.identifier;
331 }
332
333
334 protected void deleteIdentifier() {
335 if (this.identifier != -1) {
336 log.info("Freeing up " + this.identifier);
337 //this state machine should be deleted and free up the identifier
338 StateMachine.bitSet.clear(this.identifier);
339 this.identifier = -1;
340 }
341 }
342
343
344 /**
345 * Move to the next state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700346 *
Ray Milkey78e95a42015-09-24 08:36:45 -0700347 * @param msg message
Ari Saha89831742015-06-26 10:31:48 -0700348 */
Thomas Vachuskae9894202015-07-30 11:59:07 -0700349 private void next(int msg) {
Ari Saha89831742015-06-26 10:31:48 -0700350 currentState = transition[currentState][msg];
351 log.info("Current State " + currentState);
352 }
353
354 /**
355 * Client has requested the start action to allow network access.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700356 *
357 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700358 */
359 public void start() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700360 states[currentState].start();
361 //move to the next state
362 next(TRANSITION_START);
363 createIdentifier();
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700364 identifierMap.put(identifier, this);
Ari Saha89831742015-06-26 10:31:48 -0700365 }
366
367 /**
368 * An Identification information has been sent by the supplicant.
369 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700370 *
371 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700372 */
373 public void requestAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700374 states[currentState].requestAccess();
375 //move to the next state
376 next(TRANSITION_REQUEST_ACCESS);
Ari Saha89831742015-06-26 10:31:48 -0700377 }
378
379 /**
380 * RADIUS has accepted the identification.
381 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700382 *
383 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700384 */
385 public void authorizeAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700386 states[currentState].radiusAccepted();
387 //move to the next state
388 next(TRANSITION_AUTHORIZE_ACCESS);
Ari Saha89831742015-06-26 10:31:48 -0700389
Ray Milkey78e95a42015-09-24 08:36:45 -0700390 if (voltService != null) {
391 voltService.addTenant(
392 VoltTenant.builder()
393 .withHumanReadableName("VCPE-" + this.identifier)
394 .withId(this.identifier)
395 .withProviderService(1)
396 .withServiceSpecificId(String.valueOf(this.identifier))
397 .withPort(this.supplicantConnectpoint)
398 .withVlanId(String.valueOf(this.vlanId)).build());
Ari Saha89831742015-06-26 10:31:48 -0700399 }
400
Ray Milkey78e95a42015-09-24 08:36:45 -0700401 deleteIdentifier();
Ari Saha89831742015-06-26 10:31:48 -0700402 }
403
404 /**
405 * RADIUS has denied the identification.
406 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700407 *
408 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700409 */
410 public void denyAccess() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700411 states[currentState].radiusDenied();
412 //move to the next state
413 next(TRANSITION_DENY_ACCESS);
414 deleteIdentifier();
Ari Saha89831742015-06-26 10:31:48 -0700415 }
416
417 /**
418 * Logoff request has been requested.
419 * Move to the next state if possible.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700420 *
421 * @throws StateMachineException if authentication protocol is violated
Ari Saha89831742015-06-26 10:31:48 -0700422 */
423 public void logoff() throws StateMachineException {
Ray Milkey78e95a42015-09-24 08:36:45 -0700424 states[currentState].logoff();
425 //move to the next state
426 next(TRANSITION_LOGOFF);
Ari Saha89831742015-06-26 10:31:48 -0700427 }
428
429 /**
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700430 * Gets the current state.
Thomas Vachuskae9894202015-07-30 11:59:07 -0700431 *
Ari Saha89831742015-06-26 10:31:48 -0700432 * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED,
433 * STATE_UNAUTHORIZED.
434 */
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700435 public int state() {
Ari Saha89831742015-06-26 10:31:48 -0700436 return currentState;
437 }
438
Ray Milkeyf61a24e2015-09-24 16:34:02 -0700439 @Override
Ari Saha89831742015-06-26 10:31:48 -0700440 public String toString() {
441 return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" +
442 ("state: " + this.currentState);
443 }
Ari Saha89831742015-06-26 10:31:48 -0700444
Ray Milkey78e95a42015-09-24 08:36:45 -0700445 abstract class State {
446 private final Logger log = getLogger(getClass());
Thomas Vachuskae9894202015-07-30 11:59:07 -0700447
Ray Milkey78e95a42015-09-24 08:36:45 -0700448 private String name = "State";
Ari Saha89831742015-06-26 10:31:48 -0700449
Ray Milkey78e95a42015-09-24 08:36:45 -0700450 public void start() throws StateMachineInvalidTransitionException {
451 log.warn("START transition from this state is not allowed.");
452 }
Ari Saha89831742015-06-26 10:31:48 -0700453
Ray Milkey78e95a42015-09-24 08:36:45 -0700454 public void requestAccess() throws StateMachineInvalidTransitionException {
455 log.warn("REQUEST ACCESS transition from this state is not allowed.");
456 }
457
458 public void radiusAccepted() throws StateMachineInvalidTransitionException {
459 log.warn("AUTHORIZE ACCESS transition from this state is not allowed.");
460 }
461
462 public void radiusDenied() throws StateMachineInvalidTransitionException {
463 log.warn("DENY ACCESS transition from this state is not allowed.");
464 }
465
466 public void logoff() throws StateMachineInvalidTransitionException {
467 log.warn("LOGOFF transition from this state is not allowed.");
468 }
Ari Saha89831742015-06-26 10:31:48 -0700469 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700470
Ray Milkey78e95a42015-09-24 08:36:45 -0700471 /**
472 * Idle state: supplicant is logged of from the network.
473 */
474 class Idle extends State {
475 private final Logger log = getLogger(getClass());
476 private String name = "IDLE_STATE";
477
478 public void start() {
479 log.info("Moving from IDLE state to STARTED state.");
480 }
Ari Saha89831742015-06-26 10:31:48 -0700481 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700482
Ray Milkey78e95a42015-09-24 08:36:45 -0700483 /**
484 * Started state: supplicant has entered the network and informed the authenticator.
485 */
486 class Started extends State {
487 private final Logger log = getLogger(getClass());
488 private String name = "STARTED_STATE";
489
490 public void requestAccess() {
491 log.info("Moving from STARTED state to PENDING state.");
492 }
Ari Saha89831742015-06-26 10:31:48 -0700493 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700494
Ray Milkey78e95a42015-09-24 08:36:45 -0700495 /**
496 * Pending state: supplicant has been identified by the authenticator but has not access yet.
497 */
498 class Pending extends State {
499 private final Logger log = getLogger(getClass());
500 private String name = "PENDING_STATE";
501
502 public void radiusAccepted() {
503 log.info("Moving from PENDING state to AUTHORIZED state.");
504 }
505
506 public void radiusDenied() {
507 log.info("Moving from PENDING state to UNAUTHORIZED state.");
508 }
Ari Saha89831742015-06-26 10:31:48 -0700509 }
Thomas Vachuskae9894202015-07-30 11:59:07 -0700510
Ray Milkey78e95a42015-09-24 08:36:45 -0700511 /**
512 * Authorized state: supplicant port has been accepted, access is granted.
513 */
514 class Authorized extends State {
515 private final Logger log = getLogger(getClass());
516 private String name = "AUTHORIZED_STATE";
Ari Saha89831742015-06-26 10:31:48 -0700517
Ray Milkey78e95a42015-09-24 08:36:45 -0700518 public void logoff() {
Ari Saha89831742015-06-26 10:31:48 -0700519
Ray Milkey78e95a42015-09-24 08:36:45 -0700520 log.info("Moving from AUTHORIZED state to IDLE state.");
521 }
Ari Saha89831742015-06-26 10:31:48 -0700522 }
523
Ray Milkey78e95a42015-09-24 08:36:45 -0700524 /**
525 * Unauthorized state: supplicant port has been rejected, access is denied.
526 */
527 class Unauthorized extends State {
528 private final Logger log = getLogger(getClass());
529 private String name = "UNAUTHORIZED_STATE";
530
531 public void logoff() {
532 log.info("Moving from UNAUTHORIZED state to IDLE state.");
533 }
Ari Saha89831742015-06-26 10:31:48 -0700534 }
Ari Saha89831742015-06-26 10:31:48 -0700535
536
Ari Saha89831742015-06-26 10:31:48 -0700537}