static uint32_t do_estab_peer_link(struct candidate *cand) { uint32_t changed; derive_mtk(cand); cb->estab_peer_link( cand->peer_mac, cand->mtk, sizeof(cand->mtk), cand->mgtk, sizeof(cand->mgtk), cand->mgtk_expiration, (cand->has_igtk) ? cand->igtk : NULL, (cand->has_igtk) ? sizeof(cand->igtk) : 0, cand->igtk_keyid, cand->sup_rates, cand->sup_rates_len, cand->cookie); set_link_state(cand, PLINK_ESTAB); changed = mesh_set_ht_op_mode(cand->conf->mesh); sae_debug( AMPE_DEBUG_FSM, "Mesh plink with " MACSTR " ESTABLISHED\n", MAC2STR(cand->peer_mac)); rekey_verify_peer(cand); return changed; }
static void peer_ampe_init( struct ampe_config *aconf, struct candidate *cand, void *cookie) { le16 llid; assert(cand); RAND_bytes((unsigned char *)&llid, sizeof(llid)); RAND_bytes(cand->my_nonce, sizeof(cand->my_nonce)); memset(cand->peer_nonce, 0, sizeof(cand->peer_nonce)); cand->cookie = cookie; cand->my_lid = llid; cand->peer_lid = 0; set_link_state(cand, PLINK_LISTEN); cand->timeout = aconf->retry_timeout_ms; cand->conf = aconf; memset(cand->mtk, 0, sizeof(cand->mtk)); memset(cand->mgtk, 0, sizeof(cand->mgtk)); memset(cand->igtk, 0, sizeof(cand->igtk)); cand->has_igtk = false; if (aconf->mesh->conf->is_secure) { derive_aek(cand); siv_init(&cand->sivctx, cand->aek, SIV_256); } }
/** * ampe_open_peer_link - attempt to establish a peer link * @peer: MAC address of the candidate peer * @cookie: Opaque cookie that will be returned to the caller along with * frames to be transmitted. * * Returns 0 or a negative error. */ int ampe_open_peer_link(unsigned char *peer_mac, void *cookie) { struct candidate *cand; assert(peer_mac); if ((cand = find_peer(peer_mac, 0)) == NULL) { sae_debug( AMPE_DEBUG_FSM, "Mesh plink: Attempt to peer with " " non-authed peer\n"); return -EPERM; } peer_ampe_init(&e_conf, cand, cookie); set_link_state(cand, PLINK_OPN_SNT); cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); sae_debug( AMPE_DEBUG_FSM, "Mesh plink: starting establishment " "with " MACSTR "\n", MAC2STR(peer_mac)); return plink_frame_tx(cand, PLINK_OPEN, 0); }
static void peer_ampe_init(struct ampe_config *aconf, struct candidate *cand, unsigned char *me, void *cookie) { le16 llid; assert(cand && me); RAND_bytes((unsigned char *) &llid, 2); RAND_bytes(cand->my_nonce, sizeof(cand->my_nonce)); cand->cookie = cookie; cand->my_lid = llid; cand->peer_lid = 0; set_link_state(cand, PLINK_LISTEN); cand->timeout = aconf->retry_timeout_ms; cand->conf = aconf; derive_aek(cand); siv_init(&cand->sivctx, cand->aek, SIV_256); return; }
static void fsm_step(struct candidate *cand, enum plink_event event) { struct ampe_config *aconf = cand->conf; unsigned short reason = 0; uint32_t changed = 0; switch (cand->link_state) { case PLINK_LISTEN: switch (event) { case CLS_ACPT: fsm_restart(cand); break; case OPN_ACPT: cand->timeout = aconf->retry_timeout_ms; cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_OPEN, 0); plink_frame_tx(cand, PLINK_CONFIRM, 0); break; default: break; } break; case PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = htole16(MESH_CAPABILITY_POLICY_VIOLATION); case CLS_ACPT: if (!reason) reason = htole16(MESH_CLOSE_RCVD); cand->reason = reason; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: /* retry timer is left untouched */ set_link_state(cand, PLINK_OPN_RCVD); plink_frame_tx(cand, PLINK_CONFIRM, 0); break; case CNF_ACPT: set_link_state(cand, PLINK_CNF_RCVD); cand->timeout = aconf->confirm_timeout_ms; cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->timeout), plink_timer, cand); break; default: break; } break; case PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = htole16(MESH_CAPABILITY_POLICY_VIOLATION); case CLS_ACPT: if (!reason) reason = htole16(MESH_CLOSE_RCVD); cand->reason = reason; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: plink_frame_tx(cand, PLINK_CONFIRM, 0); break; case CNF_ACPT: //del_timer(&cand->plink_timer); set_link_state(cand, PLINK_ESTAB); //mesh_plink_inc_estab_count(sdata); //ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); derive_mtk(cand); estab_peer_link(cand->peer_mac, cand->mtk, sizeof(cand->mtk), cand->mgtk, sizeof(cand->mgtk), cand->mgtk_expiration, cand->sup_rates, cand->sup_rates_len, cand->cookie); changed |= mesh_set_ht_op_mode(cand->conf->mesh); sae_debug(AMPE_DEBUG_FSM, "mesh plink with " MACSTR " established\n", MAC2STR(cand->peer_mac)); break; default: break; } break; case PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: reason = htole16(MESH_CAPABILITY_POLICY_VIOLATION); case CLS_ACPT: if (!reason) reason = htole16(MESH_CLOSE_RCVD); cand->reason = reason; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: set_link_state(cand, PLINK_ESTAB); estab_peer_link(cand->peer_mac, cand->mtk, sizeof(cand->mtk), cand->mgtk, sizeof(cand->mgtk), cand->mgtk_expiration, cand->sup_rates, cand->sup_rates_len, cand->cookie); changed |= mesh_set_ht_op_mode(cand->conf->mesh); //TODO: update the number of available peer "slots" in mesh config //mesh_plink_inc_estab_count(sdata); //ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); sae_debug(AMPE_DEBUG_FSM, "Mesh plink with " MACSTR " ESTABLISHED\n", MAC2STR(cand->peer_mac)); plink_frame_tx(cand, PLINK_CONFIRM, 0); break; default: break; } break; case PLINK_ESTAB: switch (event) { case CLS_ACPT: reason = htole16(MESH_CLOSE_RCVD); cand->reason = reason; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->timeout), plink_timer, cand); changed |= mesh_set_ht_op_mode(cand->conf->mesh); //TODO: update the number of available peer "slots" in mesh config //if (deactivated) // ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: plink_frame_tx(cand, PLINK_CONFIRM, 0); break; default: break; } break; case PLINK_HOLDING: switch (event) { case CLS_ACPT: //if (del_timer(&cand->plink_timer)) // cand->ignore_plink_timer = 1; fsm_restart(cand); break; case OPN_ACPT: case CNF_ACPT: case OPN_RJCT: case CNF_RJCT: reason = cand->reason; plink_frame_tx(cand, PLINK_CLOSE, reason); break; default: break; } break; default: sae_debug(AMPE_DEBUG_FSM, "Unsupported event transition %d", event); break; } if (changed) meshd_set_mesh_conf(cand->conf->mesh, changed); }
static void plink_timer(timerid id, void *data) { __le16 reason; struct candidate *cand; cand = (struct candidate *)data; assert(cand); sae_debug(AMPE_DEBUG_FSM, "Mesh plink timer for " MACSTR " fired on state %s\n", MAC2STR(cand->peer_mac), mplstates[(cand->link_state > PLINK_BLOCKED) ? PLINK_UNDEFINED : cand->link_state]); reason = 0; switch (cand->link_state) { case PLINK_OPN_RCVD: case PLINK_OPN_SNT: /* retry timer */ sae_debug(AMPE_DEBUG_FSM, "Mesh plink:retries %d of %d\n", cand->retries, cand->conf->max_retries); if (cand->retries < cand->conf->max_retries) { unsigned int rand; sae_debug(AMPE_DEBUG_FSM, "Mesh plink for " MACSTR " (retry, timeout): %d %d\n", MAC2STR(cand->peer_mac), cand->retries, cand->timeout); RAND_bytes((unsigned char *) &rand, sizeof(rand)); if (!cand->timeout) { cand->timeout = cand->conf->retry_timeout_ms; sae_debug(AMPE_DEBUG_ERR, "WARN: cand " MACSTR " had a timeout of 0ms. Reset to %d\n", MAC2STR(cand->peer_mac),cand->timeout); } cand->timeout += rand % cand->timeout; ++cand->retries; cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_OPEN, 0); break; } reason = htole16(MESH_MAX_RETRIES); /* fall through on else */ case PLINK_CNF_RCVD: /* confirm timer */ if (!reason) reason = htole16(MESH_CONFIRM_TIMEOUT); set_link_state(cand, PLINK_HOLDING); cand->t2 = srv_add_timeout(srvctx, SRV_MSEC(cand->conf->holding_timeout_ms), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case PLINK_HOLDING: /* holding timer */ fsm_restart(cand); break; case PLINK_ESTAB: /* nothing to do */ break; default: sae_debug(AMPE_DEBUG_FSM, "Timeout for peer " MACSTR " in state %d\n", MAC2STR(cand->peer_mac), cand->link_state); break; } }
/** * fsm_restart - restart a mesh peer link finite state machine * * @cand: mesh peer link to restart * * */ static inline void fsm_restart(struct candidate *cand) { set_link_state(cand, PLINK_LISTEN); cand->my_lid = cand->peer_lid = cand->reason = 0; cand->retries = 0; }
static void fsm_step(struct candidate *cand, enum plink_event event) { struct ampe_config *aconf = cand->conf; unsigned short reason = 0; uint32_t changed = 0; switch (cand->link_state) { case PLINK_LISTEN: switch (event) { case CLS_ACPT: fsm_restart(cand); break; case OPN_ACPT: cand->timeout = aconf->retry_timeout_ms; cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); set_link_state(cand, PLINK_OPN_RCVD); plink_frame_tx(cand, PLINK_OPEN, 0); plink_frame_tx(cand, PLINK_CONFIRM, 0); break; default: break; } break; case PLINK_OPN_SNT: switch (event) { case OPN_RJCT: case CNF_RJCT: case REQ_RJCT: reason = MESH_CAPABILITY_POLICY_VIOLATION; /* no break */ case CLS_ACPT: if (!reason) reason = MESH_CLOSE_RCVD; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: /* retry timer is left untouched */ set_link_state(cand, PLINK_OPN_RCVD); plink_frame_tx(cand, PLINK_CONFIRM, 0); break; case CNF_ACPT: set_link_state(cand, PLINK_CNF_RCVD); cand->timeout = aconf->confirm_timeout_ms; cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); break; default: break; } break; case PLINK_OPN_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: case REQ_RJCT: reason = MESH_CAPABILITY_POLICY_VIOLATION; /* no break */ case CLS_ACPT: if (!reason) reason = MESH_CLOSE_RCVD; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: plink_frame_tx(cand, PLINK_CONFIRM, 0); break; case CNF_ACPT: changed |= do_estab_peer_link(cand); break; default: break; } break; case PLINK_CNF_RCVD: switch (event) { case OPN_RJCT: case CNF_RJCT: case REQ_RJCT: reason = MESH_CAPABILITY_POLICY_VIOLATION; /* no break */ case CLS_ACPT: if (!reason) reason = MESH_CLOSE_RCVD; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: changed |= do_estab_peer_link(cand); plink_frame_tx(cand, PLINK_CONFIRM, 0); break; default: break; } break; case PLINK_ESTAB: switch (event) { case OPN_RJCT: case CNF_RJCT: case REQ_RJCT: reason = MESH_CAPABILITY_POLICY_VIOLATION; case CLS_ACPT: if (!reason) reason = MESH_CLOSE_RCVD; set_link_state(cand, PLINK_HOLDING); cand->timeout = aconf->holding_timeout_ms; cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); changed |= mesh_set_ht_op_mode(cand->conf->mesh); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case OPN_ACPT: plink_frame_tx(cand, PLINK_CONFIRM, 0); break; default: break; } break; case PLINK_HOLDING: switch (event) { case CLS_ACPT: fsm_restart(cand); break; case OPN_ACPT: case CNF_ACPT: case OPN_RJCT: case CNF_RJCT: case REQ_RJCT: plink_frame_tx(cand, PLINK_CLOSE, reason); break; default: break; } break; default: sae_debug(AMPE_DEBUG_FSM, "Unsupported event transition %d", event); break; } if (changed) cb->meshd_set_mesh_conf(cand->conf->mesh, changed); }
static void plink_timer(void *data) { le16 reason; struct candidate *cand; cand = (struct candidate *)data; assert(cand); sae_debug( AMPE_DEBUG_FSM, "Mesh plink timer for " MACSTR " fired on state %s\n", MAC2STR(cand->peer_mac), mpl_states[(cand->link_state > PLINK_BLOCKED) ? PLINK_UNDEFINED : cand->link_state]); reason = 0; switch (cand->link_state) { case PLINK_OPN_RCVD: case PLINK_OPN_SNT: /* retry timer */ sae_debug( AMPE_DEBUG_FSM, "Mesh plink:retries %d of %d\n", cand->retries, cand->conf->max_retries); if (cand->retries < cand->conf->max_retries) { cand->timeout = cand->conf->retry_timeout_ms; sae_debug( AMPE_DEBUG_FSM, "Mesh plink for " MACSTR " (retry, timeout): %d %d\n", MAC2STR(cand->peer_mac), cand->retries, cand->timeout); ++cand->retries; cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout(SRV_MSEC(cand->timeout), plink_timer, cand); plink_frame_tx(cand, PLINK_OPEN, 0); break; } reason = MESH_MAX_RETRIES; /* no break / fall through on else */ case PLINK_CNF_RCVD: /* confirm timer */ if (!reason) reason = MESH_CONFIRM_TIMEOUT; set_link_state(cand, PLINK_HOLDING); cb->evl->rem_timeout(cand->t2); cand->t2 = cb->evl->add_timeout( SRV_MSEC(cand->conf->holding_timeout_ms), plink_timer, cand); plink_frame_tx(cand, PLINK_CLOSE, reason); break; case PLINK_HOLDING: /* holding timer */ fsm_restart(cand); break; case PLINK_ESTAB: /* nothing to do */ break; default: sae_debug( AMPE_DEBUG_FSM, "Timeout for peer " MACSTR " in state %d\n", MAC2STR(cand->peer_mac), cand->link_state); break; } }