xref: /wlan-dirver/qca-wifi-host-cmn/os_if/linux/mlme/src/osif_cm_connect_rsp.c (revision 97f44cd39e4ff816eaa1710279d28cf6b9e65ad9)
1 /*
2  * Copyright (c) 2012-2015, 2020 The Linux Foundation. All rights reserved.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /**
18  * DOC: osif_cm_connect_rsp.c
19  *
20  * This file maintains definitaions of connect response apis.
21  */
22 
23 #include <linux/version.h>
24 #include <linux/nl80211.h>
25 #include <net/cfg80211.h>
26 #include "wlan_osif_priv.h"
27 #include "osif_cm_rsp.h"
28 #include "osif_cm_util.h"
29 #include "wlan_cfg80211.h"
30 #include "wlan_cfg80211_scan.h"
31 
32 /**
33  * osif_validate_connect_and_reset_src_id() - Validate connect response and
34  * resets source and id
35  * @osif_priv: Pointer to vdev osif priv
36  * @cm_id: Command id received in the connect response
37  *
38  * This function validates connect response and if the connect
39  * response is valid, resets the source and id of the command
40  *
41  * Context: Any context. Takes and releases cmd id spinlock.
42  * Return: QDF_STATUS
43  */
44 static QDF_STATUS
45 osif_validate_connect_and_reset_src_id(struct vdev_osif_priv *osif_priv,
46 					    wlan_cm_id cm_id)
47 {
48 	QDF_STATUS status = QDF_STATUS_SUCCESS;
49 	/*
50 	 * Send to kernel only if last osif cmd type is connect and
51 	 * cookie match else drop. If cookie match reset the cookie
52 	 * and source.
53 	 */
54 	qdf_spinlock_acquire(&osif_priv->cm_info.cmd_id_lock);
55 	if (cm_id != osif_priv->cm_info.last_id ||
56 	    osif_priv->cm_info.last_source != CM_OSIF_CONNECT) {
57 		osif_debug("Ignore as cm_id(%d)/src(%d) didn't match stored cm_id(%d)/src(%d)",
58 			   cm_id, CM_OSIF_CONNECT,
59 			   osif_priv->cm_info.last_id,
60 			   osif_priv->cm_info.last_source);
61 		status = QDF_STATUS_E_INVAL;
62 		goto rel_lock;
63 	}
64 
65 	osif_cm_reset_id_and_src_no_lock(osif_priv);
66 rel_lock:
67 	qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock);
68 
69 	return status;
70 }
71 
72 /**
73  * osif_convert_timeout_reason() - Convert to kernel specific enum
74  * @timeout_reason: reason for connect timeout
75  *
76  * This function is used to convert host timeout
77  * reason enum to kernel specific enum.
78  *
79  * Context: Any context.
80  * Return: nl timeout enum
81  */
82 
83 static enum nl80211_timeout_reason
84 osif_convert_timeout_reason(enum wlan_cm_connect_fail_reason reason)
85 {
86 	switch (reason) {
87 	case CM_JOIN_TIMEOUT:
88 		return NL80211_TIMEOUT_SCAN;
89 	case CM_AUTH_TIMEOUT:
90 		return NL80211_TIMEOUT_AUTH;
91 	case CM_ASSOC_TIMEOUT:
92 		return NL80211_TIMEOUT_ASSOC;
93 	default:
94 		return NL80211_TIMEOUT_UNSPECIFIED;
95 	}
96 }
97 
98 #if defined CFG80211_CONNECT_BSS || \
99 	(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
100 
101 #if defined CFG80211_CONNECT_TIMEOUT_REASON_CODE || \
102 	(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0))
103 /**
104  * osif_connect_timeout() - API to send connection timeout reason
105  * @dev: network device
106  * @bssid: bssid to which we want to associate
107  * @reason: reason for connect timeout
108  *
109  * This API is used to send connection timeout reason to supplicant
110  *
111  * Context: Any context.
112  * Return: Void
113  */
114 static void
115 osif_connect_timeout(struct net_device *dev, const u8 *bssid,
116 		     enum wlan_cm_connect_fail_reason reason)
117 {
118 	enum nl80211_timeout_reason nl_timeout_reason;
119 
120 	nl_timeout_reason = osif_convert_timeout_reason(reason);
121 
122 	osif_debug("nl_timeout_reason %d", nl_timeout_reason);
123 
124 	cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL,
125 				 nl_timeout_reason);
126 }
127 
128 /**
129  * __osif_connect_bss() - API to send connection status to supplicant
130  * @dev: network device
131  * @bss: bss info
132  * @connect_rsp: Connection manager connect response
133  *
134  * Context: Any context.
135  * Return: void
136  */
137 static void __osif_connect_bss(struct net_device *dev,
138 			       struct cfg80211_bss *bss,
139 			       struct wlan_cm_connect_resp *rsp,
140 			       enum ieee80211_statuscode status)
141 {
142 	enum nl80211_timeout_reason nl_timeout_reason;
143 
144 	nl_timeout_reason = osif_convert_timeout_reason(rsp->reason);
145 
146 	osif_debug("nl_timeout_reason %d", nl_timeout_reason);
147 
148 	cfg80211_connect_bss(dev, rsp->bssid.bytes, bss,
149 			     rsp->connect_ies.assoc_req.ptr,
150 			     rsp->connect_ies.assoc_req.len,
151 			     rsp->connect_ies.assoc_rsp.ptr,
152 			     rsp->connect_ies.assoc_rsp.len,
153 			     status, GFP_KERNEL,
154 			     nl_timeout_reason);
155 }
156 #else /* CFG80211_CONNECT_TIMEOUT_REASON_CODE */
157 
158 #if defined CFG80211_CONNECT_TIMEOUT || \
159 	(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
160 static void osif_connect_timeout(
161 			struct net_device *dev,
162 			const u8 *bssid,
163 			enum wlan_cm_connect_fail_reason reason)
164 {
165 	cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL);
166 }
167 #endif
168 
169 static void __osif_connect_bss(struct net_device *dev,
170 			       struct cfg80211_bss *bss,
171 			       struct wlan_cm_connect_resp *rsp,
172 			       ieee80211_statuscode status)
173 {
174 	cfg80211_connect_bss(dev, rsp->bssid.bytes, bss,
175 			     rsp->connect_ies.assoc_req.ptr,
176 			     rsp->connect_ies.assoc_req.len,
177 			     rsp->connect_ies.assoc_rsp.ptr,
178 			     rsp->connect_ies.assoc_rsp.len,
179 			     status, GFP_KERNEL);
180 }
181 #endif /* CFG80211_CONNECT_TIMEOUT_REASON_CODE */
182 
183 /**
184  * osif_connect_bss() - API to send connection status to supplicant
185  * @dev: network device
186  * @bss: bss info
187  * @connect_rsp: Connection manager connect response
188  *
189  * The API is a wrapper to send connection status to supplicant
190  *
191  * Context: Any context.
192  * Return: Void
193  */
194 #if defined CFG80211_CONNECT_TIMEOUT || \
195 	(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))
196 static void osif_connect_bss(struct net_device *dev, struct cfg80211_bss *bss,
197 			     struct wlan_cm_connect_resp *rsp)
198 {
199 	enum ieee80211_statuscode status = WLAN_STATUS_SUCCESS;
200 
201 	osif_enter_dev(dev);
202 
203 	if (rsp->reason == CM_JOIN_TIMEOUT ||
204 	    rsp->reason == CM_AUTH_TIMEOUT ||
205 	    rsp->reason == CM_ASSOC_TIMEOUT) {
206 		osif_connect_timeout(dev, rsp->bssid.bytes,
207 				     rsp->reason);
208 	} else {
209 		if (QDF_IS_STATUS_ERROR(rsp->connect_status))
210 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
211 		__osif_connect_bss(dev, bss, rsp, status);
212 	}
213 }
214 #else /* CFG80211_CONNECT_TIMEOUT */
215 static void osif_connect_bss(struct net_device *dev, struct cfg80211_bss *bss,
216 			     struct wlan_cm_connect_resp *rsp)
217 {
218 	enum ieee80211_statuscode status = WLAN_STATUS_SUCCESS;
219 
220 	osif_enter_dev(dev);
221 
222 	if (QDF_IS_STATUS_ERROR(rsp->connect_status))
223 		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
224 
225 	__osif_connect_bss(dev, bss, rsp, status);
226 }
227 #endif /* CFG80211_CONNECT_TIMEOUT */
228 
229 #if defined(WLAN_FEATURE_FILS_SK)
230 
231 #if (defined(CFG80211_CONNECT_DONE) || \
232 	(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))) && \
233 	(LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0))
234 
235 #if defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \
236 		 (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
237 /**
238  * osif_populate_fils_params() - Populate FILS keys to connect response
239  * @conn_rsp_params: connect response to supplicant
240  * @fils_ie: Fils ie information from connection manager
241  *
242  * Context: Any context.
243  * Return: None
244  */
245 static void
246 osif_populate_fils_params(struct cfg80211_connect_resp_params *rsp_params,
247 			  struct fils_connect_rsp_params *fils_ie)
248 {
249 	/*  Increment seq number to be used for next FILS */
250 	rsp_params->fils_erp_next_seq_num = fils_ie->fils_seq_num + 1;
251 	rsp_params->update_erp_next_seq_num = true;
252 	rsp_params->fils_kek = fils_ie->kek;
253 	rsp_params->fils_kek_len = fils_ie->kek_len;
254 	rsp_params->pmk = fils_ie->fils_pmk;
255 	rsp_params->pmk_len = fils_ie->fils_pmk_len;
256 	rsp_params->pmkid = fils_ie->fils_pmkid;
257 	osif_debug("erp_next_seq_num:%d", rsp_params->fils_erp_next_seq_num);
258 }
259 #else /* CFG80211_FILS_SK_OFFLOAD_SUPPORT */
260 static inline void
261 osif_populate_fils_params(struct cfg80211_connect_resp_params *rsp_params,
262 			  struct fils_connect_rsp_params *fils_ie)
263 
264 { }
265 #endif /* CFG80211_FILS_SK_OFFLOAD_SUPPORT */
266 
267 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))
268 /**
269  * osif_populate_fils_params() - Populate FILS keys to connect response
270  * @conn_rsp_params: connect response to supplicant
271  * @fils_ie: Fils ie information from connection manager
272  *
273  * Context: Any context.
274  * Return: None
275  */
276 static void
277 osif_populate_fils_params(struct cfg80211_connect_resp_params *rsp_params,
278 			  struct fils_connect_rsp_params *fils_ie)
279 
280 {
281 	/* Increament seq number to be used for next FILS */
282 	rsp_params->fils.erp_next_seq_num = fils_ie->fils_seq_num + 1;
283 	rsp_params->fils.update_erp_next_seq_num = true;
284 	rsp_params->fils.kek = fils_ie->kek;
285 	rsp_params->fils.kek_len = fils_ie->kek_len;
286 	rsp_params->fils.pmk = fils_ie->fils_pmk;
287 	rsp_params->fils.pmk_len = fils_ie->fils_pmk_len;
288 	rsp_params->fils.pmkid = fils_ie->fils_pmkid;
289 	osif_debug("erp_next_seq_num:%d", rsp_params->fils.erp_next_seq_num);
290 }
291 #endif /* CFG80211_CONNECT_DONE */
292 
293 #if defined(CFG80211_CONNECT_DONE) || \
294 	(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
295 
296 static enum ieee80211_statuscode
297 osif_get_statuscode(enum wlan_status_code status)
298 {
299 	return (enum ieee80211_statuscode)status;
300 }
301 
302 /**
303  * osif_connect_done() - Wrapper API to call cfg80211_connect_done
304  * @dev: network device
305  * @bss: bss info
306  * @connect_rsp: Connection manager connect response
307  *
308  * This API is used as wrapper to send FILS key/sequence number
309  * params etc. to supplicant in case of FILS connection
310  *
311  * Context: Any context.
312  * Return: None
313  */
314 static void osif_connect_done(struct net_device *dev, struct cfg80211_bss *bss,
315 			      struct wlan_cm_connect_resp *rsp)
316 {
317 	struct cfg80211_connect_resp_params conn_rsp_params;
318 	enum ieee80211_statuscode status = WLAN_STATUS_SUCCESS;
319 
320 	osif_enter_dev(dev);
321 
322 	if (QDF_IS_STATUS_ERROR(rsp->connect_status)) {
323 		if (rsp->status_code)
324 			status = osif_get_statuscode(rsp->status_code);
325 		else
326 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
327 	}
328 	qdf_mem_zero(&conn_rsp_params, sizeof(conn_rsp_params));
329 
330 	if (!rsp->connect_ies.fils_ie) {
331 		conn_rsp_params.status = WLAN_STATUS_UNSPECIFIED_FAILURE;
332 	} else {
333 		conn_rsp_params.status = status;
334 		conn_rsp_params.bssid = rsp->bssid.bytes;
335 		conn_rsp_params.timeout_reason =
336 			osif_convert_timeout_reason(rsp->reason);
337 		conn_rsp_params.req_ie = rsp->connect_ies.assoc_req.ptr;
338 		conn_rsp_params.req_ie_len = rsp->connect_ies.assoc_req.len;
339 		conn_rsp_params.resp_ie = rsp->connect_ies.assoc_rsp.ptr;
340 		conn_rsp_params.resp_ie_len = rsp->connect_ies.assoc_rsp.len;
341 		conn_rsp_params.bss = bss;
342 		osif_populate_fils_params(&conn_rsp_params,
343 					  rsp->connect_ies.fils_ie);
344 		/* save GTK */
345 	}
346 
347 	cfg80211_connect_done(dev, &conn_rsp_params, GFP_KERNEL);
348 	/* hlp data for DHCP */
349 }
350 #else /* CFG80211_CONNECT_DONE */
351 static inline void
352 osif_connect_done(struct net_device *dev, struct cfg80211_bss *bss,
353 		  struct wlan_cm_connect_resp *rsp)
354 { }
355 #endif /* CFG80211_CONNECT_DONE */
356 #endif /* WLAN_FEATURE_FILS_SK */
357 
358 #if defined(WLAN_FEATURE_FILS_SK) && \
359 	(defined(CFG80211_CONNECT_DONE) || \
360 	(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)))
361 /**
362  * osif_fils_update_connect_results() - API to send fils connection status to
363  * supplicant.
364  * @dev: network device
365  * @bss: bss info
366  * @connect_rsp: Connection manager connect response
367  *
368  * The API is a wrapper to send connection status to supplicant
369  *
370  * Context: Any context.
371  * Return: 0 if success else failure
372  */
373 static int osif_update_connect_results(struct net_device *dev,
374 				       struct cfg80211_bss *bss,
375 				       struct wlan_cm_connect_resp *rsp)
376 {
377 	if (!rsp->is_fils_connection) {
378 		osif_debug("fils IE is NULL");
379 		return -EINVAL;
380 	}
381 	osif_connect_done(dev, bss, rsp);
382 
383 	return 0;
384 }
385 #else /* WLAN_FEATURE_FILS_SK && CFG80211_CONNECT_DONE */
386 
387 static inline int osif_update_connect_results(struct net_device *dev,
388 					      struct cfg80211_bss *bss,
389 					      struct wlan_cm_connect_resp *rsp)
390 {
391 	return -EINVAL;
392 }
393 #endif /* WLAN_FEATURE_FILS_SK && CFG80211_CONNECT_DONE */
394 
395 static void osif_indcate_connect_results(struct wlan_objmgr_vdev *vdev,
396 					 struct vdev_osif_priv *osif_priv,
397 					 struct wlan_cm_connect_resp *rsp)
398 {
399 	struct cfg80211_bss *bss = NULL;
400 	struct ieee80211_channel *chan;
401 
402 	if (QDF_IS_STATUS_SUCCESS(rsp->connect_status)) {
403 		chan = ieee80211_get_channel(osif_priv->wdev->wiphy,
404 					     rsp->freq);
405 		bss = wlan_cfg80211_get_bss(osif_priv->wdev->wiphy, chan,
406 					    rsp->bssid.bytes,
407 					    rsp->ssid.ssid,
408 					    rsp->ssid.length);
409 	}
410 
411 	if (osif_update_connect_results(osif_priv->wdev->netdev, bss, rsp))
412 		osif_connect_bss(osif_priv->wdev->netdev, bss, rsp);
413 }
414 #else  /* CFG80211_CONNECT_BSS */
415 static void osif_indcate_connect_results(struct wlan_objmgr_vdev *vdev,
416 					 struct vdev_osif_priv *osif_priv,
417 					 struct wlan_cm_connect_resp *rsp)
418 {
419 	enum ieee80211_statuscode status = WLAN_STATUS_SUCCESS;
420 
421 	if (QDF_IS_STATUS_ERROR(rsp->connect_status)) {
422 		if (rsp->status_code)
423 			status = rsp->status_code;
424 		else
425 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
426 	}
427 
428 	cfg80211_connect_result(osif_priv->wdev->netdev,
429 				rsp->bssid.bytes,
430 				rsp->connect_ies.assoc_req.ptr,
431 				rsp->connect_ies.assoc_req.len,
432 				rsp->connect_ies.assoc_rsp.ptr,
433 				rsp->connect_ies.assoc_rsp.len,
434 				status, GFP_KERNEL);
435 }
436 #endif /* CFG80211_CONNECT_BSS */
437 
438 static inline
439 bool osif_cm_is_unlink_bss_required(enum wlan_cm_connect_fail_reason reason)
440 {
441 	if (reason == CM_NO_CANDIDATE_FOUND ||
442 	    reason == CM_JOIN_TIMEOUT ||
443 	    reason == CM_AUTH_TIMEOUT ||
444 	    reason == CM_ASSOC_TIMEOUT)
445 		return true;
446 
447 	return false;
448 }
449 
450 QDF_STATUS osif_connect_handler(struct wlan_objmgr_vdev *vdev,
451 				struct wlan_cm_connect_resp *rsp)
452 {
453 	struct vdev_osif_priv *osif_priv  = wlan_vdev_get_ospriv(vdev);
454 	QDF_STATUS status;
455 
456 	osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " Connect with " QDF_MAC_ADDR_FMT " SSID \"%.*s\" is %s cm_id %d cm_reason %d status_code %d",
457 		       osif_priv->wdev->netdev->name, rsp->vdev_id,
458 		       QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
459 		       QDF_MAC_ADDR_REF(rsp->bssid.bytes),
460 		       rsp->ssid.length, rsp->ssid.ssid,
461 		       rsp->connect_status ? "FAILURE" : "SUCCESS", rsp->cm_id,
462 		       rsp->reason, rsp->status_code);
463 
464 	if (osif_cm_is_unlink_bss_required(rsp->reason))
465 		osif_cm_unlink_bss(vdev, osif_priv, &rsp->bssid, rsp->ssid.ssid,
466 				   rsp->ssid.length);
467 
468 	status = osif_validate_connect_and_reset_src_id(osif_priv, rsp->cm_id);
469 	if (QDF_IS_STATUS_ERROR(status)) {
470 		osif_cm_connect_comp_ind(vdev, rsp, OSIF_NOT_HANDLED);
471 		return status;
472 	}
473 
474 	osif_cm_connect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE);
475 	osif_indcate_connect_results(vdev, osif_priv, rsp);
476 	osif_cm_connect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE);
477 
478 	return QDF_STATUS_SUCCESS;
479 }
480 
481 QDF_STATUS osif_failed_candidate_handler(struct wlan_objmgr_vdev *vdev,
482 					 struct wlan_cm_connect_resp *rsp)
483 {
484 	struct vdev_osif_priv *osif_priv  = wlan_vdev_get_ospriv(vdev);
485 
486 	osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " Connect with " QDF_MAC_ADDR_FMT " SSID \"%.*s\" FAILED cm_id %d cm_reason %d reason_code %d",
487 		       osif_priv->wdev->netdev->name, rsp->vdev_id,
488 		       QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
489 		       QDF_MAC_ADDR_REF(rsp->bssid.bytes),
490 		       rsp->ssid.length, rsp->ssid.ssid, rsp->cm_id,
491 		       rsp->reason, rsp->status_code);
492 
493 	if (osif_cm_is_unlink_bss_required(rsp->reason))
494 		osif_cm_unlink_bss(vdev, osif_priv, &rsp->bssid, rsp->ssid.ssid,
495 				   rsp->ssid.length);
496 	return QDF_STATUS_SUCCESS;
497 }
498