15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine 35b9c547cSRui Paulo * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. 45b9c547cSRui Paulo * 55b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 65b9c547cSRui Paulo * See README for more details. 75b9c547cSRui Paulo */ 85b9c547cSRui Paulo 95b9c547cSRui Paulo #include "utils/includes.h" 105b9c547cSRui Paulo 115b9c547cSRui Paulo #include "utils/common.h" 125b9c547cSRui Paulo #include "utils/eloop.h" 135b9c547cSRui Paulo #include "common/defs.h" 145b9c547cSRui Paulo #include "common/ieee802_1x_defs.h" 155b9c547cSRui Paulo #include "utils/state_machine.h" 165b9c547cSRui Paulo #include "ieee802_1x_kay.h" 175b9c547cSRui Paulo #include "ieee802_1x_secy_ops.h" 185b9c547cSRui Paulo #include "pae/ieee802_1x_cp.h" 195b9c547cSRui Paulo 205b9c547cSRui Paulo #define STATE_MACHINE_DATA struct ieee802_1x_cp_sm 215b9c547cSRui Paulo #define STATE_MACHINE_DEBUG_PREFIX "CP" 225b9c547cSRui Paulo 23780fb4a2SCy Schubert static u64 default_cs_id = CS_ID_GCM_AES_128; 245b9c547cSRui Paulo 255b9c547cSRui Paulo /* The variable defined in clause 12 in IEEE Std 802.1X-2010 */ 265b9c547cSRui Paulo enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE }; 275b9c547cSRui Paulo 285b9c547cSRui Paulo struct ieee802_1x_cp_sm { 295b9c547cSRui Paulo enum cp_states { 305b9c547cSRui Paulo CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED, 315b9c547cSRui Paulo CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT, 325b9c547cSRui Paulo CP_TRANSMITTING, CP_ABANDON, CP_RETIRE 335b9c547cSRui Paulo } CP_state; 34*c1d255d3SCy Schubert bool changed; 355b9c547cSRui Paulo 365b9c547cSRui Paulo /* CP -> Client */ 37*c1d255d3SCy Schubert bool port_valid; 385b9c547cSRui Paulo 395b9c547cSRui Paulo /* Logon -> CP */ 405b9c547cSRui Paulo enum connect_type connect; 415b9c547cSRui Paulo 425b9c547cSRui Paulo /* KaY -> CP */ 43*c1d255d3SCy Schubert bool chgd_server; /* clear by CP */ 44*c1d255d3SCy Schubert bool elected_self; 455b9c547cSRui Paulo enum confidentiality_offset cipher_offset; 46780fb4a2SCy Schubert u64 cipher_suite; 47*c1d255d3SCy Schubert bool new_sak; /* clear by CP */ 485b9c547cSRui Paulo struct ieee802_1x_mka_ki distributed_ki; 495b9c547cSRui Paulo u8 distributed_an; 50*c1d255d3SCy Schubert bool using_receive_sas; 51*c1d255d3SCy Schubert bool all_receiving; 52*c1d255d3SCy Schubert bool server_transmitting; 53*c1d255d3SCy Schubert bool using_transmit_sa; 545b9c547cSRui Paulo 555b9c547cSRui Paulo /* CP -> KaY */ 565b9c547cSRui Paulo struct ieee802_1x_mka_ki *lki; 575b9c547cSRui Paulo u8 lan; 58*c1d255d3SCy Schubert bool ltx; 59*c1d255d3SCy Schubert bool lrx; 605b9c547cSRui Paulo struct ieee802_1x_mka_ki *oki; 615b9c547cSRui Paulo u8 oan; 62*c1d255d3SCy Schubert bool otx; 63*c1d255d3SCy Schubert bool orx; 645b9c547cSRui Paulo 655b9c547cSRui Paulo /* CP -> SecY */ 66*c1d255d3SCy Schubert bool protect_frames; 675b9c547cSRui Paulo enum validate_frames validate_frames; 685b9c547cSRui Paulo 69*c1d255d3SCy Schubert bool replay_protect; 705b9c547cSRui Paulo u32 replay_window; 715b9c547cSRui Paulo 72780fb4a2SCy Schubert u64 current_cipher_suite; 735b9c547cSRui Paulo enum confidentiality_offset confidentiality_offset; 74*c1d255d3SCy Schubert bool controlled_port_enabled; 755b9c547cSRui Paulo 765b9c547cSRui Paulo /* SecY -> CP */ 77*c1d255d3SCy Schubert bool port_enabled; /* SecY->CP */ 785b9c547cSRui Paulo 795b9c547cSRui Paulo /* private */ 805b9c547cSRui Paulo u32 transmit_when; 815b9c547cSRui Paulo u32 transmit_delay; 825b9c547cSRui Paulo u32 retire_when; 835b9c547cSRui Paulo u32 retire_delay; 845b9c547cSRui Paulo 855b9c547cSRui Paulo /* not defined IEEE Std 802.1X-2010 */ 865b9c547cSRui Paulo struct ieee802_1x_kay *kay; 875b9c547cSRui Paulo }; 885b9c547cSRui Paulo 895b9c547cSRui Paulo static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx, 905b9c547cSRui Paulo void *timeout_ctx); 915b9c547cSRui Paulo static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, 925b9c547cSRui Paulo void *timeout_ctx); 935b9c547cSRui Paulo 945b9c547cSRui Paulo 955b9c547cSRui Paulo static int changed_cipher(struct ieee802_1x_cp_sm *sm) 965b9c547cSRui Paulo { 975b9c547cSRui Paulo return sm->confidentiality_offset != sm->cipher_offset || 98780fb4a2SCy Schubert sm->current_cipher_suite != sm->cipher_suite; 995b9c547cSRui Paulo } 1005b9c547cSRui Paulo 1015b9c547cSRui Paulo 1025b9c547cSRui Paulo static int changed_connect(struct ieee802_1x_cp_sm *sm) 1035b9c547cSRui Paulo { 1045b9c547cSRui Paulo return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm); 1055b9c547cSRui Paulo } 1065b9c547cSRui Paulo 1075b9c547cSRui Paulo 1085b9c547cSRui Paulo SM_STATE(CP, INIT) 1095b9c547cSRui Paulo { 1105b9c547cSRui Paulo SM_ENTRY(CP, INIT); 1115b9c547cSRui Paulo 112*c1d255d3SCy Schubert sm->controlled_port_enabled = false; 1135b9c547cSRui Paulo secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); 1145b9c547cSRui Paulo 115*c1d255d3SCy Schubert sm->port_valid = false; 1165b9c547cSRui Paulo 1175b9c547cSRui Paulo os_free(sm->lki); 1185b9c547cSRui Paulo sm->lki = NULL; 119*c1d255d3SCy Schubert sm->ltx = false; 120*c1d255d3SCy Schubert sm->lrx = false; 1215b9c547cSRui Paulo 1225b9c547cSRui Paulo os_free(sm->oki); 1235b9c547cSRui Paulo sm->oki = NULL; 124*c1d255d3SCy Schubert sm->otx = false; 125*c1d255d3SCy Schubert sm->orx = false; 1265b9c547cSRui Paulo 127*c1d255d3SCy Schubert sm->port_enabled = true; 128*c1d255d3SCy Schubert sm->chgd_server = false; 1295b9c547cSRui Paulo } 1305b9c547cSRui Paulo 1315b9c547cSRui Paulo 1325b9c547cSRui Paulo SM_STATE(CP, CHANGE) 1335b9c547cSRui Paulo { 1345b9c547cSRui Paulo SM_ENTRY(CP, CHANGE); 1355b9c547cSRui Paulo 136*c1d255d3SCy Schubert sm->port_valid = false; 137*c1d255d3SCy Schubert sm->controlled_port_enabled = false; 1385b9c547cSRui Paulo secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); 1395b9c547cSRui Paulo 1405b9c547cSRui Paulo if (sm->lki) 1415b9c547cSRui Paulo ieee802_1x_kay_delete_sas(sm->kay, sm->lki); 1425b9c547cSRui Paulo if (sm->oki) 1435b9c547cSRui Paulo ieee802_1x_kay_delete_sas(sm->kay, sm->oki); 144*c1d255d3SCy Schubert /* The standard doesn't say it but we should clear out the latest 145*c1d255d3SCy Schubert * and old key values. Why would we keep advertising them if 146*c1d255d3SCy Schubert * they've been deleted and the key server has been changed? 147*c1d255d3SCy Schubert */ 148*c1d255d3SCy Schubert os_free(sm->oki); 149*c1d255d3SCy Schubert sm->oki = NULL; 150*c1d255d3SCy Schubert sm->otx = false; 151*c1d255d3SCy Schubert sm->orx = false; 152*c1d255d3SCy Schubert sm->oan = 0; 153*c1d255d3SCy Schubert ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan, 154*c1d255d3SCy Schubert sm->otx, sm->orx); 155*c1d255d3SCy Schubert os_free(sm->lki); 156*c1d255d3SCy Schubert sm->lki = NULL; 157*c1d255d3SCy Schubert sm->lrx = false; 158*c1d255d3SCy Schubert sm->ltx = false; 159*c1d255d3SCy Schubert sm->lan = 0; 160*c1d255d3SCy Schubert ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, 161*c1d255d3SCy Schubert sm->ltx, sm->lrx); 1625b9c547cSRui Paulo } 1635b9c547cSRui Paulo 1645b9c547cSRui Paulo 1655b9c547cSRui Paulo SM_STATE(CP, ALLOWED) 1665b9c547cSRui Paulo { 1675b9c547cSRui Paulo SM_ENTRY(CP, ALLOWED); 1685b9c547cSRui Paulo 169*c1d255d3SCy Schubert sm->protect_frames = false; 170*c1d255d3SCy Schubert sm->replay_protect = false; 1715b9c547cSRui Paulo sm->validate_frames = Checked; 1725b9c547cSRui Paulo 173*c1d255d3SCy Schubert sm->port_valid = false; 174*c1d255d3SCy Schubert sm->controlled_port_enabled = true; 1755b9c547cSRui Paulo 1765b9c547cSRui Paulo secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); 1775b9c547cSRui Paulo secy_cp_control_protect_frames(sm->kay, sm->protect_frames); 17885732ac8SCy Schubert secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); 1795b9c547cSRui Paulo secy_cp_control_validate_frames(sm->kay, sm->validate_frames); 1805b9c547cSRui Paulo secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); 1815b9c547cSRui Paulo } 1825b9c547cSRui Paulo 1835b9c547cSRui Paulo 1845b9c547cSRui Paulo SM_STATE(CP, AUTHENTICATED) 1855b9c547cSRui Paulo { 1865b9c547cSRui Paulo SM_ENTRY(CP, AUTHENTICATED); 1875b9c547cSRui Paulo 188*c1d255d3SCy Schubert sm->protect_frames = false; 189*c1d255d3SCy Schubert sm->replay_protect = false; 1905b9c547cSRui Paulo sm->validate_frames = Checked; 1915b9c547cSRui Paulo 192*c1d255d3SCy Schubert sm->port_valid = false; 193*c1d255d3SCy Schubert sm->controlled_port_enabled = true; 1945b9c547cSRui Paulo 1955b9c547cSRui Paulo secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); 1965b9c547cSRui Paulo secy_cp_control_protect_frames(sm->kay, sm->protect_frames); 19785732ac8SCy Schubert secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); 1985b9c547cSRui Paulo secy_cp_control_validate_frames(sm->kay, sm->validate_frames); 1995b9c547cSRui Paulo secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); 2005b9c547cSRui Paulo } 2015b9c547cSRui Paulo 2025b9c547cSRui Paulo 2035b9c547cSRui Paulo SM_STATE(CP, SECURED) 2045b9c547cSRui Paulo { 2055b9c547cSRui Paulo SM_ENTRY(CP, SECURED); 2065b9c547cSRui Paulo 207*c1d255d3SCy Schubert sm->chgd_server = false; 2085b9c547cSRui Paulo 209780fb4a2SCy Schubert sm->protect_frames = sm->kay->macsec_protect; 210780fb4a2SCy Schubert sm->replay_protect = sm->kay->macsec_replay_protect; 211780fb4a2SCy Schubert sm->validate_frames = sm->kay->macsec_validate; 2125b9c547cSRui Paulo 213780fb4a2SCy Schubert /* NOTE: now no other than default cipher suite (AES-GCM-128) */ 214780fb4a2SCy Schubert sm->current_cipher_suite = sm->cipher_suite; 215780fb4a2SCy Schubert secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite); 2165b9c547cSRui Paulo 2175b9c547cSRui Paulo sm->confidentiality_offset = sm->cipher_offset; 2185b9c547cSRui Paulo 219*c1d255d3SCy Schubert sm->port_valid = true; 2205b9c547cSRui Paulo 2215b9c547cSRui Paulo secy_cp_control_confidentiality_offset(sm->kay, 2225b9c547cSRui Paulo sm->confidentiality_offset); 2235b9c547cSRui Paulo secy_cp_control_protect_frames(sm->kay, sm->protect_frames); 22485732ac8SCy Schubert secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); 2255b9c547cSRui Paulo secy_cp_control_validate_frames(sm->kay, sm->validate_frames); 2265b9c547cSRui Paulo secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); 2275b9c547cSRui Paulo } 2285b9c547cSRui Paulo 2295b9c547cSRui Paulo 2305b9c547cSRui Paulo SM_STATE(CP, RECEIVE) 2315b9c547cSRui Paulo { 2325b9c547cSRui Paulo SM_ENTRY(CP, RECEIVE); 2335b9c547cSRui Paulo 2345b9c547cSRui Paulo sm->lki = os_malloc(sizeof(*sm->lki)); 2355b9c547cSRui Paulo if (!sm->lki) { 2365b9c547cSRui Paulo wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__); 2375b9c547cSRui Paulo return; 2385b9c547cSRui Paulo } 2395b9c547cSRui Paulo os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki)); 2405b9c547cSRui Paulo sm->lan = sm->distributed_an; 241*c1d255d3SCy Schubert sm->ltx = false; 242*c1d255d3SCy Schubert sm->lrx = false; 2435b9c547cSRui Paulo ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, 2445b9c547cSRui Paulo sm->ltx, sm->lrx); 2455b9c547cSRui Paulo ieee802_1x_kay_create_sas(sm->kay, sm->lki); 2465b9c547cSRui Paulo ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki); 247*c1d255d3SCy Schubert sm->new_sak = false; 248*c1d255d3SCy Schubert sm->all_receiving = false; 2495b9c547cSRui Paulo } 2505b9c547cSRui Paulo 2515b9c547cSRui Paulo 2525b9c547cSRui Paulo SM_STATE(CP, RECEIVING) 2535b9c547cSRui Paulo { 2545b9c547cSRui Paulo SM_ENTRY(CP, RECEIVING); 2555b9c547cSRui Paulo 256*c1d255d3SCy Schubert sm->lrx = true; 2575b9c547cSRui Paulo ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, 2585b9c547cSRui Paulo sm->ltx, sm->lrx); 2595b9c547cSRui Paulo sm->transmit_when = sm->transmit_delay; 2605b9c547cSRui Paulo eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL); 2615b9c547cSRui Paulo eloop_register_timeout(sm->transmit_when / 1000, 0, 2625b9c547cSRui Paulo ieee802_1x_cp_transmit_when_timeout, sm, NULL); 2635b9c547cSRui Paulo /* the electedSelf have been set before CP entering to RECEIVING 2645b9c547cSRui Paulo * but the CP will transmit from RECEIVING to READY under 2655b9c547cSRui Paulo * the !electedSelf when KaY is not key server */ 2665b9c547cSRui Paulo ieee802_1x_cp_sm_step(sm); 267*c1d255d3SCy Schubert sm->using_receive_sas = false; 268*c1d255d3SCy Schubert sm->server_transmitting = false; 2695b9c547cSRui Paulo } 2705b9c547cSRui Paulo 2715b9c547cSRui Paulo 2725b9c547cSRui Paulo SM_STATE(CP, READY) 2735b9c547cSRui Paulo { 2745b9c547cSRui Paulo SM_ENTRY(CP, READY); 2755b9c547cSRui Paulo 2765b9c547cSRui Paulo ieee802_1x_kay_enable_new_info(sm->kay); 2775b9c547cSRui Paulo } 2785b9c547cSRui Paulo 2795b9c547cSRui Paulo 2805b9c547cSRui Paulo SM_STATE(CP, TRANSMIT) 2815b9c547cSRui Paulo { 2825b9c547cSRui Paulo SM_ENTRY(CP, TRANSMIT); 2835b9c547cSRui Paulo 284*c1d255d3SCy Schubert sm->controlled_port_enabled = true; 2855b9c547cSRui Paulo secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); 286*c1d255d3SCy Schubert sm->ltx = true; 2875b9c547cSRui Paulo ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, 2885b9c547cSRui Paulo sm->ltx, sm->lrx); 2895b9c547cSRui Paulo ieee802_1x_kay_enable_tx_sas(sm->kay, sm->lki); 290*c1d255d3SCy Schubert sm->all_receiving = false; 291*c1d255d3SCy Schubert sm->server_transmitting = false; 2925b9c547cSRui Paulo } 2935b9c547cSRui Paulo 2945b9c547cSRui Paulo 2955b9c547cSRui Paulo SM_STATE(CP, TRANSMITTING) 2965b9c547cSRui Paulo { 2975b9c547cSRui Paulo SM_ENTRY(CP, TRANSMITTING); 2985b9c547cSRui Paulo sm->retire_when = sm->orx ? sm->retire_delay : 0; 299*c1d255d3SCy Schubert sm->otx = false; 3005b9c547cSRui Paulo ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan, 3015b9c547cSRui Paulo sm->otx, sm->orx); 3025b9c547cSRui Paulo ieee802_1x_kay_enable_new_info(sm->kay); 3035b9c547cSRui Paulo eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL); 3045b9c547cSRui Paulo eloop_register_timeout(sm->retire_when / 1000, 0, 3055b9c547cSRui Paulo ieee802_1x_cp_retire_when_timeout, sm, NULL); 306*c1d255d3SCy Schubert sm->using_transmit_sa = false; 3075b9c547cSRui Paulo } 3085b9c547cSRui Paulo 3095b9c547cSRui Paulo 3105b9c547cSRui Paulo SM_STATE(CP, ABANDON) 3115b9c547cSRui Paulo { 3125b9c547cSRui Paulo SM_ENTRY(CP, ABANDON); 313*c1d255d3SCy Schubert sm->lrx = false; 3145b9c547cSRui Paulo ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, 3155b9c547cSRui Paulo sm->ltx, sm->lrx); 3165b9c547cSRui Paulo ieee802_1x_kay_delete_sas(sm->kay, sm->lki); 3175b9c547cSRui Paulo 3185b9c547cSRui Paulo os_free(sm->lki); 3195b9c547cSRui Paulo sm->lki = NULL; 3205b9c547cSRui Paulo ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, 3215b9c547cSRui Paulo sm->ltx, sm->lrx); 3225b9c547cSRui Paulo } 3235b9c547cSRui Paulo 3245b9c547cSRui Paulo 3255b9c547cSRui Paulo SM_STATE(CP, RETIRE) 3265b9c547cSRui Paulo { 3275b9c547cSRui Paulo SM_ENTRY(CP, RETIRE); 3284bc52338SCy Schubert if (sm->oki) { 3294bc52338SCy Schubert ieee802_1x_kay_delete_sas(sm->kay, sm->oki); 3305b9c547cSRui Paulo os_free(sm->oki); 3315b9c547cSRui Paulo sm->oki = NULL; 3324bc52338SCy Schubert } 333*c1d255d3SCy Schubert sm->oki = sm->lki; 334*c1d255d3SCy Schubert sm->otx = sm->ltx; 335*c1d255d3SCy Schubert sm->orx = sm->lrx; 336*c1d255d3SCy Schubert sm->oan = sm->lan; 3375b9c547cSRui Paulo ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan, 3385b9c547cSRui Paulo sm->otx, sm->orx); 339*c1d255d3SCy Schubert sm->lki = NULL; 340*c1d255d3SCy Schubert sm->ltx = false; 341*c1d255d3SCy Schubert sm->lrx = false; 342*c1d255d3SCy Schubert sm->lan = 0; 343*c1d255d3SCy Schubert ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan, 344*c1d255d3SCy Schubert sm->ltx, sm->lrx); 3455b9c547cSRui Paulo } 3465b9c547cSRui Paulo 3475b9c547cSRui Paulo 3485b9c547cSRui Paulo /** 3495b9c547cSRui Paulo * CP state machine handler entry 3505b9c547cSRui Paulo */ 3515b9c547cSRui Paulo SM_STEP(CP) 3525b9c547cSRui Paulo { 3535b9c547cSRui Paulo if (!sm->port_enabled) 3545b9c547cSRui Paulo SM_ENTER(CP, INIT); 3555b9c547cSRui Paulo 3565b9c547cSRui Paulo switch (sm->CP_state) { 3575b9c547cSRui Paulo case CP_BEGIN: 3585b9c547cSRui Paulo SM_ENTER(CP, INIT); 3595b9c547cSRui Paulo break; 3605b9c547cSRui Paulo 3615b9c547cSRui Paulo case CP_INIT: 3625b9c547cSRui Paulo SM_ENTER(CP, CHANGE); 3635b9c547cSRui Paulo break; 3645b9c547cSRui Paulo 3655b9c547cSRui Paulo case CP_CHANGE: 3665b9c547cSRui Paulo if (sm->connect == UNAUTHENTICATED) 3675b9c547cSRui Paulo SM_ENTER(CP, ALLOWED); 3685b9c547cSRui Paulo else if (sm->connect == AUTHENTICATED) 3695b9c547cSRui Paulo SM_ENTER(CP, AUTHENTICATED); 3705b9c547cSRui Paulo else if (sm->connect == SECURE) 3715b9c547cSRui Paulo SM_ENTER(CP, SECURED); 3725b9c547cSRui Paulo break; 3735b9c547cSRui Paulo 3745b9c547cSRui Paulo case CP_ALLOWED: 3755b9c547cSRui Paulo if (sm->connect != UNAUTHENTICATED) 3765b9c547cSRui Paulo SM_ENTER(CP, CHANGE); 3775b9c547cSRui Paulo break; 3785b9c547cSRui Paulo 3795b9c547cSRui Paulo case CP_AUTHENTICATED: 3805b9c547cSRui Paulo if (sm->connect != AUTHENTICATED) 3815b9c547cSRui Paulo SM_ENTER(CP, CHANGE); 3825b9c547cSRui Paulo break; 3835b9c547cSRui Paulo 3845b9c547cSRui Paulo case CP_SECURED: 3855b9c547cSRui Paulo if (changed_connect(sm)) 3865b9c547cSRui Paulo SM_ENTER(CP, CHANGE); 3875b9c547cSRui Paulo else if (sm->new_sak) 3885b9c547cSRui Paulo SM_ENTER(CP, RECEIVE); 3895b9c547cSRui Paulo break; 3905b9c547cSRui Paulo 3915b9c547cSRui Paulo case CP_RECEIVE: 3925b9c547cSRui Paulo if (sm->using_receive_sas) 3935b9c547cSRui Paulo SM_ENTER(CP, RECEIVING); 3945b9c547cSRui Paulo break; 3955b9c547cSRui Paulo 3965b9c547cSRui Paulo case CP_RECEIVING: 3975b9c547cSRui Paulo if (sm->new_sak || changed_connect(sm)) 3985b9c547cSRui Paulo SM_ENTER(CP, ABANDON); 3995b9c547cSRui Paulo if (!sm->elected_self) 4005b9c547cSRui Paulo SM_ENTER(CP, READY); 4015b9c547cSRui Paulo if (sm->elected_self && 4024bc52338SCy Schubert (sm->all_receiving || !sm->controlled_port_enabled || 4034bc52338SCy Schubert !sm->transmit_when)) 4045b9c547cSRui Paulo SM_ENTER(CP, TRANSMIT); 4055b9c547cSRui Paulo break; 4065b9c547cSRui Paulo 4075b9c547cSRui Paulo case CP_TRANSMIT: 4085b9c547cSRui Paulo if (sm->using_transmit_sa) 4095b9c547cSRui Paulo SM_ENTER(CP, TRANSMITTING); 4105b9c547cSRui Paulo break; 4115b9c547cSRui Paulo 4125b9c547cSRui Paulo case CP_TRANSMITTING: 4135b9c547cSRui Paulo if (!sm->retire_when || changed_connect(sm)) 4145b9c547cSRui Paulo SM_ENTER(CP, RETIRE); 4155b9c547cSRui Paulo break; 4165b9c547cSRui Paulo 4175b9c547cSRui Paulo case CP_RETIRE: 4185b9c547cSRui Paulo if (changed_connect(sm)) 4195b9c547cSRui Paulo SM_ENTER(CP, CHANGE); 4205b9c547cSRui Paulo else if (sm->new_sak) 4215b9c547cSRui Paulo SM_ENTER(CP, RECEIVE); 4225b9c547cSRui Paulo break; 4235b9c547cSRui Paulo 4245b9c547cSRui Paulo case CP_READY: 4255b9c547cSRui Paulo if (sm->new_sak || changed_connect(sm)) 4264bc52338SCy Schubert SM_ENTER(CP, ABANDON); 4274bc52338SCy Schubert if (sm->server_transmitting || !sm->controlled_port_enabled) 4285b9c547cSRui Paulo SM_ENTER(CP, TRANSMIT); 4295b9c547cSRui Paulo break; 4305b9c547cSRui Paulo case CP_ABANDON: 4315b9c547cSRui Paulo if (changed_connect(sm)) 4325b9c547cSRui Paulo SM_ENTER(CP, RETIRE); 4335b9c547cSRui Paulo else if (sm->new_sak) 4345b9c547cSRui Paulo SM_ENTER(CP, RECEIVE); 4355b9c547cSRui Paulo break; 4365b9c547cSRui Paulo default: 4375b9c547cSRui Paulo wpa_printf(MSG_ERROR, "CP: the state machine is not defined"); 4385b9c547cSRui Paulo break; 4395b9c547cSRui Paulo } 4405b9c547cSRui Paulo } 4415b9c547cSRui Paulo 4425b9c547cSRui Paulo 4435b9c547cSRui Paulo /** 4445b9c547cSRui Paulo * ieee802_1x_cp_sm_init - 4455b9c547cSRui Paulo */ 446780fb4a2SCy Schubert struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) 4475b9c547cSRui Paulo { 4485b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm; 4495b9c547cSRui Paulo 4505b9c547cSRui Paulo sm = os_zalloc(sizeof(*sm)); 4515b9c547cSRui Paulo if (sm == NULL) { 4525b9c547cSRui Paulo wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__); 4535b9c547cSRui Paulo return NULL; 4545b9c547cSRui Paulo } 4555b9c547cSRui Paulo 4565b9c547cSRui Paulo sm->kay = kay; 4575b9c547cSRui Paulo 458*c1d255d3SCy Schubert sm->port_valid = false; 4595b9c547cSRui Paulo 460*c1d255d3SCy Schubert sm->chgd_server = false; 4615b9c547cSRui Paulo 462780fb4a2SCy Schubert sm->protect_frames = kay->macsec_protect; 463780fb4a2SCy Schubert sm->validate_frames = kay->macsec_validate; 464780fb4a2SCy Schubert sm->replay_protect = kay->macsec_replay_protect; 465780fb4a2SCy Schubert sm->replay_window = kay->macsec_replay_window; 4665b9c547cSRui Paulo 467*c1d255d3SCy Schubert sm->controlled_port_enabled = false; 4685b9c547cSRui Paulo 4695b9c547cSRui Paulo sm->lki = NULL; 470*c1d255d3SCy Schubert sm->lrx = false; 471*c1d255d3SCy Schubert sm->ltx = false; 4725b9c547cSRui Paulo sm->oki = NULL; 473*c1d255d3SCy Schubert sm->orx = false; 474*c1d255d3SCy Schubert sm->otx = false; 4755b9c547cSRui Paulo 476780fb4a2SCy Schubert sm->current_cipher_suite = default_cs_id; 477780fb4a2SCy Schubert sm->cipher_suite = default_cs_id; 4785b9c547cSRui Paulo sm->cipher_offset = CONFIDENTIALITY_OFFSET_0; 4795b9c547cSRui Paulo sm->confidentiality_offset = sm->cipher_offset; 4805b9c547cSRui Paulo sm->transmit_delay = MKA_LIFE_TIME; 4815b9c547cSRui Paulo sm->retire_delay = MKA_SAK_RETIRE_TIME; 4825b9c547cSRui Paulo sm->CP_state = CP_BEGIN; 483*c1d255d3SCy Schubert sm->changed = false; 4845b9c547cSRui Paulo 4855b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "CP: state machine created"); 4865b9c547cSRui Paulo 4875b9c547cSRui Paulo secy_cp_control_protect_frames(sm->kay, sm->protect_frames); 48885732ac8SCy Schubert secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); 4895b9c547cSRui Paulo secy_cp_control_validate_frames(sm->kay, sm->validate_frames); 4905b9c547cSRui Paulo secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); 4915b9c547cSRui Paulo secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); 4925b9c547cSRui Paulo secy_cp_control_confidentiality_offset(sm->kay, 4935b9c547cSRui Paulo sm->confidentiality_offset); 4945b9c547cSRui Paulo 4955b9c547cSRui Paulo SM_STEP_RUN(CP); 4965b9c547cSRui Paulo 4975b9c547cSRui Paulo return sm; 4985b9c547cSRui Paulo } 4995b9c547cSRui Paulo 5005b9c547cSRui Paulo 5015b9c547cSRui Paulo static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm) 5025b9c547cSRui Paulo { 5035b9c547cSRui Paulo enum cp_states prev_state; 5045b9c547cSRui Paulo int i; 5055b9c547cSRui Paulo 5065b9c547cSRui Paulo for (i = 0; i < 100; i++) { 5075b9c547cSRui Paulo prev_state = sm->CP_state; 5085b9c547cSRui Paulo SM_STEP_RUN(CP); 5095b9c547cSRui Paulo if (prev_state == sm->CP_state) 5105b9c547cSRui Paulo break; 5115b9c547cSRui Paulo } 5125b9c547cSRui Paulo } 5135b9c547cSRui Paulo 5145b9c547cSRui Paulo 5155b9c547cSRui Paulo static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx) 5165b9c547cSRui Paulo { 5175b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = eloop_ctx; 5185b9c547cSRui Paulo ieee802_1x_cp_step_run(sm); 5195b9c547cSRui Paulo } 5205b9c547cSRui Paulo 5215b9c547cSRui Paulo 5225b9c547cSRui Paulo /** 5235b9c547cSRui Paulo * ieee802_1x_cp_sm_deinit - 5245b9c547cSRui Paulo */ 5255b9c547cSRui Paulo void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm) 5265b9c547cSRui Paulo { 5275b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "CP: state machine removed"); 5285b9c547cSRui Paulo if (!sm) 5295b9c547cSRui Paulo return; 5305b9c547cSRui Paulo 5315b9c547cSRui Paulo eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL); 5325b9c547cSRui Paulo eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL); 5335b9c547cSRui Paulo eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL); 5345b9c547cSRui Paulo os_free(sm->lki); 5355b9c547cSRui Paulo os_free(sm->oki); 5365b9c547cSRui Paulo os_free(sm); 5375b9c547cSRui Paulo } 5385b9c547cSRui Paulo 5395b9c547cSRui Paulo 5405b9c547cSRui Paulo /** 5415b9c547cSRui Paulo * ieee802_1x_cp_connect_pending 5425b9c547cSRui Paulo */ 5435b9c547cSRui Paulo void ieee802_1x_cp_connect_pending(void *cp_ctx) 5445b9c547cSRui Paulo { 5455b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 5465b9c547cSRui Paulo 5475b9c547cSRui Paulo sm->connect = PENDING; 5485b9c547cSRui Paulo } 5495b9c547cSRui Paulo 5505b9c547cSRui Paulo 5515b9c547cSRui Paulo /** 5525b9c547cSRui Paulo * ieee802_1x_cp_connect_unauthenticated 5535b9c547cSRui Paulo */ 5545b9c547cSRui Paulo void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx) 5555b9c547cSRui Paulo { 5565b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx; 5575b9c547cSRui Paulo 5585b9c547cSRui Paulo sm->connect = UNAUTHENTICATED; 5595b9c547cSRui Paulo } 5605b9c547cSRui Paulo 5615b9c547cSRui Paulo 5625b9c547cSRui Paulo /** 5635b9c547cSRui Paulo * ieee802_1x_cp_connect_authenticated 5645b9c547cSRui Paulo */ 5655b9c547cSRui Paulo void ieee802_1x_cp_connect_authenticated(void *cp_ctx) 5665b9c547cSRui Paulo { 5675b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 5685b9c547cSRui Paulo 5695b9c547cSRui Paulo sm->connect = AUTHENTICATED; 5705b9c547cSRui Paulo } 5715b9c547cSRui Paulo 5725b9c547cSRui Paulo 5735b9c547cSRui Paulo /** 5745b9c547cSRui Paulo * ieee802_1x_cp_connect_secure 5755b9c547cSRui Paulo */ 5765b9c547cSRui Paulo void ieee802_1x_cp_connect_secure(void *cp_ctx) 5775b9c547cSRui Paulo { 5785b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 5795b9c547cSRui Paulo 5805b9c547cSRui Paulo sm->connect = SECURE; 5815b9c547cSRui Paulo } 5825b9c547cSRui Paulo 5835b9c547cSRui Paulo 5845b9c547cSRui Paulo /** 5855b9c547cSRui Paulo * ieee802_1x_cp_set_chgdserver - 5865b9c547cSRui Paulo */ 5875b9c547cSRui Paulo void ieee802_1x_cp_signal_chgdserver(void *cp_ctx) 5885b9c547cSRui Paulo { 5895b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 5905b9c547cSRui Paulo 591*c1d255d3SCy Schubert sm->chgd_server = true; 5925b9c547cSRui Paulo } 5935b9c547cSRui Paulo 5945b9c547cSRui Paulo 5955b9c547cSRui Paulo /** 5965b9c547cSRui Paulo * ieee802_1x_cp_set_electedself - 5975b9c547cSRui Paulo */ 598*c1d255d3SCy Schubert void ieee802_1x_cp_set_electedself(void *cp_ctx, bool status) 5995b9c547cSRui Paulo { 6005b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6015b9c547cSRui Paulo sm->elected_self = status; 6025b9c547cSRui Paulo } 6035b9c547cSRui Paulo 6045b9c547cSRui Paulo 6055b9c547cSRui Paulo /** 6065b9c547cSRui Paulo * ieee802_1x_cp_set_ciphersuite - 6075b9c547cSRui Paulo */ 608780fb4a2SCy Schubert void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs) 6095b9c547cSRui Paulo { 6105b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 611780fb4a2SCy Schubert sm->cipher_suite = cs; 6125b9c547cSRui Paulo } 6135b9c547cSRui Paulo 6145b9c547cSRui Paulo 6155b9c547cSRui Paulo /** 6165b9c547cSRui Paulo * ieee802_1x_cp_set_offset - 6175b9c547cSRui Paulo */ 6185b9c547cSRui Paulo void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset) 6195b9c547cSRui Paulo { 6205b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6215b9c547cSRui Paulo sm->cipher_offset = offset; 6225b9c547cSRui Paulo } 6235b9c547cSRui Paulo 6245b9c547cSRui Paulo 6255b9c547cSRui Paulo /** 6265b9c547cSRui Paulo * ieee802_1x_cp_signal_newsak - 6275b9c547cSRui Paulo */ 6285b9c547cSRui Paulo void ieee802_1x_cp_signal_newsak(void *cp_ctx) 6295b9c547cSRui Paulo { 6305b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 631*c1d255d3SCy Schubert sm->new_sak = true; 6325b9c547cSRui Paulo } 6335b9c547cSRui Paulo 6345b9c547cSRui Paulo 6355b9c547cSRui Paulo /** 6365b9c547cSRui Paulo * ieee802_1x_cp_set_distributedki - 6375b9c547cSRui Paulo */ 6385b9c547cSRui Paulo void ieee802_1x_cp_set_distributedki(void *cp_ctx, 6395b9c547cSRui Paulo const struct ieee802_1x_mka_ki *dki) 6405b9c547cSRui Paulo { 6415b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6425b9c547cSRui Paulo os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki)); 6435b9c547cSRui Paulo } 6445b9c547cSRui Paulo 6455b9c547cSRui Paulo 6465b9c547cSRui Paulo /** 6475b9c547cSRui Paulo * ieee802_1x_cp_set_distributedan - 6485b9c547cSRui Paulo */ 6495b9c547cSRui Paulo void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an) 6505b9c547cSRui Paulo { 6515b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6525b9c547cSRui Paulo sm->distributed_an = an; 6535b9c547cSRui Paulo } 6545b9c547cSRui Paulo 6555b9c547cSRui Paulo 6565b9c547cSRui Paulo /** 6575b9c547cSRui Paulo * ieee802_1x_cp_set_usingreceivesas - 6585b9c547cSRui Paulo */ 659*c1d255d3SCy Schubert void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, bool status) 6605b9c547cSRui Paulo { 6615b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6625b9c547cSRui Paulo sm->using_receive_sas = status; 6635b9c547cSRui Paulo } 6645b9c547cSRui Paulo 6655b9c547cSRui Paulo 6665b9c547cSRui Paulo /** 6675b9c547cSRui Paulo * ieee802_1x_cp_set_allreceiving - 6685b9c547cSRui Paulo */ 669*c1d255d3SCy Schubert void ieee802_1x_cp_set_allreceiving(void *cp_ctx, bool status) 6705b9c547cSRui Paulo { 6715b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6725b9c547cSRui Paulo sm->all_receiving = status; 6735b9c547cSRui Paulo } 6745b9c547cSRui Paulo 6755b9c547cSRui Paulo 6765b9c547cSRui Paulo /** 6775b9c547cSRui Paulo * ieee802_1x_cp_set_servertransmitting - 6785b9c547cSRui Paulo */ 679*c1d255d3SCy Schubert void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, bool status) 6805b9c547cSRui Paulo { 6815b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6825b9c547cSRui Paulo sm->server_transmitting = status; 6835b9c547cSRui Paulo } 6845b9c547cSRui Paulo 6855b9c547cSRui Paulo 6865b9c547cSRui Paulo /** 6875b9c547cSRui Paulo * ieee802_1x_cp_set_usingtransmitsas - 6885b9c547cSRui Paulo */ 689*c1d255d3SCy Schubert void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, bool status) 6905b9c547cSRui Paulo { 6915b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 6925b9c547cSRui Paulo sm->using_transmit_sa = status; 6935b9c547cSRui Paulo } 6945b9c547cSRui Paulo 6955b9c547cSRui Paulo 6965b9c547cSRui Paulo /** 6975b9c547cSRui Paulo * ieee802_1x_cp_sm_step - Advance EAPOL state machines 6985b9c547cSRui Paulo * @sm: EAPOL state machine 6995b9c547cSRui Paulo * 7005b9c547cSRui Paulo * This function is called to advance CP state machines after any change 7015b9c547cSRui Paulo * that could affect their state. 7025b9c547cSRui Paulo */ 7035b9c547cSRui Paulo void ieee802_1x_cp_sm_step(void *cp_ctx) 7045b9c547cSRui Paulo { 7055b9c547cSRui Paulo /* 7065b9c547cSRui Paulo * Run ieee802_1x_cp_step_run from a registered timeout 7075b9c547cSRui Paulo * to make sure that other possible timeouts/events are processed 7085b9c547cSRui Paulo * and to avoid long function call chains. 7095b9c547cSRui Paulo */ 7105b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = cp_ctx; 7115b9c547cSRui Paulo eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL); 7125b9c547cSRui Paulo eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL); 7135b9c547cSRui Paulo } 7145b9c547cSRui Paulo 7155b9c547cSRui Paulo 7165b9c547cSRui Paulo static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx, 7175b9c547cSRui Paulo void *timeout_ctx) 7185b9c547cSRui Paulo { 7195b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = eloop_ctx; 7205b9c547cSRui Paulo sm->retire_when = 0; 7215b9c547cSRui Paulo ieee802_1x_cp_step_run(sm); 7225b9c547cSRui Paulo } 7235b9c547cSRui Paulo 7245b9c547cSRui Paulo 7255b9c547cSRui Paulo static void 7265b9c547cSRui Paulo ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx) 7275b9c547cSRui Paulo { 7285b9c547cSRui Paulo struct ieee802_1x_cp_sm *sm = eloop_ctx; 7295b9c547cSRui Paulo sm->transmit_when = 0; 7305b9c547cSRui Paulo ieee802_1x_cp_step_run(sm); 7315b9c547cSRui Paulo } 732