supplicant_pae_fsm.c
Go to the documentation of this file.
1 /**
2  * @file supplicant_pae_fsm.c
3  * @brief Supplicant PAE state machine
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2022-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneEAP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SUPPLICANT_TRACE_LEVEL
33 
34 //Dependencies
35 #include "supplicant/supplicant.h"
40 #include "eap/eap_debug.h"
41 #include "debug.h"
42 
43 //Check TCP/IP stack configuration
44 #if (SUPPLICANT_SUPPORT == ENABLED)
45 
46 //Supplicant PAE states
48 {
49  {SUPPLICANT_PAE_STATE_LOGOFF, "LOGOFF"},
50  {SUPPLICANT_PAE_STATE_DISCONNECTED, "DISCONNECTED"},
51  {SUPPLICANT_PAE_STATE_CONNECTING, "CONNECTING"},
52  {SUPPLICANT_PAE_STATE_AUTHENTICATING, "AUTHENTICATING"},
53  {SUPPLICANT_PAE_STATE_AUTHENTICATED, "AUTHENTICATED"},
54  {SUPPLICANT_PAE_STATE_HELD, "HELD"},
55  {SUPPLICANT_PAE_STATE_RESTART, "RESTART"},
56  {SUPPLICANT_PAE_STATE_S_FORCE_AUTH, "S_FORCE_AUTH"},
57  {SUPPLICANT_PAE_STATE_S_FORCE_UNAUTH, "S_FORCE_UNAUTH"}
58 };
59 
60 
61 /**
62  * @brief Supplicant PAE state machine initialization
63  * @param[in] context Pointer to the 802.1X supplicant context
64  **/
65 
67 {
68  //Enter initial state
70 }
71 
72 
73 /**
74  * @brief Supplicant PAE state machine implementation
75  * @param[in] context Pointer to the 802.1X supplicant context
76  **/
77 
79 {
80  //A global transition can occur from any of the possible states. When the
81  //condition associated with a global transition is met, it supersedes all
82  //other exit conditions
83  if(context->userLogoff && !context->logoffSent &&
84  !(context->initialize || !context->portEnabled))
85  {
86  //Switch to the LOGOFF state
88  }
89  else if((context->portControl == SUPPLICANT_PORT_MODE_AUTO &&
90  context->sPortMode != context->portControl) ||
91  context->initialize || !context->portEnabled)
92  {
93  //Switch to the DISCONNECTED state
95  }
96  else if(context->portControl == SUPPLICANT_PORT_MODE_FORCE_AUTH &&
97  context->sPortMode != context->portControl &&
98  !(context->initialize || !context->portEnabled))
99  {
100  //Switch to the S_FORCE_AUTH state
102  }
103  else if(context->portControl == SUPPLICANT_PORT_MODE_FORCE_UNAUTH &&
104  context->sPortMode != context->portControl &&
105  !(context->initialize || !context->portEnabled))
106  {
107  //Switch to the S_FORCE_UNAUTH state
109  }
110  else
111  {
112  //All exit conditions for the state are evaluated continuously until one
113  //of the conditions is met (refer to IEEE Std 802.1X-2004, section 8.2.1)
114  switch(context->suppPaeState)
115  {
116  //LOGOFF state?
118  //Evaluate conditions for the current state
119  if(!context->userLogoff)
120  {
121  //Switch to the DISCONNECTED state
123  }
124 
125  break;
126 
127  //DISCONNECTED state?
129  //Unconditional transition (UCT) to CONNECTING state
131  break;
132 
133  //CONNECTING state?
135  //Evaluate conditions for the current state
136  if(context->startWhen == 0)
137  {
138  //If the startWhen timer expires, the transmission is repeated up
139  //to a maximum of maxStart transmissions
140  if(context->startCount < context->maxStart)
141  {
142  //An EAPOL-Start packet is transmitted by the supplicant, and
143  //the startWhen timer is started, to cause retransmission if no
144  //response is received from the authenticator
146  }
147  else
148  {
149  //If no response is received after maxStart transmissions, the state
150  //machine assumes that it is attached to a system that is not EAPOL
151  //aware, and transitions to AUTHENTICATED state if portValid is TRUE
152  if(context->portValid)
153  {
154  //Switch to the AUTHENTICATED state
156  }
157  else
158  {
159  //Switch to the HELD state
161  }
162  }
163  }
164  else if(context->eapolEap)
165  {
166  //If an EAP-Request frame is received, the supplicant transitions
167  //to the RESTART state
169  }
170  else if(context->eapSuccess || context->eapFail)
171  {
172  //If the higher layer has decided it is satisfied with an
173  //eapSuccess or eapFail, the supplicant transitions directly to
174  //the AUTHENTICATING state
176  }
177  else
178  {
179  //Just for sanity
180  }
181 
182  break;
183 
184  //AUTHENTICATING state?
186  //Evaluate conditions for the current state
187  if(context->suppSuccess && context->portValid)
188  {
189  //Switch to the AUTHENTICATED state
191  }
192  else if(context->suppFail || (context->keyDone && !context->portValid))
193  {
194  //Switch to the HELD state
196  }
197  else if(context->suppTimeout)
198  {
199  //Switch to the CONNECTING state
201  }
202  else
203  {
204  //Just for sanity
205  }
206 
207  break;
208 
209  //AUTHENTICATED state?
211  //Evaluate conditions for the current state
212  if(context->eapolEap && context->portValid)
213  {
214  //Switch to the RESTART state
216  }
217  else if(!context->portValid)
218  {
219  //Switch to the DISCONNECTED state
221  }
222  else
223  {
224  //Just for sanity
225  }
226 
227  break;
228 
229  //HELD state?
231  //Evaluate conditions for the current state
232  if(context->heldWhile == 0)
233  {
234  //Switch to the CONNECTING state
236  }
237  else if(context->eapolEap)
238  {
239  //Switch to the RESTART state
241  }
242  else
243  {
244  //Just for sanity
245  }
246 
247  break;
248 
249  //RESTART state?
251  //Evaluate conditions for the current state
252  if(!context->eapRestart)
253  {
254  //Switch to the AUTHENTICATING state
256  }
257 
258  break;
259 
260  //S_FORCE_AUTH state?
262  //Force the port state to Authorized
263  break;
264 
265  //S_FORCE_UNAUTH state?
267  //Force the port state to Unauthorized
268  break;
269 
270  //Invalid state?
271  default:
272  //Just for sanity
273  supplicantFsmError(context);
274  break;
275  }
276  }
277 }
278 
279 
280 /**
281  * @brief Update supplicant PAE state
282  * @param[in] context Pointer to the 802.1X supplicant context
283  * @param[in] newState New state to switch to
284  **/
285 
287  SupplicantPaeState newState)
288 {
289  SupplicantPaeState oldState;
290 
291  //Retrieve current state
292  oldState = context->suppPaeState;
293 
294  //Any state change?
295  if(newState != oldState)
296  {
297  //Dump the state transition
298  TRACE_DEBUG("Supplicant PAE state machine %s -> %s\r\n",
303  }
304 
305  //Switch to the new state
306  context->suppPaeState = newState;
307 
308  //On entry to a state, the procedures defined for the state are executed
309  //exactly once (refer to IEEE Std 802.1X-2004, section 8.2.1)
310  switch(newState)
311  {
312  //LOGOFF state?
314  //The user of the system requests an explicit logoff
315  supplicantTxLogoff(context);
316  context->logoffSent = TRUE;
317  context->suppPortStatus = SUPPLICANT_PORT_STATUS_UNAUTH;
318  break;
319 
320  //DISCONNECTED state?
322  //This state is entered from any other state when the MAC service
323  //associated with the port is inoperable, or when the system is
324  //initialized or reinitialized
325  context->sPortMode = SUPPLICANT_PORT_MODE_AUTO;
326  context->startCount = 0;
327  context->logoffSent = FALSE;
328  context->suppPortStatus = SUPPLICANT_PORT_STATUS_UNAUTH;
329  context->suppAbort = TRUE;
330  break;
331 
332  //CONNECTING state?
334  //In this state, the port has become operable and the supplicant is
335  //attempting to acquire an authenticator
336  context->startWhen = context->startPeriod;
337  context->startCount++;
338  context->eapolEap = FALSE;
339  supplicantTxStart(context);
340  break;
341 
342  //AUTHENTICATING state?
344  //An EAP Request packet has been received from the authenticator
345  context->startCount = 0;
346  context->suppSuccess = FALSE;
347  context->suppFail = FALSE;
348  context->suppTimeout = FALSE;
349  context->keyRun = FALSE;
350  context->keyDone = FALSE;
351  context->suppStart = TRUE;
352  break;
353 
354  //AUTHENTICATED state?
356  //The supplicant has been successfully authenticated by the authenticator,
357  //or it has assumed that the authenticator is not EAPOL aware
358  context->suppPortStatus = SUPPLICANT_PORT_STATUS_AUTH;
359  break;
360 
361  //HELD state?
363  //The state provides a delay period before the supplicant will attempt to
364  //acquire an authenticator
365  context->heldWhile = context->heldPeriod;
366  context->suppPortStatus = SUPPLICANT_PORT_STATUS_UNAUTH;
367  break;
368 
369  //RESTART state?
371  //The RESTART state is entered when the supplicant PAE needs to inform the
372  //higher layer that it has restarted
373  context->eapRestart = TRUE;
374  break;
375 
376  //S_FORCE_AUTH state?
378  //The effect of these actions is to force the port state to Authorized
379  context->suppPortStatus = SUPPLICANT_PORT_STATUS_AUTH;
380  context->sPortMode = SUPPLICANT_PORT_MODE_FORCE_AUTH;
381  break;
382 
383  //S_FORCE_UNAUTH state?
385  //The effect of this set of actions is to force the port state to
386  //Unauthorized, and to reflect this state back to the authenticator by
387  //issuing a logoff request
388  context->suppPortStatus = SUPPLICANT_PORT_STATUS_UNAUTH;
389  context->sPortMode = SUPPLICANT_PORT_MODE_FORCE_UNAUTH;
390  supplicantTxLogoff(context);
391  break;
392 
393  //Invalid state?
394  default:
395  //Just for sanity
396  break;
397  }
398 
399  //Any state change?
400  if(newState != oldState)
401  {
402  //Any registered callback?
403  if(context->paeStateChangeCallback != NULL)
404  {
405  //Invoke user callback function
406  context->paeStateChangeCallback(context, newState);
407  }
408  }
409 
410  //Check whether the port is enabled
411  if(!context->initialize && context->portEnabled)
412  {
413  //The supplicant PAE state machine is busy
414  context->busy = TRUE;
415  }
416 }
417 
418 #endif
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
const char_t * eapGetParamName(uint_t value, const EapParamName *paramList, size_t paramListLen)
Convert a parameter to string representation.
Definition: eap_debug.c:219
Data logging functions for debugging purpose (EAP)
#define arraysize(a)
Definition: os_port.h:71
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
Parameter value/name binding.
Definition: eap_debug.h:50
802.1X supplicant
#define SupplicantContext
Definition: supplicant.h:36
void supplicantFsmError(SupplicantContext *context)
Supplicant state machine error handler.
Supplicant state machine.
Helper functions for 802.1X supplicant.
void supplicantPaeChangeState(SupplicantContext *context, SupplicantPaeState newState)
Update supplicant PAE state.
void supplicantPaeFsm(SupplicantContext *context)
Supplicant PAE state machine implementation.
void supplicantPaeInitFsm(SupplicantContext *context)
Supplicant PAE state machine initialization.
const EapParamName supplicantPaeStates[]
Supplicant PAE state machine.
@ SUPPLICANT_PORT_STATUS_UNAUTH
@ SUPPLICANT_PORT_STATUS_AUTH
SupplicantPaeState
Supplicant PAE states.
@ SUPPLICANT_PAE_STATE_CONNECTING
@ SUPPLICANT_PAE_STATE_RESTART
@ SUPPLICANT_PAE_STATE_DISCONNECTED
@ SUPPLICANT_PAE_STATE_LOGOFF
@ SUPPLICANT_PAE_STATE_S_FORCE_AUTH
@ SUPPLICANT_PAE_STATE_HELD
@ SUPPLICANT_PAE_STATE_AUTHENTICATING
@ SUPPLICANT_PAE_STATE_AUTHENTICATED
@ SUPPLICANT_PAE_STATE_S_FORCE_UNAUTH
@ SUPPLICANT_PORT_MODE_FORCE_AUTH
@ SUPPLICANT_PORT_MODE_FORCE_UNAUTH
@ SUPPLICANT_PORT_MODE_AUTO
void supplicantTxLogoff(SupplicantContext *context)
Transmit an EAPOL-Logoff packet (8.2.11.1.3 b)
void supplicantTxStart(SupplicantContext *context)
Transmit an EAPOL-Start packet (8.2.11.1.3 a)
Supplicant state machine procedures.