/* Cesar project {{{ * * Copyright (C) 2008 Spidcom * * <<>> * * }}} */ /** * \file cp/sta/action/src/assoc.c * \brief STA action, association related definitions. * \ingroup cp_sta_action */ #include "common/std.h" #include "action.h" #include "cp/inc/context.h" #include "cp/fsm/fsm.h" #include "cp/msg/msg.h" #include "cp/beacon/beacon.h" #include "cp/beacon/ntb/ntb.h" #include "lib/rnd.h" #include "cp/secu/defs.h" #define RETRY_TIMEOUT_MS 1000 #define RENEW_MARGIN_MS (5 * 60 * 1000) #define LEAVING_TIMEOUT_MS (40 * 3) #define LEAVE_WAIT_TIMEOUT_MS 1000 void cp_sta_action_assoc_init (cp_t *ctx) { dbg_assert (ctx); ctx->sta_action.assoc.cco_net = NULL; } void cp_sta_action_sc_assoc_start (cp_t *ctx, cp_net_t *cco_net, cp_sta_t *cco) { /* Check parameters. */ dbg_assert (ctx); dbg_assert (cco_net); /* cco can be NULL. */ /* Check NID. */ dbg_assert (cp_sta_own_data_get_nid (ctx) == cp_net_get_nid (ctx, cco_net)); /* Store CCo net. */ ctx->sta_action.assoc.cco_net = cco_net; /* Do we know the CCo? */ if (cco) /* Store CCo peer. */ cp_sta_get_peer (cco, &ctx->sta_action.assoc.peer); else { /* Let's try to find the CCo from the net. */ cp_sta_t *cco_from_net = cp_net_get_cco (ctx, cco_net); if (cco_from_net) { /* Get peer CCo. */ cp_sta_get_peer (cco_from_net, &ctx->sta_action.assoc.peer); /* Release. */ slab_release (cco_from_net); } else { /* We do not know the CCo, broadcast to all STA. */ ctx->sta_action.assoc.peer = CP_MME_PEER (MAC_BROADCAST, MAC_TEI_UNASSOCIATED); } } ctx->sta_action.assoc.retry = 3; /* Trigger to_sc_assoc event. */ cp_fsm_trigger_new_event (ctx, bare, to_sc_assoc); } void cp_sta_action_assoc_start (cp_t *ctx, cp_net_t *cco_net, cp_sta_t *cco) { dbg_assert (ctx); dbg_assert (cco_net); dbg_assert (cco); /* Remember CCo to associate with. */ dbg_assert (cp_sta_own_data_get_nid (ctx) == cp_net_get_nid (ctx, cco_net)); ctx->sta_action.assoc.cco_net = cco_net; cp_sta_get_peer (cco, &ctx->sta_action.assoc.peer); ctx->sta_action.assoc.retry = 3; /* Trigger to_assoc event. */ cp_fsm_trigger_new_event (ctx, bare, to_assoc); } void cp_sta_action_assoc_leave (cp_t *ctx) { dbg_assert (ctx); /* Trigger to_leave event. */ cp_fsm_trigger_new_event (ctx, bare, to_leave); } void cp_sta_action_assoc__start_retry_timer (cp_t *ctx) { dbg_assert (ctx); cp_fsm_event_t *event = cp_fsm_event_bare_new ( ctx, CP_FSM_EVENT_TYPE_assoc_timeout); cp_sta_core_gen_timed_event (ctx, &ctx->sta_action.assoc.timer, event, RETRY_TIMEOUT_MS); } void cp_sta_action_assoc__stop_retry_timer (cp_t *ctx) { dbg_assert (ctx); cp_sta_core_stop_timed_or_cyclic_event ( ctx, &ctx->sta_action.assoc.timer); } void cp_sta_action_assoc__unassociated__enter (cp_t *ctx) { dbg_assert (ctx); cp_sta_own_data_set_authenticated_status (ctx, false); cp_sta_mgr_set_our_avln (ctx, NULL); cp_sta_own_data_set_tei (ctx, 0); // TODO cp_beacon_ntb_clear (ctx); cp_fsm_trigger_new_event (ctx, bare, left); } void cp_sta_action_assoc__unassociated__to_assoc (cp_t *ctx) { dbg_assert (ctx); /* Send association request. */ cp_msg_cc_assoc_req_t data = { CP_MSG_CC_ASSOC_REQ_TYPE_NEW, cp_sta_own_data_get_nid (ctx), CP_CCO_LEVEL, CP_PCO_CAP }; cp_msg_cc_assoc_req_send (ctx, &ctx->sta_action.assoc.peer, &data); } void cp_sta_action_assoc__wait_assoc_cnf__cc_assoc_cnf__common (cp_t *ctx, cp_mme_rx_t *mme, bool sc) { cp_msg_cc_assoc_cnf_t cnf; dbg_assert (ctx); dbg_assert (mme); /* Check response. */ if ((mme->peer.mac != ctx->sta_action.assoc.peer.mac && ctx->sta_action.assoc.peer.mac != MAC_BROADCAST) || mme->peer.tei == 0 || !cp_msg_cc_assoc_cnf_receive (ctx, mme, &cnf) || cnf.nid != cp_sta_own_data_get_nid (ctx)) { /* Unrelated message, drop. */ if (!sc) cp_fsm_branch (ctx, WAIT_ASSOC_CONF, CC_ASSOC_CNF, unrelated); else cp_fsm_branch (ctx, SC_WAIT_ASSOC_CONF, CC_ASSOC_CNF, unrelated); } else if (cnf.result != CP_MSG_CC_ASSOC_CNF_RESULT_SUCCESS) { /* Failure indication. */ if (!sc) cp_fsm_branch (ctx, WAIT_ASSOC_CONF, CC_ASSOC_CNF, nok); else cp_fsm_branch (ctx, SC_WAIT_ASSOC_CONF, CC_ASSOC_CNF, nok); } else { cp_net_t *our_net; cp_sta_own_data_t *own_data = cp_sta_mgr_get_sta_own_data (ctx); /* Start lease timer, it can not expire before we go to authenticated * state because authentication timeout is shorter. */ int lease_time_ms = cnf.lease_time_min * 60 * 1000 - RENEW_MARGIN_MS; if (lease_time_ms > 0) { cp_fsm_event_t *event = cp_fsm_event_bare_new ( ctx, CP_FSM_EVENT_TYPE_renew); cp_sta_core_gen_timed_event (ctx, &ctx->sta_action.assoc.lease_timer, event, lease_time_ms); } /* Update state. */ cp_sta_own_data_set_tei (ctx, cnf.sta_tei); /* Set our net to the CCo net. */ our_net = ctx->sta_action.assoc.cco_net; cp_sta_mgr_set_our_avln (ctx, our_net); /* Promote UCCo to CCo. Or the CCo's mac address was not know. */ if ((ctx->sta_action.assoc.peer.tei == MAC_TEI_UNASSOCIATED) || (ctx->sta_action.assoc.peer.mac == MAC_BROADCAST)) { cp_sta_t *cco = cp_sta_mgr_sta_add (ctx, our_net, mme->peer.tei, mme->peer.mac); cp_net_set_cco (ctx, our_net, mme->peer.tei); ctx->sta_action.assoc.peer = mme->peer; slab_release (cco); } /* Not in SC procedure. */ if (!sc) { /* Send get key request. */ cp_msg_cm_get_key_req_t get_key = { .relayed = false, .key_type = CP_MSG_KEY_NEK, .nid = cp_sta_own_data_get_nid (ctx) }; cp_secu_protocol_run_new (&ctx->sta_action.assoc.prun, 0, &ctx->rnd); cp_msg_cm_get_key_req_send (ctx, &mme->peer, CP_MME_PEKS_NMK, &ctx->sta_action.assoc.prun, &get_key); /* Change state. */ cp_fsm_branch (ctx, WAIT_ASSOC_CONF, CC_ASSOC_CNF, ok); } else { /* In SC procedure. */ /* Ensure SC peer is correct, so let's copy it back (to handle TEI * change (UCCO to CCO) and/or MAC change (broadcast). */ ctx->sta_action.sc.peer = mme->peer; /* Nothing to send. */ /* Change state. */ cp_fsm_branch (ctx, SC_WAIT_ASSOC_CONF, CC_ASSOC_CNF, ok); } /* Set the TEI track to the CCo's TEI to keep synchronisation. */ own_data->tei_track = ctx->sta_action.assoc.peer.tei; } } void cp_sta_action_assoc__wait_assoc_cnf__cc_assoc_cnf (cp_t *ctx, cp_mme_rx_t *mme) { cp_sta_action_assoc__wait_assoc_cnf__cc_assoc_cnf__common (ctx, mme, false); } void cp_sta_action_assoc__sc_wait_assoc_cnf__cc_assoc_cnf (cp_t *ctx, cp_mme_rx_t *mme) { cp_sta_action_assoc__wait_assoc_cnf__cc_assoc_cnf__common (ctx, mme, true); } void cp_sta_action_assoc__wait_assoc_cnf__timeout_common (cp_t *ctx, bool sc) { dbg_assert (ctx); ctx->sta_action.assoc.retry--; if (ctx->sta_action.assoc.retry) { /* Restart timer. */ cp_sta_action_assoc__start_retry_timer (ctx); /* Resend association request. */ cp_sta_action_assoc__unassociated__to_assoc (ctx); if (!sc) cp_fsm_branch (ctx, WAIT_ASSOC_CONF, assoc_timeout, retry); else cp_fsm_branch (ctx, SC_WAIT_ASSOC_CONF, assoc_timeout, retry); } else { /* Give up. */ if (!sc) cp_fsm_branch (ctx, WAIT_ASSOC_CONF, assoc_timeout, no_retry); else cp_fsm_branch (ctx, SC_WAIT_ASSOC_CONF, assoc_timeout, no_retry); } } void cp_sta_action_assoc__wait_assoc_cnf__timeout (cp_t *ctx) { cp_sta_action_assoc__wait_assoc_cnf__timeout_common (ctx, false); } void cp_sta_action_assoc__sc_wait_assoc_cnf__timeout (cp_t *ctx) { cp_sta_action_assoc__wait_assoc_cnf__timeout_common (ctx, true); } void cp_sta_action_assoc__cm_get_key_cnf__common (cp_t *ctx, cp_mme_rx_t *mme, bool sc) { dbg_assert (ctx); dbg_assert (mme); cp_msg_cm_get_key_cnf_t cnf; /* Check response. */ if (!cp_mme_peer_cmp (&mme->peer, &ctx->sta_action.assoc.peer) || !cp_msg_cm_get_key_cnf_receive (ctx, mme, &cnf) || (!cp_secu_protocol_check (&ctx->sta_action.assoc.prun, &mme->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_LAST)) || cnf.nid != cp_sta_own_data_get_nid (ctx) || cnf.key_type != CP_MSG_KEY_NEK || (sc && mme->peks != CP_MME_PEKS_NMK)) { /* Unrelated message, drop. */ if (!sc) cp_fsm_branch (ctx, ASSOCIATED, CM_GET_KEY_CNF_PID0, unrelated); else cp_fsm_branch (ctx, SC_NMK_EXCHANGED, CM_GET_KEY_CNF_PID0, unrelated); } else if (cnf.result != CP_MSG_CM_GET_KEY_CNF_RESULT_KEY_GRANTED) { /* Failure indication. */ if (!sc) cp_fsm_branch (ctx, ASSOCIATED, CM_GET_KEY_CNF_PID0, nok); else cp_fsm_branch (ctx, SC_NMK_EXCHANGED, CM_GET_KEY_CNF_PID0, nok); } else { /* Use the new key. */ cp_sta_own_data_set_authenticated_status (ctx, true); cp_beacon_change_nek (ctx, cnf.eks, cnf.key, true); /* Signal joined state. */ cp_fsm_trigger_new_event (ctx, bare, joined); /* Reset beacon loss counter. */ ctx->sta_action.assoc.beacon_loss = 0; if (!sc) cp_fsm_branch (ctx, ASSOCIATED, CM_GET_KEY_CNF_PID0, ok); else cp_fsm_branch (ctx, SC_NMK_EXCHANGED, CM_GET_KEY_CNF_PID0, ok); } } void cp_sta_action_assoc__sc_nmk_exchanged__cm_get_key_cnf_pid_0 (cp_t *ctx, cp_mme_rx_t * mme) { cp_sta_action_assoc__cm_get_key_cnf__common (ctx, mme, true); } void cp_sta_action_assoc__associated__cm_get_key_cnf_pid_0 (cp_t *ctx, cp_mme_rx_t *mme) { cp_sta_action_assoc__cm_get_key_cnf__common (ctx, mme, false); } void cp_sta_action_assoc__associated__timeout (cp_t *ctx) { dbg_assert (ctx); /* Stop lease timer. */ cp_sta_core_stop_timed_or_cyclic_event ( ctx, &ctx->sta_action.assoc.lease_timer); } void cp_sta_action_assoc__associated__to_leave (cp_t *ctx) { dbg_assert (ctx); /* Stop lease timer. */ cp_sta_core_stop_timed_or_cyclic_event ( ctx, &ctx->sta_action.assoc.lease_timer); } void cp_sta_action_assoc__sc_associated__cm_get_key_req_pid_3 (cp_t *ctx, cp_mme_rx_t *mme) { /* Check parameters. */ dbg_assert (ctx); dbg_assert (mme); cp_msg_cm_get_key_req_t req; /* Check response (note, the MME should be sent by the STA with which are * doing the SC (it can be different from the CCo); */ if (!cp_mme_peer_cmp (&mme->peer, &ctx->sta_action.sc.peer) || !cp_msg_cm_get_key_req_receive (ctx, mme, &req) || (!cp_secu_protocol_check (NULL, &mme->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_NEW)) || (req.nid != cp_sta_own_data_get_nid (ctx)) || (req.key_type != CP_MSG_KEY_HASH_KEY) || (mme->peks != CP_MME_PEKS_SPC_NOT_EMBEDDED) ) { /* Unrelated message, drop it. */ cp_fsm_branch (ctx, SC_ASSOCIATED, CM_GET_KEY_REQ_PID3, unrelated); } else { /* Message is valid, reply. */ /* Copy security protocol run for authentication. */ ctx->sta_action.assoc.prun = mme->prun; /* Prepare message. */ cp_msg_cm_get_key_cnf_t cnf; cnf.result = CP_MSG_CM_GET_KEY_CNF_RESULT_KEY_GRANTED; cnf.key_type = CP_MSG_KEY_HASH_KEY; cnf.nid = cp_sta_own_data_get_nid (ctx); cnf.eks = CP_MME_PEKS_TEK_MIN; /* Generate an hash key. */ cp_secu_generate_hash (ctx, lib_rnd32 (&ctx->rnd), cnf.hash_key, CP_SECU_HASH_KEY_FOR_TEK_SIZE); /* Generate TEK with hash key from CM_HET_KEY REQ and CNF. */ cp_secu_tek_gen ((u32 *)req.hash_key, (u32 *)cnf.hash_key, &ctx->sta_action.sc.tek); /* Send CM_GET_KEY.CNF. */ cp_secu_protocol_next (&ctx->sta_action.assoc.prun, &ctx->rnd, false); cp_msg_cm_get_key_cnf_send (ctx, &ctx->sta_action.sc.peer, CP_MME_PEKS_SPC_NOT_EMBEDDED, &ctx->sta_action.assoc.prun, &cnf); /* Set TEK for encryption/decryption of cm_encrypted payload. */ cp_sta_own_data_set_tek (ctx, ctx->sta_action.sc.tek); cp_fsm_branch (ctx, SC_ASSOCIATED, CM_GET_KEY_REQ_PID3, ok); } } void cp_sta_action_assoc__sc_tek_exchanged__cm_set_key_req_pid_3 (cp_t *ctx, cp_mme_rx_t *mme) { /* Check parameters. */ dbg_assert (ctx); dbg_assert (mme); cp_msg_cm_set_key_req_t req; /* Check response (note, the MME should be sent by the STA with which are * doing the SC (it can be different from the CCo); */ if (!cp_mme_peer_cmp (&mme->peer, &ctx->sta_action.sc.peer) || !cp_msg_cm_set_key_req_receive (ctx, mme, &req) || (!cp_secu_protocol_check (&ctx->sta_action.assoc.prun, &mme->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_NEXT)) || (req.nid != cp_sta_own_data_get_nid (ctx)) || (req.key_type != CP_MSG_KEY_NMK) ) { /* Unrelated message, drop it. */ cp_fsm_branch (ctx, SC_TEK_EXCHANGED, CM_SET_KEY_REQ_PID3, unrelated); } else { /* Message is valid. */ /* Set NMK. */ cp_sta_own_data_set_nmk (ctx, req.new_key); /* Copy security protocol run for authentication. */ ctx->sta_action.assoc.prun = mme->prun; /* Prepare message for reply. */ cp_msg_cm_set_key_cnf_t cnf; cnf.result = CP_MSG_CM_SET_KEY_CNF_RESULT_SUCCESS; cnf.cco_cap = CP_CCO_LEVEL; /* Send CM_SET_KEY.CNF. */ cp_secu_protocol_next (&ctx->sta_action.assoc.prun, &ctx->rnd, true); cp_msg_cm_set_key_cnf_send (ctx, &ctx->sta_action.sc.peer, CP_MME_PEKS_NMK, &ctx->sta_action.assoc.prun, &cnf); /* Start authentication. */ cp_msg_cm_get_key_req_t get_key = { .relayed = false, .key_type = CP_MSG_KEY_NEK, .nid = cp_sta_own_data_get_nid (ctx) }; cp_secu_protocol_run_new (&ctx->sta_action.assoc.prun, 0, &ctx->rnd); cp_msg_cm_get_key_req_send (ctx, &ctx->sta_action.assoc.peer, CP_MME_PEKS_NMK, &ctx->sta_action.assoc.prun, &get_key); cp_fsm_branch (ctx, SC_TEK_EXCHANGED, CM_SET_KEY_REQ_PID3, ok); } } void cp_sta_action_assoc__authenticated__leave (cp_t *ctx) { dbg_assert (ctx); /* Stop lease timer. */ cp_sta_core_stop_timed_or_cyclic_event ( ctx, &ctx->sta_action.assoc.lease_timer); } void cp_sta_action_assoc__authenticated__renew (cp_t *ctx) { dbg_assert (ctx); /* Restart timer. */ cp_fsm_event_t *event = cp_fsm_event_bare_new ( ctx, CP_FSM_EVENT_TYPE_renew); cp_sta_core_gen_timed_event (ctx, &ctx->sta_action.assoc.lease_timer, event, RENEW_MARGIN_MS / 2); /* Get our CCo. */ cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); cp_mme_peer_t peer; cp_net_get_cco_peer (ctx, our_net, &peer); /* Send CC_ASSOC.REQ to renew lease. */ cp_msg_cc_assoc_req_t req = { CP_MSG_CC_ASSOC_REQ_TYPE_RENEW, cp_sta_own_data_get_nid (ctx), CP_CCO_LEVEL, CP_PCO_CAP }; cp_msg_cc_assoc_req_send (ctx, &peer, &req); } void cp_sta_action_assoc__authenticated__cc_assoc_cnf (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); /* Get our CCo. */ cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); cp_mme_peer_t peer; cp_net_get_cco_peer (ctx, our_net, &peer); /* Check received message. */ cp_msg_cc_assoc_cnf_t cnf; if (cp_mme_peer_cmp (&mme->peer, &peer) && cp_msg_cc_assoc_cnf_receive (ctx, mme, &cnf) && cnf.result == CP_MSG_CC_ASSOC_CNF_RESULT_SUCCESS && cnf.nid == cp_sta_own_data_get_nid (ctx)) { /* Update lease. */ cp_sta_core_stop_timed_or_cyclic_event ( ctx, &ctx->sta_action.assoc.lease_timer); int lease_time_ms = cnf.lease_time_min * 60 * 1000 - RENEW_MARGIN_MS; if (lease_time_ms > 0) { cp_fsm_event_t *event = cp_fsm_event_bare_new ( ctx, CP_FSM_EVENT_TYPE_renew); cp_sta_core_gen_timed_event (ctx, &ctx->sta_action.assoc.lease_timer, event, lease_time_ms); } } } void cp_sta_action_assoc__authenticated__cm_set_key_req_pid_1 (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); /* Get our CCo. */ cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); cp_mme_peer_t peer; cp_net_get_cco_peer (ctx, our_net, &peer); /* Check received message. */ cp_msg_cm_set_key_req_t req; if (cp_mme_peer_cmp (&mme->peer, &peer) && cp_msg_cm_set_key_req_receive (ctx, mme, &req) && req.nid == cp_sta_own_data_get_nid (ctx)) { if ((req.key_type == CP_MSG_KEY_NEK) && (mme->peks == CP_MME_PEKS_NMK) && (cp_secu_protocol_check (&ctx->sta_action.nek_prun, &mme->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_NEXT))) { ctx->sta_action.nek_prun = mme->prun; /* Send response. */ cp_msg_cm_set_key_cnf_t cnf = { CP_MSG_CM_SET_KEY_CNF_RESULT_SUCCESS, CP_CCO_LEVEL }; cp_secu_protocol_next (&ctx->sta_action.nek_prun, &ctx->rnd, true); cp_msg_cm_set_key_cnf_send (ctx, &peer, CP_MME_PEKS_NMK, &ctx->sta_action.nek_prun, &cnf); /* Update NEK. */ cp_beacon_change_nek (ctx, req.new_eks, req.new_key, false); } else if ((req.key_type == CP_MSG_KEY_NONCE_ONLY) && (mme->peks == CP_MME_PEKS_SPC_NOT_EMBEDDED || mme->peks == CP_MME_PEKS_NONE) && (cp_secu_protocol_check (NULL, &mme->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_NEW))) { /* Send response. */ cp_msg_cm_set_key_cnf_t cnf = { CP_MSG_CM_SET_KEY_CNF_RESULT_SUCCESS, CP_CCO_LEVEL }; ctx->sta_action.nek_prun = mme->prun; cp_secu_protocol_next (&ctx->sta_action.nek_prun, &ctx->rnd, false); ctx->sta_action.nek_prun.my_nonce = lib_rnd32 (&ctx->rnd); cp_msg_cm_set_key_cnf_send (ctx, &peer, mme->peks, &ctx->sta_action.nek_prun, &cnf); } } else { /* Send response. */ cp_msg_cm_set_key_cnf_t cnf = { CP_MSG_CM_SET_KEY_CNF_RESULT_FAILURE, CP_CCO_LEVEL }; cp_secu_protocol_run_t prun = mme->prun; prun.my_nonce = lib_rnd32 (&ctx->rnd); cp_secu_protocol_next (&prun, &ctx->rnd, true); cp_msg_cm_set_key_cnf_send (ctx, &peer, CP_MME_PEKS_NONE, &prun, &cnf); } } void cp_sta_action_assoc__authenticated__to_leave (cp_t *ctx) { dbg_assert (ctx); /* Get our CCo. */ cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); cp_mme_peer_t peer; cp_net_get_cco_peer (ctx, our_net, &peer); /* Start timer. */ cp_fsm_event_t *event = cp_fsm_event_bare_new ( ctx, CP_FSM_EVENT_TYPE_assoc_timeout); cp_sta_core_gen_timed_event (ctx, &ctx->sta_action.assoc.timer, event, LEAVING_TIMEOUT_MS); /* Send leave request. */ cp_msg_cc_leave_req_send (ctx, &peer, CP_MSG_CC_LEAVE_REQ_REASON_USER_REQUEST); } void cp_sta_action_assoc__authenticated__cc_leave_ind (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); /* Get our CCo. */ cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); cp_mme_peer_t peer; cp_net_get_cco_peer (ctx, our_net, &peer); /* Check received message. */ enum cp_msg_cc_leave_ind_reason_t reason; cp_nid_t nid; if (cp_mme_peer_cmp (&mme->peer, &peer) && cp_msg_cc_leave_ind_receive (ctx, mme, &reason, &nid) && nid == cp_sta_own_data_get_nid (ctx)) { cp_msg_cc_leave_rsp_send (ctx, &mme->peer); cp_fsm_branch (ctx, AUTHENTICATED, CC_LEAVE_IND, ok); } else { cp_fsm_branch (ctx, AUTHENTICATED, CC_LEAVE_IND, nok); } } void cp_sta_action_assoc__authenticated__beacon (cp_t *ctx, cp_beacon_desc_t *beacon, cp_net_t *net, cp_sta_t *sta) { dbg_assert (ctx); dbg_assert (beacon); dbg_assert (net); dbg_assert (sta); if (cp_net_get_nid (ctx, net) == cp_sta_own_data_get_nid (ctx)) { /* Only on central beacon, no support for proxy networking. */ if (cp_sta_get_cco_status (sta)) { ctx->sta_action.assoc.beacon_loss = 0; } } } void cp_sta_action_assoc__authenticated__beacon_not_received (cp_t *ctx) { dbg_assert (ctx); ctx->sta_action.assoc.beacon_loss++; if (ctx->sta_action.assoc.beacon_loss > CP_MAX_NO_BEACON) { cp_fsm_trigger_new_event (ctx, bare, avln_failure); cp_fsm_branch (ctx, AUTHENTICATED, BEACON_NOT_RECEIVED, avln_failure); } else cp_fsm_branch (ctx, AUTHENTICATED, BEACON_NOT_RECEIVED, else); } void cp_sta_action_assoc__leaving__cc_leave_cnf (cp_t *ctx, cp_mme_rx_t *mme) { dbg_assert (ctx); dbg_assert (mme); /* Get our CCo. */ cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); cp_mme_peer_t peer; cp_net_get_cco_peer (ctx, our_net, &peer); /* Check received message. */ if (cp_mme_peer_cmp (&mme->peer, &peer)) { /* Ignore bad MME (confirmation is sufficient, we do not expect any * parameter). */ cp_msg_cc_leave_cnf_receive (ctx, mme); /* Stop timer. */ cp_sta_core_stop_timed_or_cyclic_event ( ctx, &ctx->sta_action.assoc.timer); cp_fsm_branch (ctx, LEAVING, CC_LEAVE_CNF, ok); } else { cp_fsm_branch (ctx, LEAVING, CC_LEAVE_CNF, unrelated); } } void cp_sta_action_assoc__leaving__timeout (cp_t *ctx) { dbg_assert (ctx); /* Get our CCo. */ cp_net_t *our_net = cp_sta_mgr_get_our_avln (ctx); cp_mme_peer_t peer; cp_net_get_cco_peer (ctx, our_net, &peer); /* Send leave request again. */ cp_msg_cc_leave_req_send (ctx, &peer, CP_MSG_CC_LEAVE_REQ_REASON_USER_REQUEST); } void cp_sta_action_assoc__leave_wait__enter (cp_t *ctx) { dbg_assert (ctx); /* Start timer. */ cp_fsm_event_t *event = cp_fsm_event_bare_new ( ctx, CP_FSM_EVENT_TYPE_assoc_timeout); cp_sta_core_gen_timed_event (ctx, &ctx->sta_action.assoc.timer, event, LEAVE_WAIT_TIMEOUT_MS); } void cp_sta_action_assoc__leave_wait__timeout (cp_t *ctx) { dbg_assert (ctx); }