1/*
2 * IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine
3 * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "utils/includes.h"
10
11#include "utils/common.h"
12#include "utils/eloop.h"
13#include "common/defs.h"
14#include "common/ieee802_1x_defs.h"
15#include "utils/state_machine.h"
16#include "ieee802_1x_kay.h"
17#include "ieee802_1x_secy_ops.h"
18#include "pae/ieee802_1x_cp.h"
19
20#define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
21#define STATE_MACHINE_DEBUG_PREFIX "CP"
22
23static u64 default_cs_id = CS_ID_GCM_AES_128;
24
25/* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
26enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
27
28struct ieee802_1x_cp_sm {
29	enum cp_states {
30		CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED,
31		CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT,
32		CP_TRANSMITTING, CP_ABANDON, CP_RETIRE
33	} CP_state;
34	Boolean changed;
35
36	/* CP -> Client */
37	Boolean port_valid;
38
39	/* Logon -> CP */
40	enum connect_type connect;
41
42	/* KaY -> CP */
43	Boolean chgd_server; /* clear by CP */
44	Boolean elected_self;
45	enum confidentiality_offset cipher_offset;
46	u64 cipher_suite;
47	Boolean new_sak; /* clear by CP */
48	struct ieee802_1x_mka_ki distributed_ki;
49	u8 distributed_an;
50	Boolean using_receive_sas;
51	Boolean all_receiving;
52	Boolean server_transmitting;
53	Boolean using_transmit_sa;
54
55	/* CP -> KaY */
56	struct ieee802_1x_mka_ki *lki;
57	u8 lan;
58	Boolean ltx;
59	Boolean lrx;
60	struct ieee802_1x_mka_ki *oki;
61	u8 oan;
62	Boolean otx;
63	Boolean orx;
64
65	/* CP -> SecY */
66	Boolean protect_frames;
67	enum validate_frames validate_frames;
68
69	Boolean replay_protect;
70	u32 replay_window;
71
72	u64 current_cipher_suite;
73	enum confidentiality_offset confidentiality_offset;
74	Boolean controlled_port_enabled;
75
76	/* SecY -> CP */
77	Boolean port_enabled; /* SecY->CP */
78
79	/* private */
80	u32 transmit_when;
81	u32 transmit_delay;
82	u32 retire_when;
83	u32 retire_delay;
84
85	/* not defined IEEE Std 802.1X-2010 */
86	struct ieee802_1x_kay *kay;
87};
88
89static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
90					      void *timeout_ctx);
91static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx,
92						void *timeout_ctx);
93
94
95static int changed_cipher(struct ieee802_1x_cp_sm *sm)
96{
97	return sm->confidentiality_offset != sm->cipher_offset ||
98		sm->current_cipher_suite != sm->cipher_suite;
99}
100
101
102static int changed_connect(struct ieee802_1x_cp_sm *sm)
103{
104	return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm);
105}
106
107
108SM_STATE(CP, INIT)
109{
110	SM_ENTRY(CP, INIT);
111
112	sm->controlled_port_enabled = FALSE;
113	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
114
115	sm->port_valid = FALSE;
116
117	os_free(sm->lki);
118	sm->lki = NULL;
119	sm->ltx = FALSE;
120	sm->lrx = FALSE;
121
122	os_free(sm->oki);
123	sm->oki = NULL;
124	sm->otx = FALSE;
125	sm->orx = FALSE;
126
127	sm->port_enabled = TRUE;
128	sm->chgd_server = FALSE;
129}
130
131
132SM_STATE(CP, CHANGE)
133{
134	SM_ENTRY(CP, CHANGE);
135
136	sm->port_valid = FALSE;
137	sm->controlled_port_enabled = FALSE;
138	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
139
140	if (sm->lki)
141		ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
142	if (sm->oki)
143		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
144}
145
146
147SM_STATE(CP, ALLOWED)
148{
149	SM_ENTRY(CP, ALLOWED);
150
151	sm->protect_frames = FALSE;
152	sm->replay_protect = FALSE;
153	sm->validate_frames = Checked;
154
155	sm->port_valid = FALSE;
156	sm->controlled_port_enabled = TRUE;
157
158	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
159	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
160	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
161	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
162	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
163}
164
165
166SM_STATE(CP, AUTHENTICATED)
167{
168	SM_ENTRY(CP, AUTHENTICATED);
169
170	sm->protect_frames = FALSE;
171	sm->replay_protect = FALSE;
172	sm->validate_frames = Checked;
173
174	sm->port_valid = FALSE;
175	sm->controlled_port_enabled = TRUE;
176
177	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
178	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
179	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
180	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
181	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
182}
183
184
185SM_STATE(CP, SECURED)
186{
187	SM_ENTRY(CP, SECURED);
188
189	sm->chgd_server = FALSE;
190
191	sm->protect_frames = sm->kay->macsec_protect;
192	sm->replay_protect = sm->kay->macsec_replay_protect;
193	sm->validate_frames = sm->kay->macsec_validate;
194
195	/* NOTE: now no other than default cipher suite (AES-GCM-128) */
196	sm->current_cipher_suite = sm->cipher_suite;
197	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
198
199	sm->confidentiality_offset = sm->cipher_offset;
200
201	sm->port_valid = TRUE;
202
203	secy_cp_control_confidentiality_offset(sm->kay,
204					       sm->confidentiality_offset);
205	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
206	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
207	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
208	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
209}
210
211
212SM_STATE(CP, RECEIVE)
213{
214	SM_ENTRY(CP, RECEIVE);
215	/* RECEIVE state machine not keep with Figure 12-2 in
216	 * IEEE Std 802.1X-2010 */
217	if (sm->oki) {
218		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
219		os_free(sm->oki);
220	}
221	sm->oki = sm->lki;
222	sm->oan = sm->lan;
223	sm->otx = sm->ltx;
224	sm->orx = sm->lrx;
225	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
226				       sm->otx, sm->orx);
227
228	sm->lki = os_malloc(sizeof(*sm->lki));
229	if (!sm->lki) {
230		wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__);
231		return;
232	}
233	os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki));
234	sm->lan = sm->distributed_an;
235	sm->ltx = FALSE;
236	sm->lrx = FALSE;
237	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
238					  sm->ltx, sm->lrx);
239	ieee802_1x_kay_create_sas(sm->kay, sm->lki);
240	ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki);
241	sm->new_sak = FALSE;
242	sm->all_receiving = FALSE;
243}
244
245
246SM_STATE(CP, RECEIVING)
247{
248	SM_ENTRY(CP, RECEIVING);
249
250	sm->lrx = TRUE;
251	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
252					  sm->ltx, sm->lrx);
253	sm->transmit_when = sm->transmit_delay;
254	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
255	eloop_register_timeout(sm->transmit_when / 1000, 0,
256			       ieee802_1x_cp_transmit_when_timeout, sm, NULL);
257	/* the electedSelf have been set before CP entering to RECEIVING
258	 * but the CP will transmit from RECEIVING to READY under
259	 * the !electedSelf when KaY is not key server */
260	ieee802_1x_cp_sm_step(sm);
261	sm->using_receive_sas = FALSE;
262	sm->server_transmitting = FALSE;
263}
264
265
266SM_STATE(CP, READY)
267{
268	SM_ENTRY(CP, READY);
269
270	ieee802_1x_kay_enable_new_info(sm->kay);
271}
272
273
274SM_STATE(CP, TRANSMIT)
275{
276	SM_ENTRY(CP, TRANSMIT);
277
278	sm->controlled_port_enabled = TRUE;
279	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
280	sm->ltx = TRUE;
281	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
282					  sm->ltx, sm->lrx);
283	ieee802_1x_kay_enable_tx_sas(sm->kay,  sm->lki);
284	sm->all_receiving = FALSE;
285	sm->server_transmitting = FALSE;
286}
287
288
289SM_STATE(CP, TRANSMITTING)
290{
291	SM_ENTRY(CP, TRANSMITTING);
292	sm->retire_when = sm->orx ? sm->retire_delay : 0;
293	sm->otx = FALSE;
294	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
295				       sm->otx, sm->orx);
296	ieee802_1x_kay_enable_new_info(sm->kay);
297	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
298	eloop_register_timeout(sm->retire_when / 1000, 0,
299			       ieee802_1x_cp_retire_when_timeout, sm, NULL);
300	sm->using_transmit_sa = FALSE;
301}
302
303
304SM_STATE(CP, ABANDON)
305{
306	SM_ENTRY(CP, ABANDON);
307	sm->lrx = FALSE;
308	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
309					  sm->ltx, sm->lrx);
310	ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
311
312	os_free(sm->lki);
313	sm->lki = NULL;
314	ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
315					  sm->ltx, sm->lrx);
316	sm->new_sak = FALSE;
317}
318
319
320SM_STATE(CP, RETIRE)
321{
322	SM_ENTRY(CP, RETIRE);
323	/* RETIRE state machine not keep with Figure 12-2 in
324	 * IEEE Std 802.1X-2010 */
325	if (sm->oki) {
326		ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
327		os_free(sm->oki);
328		sm->oki = NULL;
329	}
330	sm->orx = FALSE;
331	sm->otx = FALSE;
332	ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
333				       sm->otx, sm->orx);
334}
335
336
337/**
338 * CP state machine handler entry
339 */
340SM_STEP(CP)
341{
342	if (!sm->port_enabled)
343		SM_ENTER(CP, INIT);
344
345	switch (sm->CP_state) {
346	case CP_BEGIN:
347		SM_ENTER(CP, INIT);
348		break;
349
350	case CP_INIT:
351		SM_ENTER(CP, CHANGE);
352		break;
353
354	case CP_CHANGE:
355		if (sm->connect == UNAUTHENTICATED)
356			SM_ENTER(CP, ALLOWED);
357		else if (sm->connect == AUTHENTICATED)
358			SM_ENTER(CP, AUTHENTICATED);
359		else if (sm->connect == SECURE)
360			SM_ENTER(CP, SECURED);
361		break;
362
363	case CP_ALLOWED:
364		if (sm->connect != UNAUTHENTICATED)
365			SM_ENTER(CP, CHANGE);
366		break;
367
368	case CP_AUTHENTICATED:
369		if (sm->connect != AUTHENTICATED)
370			SM_ENTER(CP, CHANGE);
371		break;
372
373	case CP_SECURED:
374		if (changed_connect(sm))
375			SM_ENTER(CP, CHANGE);
376		else if (sm->new_sak)
377			SM_ENTER(CP, RECEIVE);
378		break;
379
380	case CP_RECEIVE:
381		if (sm->using_receive_sas)
382			SM_ENTER(CP, RECEIVING);
383		break;
384
385	case CP_RECEIVING:
386		if (sm->new_sak || changed_connect(sm))
387			SM_ENTER(CP, ABANDON);
388		if (!sm->elected_self)
389			SM_ENTER(CP, READY);
390		if (sm->elected_self &&
391		    (sm->all_receiving || !sm->controlled_port_enabled ||
392		     !sm->transmit_when))
393			SM_ENTER(CP, TRANSMIT);
394		break;
395
396	case CP_TRANSMIT:
397		if (sm->using_transmit_sa)
398			SM_ENTER(CP, TRANSMITTING);
399		break;
400
401	case CP_TRANSMITTING:
402		if (!sm->retire_when || changed_connect(sm))
403			SM_ENTER(CP, RETIRE);
404		break;
405
406	case CP_RETIRE:
407		if (changed_connect(sm))
408			SM_ENTER(CP, CHANGE);
409		else if (sm->new_sak)
410			SM_ENTER(CP, RECEIVE);
411		break;
412
413	case CP_READY:
414		if (sm->new_sak || changed_connect(sm))
415			SM_ENTER(CP, ABANDON);
416		if (sm->server_transmitting || !sm->controlled_port_enabled)
417			SM_ENTER(CP, TRANSMIT);
418		break;
419	case CP_ABANDON:
420		if (changed_connect(sm))
421			SM_ENTER(CP, RETIRE);
422		else if (sm->new_sak)
423			SM_ENTER(CP, RECEIVE);
424		break;
425	default:
426		wpa_printf(MSG_ERROR, "CP: the state machine is not defined");
427		break;
428	}
429}
430
431
432/**
433 * ieee802_1x_cp_sm_init -
434 */
435struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
436{
437	struct ieee802_1x_cp_sm *sm;
438
439	sm = os_zalloc(sizeof(*sm));
440	if (sm == NULL) {
441		wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
442		return NULL;
443	}
444
445	sm->kay = kay;
446
447	sm->port_valid = FALSE;
448
449	sm->chgd_server = FALSE;
450
451	sm->protect_frames = kay->macsec_protect;
452	sm->validate_frames = kay->macsec_validate;
453	sm->replay_protect = kay->macsec_replay_protect;
454	sm->replay_window = kay->macsec_replay_window;
455
456	sm->controlled_port_enabled = FALSE;
457
458	sm->lki = NULL;
459	sm->lrx = FALSE;
460	sm->ltx = FALSE;
461	sm->oki = NULL;
462	sm->orx = FALSE;
463	sm->otx = FALSE;
464
465	sm->current_cipher_suite = default_cs_id;
466	sm->cipher_suite = default_cs_id;
467	sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
468	sm->confidentiality_offset = sm->cipher_offset;
469	sm->transmit_delay = MKA_LIFE_TIME;
470	sm->retire_delay = MKA_SAK_RETIRE_TIME;
471	sm->CP_state = CP_BEGIN;
472	sm->changed = FALSE;
473
474	wpa_printf(MSG_DEBUG, "CP: state machine created");
475
476	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
477	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
478	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
479	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
480	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
481	secy_cp_control_confidentiality_offset(sm->kay,
482					       sm->confidentiality_offset);
483
484	SM_STEP_RUN(CP);
485
486	return sm;
487}
488
489
490static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm)
491{
492	enum cp_states prev_state;
493	int i;
494
495	for (i = 0; i < 100; i++) {
496		prev_state = sm->CP_state;
497		SM_STEP_RUN(CP);
498		if (prev_state == sm->CP_state)
499			break;
500	}
501}
502
503
504static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx)
505{
506	struct ieee802_1x_cp_sm *sm = eloop_ctx;
507	ieee802_1x_cp_step_run(sm);
508}
509
510
511/**
512 * ieee802_1x_cp_sm_deinit -
513 */
514void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
515{
516	wpa_printf(MSG_DEBUG, "CP: state machine removed");
517	if (!sm)
518		return;
519
520	eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
521	eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
522	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
523	os_free(sm->lki);
524	os_free(sm->oki);
525	os_free(sm);
526}
527
528
529/**
530 * ieee802_1x_cp_connect_pending
531 */
532void ieee802_1x_cp_connect_pending(void *cp_ctx)
533{
534	struct ieee802_1x_cp_sm *sm = cp_ctx;
535
536	sm->connect = PENDING;
537}
538
539
540/**
541 * ieee802_1x_cp_connect_unauthenticated
542 */
543void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx)
544{
545	struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx;
546
547	sm->connect = UNAUTHENTICATED;
548}
549
550
551/**
552 * ieee802_1x_cp_connect_authenticated
553 */
554void ieee802_1x_cp_connect_authenticated(void *cp_ctx)
555{
556	struct ieee802_1x_cp_sm *sm = cp_ctx;
557
558	sm->connect = AUTHENTICATED;
559}
560
561
562/**
563 * ieee802_1x_cp_connect_secure
564 */
565void ieee802_1x_cp_connect_secure(void *cp_ctx)
566{
567	struct ieee802_1x_cp_sm *sm = cp_ctx;
568
569	sm->connect = SECURE;
570}
571
572
573/**
574 * ieee802_1x_cp_set_chgdserver -
575 */
576void ieee802_1x_cp_signal_chgdserver(void *cp_ctx)
577{
578	struct ieee802_1x_cp_sm *sm = cp_ctx;
579
580	sm->chgd_server = TRUE;
581}
582
583
584/**
585 * ieee802_1x_cp_set_electedself -
586 */
587void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status)
588{
589	struct ieee802_1x_cp_sm *sm = cp_ctx;
590	sm->elected_self = status;
591}
592
593
594/**
595 * ieee802_1x_cp_set_ciphersuite -
596 */
597void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs)
598{
599	struct ieee802_1x_cp_sm *sm = cp_ctx;
600	sm->cipher_suite = cs;
601}
602
603
604/**
605 * ieee802_1x_cp_set_offset -
606 */
607void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset)
608{
609	struct ieee802_1x_cp_sm *sm = cp_ctx;
610	sm->cipher_offset = offset;
611}
612
613
614/**
615 * ieee802_1x_cp_signal_newsak -
616 */
617void ieee802_1x_cp_signal_newsak(void *cp_ctx)
618{
619	struct ieee802_1x_cp_sm *sm = cp_ctx;
620	sm->new_sak = TRUE;
621}
622
623
624/**
625 * ieee802_1x_cp_set_distributedki -
626 */
627void ieee802_1x_cp_set_distributedki(void *cp_ctx,
628				     const struct ieee802_1x_mka_ki *dki)
629{
630	struct ieee802_1x_cp_sm *sm = cp_ctx;
631	os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki));
632}
633
634
635/**
636 * ieee802_1x_cp_set_distributedan -
637 */
638void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an)
639{
640	struct ieee802_1x_cp_sm *sm = cp_ctx;
641	sm->distributed_an = an;
642}
643
644
645/**
646 * ieee802_1x_cp_set_usingreceivesas -
647 */
648void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status)
649{
650	struct ieee802_1x_cp_sm *sm = cp_ctx;
651	sm->using_receive_sas = status;
652}
653
654
655/**
656 * ieee802_1x_cp_set_allreceiving -
657 */
658void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status)
659{
660	struct ieee802_1x_cp_sm *sm = cp_ctx;
661	sm->all_receiving = status;
662}
663
664
665/**
666 * ieee802_1x_cp_set_servertransmitting -
667 */
668void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status)
669{
670	struct ieee802_1x_cp_sm *sm = cp_ctx;
671	sm->server_transmitting = status;
672}
673
674
675/**
676 * ieee802_1x_cp_set_usingtransmitsas -
677 */
678void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status)
679{
680	struct ieee802_1x_cp_sm *sm = cp_ctx;
681	sm->using_transmit_sa = status;
682}
683
684
685/**
686 * ieee802_1x_cp_sm_step - Advance EAPOL state machines
687 * @sm: EAPOL state machine
688 *
689 * This function is called to advance CP state machines after any change
690 * that could affect their state.
691 */
692void ieee802_1x_cp_sm_step(void *cp_ctx)
693{
694	/*
695	 * Run ieee802_1x_cp_step_run from a registered timeout
696	 * to make sure that other possible timeouts/events are processed
697	 * and to avoid long function call chains.
698	 */
699	struct ieee802_1x_cp_sm *sm = cp_ctx;
700	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
701	eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL);
702}
703
704
705static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
706					      void *timeout_ctx)
707{
708	struct ieee802_1x_cp_sm *sm = eloop_ctx;
709	sm->retire_when = 0;
710	ieee802_1x_cp_step_run(sm);
711}
712
713
714static void
715ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx)
716{
717	struct ieee802_1x_cp_sm *sm = eloop_ctx;
718	sm->transmit_when = 0;
719	ieee802_1x_cp_step_run(sm);
720}
721