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