xref: /wlan-dirver/qcacld-3.0/core/mac/src/pe/lim/lim_process_disassoc_frame.c (revision eff16d956b6c25bc860fac91ea57d737c47dd7a7)
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