1 /* 2 * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * 21 * This file lim_process_disassoc_frame.cc contains the code 22 * for processing Disassocation Frame. 23 * Author: Chandra Modumudi 24 * Date: 03/24/02 25 * History:- 26 * Date Modified by Modification Information 27 * -------------------------------------------------------------------- 28 * 29 */ 30 #include "cds_api.h" 31 #include "wni_api.h" 32 #include "sir_api.h" 33 #include "ani_global.h" 34 #include "wni_cfg.h" 35 36 #include "utils_api.h" 37 #include "lim_types.h" 38 #include "lim_utils.h" 39 #include "lim_assoc_utils.h" 40 #include "lim_security_utils.h" 41 #include "lim_ser_des_utils.h" 42 #include "lim_send_messages.h" 43 #include "sch_api.h" 44 45 /** 46 * lim_process_disassoc_frame 47 * 48 ***FUNCTION: 49 * This function is called by limProcessMessageQueue() upon 50 * Disassociation frame reception. 51 * 52 ***LOGIC: 53 * 54 ***ASSUMPTIONS: 55 * DPH drops packets for STA with 'valid' bit in pStaDs set to '0'. 56 * 57 ***NOTE: 58 * 59 * @param pMac - Pointer to Global MAC structure 60 * @param *pRxPacketInfo - A pointer to Rx packet info structure 61 * @return None 62 */ 63 void 64 lim_process_disassoc_frame(tpAniSirGlobal pMac, uint8_t *pRxPacketInfo, 65 tpPESession psessionEntry) 66 { 67 uint8_t *pBody; 68 uint16_t aid, reasonCode; 69 tpSirMacMgmtHdr pHdr; 70 tpDphHashNode pStaDs; 71 uint32_t frame_len; 72 int32_t frame_rssi; 73 74 pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo); 75 pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo); 76 frame_len = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo); 77 78 frame_rssi = (int32_t)WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo); 79 80 if (lim_is_group_addr(pHdr->sa)) { 81 /* Received Disassoc frame from a BC/MC address */ 82 /* Log error and ignore it */ 83 pe_err("received Disassoc frame from a BC/MC address"); 84 return; 85 } 86 87 if (lim_is_group_addr(pHdr->da) && !lim_is_addr_bc(pHdr->da)) { 88 /* Received Disassoc frame for a MC address */ 89 /* Log error and ignore it */ 90 pe_err("received Disassoc frame for a MC address"); 91 return; 92 } 93 if (!lim_validate_received_frame_a1_addr(pMac, 94 pHdr->da, psessionEntry)) { 95 pe_err("rx frame doesn't have valid a1 address, drop it"); 96 return; 97 } 98 99 if (LIM_IS_STA_ROLE(psessionEntry) && 100 ((eLIM_SME_WT_DISASSOC_STATE == psessionEntry->limSmeState) || 101 (eLIM_SME_WT_DEAUTH_STATE == psessionEntry->limSmeState))) { 102 if (!(pMac->lim.disassocMsgCnt & 0xF)) { 103 pe_debug("received Disassoc frame in %s" 104 "already processing previously received Disassoc frame, dropping this %d", 105 lim_sme_state_str(psessionEntry->limSmeState), 106 ++pMac->lim.disassocMsgCnt); 107 } else { 108 pMac->lim.disassocMsgCnt++; 109 } 110 return; 111 } 112 #ifdef WLAN_FEATURE_11W 113 /* PMF: If this session is a PMF session, then ensure that this frame was protected */ 114 if (psessionEntry->limRmfEnabled 115 && (WMA_GET_RX_DPU_FEEDBACK(pRxPacketInfo) & 116 DPU_FEEDBACK_UNPROTECTED_ERROR)) { 117 pe_err("received an unprotected disassoc from AP"); 118 /* 119 * When 11w offload is enabled then 120 * firmware should not fwd this frame 121 */ 122 if (LIM_IS_STA_ROLE(psessionEntry) && pMac->pmf_offload) { 123 pe_err("11w offload is enable,unprotected disassoc is not expected"); 124 return; 125 } 126 127 /* If the frame received is unprotected, forward it to the supplicant to initiate */ 128 /* an SA query */ 129 /* send the unprotected frame indication to SME */ 130 lim_send_sme_unprotected_mgmt_frame_ind(pMac, pHdr->fc.subType, 131 (uint8_t *) pHdr, 132 (frame_len + 133 sizeof(tSirMacMgmtHdr)), 134 psessionEntry->smeSessionId, 135 psessionEntry); 136 return; 137 } 138 #endif 139 140 if (frame_len < 2) { 141 pe_err("frame len less than 2"); 142 return; 143 } 144 145 /* Get reasonCode from Disassociation frame body */ 146 reasonCode = sir_read_u16(pBody); 147 148 pe_debug("Received Disassoc frame for Addr: " MAC_ADDRESS_STR 149 "(mlm state=%s, sme state=%d RSSI=%d)," 150 "with reason code %d [%s] from " MAC_ADDRESS_STR, 151 MAC_ADDR_ARRAY(pHdr->da), 152 lim_mlm_state_str(psessionEntry->limMlmState), 153 psessionEntry->limSmeState, frame_rssi, reasonCode, 154 lim_dot11_reason_str(reasonCode), MAC_ADDR_ARRAY(pHdr->sa)); 155 lim_diag_event_report(pMac, WLAN_PE_DIAG_DISASSOC_FRAME_EVENT, 156 psessionEntry, 0, reasonCode); 157 158 if (pMac->roam.configParam.enable_fatal_event && 159 (reasonCode != eSIR_MAC_UNSPEC_FAILURE_REASON && 160 reasonCode != eSIR_MAC_DEAUTH_LEAVING_BSS_REASON && 161 reasonCode != eSIR_MAC_DISASSOC_LEAVING_BSS_REASON)) { 162 cds_flush_logs(WLAN_LOG_TYPE_FATAL, 163 WLAN_LOG_INDICATOR_HOST_DRIVER, 164 WLAN_LOG_REASON_DISCONNECT, 165 false, false); 166 } 167 /** 168 * Extract 'associated' context for STA, if any. 169 * This is maintained by DPH and created by LIM. 170 */ 171 pStaDs = 172 dph_lookup_hash_entry(pMac, pHdr->sa, &aid, 173 &psessionEntry->dph.dphHashTable); 174 175 if (pStaDs == NULL) { 176 /** 177 * Disassociating STA is not associated. 178 * Log error. 179 */ 180 pe_err("received Disassoc frame from STA that does not have context" 181 "reasonCode=%d, addr " MAC_ADDRESS_STR, 182 reasonCode, MAC_ADDR_ARRAY(pHdr->sa)); 183 return; 184 } 185 186 if (lim_check_disassoc_deauth_ack_pending(pMac, (uint8_t *) pHdr->sa)) { 187 pe_err("Ignore the DisAssoc received, while waiting for ack of disassoc/deauth"); 188 lim_clean_up_disassoc_deauth_req(pMac, (uint8_t *) pHdr->sa, 1); 189 return; 190 } 191 192 if (pMac->lim.disassocMsgCnt != 0) { 193 pMac->lim.disassocMsgCnt = 0; 194 } 195 196 /** If we are in the Wait for ReAssoc Rsp state */ 197 if (lim_is_reassoc_in_progress(pMac, psessionEntry)) { 198 /* 199 * For LFR3, the roaming bssid is not known during ROAM_START, 200 * so check if the disassoc is received from current AP when 201 * roaming is being done in the firmware 202 */ 203 if (psessionEntry->fw_roaming_started && 204 IS_CURRENT_BSSID(pMac, pHdr->sa, psessionEntry)) { 205 pe_debug("Dropping disassoc frame from connected AP"); 206 psessionEntry->recvd_disassoc_while_roaming = true; 207 psessionEntry->deauth_disassoc_rc = reasonCode; 208 return; 209 } 210 /** If we had received the DisAssoc from, 211 * a. the Current AP during ReAssociate to different AP in same ESS 212 * b. Unknown AP 213 * drop/ignore the DisAssoc received 214 */ 215 if (!IS_REASSOC_BSSID(pMac, pHdr->sa, psessionEntry)) { 216 pe_err("Ignore DisAssoc while Processing ReAssoc"); 217 return; 218 } 219 /** If the Disassoc is received from the new AP to which we tried to ReAssociate 220 * Drop ReAssoc and Restore the Previous context( current connected AP). 221 */ 222 if (!IS_CURRENT_BSSID(pMac, pHdr->sa, psessionEntry)) { 223 pe_debug("received Disassoc from the New AP to which ReAssoc is sent"); 224 lim_restore_pre_reassoc_state(pMac, 225 eSIR_SME_REASSOC_REFUSED, 226 reasonCode, 227 psessionEntry); 228 return; 229 } 230 } 231 232 if (LIM_IS_AP_ROLE(psessionEntry)) { 233 switch (reasonCode) { 234 case eSIR_MAC_UNSPEC_FAILURE_REASON: 235 case eSIR_MAC_DISASSOC_DUE_TO_INACTIVITY_REASON: 236 case eSIR_MAC_DISASSOC_LEAVING_BSS_REASON: 237 case eSIR_MAC_MIC_FAILURE_REASON: 238 case eSIR_MAC_4WAY_HANDSHAKE_TIMEOUT_REASON: 239 case eSIR_MAC_GR_KEY_UPDATE_TIMEOUT_REASON: 240 case eSIR_MAC_RSN_IE_MISMATCH_REASON: 241 case eSIR_MAC_1X_AUTH_FAILURE_REASON: 242 /* Valid reasonCode in received Disassociation frame */ 243 break; 244 245 default: 246 /* Invalid reasonCode in received Disassociation frame */ 247 pe_warn("received Disassoc frame with invalid reasonCode: %d from " MAC_ADDRESS_STR, 248 reasonCode, MAC_ADDR_ARRAY(pHdr->sa)); 249 break; 250 } 251 } else if (LIM_IS_STA_ROLE(psessionEntry) && 252 ((psessionEntry->limSmeState != eLIM_SME_WT_JOIN_STATE) && 253 (psessionEntry->limSmeState != eLIM_SME_WT_AUTH_STATE) && 254 (psessionEntry->limSmeState != eLIM_SME_WT_ASSOC_STATE) && 255 (psessionEntry->limSmeState != eLIM_SME_WT_REASSOC_STATE))) { 256 switch (reasonCode) { 257 case eSIR_MAC_DEAUTH_LEAVING_BSS_REASON: 258 case eSIR_MAC_DISASSOC_LEAVING_BSS_REASON: 259 /* Valid reasonCode in received Disassociation frame */ 260 /* as long as we're not about to channel switch */ 261 if (psessionEntry->gLimChannelSwitch.state != 262 eLIM_CHANNEL_SWITCH_IDLE) { 263 pe_err("Ignoring disassoc frame due to upcoming channel switch, from "MAC_ADDRESS_STR, 264 MAC_ADDR_ARRAY(pHdr->sa)); 265 return; 266 } 267 break; 268 269 default: 270 break; 271 } 272 } else { 273 /* Received Disassociation frame in either IBSS */ 274 /* or un-known role. Log and ignore it */ 275 pe_err("received Disassoc frame with invalid reasonCode: %d in role:" 276 "%d in sme state: %d from " MAC_ADDRESS_STR, reasonCode, 277 GET_LIM_SYSTEM_ROLE(psessionEntry), psessionEntry->limSmeState, 278 MAC_ADDR_ARRAY(pHdr->sa)); 279 280 return; 281 } 282 283 if ((pStaDs->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_STA_RSP_STATE) || 284 (pStaDs->mlmStaContext.mlmState == eLIM_MLM_WT_DEL_BSS_RSP_STATE) || 285 pStaDs->sta_deletion_in_progress) { 286 /** 287 * Already in the process of deleting context for the peer 288 * and received Disassociation frame. Log and Ignore. 289 */ 290 pe_debug("Deletion is in progress (%d) for peer:%pM in mlmState %d", 291 pStaDs->sta_deletion_in_progress, pHdr->sa, 292 pStaDs->mlmStaContext.mlmState); 293 return; 294 } 295 pStaDs->sta_deletion_in_progress = true; 296 lim_disassoc_tdls_peers(pMac, psessionEntry, pHdr->sa); 297 if (pStaDs->mlmStaContext.mlmState != eLIM_MLM_LINK_ESTABLISHED_STATE) { 298 /** 299 * Requesting STA is in some 'transient' state? 300 * Log error. 301 */ 302 if (pStaDs->mlmStaContext.mlmState == 303 eLIM_MLM_WT_ASSOC_CNF_STATE) 304 pStaDs->mlmStaContext.updateContext = 1; 305 306 pe_err("received Disassoc frame from peer that is in state: %X, addr "MAC_ADDRESS_STR, 307 pStaDs->mlmStaContext.mlmState, 308 MAC_ADDR_ARRAY(pHdr->sa)); 309 310 } /* if (pStaDs->mlmStaContext.mlmState != eLIM_MLM_LINK_ESTABLISHED_STATE) */ 311 312 lim_perform_disassoc(pMac, frame_rssi, reasonCode, 313 psessionEntry, pHdr->sa); 314 315 } /*** end lim_process_disassoc_frame() ***/ 316 317 #ifdef FEATURE_WLAN_TDLS 318 void lim_disassoc_tdls_peers(tpAniSirGlobal mac_ctx, 319 tpPESession pe_session, tSirMacAddr addr) 320 { 321 tpDphHashNode sta_ds; 322 uint16_t aid; 323 324 sta_ds = dph_lookup_hash_entry(mac_ctx, addr, &aid, 325 &pe_session->dph.dphHashTable); 326 if (sta_ds == NULL) { 327 pe_debug("Hash entry not found"); 328 return; 329 } 330 /** 331 * Delete all the TDLS peers only if Disassoc is received 332 * from the AP 333 */ 334 if ((LIM_IS_STA_ROLE(pe_session)) && 335 ((sta_ds->mlmStaContext.mlmState == 336 eLIM_MLM_LINK_ESTABLISHED_STATE) || 337 (sta_ds->mlmStaContext.mlmState == 338 eLIM_MLM_IDLE_STATE)) && 339 (IS_CURRENT_BSSID(mac_ctx, addr, pe_session))) 340 lim_delete_tdls_peers(mac_ctx, pe_session); 341 } 342 #endif 343 344 void lim_perform_disassoc(tpAniSirGlobal mac_ctx, int32_t frame_rssi, 345 uint16_t rc, tpPESession pe_session, tSirMacAddr addr) 346 { 347 tLimMlmDisassocInd mlmDisassocInd; 348 uint16_t aid; 349 tpDphHashNode sta_ds; 350 351 sta_ds = dph_lookup_hash_entry(mac_ctx, addr, &aid, 352 &pe_session->dph.dphHashTable); 353 if (sta_ds == NULL) { 354 pe_debug("Hash entry not found"); 355 return; 356 } 357 sta_ds->mlmStaContext.cleanupTrigger = eLIM_PEER_ENTITY_DISASSOC; 358 sta_ds->mlmStaContext.disassocReason = (tSirMacReasonCodes) rc; 359 360 /* Issue Disassoc Indication to SME. */ 361 qdf_mem_copy((uint8_t *) &mlmDisassocInd.peerMacAddr, 362 (uint8_t *) sta_ds->staAddr, sizeof(tSirMacAddr)); 363 mlmDisassocInd.reasonCode = 364 (uint8_t) sta_ds->mlmStaContext.disassocReason; 365 mlmDisassocInd.disassocTrigger = eLIM_PEER_ENTITY_DISASSOC; 366 367 /* Update PE session Id */ 368 mlmDisassocInd.sessionId = pe_session->peSessionId; 369 370 if (lim_is_reassoc_in_progress(mac_ctx, pe_session)) { 371 372 /* If we're in the middle of ReAssoc and received disassoc from 373 * the ReAssoc AP, then notify SME by sending REASSOC_RSP with 374 * failure result code. By design, SME will then issue "Disassoc" 375 * and cleanup will happen at that time. 376 */ 377 pe_debug("received Disassoc from AP while waiting for Reassoc Rsp"); 378 379 if (pe_session->limAssocResponseData) { 380 qdf_mem_free(pe_session->limAssocResponseData); 381 pe_session->limAssocResponseData = NULL; 382 } 383 384 lim_restore_pre_reassoc_state(mac_ctx, eSIR_SME_REASSOC_REFUSED, 385 rc, pe_session); 386 return; 387 } 388 389 lim_update_lost_link_info(mac_ctx, pe_session, frame_rssi); 390 lim_post_sme_message(mac_ctx, LIM_MLM_DISASSOC_IND, 391 (uint32_t *) &mlmDisassocInd); 392 393 /* send eWNI_SME_DISASSOC_IND to SME */ 394 lim_send_sme_disassoc_ind(mac_ctx, sta_ds, pe_session); 395 396 return; 397 } 398