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