/* Cesar project {{{ * * Copyright (C) 2008 Spidcom * * <<>> * * }}} */ /** * \file cp/av/cco/action/src/cco_action.c * \brief CCo Action functions. * \ingroup cp_av_cco_action * */ #include #include "common/std.h" #include "common/defs/homeplugAV.h" #include "lib/set.h" #include "lib/utils.h" #include "lib/rnd.h" #include "lib/slab.h" #include "common/defs/ethernet.h" #include "cl/cl_mactotei.h" #include "mac/common/timings.h" #include "cp/defs.h" #include "cp/cp.h" #include "cp/fsm/fsm.h" #include "cp/msg/msg.h" #include "cp/cco/action/cco_action.h" #include "cp/av/cco/action/cco_action.h" #include "cp/sta/mgr/sta_mgr.h" #include "cp/sta/mgr/net.h" #include "cp/sta/mgr/sta.h" #include "cp/beacon/beacon.h" #include "cp/av/beacon/discover.h" #include "cp/sta/core/core.h" #include "cp/beacon/beacon.h" #include "cp/cco/region/region.h" #include "cp/av/cco/region/region.h" #include "cp/cco/bw/bw.h" #include "cp/av/cco/bw/bw.h" #include "cp/av/beacon/beacon.h" #include "cp/inc/context.h" #include "cp/inc/trace.h" #include "cp/cco/action/inc/cco_action.h" #include "cp/av/cco/action/inc/cco_action.h" #include "bsu/bsu.h" /** Minimal value for NEK change period because it can be configured by * internal.conf.*/ #define NEK_CHANGE_PERIOD_MIN_MS 300000 /** * Progam a timer to post a cco__nek_change event * \param ctx the module context. */ static void cp_av_cco_action_nek_change_timer_program (cp_t *ctx) { uint period_ms = ctx->cco_action.nek_change_period_ms; /* Don't change NEK if period is null. */ if (period_ms) { /* Prepare next change. */ cp_fsm_event_t *event = cp_fsm_event_bare_new (ctx, CP_FSM_EVENT_TYPE_cco__nek_change); /* Limit the minimal of nek change period that could be inconsistence * with other control plane fsm timings */ if (period_ms < NEK_CHANGE_PERIOD_MIN_MS) period_ms = NEK_CHANGE_PERIOD_MIN_MS; cp_sta_core_gen_timed_event (ctx, &ctx->cco_action.nek_change, event, period_ms); } } /** * Choose a SNID between the ones available. * \param ctx the CP context. */ static cp_snid_t cp_av_cco_action_snid_choose (cp_t *ctx) { u16 flags; u16 table [16]; uint nb_elem; uint i; dbg_assert (ctx); // Get the present flags. flags = ~cp_sta_mgr_get_present_snids (ctx); // fill the table. for (i = 0, nb_elem = 0; i < 16; i++) { if ((flags >> i) & true) { table[nb_elem] = i; nb_elem ++; } } // Chose the new SNID. i = lib_rnd32 (&ctx->rnd) % nb_elem; return table[i]; } /** * Function to sort the station in the CCo selection set. * \param left the left node. * \param right the right node. */ static inline bool cp_av_cco_action_cco_selection__less (heap_node_t *left, heap_node_t *right) { cp_sta_t *stal; cp_sta_t *star; dbg_assert (left); dbg_assert (right); stal = PARENT_OF (cp_sta_t, cco_selection_node, left); star = PARENT_OF (cp_sta_t, cco_selection_node, right); if (stal->cco_cap > star->cco_cap) return true; return false; } /** * Search for an available TEI in the AVLN list. * \param ctx the module context. * \return return the TEI found. * */ cp_tei_t cp_av_cco_action_tei_compute (cp_t *ctx) { uint row; uint tei; uint i; dbg_assert (ctx); for (i = MAC_TEI_STA_MIN; MAC_TEI_IS_STA (i); i++) { row = i / CP_CCO_ACTION_TEI_FLAGS_ROW_SIZE_BITS; tei = ctx->cco_action.tei_flags[row] >> ((i % CP_CCO_ACTION_TEI_FLAGS_ROW_SIZE_BITS)- 1); if ((tei & true) == false) { tei = i; cp_av_cco_action_tei_in_use (ctx, tei); break; } else tei = MAC_TEI_UNASSOCIATED; } // No TEI are available. return tei; } void cp_av_cco_action_tei_in_use (cp_t *ctx, cp_tei_t tei) { uint row; dbg_assert (ctx); dbg_assert (MAC_TEI_IS_STA (tei)); row = tei / CP_CCO_ACTION_TEI_FLAGS_ROW_SIZE_BITS; ctx->cco_action.tei_flags[row] |= (1 << ((tei % CP_CCO_ACTION_TEI_FLAGS_ROW_SIZE_BITS) - 1)); } void cp_av_cco_action_tei_clear (cp_t *ctx) { dbg_assert (ctx); memset (&ctx->cco_action.tei_flags, 0, sizeof (uint) * CP_CCO_ACTION_TEI_FLAGS_ROW); } /** * Start common operations. * \param ctx the CP context. * * This should promote the station as CCo. * Program the nek change timer. * Untrack the old CCo. * Update beacon module. * Initialise the discover procedure. */ static void cp_av_cco_action_cco_start_common (cp_t *ctx) { /* Set the CCo status in the station own data. */ cp_sta_own_data_set_authenticated_status (ctx, true); cp_sta_own_data_set_cco_status (ctx, true); // Program the timer to change the NEKs. cp_av_cco_action_nek_change_timer_program (ctx); cp_beacon_process_untracked_avln (ctx); cp_net_t *net = cp_sta_mgr_get_our_avln (ctx); bsu_update_nid_snid (ctx->bsu, cp_net_get_nid (ctx, net), cp_net_get_snid (ctx, net)); cp_beacon_cco_update_beacon_data (ctx); cp_beacon_reconfigure_timer (ctx, true); cp_av_beacon_discover_init (ctx); } /** * Start to act as a CCo coming from the unassociated state. * \param ctx the module context. * * This function shall: * - Choose a TEI if the station don't have one yet. * - Create and set our AVLN in the station manager. * - Generate the current NEK and order to change it. * - Clear schedules and set the network mode to CSMA_ONLY. * - Set the station's CCo and authenticate status. * - Program the NEK change timer. * - Clear tracking data. * - Update the beacon for BSU. * - Initialise the discover procedure context. */ static void cp_av_cco_action_start (cp_t *ctx) { cp_tei_t tei; cp_net_t *net; cp_nid_t nid; cp_snid_t snid; dbg_assert (ctx); /* Verify if the station does not already have a TEI. */ snid = cp_sta_own_data_get_snid (ctx); nid = cp_sta_own_data_get_nid (ctx); if (!cp_sta_own_data_get_tei(ctx)) { tei = cp_av_cco_action_tei_compute (ctx); cp_sta_own_data_set_tei (ctx, tei); /* Clear the AVLN. */ cp_sta_mgr_remove_avln (ctx, snid, nid); } /* Set our AVLN. */ net = cp_sta_mgr_add_avln (ctx, snid, nid); cp_sta_mgr_set_our_avln (ctx, net); /* Set the NEK. */ cp_cco_action_gen_nek (ctx); cp_beacon_change_nek (ctx, ctx->cco_action.new_nek.eks, ctx->cco_action.new_nek.nek_enc, true /* now. */); /* Reinitialise schedules and regions. */ cp_cco_region_alloc_clean (ctx, &ctx->region.region_list); cp_av_cco_region_default (ctx); cp_cco_bw_alloc_clean (ctx, &ctx->bw.alloc_list); cp_av_cco_bw_schedules_default (ctx); /* Set network mode as CSMA only mode. */ cp_net_set_nm (ctx, net, MAC_NM_CSMA_ONLY); /* Set hybrid mode to Full hybrid mode. */ cp_sta_own_data_t *own = cp_sta_mgr_get_sta_own_data (ctx); own->hybrid_mode = MAC_COEXISTENCE_HYBRID_DELIMITERS_MODE; cp_av_cco_action_cco_start_common (ctx); } /** * Stop acting as a CCo and become a simple station. * \param ctx the module context. * * This function shall: * - Change the station CCo status. * - Stop the NEK timer. * - Clear the TEI table attribution. * - Clear the CCo selection context (handover). * - Reconfigure becon timer. * - Update the beacon to act as a STA. * - Uninitialise discover procedure. */ static void cp_av_cco_action_stop (cp_t *ctx) { dbg_assert (ctx); /* Change the CCo status to false, the station leave the CCo status. */ cp_sta_own_data_set_cco_status (ctx, false); /* Stop the timer for the NEK generation keys. */ cp_sta_core_stop_timed_or_cyclic_event (ctx, &ctx->cco_action.nek_change); /* clear the TEI list. */ cp_av_cco_action_tei_clear (ctx); cp_av_cco_action_cco_selection__clear (ctx); cp_beacon_reconfigure_timer (ctx, false); cp_av_beacon_sta_update_beacon_data (ctx); cp_av_beacon_discover_uninit (ctx); } /** * initialisation of CCo action module. * \param ctx the module context. * */ void cp_av_cco_action_init (cp_t *ctx) { dbg_assert (ctx); memset (&ctx->cco_action, 0, sizeof (cp_cco_action_t)); /* Initialise the SET. */ heap_init (&ctx->cco_action.selection_heap, CALLBACK (cp_av_cco_action_cco_selection__less)); /* Create default region. */ cp_av_cco_region_default (ctx); ctx->cco_action.nek_change_period_ms = HPAV_NEK_CHANGE_MS; ctx->cco_action.new_nek.eks = MAC_EKS_MAX; /* Initialized to MAC_EKS_MAX in order to have MAC_EKS_MIN as the EKS of the first generated NEK. */ ctx->cco_action.tei_lease_assoc_min = CP_LEASE_ASSOC_MIN; ctx->cco_action.tei_lease_auth_min = CP_LEASE_AUTH_MIN; if (CONFIG_STATS) { lib_stats_set_stat_value_notype ( "CP_CCO_ACTION_NEK_CHANGE_PERIOD_MS", &ctx->cco_action.nek_change_period_ms, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "TEI_LEASE_ASSOCIATION_MIN", &ctx->cco_action.tei_lease_assoc_min, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ( "TEI_LEASE_AUTHENTICATION_MIN", &ctx->cco_action.tei_lease_auth_min, LIB_STATS_ACCESS_READ_WRITE, LIB_STATS_DEBUG); } } /** * Uninitialisation of CCo action module. * \param ctx the module context. * */ void cp_av_cco_action_uninit (cp_t *ctx) { dbg_assert (ctx); cp_av_cco_action_cco_selection__clear (ctx); } void cp_av_cco_action_ucco_start (cp_t *ctx) { dbg_assert (ctx); cp_av_cco_action_start (ctx); } void cp_av_cco_action_ucco_stop (cp_t *ctx) { dbg_assert (ctx); cp_av_cco_action_stop (ctx); cp_sta_own_data_set_authenticated_status (ctx, false); /* Set our AVLN as null. */ cp_sta_mgr_set_our_avln (ctx, NULL); /* Leave our TEI. */ cp_sta_own_data_set_tei (ctx, MAC_TEI_UNASSOCIATED); cp_fsm_trigger_new_event (ctx, bare, assoc_cco_leave); } void cp_av_cco_action_ucco__to_cco (cp_t *ctx) { dbg_assert (ctx); cp_fsm_trigger_new_event (ctx, bare, assoc_become_cco); } void cp_av_cco_action_cco__to_ucco (cp_t *ctx) { dbg_assert (ctx); cp_fsm_trigger_new_event (ctx, bare, assoc_become_ucco); } void cp_av_cco_action_cco__unassoc_start (cp_t *ctx) { dbg_assert (ctx); /* Take a snid. */ cp_snid_t snid = cp_av_cco_action_snid_choose (ctx); cp_sta_own_data_set_snid (ctx, snid); bsu_power_on (ctx->bsu, snid); cp_av_cco_action_start (ctx); cp_fsm_trigger_new_event (ctx, bare, assoc_become_cco); } void cp_av_cco_action_cco__assoc_start (cp_t *ctx) { cp_av_cco_action_cco_start_common (ctx); /* Clear tracking data. */ cp_fsm_trigger_new_event (ctx, bare, assoc_become_cco); } void cp_av_cco_action_cco__assoc_stop (cp_t *ctx) { cp_av_cco_action_stop (ctx); cp_fsm_trigger_new_event (ctx, bare, assoc_become_sta); } void cp_av_cco_action_cco__unassoc_stop (cp_t *ctx) { dbg_assert (ctx); cp_av_cco_action_stop (ctx); cp_fsm_trigger_new_event (ctx, bare, assoc_cco_leave); } /** * Change the SNID. * \param ctx the module context. * * It shall request the SNIDs in use from the station manager to choose * one randomly in the available SNIDs */ void cp_av_cco_action_cco__cco_snid_conflict (cp_t *ctx) { cp_snid_t snid; dbg_assert (ctx); snid = cp_av_cco_action_snid_choose (ctx); // Provide the SNID to the beacon module. cp_beacon_change_snid (ctx, snid); } /** * manage explicit leave request of a station. * \param ctx the module context. * \param sta provided by the garbage function to send a CC_LEAVE_IND. * * Remove the station from the station manager. * \warn It does not release the reference of the station provided * in parameters. */ void cp_av_cco_action_tei_expired (cp_t *ctx, cp_sta_t *sta) { cp_mme_peer_t peer; cp_net_t *net; dbg_assert (ctx); /* Get the network of the station. */ net = cp_sta_mgr_get_our_avln (ctx); cp_sta_get_peer (sta, &peer); cp_msg_cc_leave_ind_send (ctx, &peer, CP_MSG_CC_LEAVE_IND_REASON_TEI_LEASE_EXPIRED, cp_net_get_nid(ctx, net)); CP_TRACE (CCO_TEI_EXPIRED, TRACE_U64 (peer.mac), peer.tei); cp_av_cco_action_cco_sta_leave_send_tei_map (ctx, sta); cp_sta_mgr_release_station (ctx, cp_sta_get_tei (sta)); } /** * perform garbage actions of CCo's responsibility. * \param ctx the module context. */ void cp_av_cco_action_garbage (cp_t *ctx) { cp_sta_t *sta; cp_sta_t *sta_next; cp_net_t *net; dbg_assert (ctx); if (cp_sta_own_data_get_tei (ctx)) { net = cp_sta_mgr_get_our_avln (ctx); sta = cp_net_sta_get_first (ctx, net, CP_NET_STA_ASSOC); while (sta) { slab_addref (sta); sta_next = cp_net_sta_get_next (ctx, net, sta); u32 date_ms = cp_sta_core_get_date_ms (ctx); if (less_mod2p32(sta->tei_lease_date_ms, date_ms) || less_mod2p32 (sta->expired_date_ms, date_ms)) { // Remove the node from the tei lease. cp_av_cco_action_tei_expired (ctx, sta); } slab_release (sta); sta = sta_next; } cp_sta_mgr__assoc__timeout (ctx, cp_sta_core_get_date_ms (ctx)); } } /** * generate a new NEK value. * \param ctx the module context. * * NEK shall be used no more than 1 hour, so must be changed after 1 * hour for that purpose, NEK expiration date is checked at every occurrence * of the global garbage collector timer and if current NEK expiration * date reached, a new NEK value shall be generated by calling this function */ void cp_av_cco_action_cco__cco_nek_change (cp_t *ctx) { dbg_assert (ctx); cp_cco_action_gen_nek (ctx); /* Prepare next change. */ cp_av_cco_action_nek_change_timer_program (ctx); /* POST the FSM event. */ cp_fsm_post_new_event (ctx, bare, cco_nek_change__nek_provide); } /** * Send the SET tei map IND to all the station with the TEI of the new one. * \param ctx the CP context. * \param tei the tei of the station which has joined the AVLN. * \param mac the station mac address. */ static void cp_av_cco_action_send_set_tei_map_to_new_sta (cp_t *ctx, cp_tei_t tei, mac_t mac) { cp_mme_tx_t *mme_tx; cp_sta_t *sta_list; cp_mme_peer_t *peer; cp_net_t *net; dbg_assert (ctx); dbg_assert (tei); dbg_assert (mac); // Send unicast. // It will send all the TEI MAP to the station just associated. net = cp_sta_mgr_get_our_avln (ctx); peer = &CP_MME_PEER (mac, tei); // +1 For our own station which is not in the AVLN. mme_tx = cp_msg_cc_set_tei_map_ind_send_begin (ctx, peer, CP_MSG_CC_SET_TEI_MAP_IND_MODE_UPDATE, cp_net_get_num_stas (ctx, net) + 1); // Add our station first. cp_msg_cc_set_tei_map_ind_send_sta (ctx, mme_tx, cp_sta_own_data_get_tei(ctx), cp_sta_own_data_get_mac_address (ctx), CP_MSG_CC_SET_TEI_MAP_IND_STATUS_AUTHENTICATED); sta_list = cp_net_sta_get_first (ctx, net, CP_NET_STA_ASSOC); while (sta_list) { enum cp_msg_cc_set_tei_map_ind_status_t station_status; if (cp_sta_get_authenticated (ctx, sta_list)) { station_status = CP_MSG_CC_SET_TEI_MAP_IND_STATUS_AUTHENTICATED; } else if (cp_sta_get_tei (sta_list)) { station_status = CP_MSG_CC_SET_TEI_MAP_IND_STATUS_ASSOCIATED; } else { station_status = CP_MSG_CC_SET_TEI_MAP_IND_STATUS_DISASSOCIATED; } cp_msg_cc_set_tei_map_ind_send_sta (ctx, mme_tx, cp_sta_get_tei (sta_list), cp_sta_get_mac_address (sta_list), station_status); sta_list = cp_net_sta_get_next (ctx, net, sta_list); } cp_msg_cc_set_tei_map_ind_send_end (ctx, mme_tx); } /** * Send the SET TEI MAP to all the station of the NET. * \param ctx the CP context. * \param tei the new station tei. * \param mac the station mac address. */ static void cp_av_cco_action_send_set_tei_map_to_all_sta (cp_t *ctx, cp_tei_t tei, mac_t mac) { cp_mme_peer_t *peer; cp_mme_tx_t *mme_tx; dbg_assert (ctx); dbg_assert (tei); dbg_assert (mac); peer = &CP_MME_PEER_ALL_STA; mme_tx = cp_msg_cc_set_tei_map_ind_send_begin (ctx, peer, CP_MSG_CC_SET_TEI_MAP_IND_MODE_ADD, 1); cp_msg_cc_set_tei_map_ind_send_sta (ctx, mme_tx, tei, mac, CP_MSG_CC_SET_TEI_MAP_IND_STATUS_ASSOCIATED); cp_msg_cc_set_tei_map_ind_send_end (ctx, mme_tx); } /** * Assoc type join. * \param ctx the CP context. * \param msg the assoc MME message. * \param assoc the assoc data read from the MME. */ static void cp_av_cco_action__assoc__join (cp_t *ctx, cp_mme_rx_t *msg, cp_msg_cc_assoc_req_t *assoc) { cp_net_t *net; cp_sta_t *sta; cp_tei_t tei = MAC_TEI_UNASSOCIATED; cp_msg_cc_assoc_cnf_t cnf; dbg_assert (ctx); dbg_assert (msg); dbg_assert (assoc); /* Add the station to the AVLN. */ net = cp_sta_mgr_get_our_avln (ctx); cnf.nid = cp_net_get_nid (ctx, net); cnf.snid = cp_net_get_snid (ctx, net); /* Try to get the TEI from the STA manager if we already know it. */ sta = cp_sta_mgr_sta_get_from_mac (ctx, msg->peer.mac); /* If the station is known. */ if (sta) { /* If the station is currently in our AVLN/net. */ if (net == cp_sta_get_net (sta)) { // The station already exists get the TEI. tei = cp_sta_get_tei (sta); } /* Remove the station from the station manager and the mac store. */ cp_sta_mgr_sta_remove (ctx, sta); slab_release (sta); } /* The station is in the release list ? */ else { sta = cp_sta_mgr_release_find_sta (ctx, msg->peer.mac); if (sta) { tei = cp_sta_get_tei (sta); cp_sta_mgr_release_remove_sta (ctx, sta); sar_sta_remove (ctx->sar, cp_sta_get_tei (sta)); slab_release (sta); } } /* If the station has no TEI, try to generate one. */ if (!MAC_TEI_IS_STA (tei)) { /* Try to get a TEI. */ tei = cp_av_cco_action_tei_compute (ctx); } /* If we manage to have a TEI, it's a success. */ if (MAC_TEI_IS_STA (tei)) { cnf.result = CP_MSG_CC_ASSOC_CNF_RESULT_SUCCESS; cnf.lease_time_min = ctx->cco_action.tei_lease_assoc_min; } /* Otherwise, it's an error. */ else { cnf.result = CP_MSG_CC_ASSOC_CNF_RESULT_FAILURE_TEMPORARY_RESSOURCE_EXHAUSTION; } /* Send the answer. */ cnf.sta_tei = tei; cp_msg_cc_assoc_cnf_send (ctx, &msg->peer, &cnf); CP_TRACE (CCO_ASSOC, TRACE_U64 (msg->peer.mac), tei); if (tei) { /* Send TEI MAP to all STA except the new one (need to do it before * adding the new STA to the STA manager). */ cp_av_cco_action_send_set_tei_map_to_all_sta (ctx, tei, msg->peer.mac); sta = cp_sta_mgr_sta_add (ctx, net, tei, msg->peer.mac); /* Add the TEI lease corresponding to an unassociated STA. */ cp_sta_mgr_commit_to_dataplane (ctx); u32 date_ms = cp_sta_core_get_date_ms (ctx); sta->tei_lease_date_ms = date_ms + MAC_SEC_TO_MS(ctx->cco_action.tei_lease_assoc_min * 60); /* Update the last request date in the sta. */ cp_sta_set_assoc_confirmed (ctx, sta, false); sta->assoc_req_last_ms = cp_sta_core_get_date_ms (ctx); /* A new STA has joined an AVLN for the first time, generate an * event. */ cp_fsm_event_t *event = cp_fsm_event_sta_new (ctx, CP_FSM_EVENT_TYPE_new_sta_associated, net, (cp_sta_t *) sta); cp_fsm_post (ctx, event); /* Release the reference on the station. */ slab_release (sta); } if (!tei) { sta = cp_sta_mgr_sta_add (ctx, net, 0, msg->peer.mac); /* Release the reference on the station. */ slab_release (sta); } } /** * Assoc type renew. * \param ctx the CP context. * \param msg the assoc MME message. * \param assoc the assoc data read from the MME. */ static void cp_av_cco_action__assoc__renew (cp_t *ctx, cp_mme_rx_t *msg, cp_msg_cc_assoc_req_t *assoc) { cp_net_t *net; cp_sta_t *sta; cp_msg_cc_assoc_cnf_t cnf; dbg_assert (ctx); dbg_assert (msg); dbg_assert (assoc); /* Add the station to the AVLN. */ net = cp_sta_mgr_get_our_avln (ctx); cnf.nid = cp_net_get_nid (ctx, net); cnf.snid = cp_net_get_snid (ctx, net); /* Renew the TEI. */ sta = cp_sta_mgr_sta_get_assoc (ctx, net, msg->peer.tei); if (sta) { /* Renew the TEI. */ u32 date_ms = cp_sta_core_get_date_ms (ctx); sta->tei_lease_date_ms = date_ms + MAC_SEC_TO_MS(ctx->cco_action.tei_lease_auth_min * 60); cnf.result = CP_MSG_CC_ASSOC_CNF_RESULT_SUCCESS; cnf.sta_tei = cp_sta_get_tei (sta); cnf.lease_time_min = ctx->cco_action.tei_lease_auth_min; slab_release (sta); } else { cnf.result = CP_MSG_CC_ASSOC_CNF_RESULT_FAILURE_OTHER_REASON; cnf.sta_tei = 0; cnf.lease_time_min = 0; } /* Send the answer. */ cp_msg_cc_assoc_cnf_send (ctx, &msg->peer, &cnf); CP_TRACE (CCO_ASSOC_RENEW, TRACE_U64 (msg->peer.mac), msg->peer.tei); } /** * Static function part. * \param ctx the CP module context. * \param assoc_req the MME context. * \param assoc The mme in the processor format. */ static void cp_av_cco_action_manage_sta_assoc_common (cp_t *ctx, cp_mme_rx_t *assoc_req, cp_msg_cc_assoc_req_t *assoc) { dbg_assert (ctx); dbg_assert (assoc_req); dbg_assert (assoc); dbg_assert (ctx->mac_store); if (assoc->request_type == CP_MSG_CC_ASSOC_REQ_TYPE_NEW) cp_av_cco_action__assoc__join (ctx, assoc_req, assoc); else if (assoc->request_type == CP_MSG_CC_ASSOC_REQ_TYPE_RENEW) cp_av_cco_action__assoc__renew (ctx, assoc_req, assoc); else dbg_assert (assoc->request_type == CP_MSG_CC_ASSOC_REQ_TYPE_NB); } /** * manage association of a station. * \param ctx the module context. * \param assoc_req CM_ASSOC.REQ MME msg having being received */ void cp_av_cco_action_cco__cc_assoc_req (cp_t *ctx, cp_mme_rx_t * assoc_req) { cp_msg_cc_assoc_req_t assoc; dbg_assert (ctx); dbg_assert (assoc_req); /* Reading the data in the MME. */ if (cp_msg_cc_assoc_req_receive(ctx, assoc_req, &assoc)) cp_av_cco_action_manage_sta_assoc_common (ctx, assoc_req, &assoc); } static void cp_av_cco_action_ucco__cc_assoc_req_common ( cp_t *ctx, cp_mme_rx_t * assoc_req, cp_fsm_branch_t branch_ok, cp_fsm_branch_t branch_nok) { cp_msg_cc_assoc_req_t assoc; dbg_assert (ctx); dbg_assert (assoc_req); dbg_assert (cp_sta_own_data_get_tei (ctx)); dbg_assert (cp_sta_own_data_get_authenticated_status (ctx)); dbg_assert (cp_sta_own_data_get_cco_status (ctx)); /* Reading the data in the MME. */ cp_msg_cc_assoc_req_receive(ctx, assoc_req, &assoc); /* The MME is not for the station, it shall answer to the source station * with a Failure for others reason. */ if ((assoc.request_type == CP_MSG_CC_ASSOC_REQ_TYPE_NEW) && (assoc.nid == cp_sta_own_data_get_nid (ctx))) { /* Become CCo. */ cp_av_cco_action_ucco__to_cco (ctx); /* Continue the association procedure. */ cp_av_cco_action_manage_sta_assoc_common (ctx, assoc_req, &assoc); cp_fsm_branch_ (ctx, branch_ok); } else cp_fsm_branch_ (ctx, branch_nok); } void cp_av_cco_action_ucco__cc_assoc_req (cp_t *ctx, cp_mme_rx_t * assoc_req) { cp_av_cco_action_ucco__cc_assoc_req_common ( ctx, assoc_req, CP_FSM_BRANCH (UCCO, CC_ASSOC_REQ, ok), CP_FSM_BRANCH (UCCO, CC_ASSOC_REQ, nok)); } void cp_av_cco_action_sc_ucco__cc_assoc_req (cp_t *ctx, cp_mme_rx_t * assoc_req) { cp_av_cco_action_ucco__cc_assoc_req_common ( ctx, assoc_req, CP_FSM_BRANCH (SC_UCCO, CC_ASSOC_REQ, ok), CP_FSM_BRANCH (SC_UCCO, CC_ASSOC_REQ, nok)); } /** * manage authentication of a station. * \param ctx the module context. * \param get_key_req CM_GET_KEY.REQ MME msg having being decrypted */ void cp_av_cco_action_cco__cm_get_key_req_pid0 (cp_t *ctx, cp_mme_rx_t * get_key_req) { cp_msg_cm_get_key_req_t req; cp_msg_cm_get_key_cnf_t cnf; cp_net_t *net; cp_sta_t *sta; memset (&cnf, 0, sizeof (cp_msg_cm_get_key_cnf_t)); cnf.eks = MAC_EKS_CLEAR; dbg_assert (ctx); dbg_assert (get_key_req); // get the data in the payload of the mme. if (cp_msg_cm_get_key_req_receive (ctx, get_key_req, &req) && (cp_secu_protocol_check (NULL, &get_key_req->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_NEW)) && (req.key_type == CP_MSG_KEY_NEK) && MAC_TEI_IS_STA (get_key_req->peer.tei)) { // Get the network. net = cp_sta_mgr_get_our_avln (ctx); // Get the station. sta = cp_sta_mgr_sta_get_assoc (ctx, net, get_key_req->peer.tei); if (sta) { uint nb_sta; cnf.result = CP_MSG_CM_GET_KEY_CNF_RESULT_KEY_GRANTED; cp_sta_set_assoc_confirmed (ctx, sta, true); if (req.key_type == CP_MSG_KEY_NEK) { uint i; mac_nek_t *nek = bsu_nek_get_current (ctx->bsu); cnf.eks = nek->eks; for (i = 0; i < COUNT (nek->nek_enc); i++) cnf.key.key[i] = nek->nek_enc[i]; } /* Send the TEI map. */ nb_sta = cp_net_get_num_stas (ctx, net); if (nb_sta) { cp_av_cco_action_send_set_tei_map_to_new_sta (ctx, cp_sta_get_tei(sta), get_key_req->peer.mac); } slab_release (sta); } else cnf.result = CP_MSG_CM_GET_KEY_CNF_RESULT_REQUEST_REFUSED; } // Refuse the request. else cnf.result = CP_MSG_CM_GET_KEY_CNF_RESULT_REQUEST_REFUSED; cnf.key_type = req.key_type; cnf.nid = req.nid; cp_secu_protocol_next (&get_key_req->prun, &ctx->rnd, true); cp_msg_cm_get_key_cnf_send (ctx, &get_key_req->peer, get_key_req->peks, &get_key_req->prun, &cnf); } void cp_av_cco_action_cco__cm_get_key_req_pid1 (cp_t *ctx, cp_mme_rx_t * get_key_req) { dbg_assert (ctx); dbg_assert (get_key_req); cp_msg_cm_get_key_req_t req; cp_msg_cm_get_key_cnf_t cnf; memset (&cnf, 0, sizeof (cp_msg_cm_get_key_cnf_t)); cnf.eks = MAC_EKS_CLEAR; if (cp_msg_cm_get_key_req_receive (ctx, get_key_req, &req) && (cp_secu_protocol_check (NULL, &get_key_req->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_NEW)) && MAC_TEI_IS_STA (get_key_req->peer.tei)) { if (req.key_type == CP_MSG_KEY_NEK) { cnf.result = CP_MSG_CM_GET_KEY_CNF_RESULT_KEY_GRANTED; uint i; cnf.eks = ctx->cco_action.new_nek.eks; cp_key_t *key = &ctx->cco_action.new_nek.nek_enc; for (i = 0; i < COUNT (key->key); i++) cnf.key.key[i] = key->key[i]; } else cnf.result = CP_MSG_CM_GET_KEY_CNF_RESULT_UNSUPPORTED_METHOD_KEY_TYPE; } else cnf.result = CP_MSG_CM_GET_KEY_CNF_RESULT_REQUEST_REFUSED; cnf.key_type = req.key_type; cnf.nid = req.nid; cp_secu_protocol_next (&get_key_req->prun, &ctx->rnd, true); cp_msg_cm_get_key_cnf_send (ctx, &get_key_req->peer, get_key_req->peks, &get_key_req->prun, &cnf); } void cp_av_cco_action_cco_sta_leave_send_tei_map (cp_t *ctx, cp_sta_t *sta) { cp_mme_peer_t *peer; cp_net_t *net = NULL; uint status; cp_mme_tx_t *mme; dbg_assert (ctx); dbg_assert (sta); // Send the TEI MAP to the station. net = cp_sta_mgr_get_our_avln (ctx); if (!cp_net_is_empty (ctx, net)) { peer = &CP_MME_PEER_ALL_STA; if (cp_sta_get_authenticated (ctx, sta)) { status = CP_MSG_CC_SET_TEI_MAP_IND_STATUS_AUTHENTICATED; } else if (cp_sta_get_tei (sta)) { status = CP_MSG_CC_SET_TEI_MAP_IND_STATUS_ASSOCIATED; } else status = CP_MSG_CC_SET_TEI_MAP_IND_STATUS_DISASSOCIATED; mme = cp_msg_cc_set_tei_map_ind_send_begin (ctx, peer, CP_MSG_CC_SET_TEI_MAP_IND_MODE_DELETE, 1); cp_msg_cc_set_tei_map_ind_send_sta (ctx, mme, cp_sta_get_tei (sta), cp_sta_get_mac_address (sta), status); cp_msg_cc_set_tei_map_ind_send_end (ctx, mme); } } void cp_av_cco_action_cco__cc_leave_req (cp_t *ctx, cp_mme_rx_t *mme) { cp_sta_t *sta; cp_net_t *net; bool res_read; enum cp_msg_cc_leave_req_reason_t reason_req; cp_nid_t nid; enum cp_msg_cc_leave_ind_reason_t reason_ind; dbg_assert (ctx); dbg_assert (mme); dbg_assert (mme->peer.tei); switch (mme->mmtype) { case CC_LEAVE_REQ: res_read = cp_msg_cc_leave_req_receive (ctx, mme, &reason_req); break; case CC_LEAVE_IND: res_read = cp_msg_cc_leave_ind_receive (ctx, mme, &reason_ind, &nid); break; default: dbg_assert_default (); } if (res_read) { net = cp_sta_mgr_get_our_avln (ctx); if (mme->mmtype == CC_LEAVE_REQ) cp_msg_cc_leave_cnf_send (ctx, &mme->peer); else cp_msg_cc_leave_rsp_send (ctx, &mme->peer); /* Get the station. */ sta = cp_sta_mgr_sta_get_assoc (ctx, net, mme->peer.tei); if (sta) { cp_sta_mgr_release_station (ctx, cp_sta_get_tei (sta)); cp_av_cco_action_cco_sta_leave_send_tei_map (ctx, sta); slab_release (sta); } } } void cp_av_cco_action_drv_mac_stop (cp_t *ctx) { dbg_assert (ctx); dbg_assert (cp_sta_own_data_get_cco_status (ctx)); if (!cp_net_is_empty (ctx, cp_sta_mgr_get_our_avln (ctx)) && (!cp_beacon_any_countdown_active (ctx))) { cp_sta_t *sta; cp_net_t *net; /* Clear the SET of cco selection. */ cp_av_cco_action_cco_selection__clear (ctx); ctx->handover.reason = CP_HANDOVER_REASON_CCO_LEAVING; net = cp_sta_mgr_get_our_avln (ctx); for (sta = cp_net_sta_get_first (ctx, net, CP_NET_STA_ASSOC); sta; sta = cp_net_sta_get_next (ctx, net, sta)) { if (cp_sta_get_authenticated (ctx, sta)) cp_av_cco_action_cco_selection__mac_stop_sta_add (ctx, sta); } /* Trigger the event to start the handover CCo FSM. */ cp_fsm_trigger_new_event (ctx, bare, CCO_HANDOVER_START); /* Trigger an event to change the Main FSM to go to the * CCO_LEAVING_HOIP state. */ cp_fsm_trigger_new_event (ctx, bare, CCO_LEAVING_HANDOVER); } else if (cp_net_is_empty (ctx, cp_sta_mgr_get_our_avln (ctx)) && (!cp_beacon_any_countdown_active (ctx))) cp_fsm_trigger_new_event (ctx, bare, cco__all_sta_leaved); } void cp_av_cco_action_drv_mac_stop_suspend (cp_t *ctx) { cp_beacon_cco_update_beacon_data (ctx); cp_av_cco_action_drv_mac_stop (ctx); } void cp_av_cco_action_drv_mac_stop_ended (cp_t *ctx) { dbg_assert (ctx); cp_av_cco_action_cco__assoc_stop (ctx); cp_fsm_trigger_new_event (ctx, bare, to_stop); } void cp_av_cco_action_ucco__to_stop (cp_t *ctx) { dbg_assert (ctx); cp_av_cco_action_ucco_stop (ctx); cp_fsm_trigger_new_event (ctx, bare, to_stop); } void cp_av_cco_action_cco_selection__mac_stop_sta_add (cp_t *ctx, cp_sta_t *sta) { dbg_assert (ctx); dbg_assert (sta); slab_addref (sta); heap_node_init (&sta->cco_selection_node); heap_insert (&ctx->cco_action.selection_heap, &sta->cco_selection_node); } void cp_av_cco_action_cco_selection__sta_add (cp_t *ctx, cp_net_t *net, cp_sta_t *sta) { cp_sta_own_data_t *own; dbg_assert (ctx); dbg_assert (net); dbg_assert (sta); if ((net == cp_sta_mgr_get_our_avln (ctx)) && (MAC_TEI_IS_STA (cp_sta_get_tei (sta))) && (cp_sta_get_authenticated (ctx, sta))) { own = cp_sta_mgr_get_sta_own_data (ctx); if (own->cco_prefered == false && sta->cco_cap > CP_CCO_LEVEL) { slab_addref (sta); heap_node_init (&sta->cco_selection_node); heap_insert (&ctx->cco_action.selection_heap, &sta->cco_selection_node); } } } void cp_av_cco_action_cco_selection__sta_remove (cp_t *ctx, cp_sta_t *sta) { dbg_assert (ctx); dbg_assert (sta); dbg_assert (!heap_empty (&ctx->cco_action.selection_heap)); heap_remove (&ctx->cco_action.selection_heap, &sta->cco_selection_node); heap_node_init (&sta->cco_selection_node); slab_release (sta); } void cp_av_cco_action_cco_selection__clear (cp_t *ctx) { cp_sta_t *sta; heap_node_t *node; dbg_assert (ctx); /* Verify the set. */ while (!heap_empty (&ctx->cco_action.selection_heap)) { node = heap_get_root (&ctx->cco_action.selection_heap); dbg_assert (node); sta = PARENT_OF (cp_sta_t, cco_selection_node, node); dbg_assert (sta); /* Remove the node from the heap. */ heap_remove (&ctx->cco_action.selection_heap, &sta->cco_selection_node); heap_node_init (&sta->cco_selection_node); slab_release (sta); } } cp_sta_t * cp_av_cco_action_cco_selection__selection (cp_t *ctx) { cp_sta_own_data_t *own; cp_sta_t *sta = NULL; heap_node_t *node; dbg_assert (ctx); own = cp_sta_mgr_get_sta_own_data (ctx); if ((!own->cco_prefered) && (!heap_empty (&ctx->cco_action.selection_heap))) { node = heap_get_root (&ctx->cco_action.selection_heap); dbg_assert (node); sta = PARENT_OF (cp_sta_t, cco_selection_node, node); dbg_assert (sta); slab_addref (sta); } return sta; } void cp_av_cco_action_sc__sc_cco__cc_assoc_req (cp_t *ctx, cp_mme_rx_t *mme) { /* Check parameters. */ dbg_assert (ctx); dbg_assert (mme); /* Check received MME. */ cp_msg_cc_assoc_req_t req; if (!cp_msg_cc_assoc_req_receive (ctx, mme, &req)) { /* Error in the MME, do nothing. */ } else { if (req.request_type == CP_MSG_CC_ASSOC_REQ_TYPE_NEW) { /* Request of association. */ /* Peer is SC? */ if (mme->peer.mac == ctx->sta_action.sc.peer.mac) { /* Ok, reply */ cp_av_cco_action__assoc__join (ctx, mme, &req); cp_sta_t *sta = cp_sta_mgr_sta_get_from_mac (ctx, ctx->sta_action.sc.peer.mac); ctx->sta_action.sc.peer.tei = cp_sta_get_tei (sta); slab_release (sta); } else { /* Deny assoc request. */ cp_msg_cc_assoc_cnf_t cnf; cnf.result = CP_MSG_CC_ASSOC_CNF_RESULT_FAILURE_OTHER_REASON; /* Get our net. */ cp_net_t *net = cp_sta_mgr_get_our_avln (ctx); /* Set NID/SNID. */ cnf.nid = cp_net_get_nid (ctx, net); cnf.snid = cp_net_get_snid (ctx, net); /* No TEI for the STA. */ cnf.sta_tei = 0; /* Lease default value. */ cnf.lease_time_min = ctx->cco_action.tei_lease_assoc_min; /* Send reply. */ cp_msg_cc_assoc_cnf_send (ctx, &mme->peer, &cnf); } } else if (req.request_type == CP_MSG_CC_ASSOC_REQ_TYPE_RENEW) /* Renew is ok. */ cp_av_cco_action__assoc__renew (ctx, mme, &req); else /* Should not occur. */ dbg_assert (req.request_type == CP_MSG_CC_ASSOC_REQ_TYPE_NB); } } void cp_av_cco_action_handover__ended (cp_t *ctx) { cp_av_cco_action_cco_selection__clear (ctx); cp_av_cco_action_cco__assoc_stop (ctx); } /** * Last transaction ends try next STA. * \param ctx the module context. * \return The next station to start a transaction with. */ static cp_sta_t* cp_av_cco_action_nek_next_sta (cp_t *ctx) { cp_sta_t *sta; cp_sta_t *sta_next; cp_net_t *net; dbg_assert (ctx); net = cp_sta_mgr_get_our_avln (ctx); if ((sta = cp_sta_mgr_sta_get_from_mac (ctx, ctx->cco_action.eks_sta_current_peer.mac))) { sta_next = cp_net_sta_get_next (ctx, net, sta); if (sta_next) { cp_sta_get_peer (sta_next, &ctx->cco_action.eks_sta_current_peer); return sta_next; } return NULL; } return NULL; } /** * Request the station for the NEK change. * \param ctx the module context. * \param sta the station to start the NEK exchange. */ static void cp_av_cco_action_nek_change_start_transaction (cp_t *ctx, cp_sta_t *sta) { cp_msg_cm_set_key_req_t data; cp_fsm_event_t *event; dbg_assert (ctx); dbg_assert (sta); /* Get the station peer to send the MME. */ cp_sta_get_peer (sta, &ctx->cco_action.eks_sta_current_peer); /* Initialise the protocol run. */ cp_secu_protocol_run_new (&ctx->cco_action.eks_prun, 1, &ctx->rnd); /* Fill the data structure with key type == NONCE_ONLY. */ memset (&data, 0, sizeof (cp_msg_cm_set_key_req_t)); data.key_type = CP_MSG_KEY_NONCE_ONLY; data.cco_cap = CP_CCO_LEVEL; data.nid = cp_sta_own_data_get_nid (ctx); /* Send the MME. */ cp_msg_cm_set_key_req_send (ctx, &ctx->cco_action.eks_sta_current_peer, CP_MME_PEKS_NONE, &ctx->cco_action.eks_prun, &data); /* Start the timeout timer. */ event = cp_fsm_event_bare_new (ctx, CP_FSM_EVENT_TYPE_cco_nek_change__nek_timeout); cp_sta_core_gen_timed_event(ctx, &ctx->cco_action.eks_timer, event, CP_CCO_ACTION_EKS_TIMEOUT_MS); } void cp_av_cco_action_cco__cco_nek_change__nek_provide (cp_t *ctx) { cp_net_t *net; cp_sta_t *sta; dbg_assert (ctx); net = cp_sta_mgr_get_our_avln (ctx); sta = cp_net_sta_get_first (ctx, net, CP_NET_STA_ASSOC); /* If the current STA is not authenticated, it is not necessary to send * the MME. */ while (sta && !cp_sta_get_authenticated (ctx, sta)) sta = cp_net_sta_get_next (ctx, net, sta); if (sta) { cp_av_cco_action_nek_change_start_transaction (ctx, sta); slab_release (sta); /* Change branch. */ cp_fsm_branch (ctx, CCO_NEK_CHANGE_IDLE, cco_nek_change__nek_provide, sta); } else cp_fsm_branch (ctx, CCO_NEK_CHANGE_IDLE, cco_nek_change__nek_provide, nosta); } void cp_av_cco_action_cco__cco_nek_change__nek_timeout__wait (cp_t *ctx) { cp_sta_t *sta; dbg_assert (ctx); /* Stop the time out timer. */ cp_sta_core_stop_timed_or_cyclic_event (ctx, &ctx->cco_action.eks_timer); if ((sta = cp_av_cco_action_nek_next_sta (ctx))) { cp_av_cco_action_nek_change_start_transaction (ctx, sta); slab_release (sta); cp_fsm_branch (ctx, CCO_NEK_CHANGE_WAIT_STA_CNF, cco_nek_change__nek_timeout, yes); } else { cp_fsm_branch (ctx, CCO_NEK_CHANGE_WAIT_STA_CNF, cco_nek_change__nek_timeout, no); } } void cp_av_cco_action_cco__cco_nek_change__timeout (cp_t *ctx) { cp_sta_t *sta; dbg_assert (ctx); /* Stop the time out timer. */ cp_sta_core_stop_timed_or_cyclic_event (ctx, &ctx->cco_action.eks_timer); if ((sta = cp_av_cco_action_nek_next_sta (ctx))) { cp_av_cco_action_nek_change_start_transaction (ctx, sta); slab_release (sta); cp_fsm_branch (ctx, CCO_NEK_CHANGE_PROCESS_CM_SET_KEY_CNF, cco_nek_change__nek_timeout, yes); } else { cp_fsm_branch (ctx, CCO_NEK_CHANGE_PROCESS_CM_SET_KEY_CNF, cco_nek_change__nek_timeout, no); } } void cp_av_cco_action_cm_set_key_cnf_receive (cp_t *ctx, cp_mme_rx_t *mme) { cp_msg_cm_set_key_cnf_t data; dbg_assert (ctx); dbg_assert (mme); /* Stop the time out timer. */ cp_sta_core_stop_timed_or_cyclic_event (ctx, &ctx->cco_action.eks_timer); if ((cp_msg_cm_set_key_cnf_receive (ctx, mme, &data) && (cp_secu_protocol_check (&ctx->cco_action.eks_prun, &mme->prun, CP_SECU_PROTOCOL_RUN_CHECK_RESULT_NEXT))) && ((mme->prun.pid == 1) && (mme->prun.pmn == 2))) { uint i; cp_msg_cm_set_key_req_t data_req; cp_fsm_event_t *event; data_req.key_type = CP_MSG_KEY_NEK; data_req.cco_cap = CP_CCO_LEVEL; data_req.nid = cp_sta_own_data_get_nid (ctx); data_req.new_eks = ctx->cco_action.new_nek.eks; for (i = 0; i < COUNT (ctx->cco_action.new_nek.nek_enc.key); i++) data_req.new_key.key[i] = ctx->cco_action.new_nek.nek_enc.key[i]; /* Prepare the EKS NEK exchange. */ ctx->cco_action.eks_prun = mme->prun; cp_secu_protocol_next (&ctx->cco_action.eks_prun, &ctx->rnd, false /* not last*/); cp_msg_cm_set_key_req_send (ctx, &ctx->cco_action.eks_sta_current_peer, CP_MME_PEKS_NMK, &ctx->cco_action.eks_prun, &data_req); /* Start the timeout timer. */ event = cp_fsm_event_bare_new ( ctx, CP_FSM_EVENT_TYPE_cco_nek_change__nek_timeout); cp_sta_core_gen_timed_event(ctx, &ctx->cco_action.eks_timer, event, CP_CCO_ACTION_EKS_TIMEOUT_MS); } /* Last message or error, try the next station. * If it was an error the STA is responsible to request the key when it * see the EKS bentry in the central beacon. */ else { cp_sta_t *sta = cp_sta_mgr_sta_get_from_mac (ctx, mme->peer.mac); cp_net_t *net = cp_sta_mgr_get_our_avln (ctx); sta = cp_net_sta_get_next (ctx, net, sta); /* If the current STA is not authenticated, it is not necessary to send * the MME. */ while (sta && !cp_sta_get_authenticated (ctx, sta)) sta = cp_net_sta_get_next (ctx, net, sta); if (sta) { cp_av_cco_action_nek_change_start_transaction (ctx, sta); slab_release (sta); cp_fsm_branch (ctx, CCO_NEK_CHANGE_PROCESS_CM_SET_KEY_CNF, CM_SET_KEY_CNF_PID1, ok_continue); } else { cp_fsm_branch (ctx, CCO_NEK_CHANGE_PROCESS_CM_SET_KEY_CNF, CM_SET_KEY_CNF_PID1, end); } } } void cp_av_cco_action_nek_change_prevent (cp_t *ctx) { dbg_assert (ctx); /* If there is no new EKS then do not change it. * This disallow the stations to store a false nek in mac config. */ if (cp_sta_own_data_get_cco_status (ctx)) { cp_beacon_change_nek (ctx, ctx->cco_action.new_nek.eks, ctx->cco_action.new_nek.nek_enc, false /* central beacon change. */); } } void cp_av_cco_action_cco__cc_discover_list_req (cp_t *ctx, cp_net_t *net, cp_sta_t *sta) { dbg_assert (ctx); cp_mme_peer_t peer; cp_sta_get_peer (sta, &peer); cp_msg_cc_discover_list_req_send (ctx, &peer); } void cp_av_cco_action_beacon_with_same_nid ( cp_t *ctx, bsu_beacon_t *beacon, cp_net_t *net, cp_sta_t *sta) { dbg_assert (beacon); /* If we have the highest mac address we do nothing. */ if (beacon->bmis.mac_address.present) if (MAC_REVERSE (beacon->bmis.mac_address.mac_address) < MAC_REVERSE (cp_sta_own_data_get_mac_address (ctx))) return; /* To merge the two avlns we shut our network by turning the cco into * an unassociated sta. */ cp_fsm_post_new_event (ctx, bare, cco_leave_merge_avln); }