blob: 59887183c5dc8d0f9d62b309649ffdfdf08fa3b1 [file] [log] [blame]
/*
* Copyright (c) 2011-2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
*
* This file lim_process_deauth_frame.cc contains the code
* for processing Deauthentication Frame.
* Author: Chandra Modumudi
* Date: 03/24/02
* History:-
* Date Modified by Modification Information
* --------------------------------------------------------------------
*
*/
#include "cds_api.h"
#include "ani_global.h"
#include "utils_api.h"
#include "lim_types.h"
#include "lim_utils.h"
#include "lim_assoc_utils.h"
#include "lim_security_utils.h"
#include "lim_ser_des_utils.h"
#include "sch_api.h"
#include "lim_send_messages.h"
/**
* lim_process_deauth_frame
*
***FUNCTION:
* This function is called by limProcessMessageQueue() upon
* Deauthentication frame reception.
*
***LOGIC:
*
***ASSUMPTIONS:
*
***NOTE:
*
* @param mac - Pointer to Global MAC structure
* @param *pRxPacketInfo - A pointer to Buffer descriptor + associated PDUs
* @return None
*/
void
lim_process_deauth_frame(struct mac_context *mac, uint8_t *pRxPacketInfo,
struct pe_session *pe_session)
{
uint8_t *pBody;
uint16_t reasonCode;
tpSirMacMgmtHdr pHdr;
struct pe_session *pRoamSessionEntry = NULL;
uint8_t roamSessionId;
#ifdef WLAN_FEATURE_11W
uint32_t frameLen;
#endif
int32_t frame_rssi;
pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo);
pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo);
frame_rssi = (int32_t)WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo);
frameLen = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo);
if (frameLen < sizeof(reasonCode)) {
pe_err("Deauth Frame length invalid %d", frameLen);
return ;
}
if (LIM_IS_STA_ROLE(pe_session) &&
((eLIM_SME_WT_DISASSOC_STATE == pe_session->limSmeState) ||
(eLIM_SME_WT_DEAUTH_STATE == pe_session->limSmeState))) {
/*Every 15th deauth frame will be logged in kmsg */
if (!(mac->lim.deauthMsgCnt & 0xF)) {
pe_debug("received Deauth frame in DEAUTH_WT_STATE"
"(already processing previously received DEAUTH frame)"
"Dropping this.. Deauth Failed %d",
++mac->lim.deauthMsgCnt);
} else {
mac->lim.deauthMsgCnt++;
}
return;
}
if (lim_is_group_addr(pHdr->sa)) {
/* Received Deauth frame from a BC/MC address */
/* Log error and ignore it */
pe_debug("received Deauth frame from a BC/MC address");
return;
}
if (lim_is_group_addr(pHdr->da) && !lim_is_addr_bc(pHdr->da)) {
/* Received Deauth frame for a MC address */
/* Log error and ignore it */
pe_debug("received Deauth frame for a MC address");
return;
}
if (!lim_validate_received_frame_a1_addr(mac,
pHdr->da, pe_session)) {
pe_err("rx frame doesn't have valid a1 address, drop it");
return;
}
#ifdef WLAN_FEATURE_11W
/* PMF: If this session is a PMF session, then ensure that this frame was protected */
if (pe_session->limRmfEnabled
&& (WMA_GET_RX_DPU_FEEDBACK(pRxPacketInfo) &
DPU_FEEDBACK_UNPROTECTED_ERROR)) {
pe_debug("received an unprotected deauth from AP");
/*
* When 11w offload is enabled then
* firmware should not fwd this frame
*/
if (LIM_IS_STA_ROLE(pe_session) && mac->pmf_offload) {
pe_err("11w offload is enable,unprotected deauth is not expected");
return;
}
/* If the frame received is unprotected, forward it to the supplicant to initiate */
/* an SA query */
/* send the unprotected frame indication to SME */
lim_send_sme_unprotected_mgmt_frame_ind(mac, pHdr->fc.subType,
(uint8_t *) pHdr,
(frameLen +
sizeof(tSirMacMgmtHdr)),
pe_session->smeSessionId,
pe_session);
return;
}
#endif
/* Get reasonCode from Deauthentication frame body */
reasonCode = sir_read_u16(pBody);
pe_debug("Received Deauth frame for Addr: " MAC_ADDRESS_STR
"(mlm state = %s, sme state = %d systemrole = %d "
"RSSI = %d) with reason code %d [%s] from "
MAC_ADDRESS_STR, MAC_ADDR_ARRAY(pHdr->da),
lim_mlm_state_str(pe_session->limMlmState),
pe_session->limSmeState,
GET_LIM_SYSTEM_ROLE(pe_session), frame_rssi,
reasonCode, lim_dot11_reason_str(reasonCode),
MAC_ADDR_ARRAY(pHdr->sa));
if (mac->mlme_cfg->gen.fatal_event_trigger &&
(reasonCode != eSIR_MAC_UNSPEC_FAILURE_REASON &&
reasonCode != eSIR_MAC_DEAUTH_LEAVING_BSS_REASON &&
reasonCode != eSIR_MAC_DISASSOC_LEAVING_BSS_REASON)) {
cds_flush_logs(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_DISCONNECT,
false, false);
}
lim_diag_event_report(mac, WLAN_PE_DIAG_DEAUTH_FRAME_EVENT,
pe_session, 0, reasonCode);
if (lim_check_disassoc_deauth_ack_pending(mac, (uint8_t *) pHdr->sa)) {
pe_debug("Ignore the Deauth received, while waiting for ack of "
"disassoc/deauth");
lim_clean_up_disassoc_deauth_req(mac, (uint8_t *) pHdr->sa, 1);
return;
}
if (LIM_IS_AP_ROLE(pe_session)) {
switch (reasonCode) {
case eSIR_MAC_UNSPEC_FAILURE_REASON:
case eSIR_MAC_DEAUTH_LEAVING_BSS_REASON:
/* Valid reasonCode in received Deauthentication frame */
break;
default:
/* Invalid reasonCode in received Deauthentication frame */
/* Log error and ignore the frame */
pe_err("received Deauth frame with invalid reasonCode %d from "
MAC_ADDRESS_STR, reasonCode,
MAC_ADDR_ARRAY(pHdr->sa));
break;
}
} else if (LIM_IS_STA_ROLE(pe_session)) {
switch (reasonCode) {
case eSIR_MAC_UNSPEC_FAILURE_REASON:
case eSIR_MAC_PREV_AUTH_NOT_VALID_REASON:
case eSIR_MAC_DEAUTH_LEAVING_BSS_REASON:
case eSIR_MAC_CLASS2_FRAME_FROM_NON_AUTH_STA_REASON:
case eSIR_MAC_CLASS3_FRAME_FROM_NON_ASSOC_STA_REASON:
case eSIR_MAC_STA_NOT_PRE_AUTHENTICATED_REASON:
/* Valid reasonCode in received Deauth frame */
break;
default:
/* Invalid reasonCode in received Deauth frame */
/* Log error and ignore the frame */
pe_err("received Deauth frame with invalid reasonCode %d from "
MAC_ADDRESS_STR, reasonCode,
MAC_ADDR_ARRAY(pHdr->sa));
break;
}
} else {
/* Received Deauth frame in either IBSS */
/* or un-known role. Log and ignore it */
pe_err("received Deauth frame with reasonCode %d in role %d from "
MAC_ADDRESS_STR, reasonCode,
GET_LIM_SYSTEM_ROLE(pe_session),
MAC_ADDR_ARRAY(pHdr->sa));
return;
}
/** If we are in the middle of ReAssoc, a few things could happen:
* - STA is reassociating to current AP, and receives deauth from:
* a) current AP
* b) other AP
* - STA is reassociating to a new AP, and receives deauth from:
* c) current AP
* d) reassoc AP
* e) other AP
*
* The logic is:
* 1) If rcv deauth from an AP other than the one we're trying to
* reassociate with, then drop the deauth frame (case b, c, e)
* 2) If rcv deauth from the "new" reassoc AP (case d), then restore
* context with previous AP and send SME_REASSOC_RSP failure.
* 3) If rcv deauth from the reassoc AP, which is also the same
* AP we're currently associated with (case a), then proceed
* with normal deauth processing.
*/
pRoamSessionEntry =
pe_find_session_by_bssid(mac, pe_session->limReAssocbssId,
&roamSessionId);
if (lim_is_reassoc_in_progress(mac, pe_session) ||
lim_is_reassoc_in_progress(mac, pRoamSessionEntry) ||
pe_session->fw_roaming_started) {
/*
* For LFR3, the roaming bssid is not known during ROAM_START,
* so check if the deauth is received from current AP when
* roaming is being done in the firmware
*/
if (pe_session->fw_roaming_started &&
IS_CURRENT_BSSID(mac, pHdr->sa, pe_session)) {
pe_debug("LFR3: Drop deauth frame from connected AP");
/*
* recvd_deauth_while_roaming will be stored in the
* current AP session amd if roaming has been aborted
* for some reason and come back to same AP, then issue
* a disconnect internally if this flag is true. There
* is no need to reset this flag to false, because if
* roaming succeeds, then this session gets deleted and
* new session is created.
*/
pe_session->recvd_deauth_while_roaming = true;
pe_session->deauth_disassoc_rc = reasonCode;
return;
}
if (!IS_REASSOC_BSSID(mac, pHdr->sa, pe_session)) {
pe_debug("Rcv Deauth from unknown/different "
"AP while ReAssoc. Ignore " MAC_ADDRESS_STR
"limReAssocbssId : " MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(pHdr->sa),
MAC_ADDR_ARRAY(pe_session->limReAssocbssId));
return;
}
/** Received deauth from the new AP to which we tried to ReAssociate.
* Drop ReAssoc and Restore the Previous context( current connected AP).
*/
if (!IS_CURRENT_BSSID(mac, pHdr->sa, pe_session)) {
pe_debug("received DeAuth from the New AP to "
"which ReAssoc is sent " MAC_ADDRESS_STR
"pe_session->bssId: " MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(pHdr->sa),
MAC_ADDR_ARRAY(pe_session->bssId));
lim_restore_pre_reassoc_state(mac,
eSIR_SME_REASSOC_REFUSED,
reasonCode,
pe_session);
return;
}
}
/* If received DeAuth from AP other than the one we're trying to join with
* nor associated with, then ignore deauth and delete Pre-auth entry.
*/
if (!LIM_IS_AP_ROLE(pe_session)) {
if (!IS_CURRENT_BSSID(mac, pHdr->bssId, pe_session)) {
pe_err("received DeAuth from an AP other "
"than we're trying to join. Ignore. "
MAC_ADDRESS_STR, MAC_ADDR_ARRAY(pHdr->sa));
if (lim_search_pre_auth_list(mac, pHdr->sa)) {
pe_debug("Preauth entry exist. Deleting");
lim_delete_pre_auth_node(mac, pHdr->sa);
}
return;
}
}
lim_perform_deauth(mac, pe_session, reasonCode, pHdr->sa,
frame_rssi);
} /*** end lim_process_deauth_frame() ***/
void lim_perform_deauth(struct mac_context *mac_ctx, struct pe_session *pe_session,
uint16_t rc, tSirMacAddr addr, int32_t frame_rssi)
{
tLimMlmDeauthInd mlmDeauthInd;
tLimMlmAssocCnf mlmAssocCnf;
uint16_t aid;
tpDphHashNode sta_ds;
sta_ds = dph_lookup_hash_entry(mac_ctx, addr, &aid,
&pe_session->dph.dphHashTable);
if (sta_ds == NULL) {
pe_debug("Hash entry not found");
return;
}
/* Check for pre-assoc states */
switch (GET_LIM_SYSTEM_ROLE(pe_session)) {
case eLIM_STA_ROLE:
switch (pe_session->limMlmState) {
case eLIM_MLM_WT_AUTH_FRAME2_STATE:
/**
* AP sent Deauth frame while waiting
* for Auth frame2. Report Auth failure
* to SME.
*/
pe_debug("received Deauth frame state %X with failure "
"code %d from " MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
lim_restore_from_auth_state(mac_ctx,
eSIR_SME_DEAUTH_WHILE_JOIN,
rc, pe_session);
return;
case eLIM_MLM_AUTHENTICATED_STATE:
pe_debug("received Deauth frame state %X with "
"reasonCode=%d from " MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
/* / Issue Deauth Indication to SME. */
qdf_mem_copy((uint8_t *) &mlmDeauthInd.peerMacAddr,
addr, sizeof(tSirMacAddr));
mlmDeauthInd.reasonCode = rc;
pe_session->limMlmState = eLIM_MLM_IDLE_STATE;
MTRACE(mac_trace
(mac_ctx, TRACE_CODE_MLM_STATE,
pe_session->peSessionId,
pe_session->limMlmState));
lim_post_sme_message(mac_ctx,
LIM_MLM_DEAUTH_IND,
(uint32_t *) &mlmDeauthInd);
return;
case eLIM_MLM_WT_ASSOC_RSP_STATE:
/**
* AP may have 'aged-out' our Pre-auth
* context. Delete local pre-auth context
* if any and issue ASSOC_CNF to SME.
*/
pe_debug("received Deauth frame state %X with "
"reasonCode=%d from " MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
if (lim_search_pre_auth_list(mac_ctx, addr))
lim_delete_pre_auth_node(mac_ctx, addr);
if (pe_session->pLimMlmJoinReq) {
qdf_mem_free(pe_session->pLimMlmJoinReq);
pe_session->pLimMlmJoinReq = NULL;
}
mlmAssocCnf.resultCode = eSIR_SME_DEAUTH_WHILE_JOIN;
mlmAssocCnf.protStatusCode = rc;
/* PE session Id */
mlmAssocCnf.sessionId = pe_session->peSessionId;
pe_session->limMlmState =
pe_session->limPrevMlmState;
MTRACE(mac_trace
(mac_ctx, TRACE_CODE_MLM_STATE,
pe_session->peSessionId,
pe_session->limMlmState));
/* Deactive Association response timeout */
lim_deactivate_and_change_timer(mac_ctx,
eLIM_ASSOC_FAIL_TIMER);
lim_post_sme_message(mac_ctx,
LIM_MLM_ASSOC_CNF,
(uint32_t *) &mlmAssocCnf);
return;
case eLIM_MLM_WT_ADD_STA_RSP_STATE:
pe_session->fDeauthReceived = true;
pe_debug("Received Deauth frame in state %X with Reason "
"Code %d from Peer" MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
return;
case eLIM_MLM_IDLE_STATE:
case eLIM_MLM_LINK_ESTABLISHED_STATE:
#ifdef FEATURE_WLAN_TDLS
if ((NULL != sta_ds)
&& (STA_ENTRY_TDLS_PEER == sta_ds->staType)) {
pe_err("received Deauth frame in state %X with "
"reason code %d from Tdls peer"
MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
lim_send_sme_tdls_del_sta_ind(mac_ctx, sta_ds,
pe_session,
rc);
return;
} else {
/*
* Delete all the TDLS peers only if Deauth
* is received from the AP
*/
if (IS_CURRENT_BSSID(mac_ctx, addr, pe_session))
lim_delete_tdls_peers(mac_ctx, pe_session);
#endif
/**
* This could be Deauthentication frame from
* a BSS with which pre-authentication was
* performed. Delete Pre-auth entry if found.
*/
if (lim_search_pre_auth_list(mac_ctx, addr))
lim_delete_pre_auth_node(mac_ctx, addr);
#ifdef FEATURE_WLAN_TDLS
}
#endif
break;
case eLIM_MLM_WT_REASSOC_RSP_STATE:
pe_err("received Deauth frame state %X with "
"reasonCode=%d from " MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
break;
case eLIM_MLM_WT_FT_REASSOC_RSP_STATE:
pe_err("received Deauth frame in FT state %X with "
"reasonCode=%d from " MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
break;
default:
pe_err("received Deauth frame in state %X with "
"reasonCode=%d from " MAC_ADDRESS_STR,
pe_session->limMlmState, rc,
MAC_ADDR_ARRAY(addr));
return;
}
break;
case eLIM_STA_IN_IBSS_ROLE:
break;
case eLIM_AP_ROLE:
break;
default:
return;
} /* end switch (mac->lim.gLimSystemRole) */
/**
* Extract 'associated' context for STA, if any.
* This is maintained by DPH and created by LIM.
*/
if (NULL == sta_ds) {
pe_err("sta_ds is NULL");
return;
}
if ((sta_ds->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_STA_RSP_STATE) ||
(sta_ds->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_BSS_RSP_STATE) ||
sta_ds->sta_deletion_in_progress) {
/**
* Already in the process of deleting context for the peer
* and received Deauthentication frame. Log and Ignore.
*/
pe_debug("Deletion is in progress (%d) for peer:%pM in mlmState %d",
sta_ds->sta_deletion_in_progress, addr,
sta_ds->mlmStaContext.mlmState);
return;
}
sta_ds->mlmStaContext.disassocReason = (tSirMacReasonCodes) rc;
sta_ds->mlmStaContext.cleanupTrigger = eLIM_PEER_ENTITY_DEAUTH;
sta_ds->sta_deletion_in_progress = true;
/* / Issue Deauth Indication to SME. */
qdf_mem_copy((uint8_t *) &mlmDeauthInd.peerMacAddr,
sta_ds->staAddr, sizeof(tSirMacAddr));
mlmDeauthInd.reasonCode =
(uint8_t) sta_ds->mlmStaContext.disassocReason;
mlmDeauthInd.deauthTrigger = eLIM_PEER_ENTITY_DEAUTH;
/*
* If we're in the middle of ReAssoc and received deauth from
* the ReAssoc AP, then notify SME by sending REASSOC_RSP with
* failure result code. SME will post the disconnect to the
* supplicant and the latter would start a fresh assoc.
*/
if (lim_is_reassoc_in_progress(mac_ctx, pe_session)) {
/**
* AP may have 'aged-out' our Pre-auth
* context. Delete local pre-auth context
* if any and issue REASSOC_CNF to SME.
*/
if (lim_search_pre_auth_list(mac_ctx, addr))
lim_delete_pre_auth_node(mac_ctx, addr);
if (pe_session->limAssocResponseData) {
qdf_mem_free(pe_session->limAssocResponseData);
pe_session->limAssocResponseData = NULL;
}
pe_debug("Rcv Deauth from ReAssoc AP Issue REASSOC_CNF");
/*
* TODO: Instead of overloading eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE
* it would have been good to define/use a different failure type.
* Using eSIR_SME_FT_REASSOC_FAILURE does not seem to clean-up
* properly and we end up seeing "transmit queue timeout".
*/
lim_post_reassoc_failure(mac_ctx,
eSIR_SME_FT_REASSOC_TIMEOUT_FAILURE,
eSIR_MAC_UNSPEC_FAILURE_STATUS,
pe_session);
return;
}
/* reset the deauthMsgCnt here since we are able to Process
* the deauth frame and sending up the indication as well */
if (mac_ctx->lim.deauthMsgCnt != 0) {
mac_ctx->lim.deauthMsgCnt = 0;
}
if (LIM_IS_STA_ROLE(pe_session))
wma_tx_abort(pe_session->smeSessionId);
lim_update_lost_link_info(mac_ctx, pe_session, frame_rssi);
/* / Deauthentication from peer MAC entity */
if (LIM_IS_STA_ROLE(pe_session))
lim_post_sme_message(mac_ctx, LIM_MLM_DEAUTH_IND,
(uint32_t *) &mlmDeauthInd);
/* send eWNI_SME_DEAUTH_IND to SME */
lim_send_sme_deauth_ind(mac_ctx, sta_ds, pe_session);
return;
}