xref: /wlan-dirver/qca-wifi-host-cmn/os_if/linux/mlme/src/osif_cm_req.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_req.c
19  *
20  * This file maintains definitaions of connect, disconnect, roam
21  * request apis.
22  */
23 
24 #include "wlan_osif_priv.h"
25 #include "osif_cm_req.h"
26 #include "wlan_cm_ucfg_api.h"
27 #include "wlan_nl_to_crypto_params.h"
28 #include <wlan_cfg80211.h>
29 #include "osif_cm_util.h"
30 
31 static void osif_cm_free_wep_key_params(struct wlan_cm_connect_req *connect_req)
32 {
33 	if (connect_req->crypto.wep_keys.key) {
34 		qdf_mem_zero(connect_req->crypto.wep_keys.key,
35 			     connect_req->crypto.wep_keys.key_len);
36 		qdf_mem_free(connect_req->crypto.wep_keys.key);
37 		connect_req->crypto.wep_keys.key = NULL;
38 	}
39 	if (connect_req->crypto.wep_keys.seq) {
40 		qdf_mem_zero(connect_req->crypto.wep_keys.seq,
41 			     connect_req->crypto.wep_keys.seq_len);
42 		qdf_mem_free(connect_req->crypto.wep_keys.seq);
43 		connect_req->crypto.wep_keys.seq = NULL;
44 	}
45 }
46 
47 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
48 static QDF_STATUS
49 osif_cm_update_wep_seq_info(struct wlan_cm_connect_req *connect_req,
50 			    const struct cfg80211_connect_params *req)
51 {
52 	if (req->crypto.wep_keys->seq_len) {
53 		connect_req->crypto.wep_keys.seq_len =
54 						req->crypto.wep_keys->seq_len;
55 		connect_req->crypto.wep_keys.seq =
56 			qdf_mem_malloc(connect_req->crypto.wep_keys.seq_len);
57 		if (!connect_req->crypto.wep_keys.seq) {
58 			osif_cm_free_wep_key_params(connect_req);
59 			return QDF_STATUS_E_NOMEM;
60 		}
61 		qdf_mem_copy(connect_req->crypto.wep_keys.seq,
62 			     req->crypto.wep_keys->seq,
63 			     connect_req->crypto.wep_keys.seq_len);
64 	}
65 	return QDF_STATUS_SUCCESS;
66 }
67 #else
68 static inline QDF_STATUS
69 osif_cm_update_wep_seq_info(struct wlan_cm_connect_req *connect_req,
70 			    const struct cfg80211_connect_params *req)
71 {
72 	return QDF_STATUS_SUCCESS;
73 }
74 #endif
75 
76 static QDF_STATUS
77 osif_cm_set_wep_key_params(struct wlan_cm_connect_req *connect_req,
78 			   const struct cfg80211_connect_params *req)
79 {
80 	if (!req->key_len)
81 		return QDF_STATUS_SUCCESS;
82 
83 	connect_req->crypto.wep_keys.key_len = req->key_len;
84 	connect_req->crypto.wep_keys.key_idx = req->key_idx;
85 
86 	connect_req->crypto.wep_keys.key =
87 			qdf_mem_malloc(connect_req->crypto.wep_keys.key_len);
88 	if (!connect_req->crypto.wep_keys.key)
89 		return QDF_STATUS_E_NOMEM;
90 
91 	qdf_mem_copy(connect_req->crypto.wep_keys.key, req->key,
92 		     connect_req->crypto.wep_keys.key_len);
93 
94 	return osif_cm_update_wep_seq_info(connect_req, req);
95 }
96 
97 static void osif_cm_set_auth_type(struct wlan_cm_connect_req *connect_req,
98 				  const struct cfg80211_connect_params *req)
99 {
100 	wlan_crypto_auth_mode crypto_auth_type =
101 			osif_nl_to_crypto_auth_type(req->auth_type);
102 
103 	/* For auto check wpa version to decide WPA or RSNA */
104 	if (crypto_auth_type == WLAN_CRYPTO_AUTH_AUTO &&
105 	    req->crypto.wpa_versions) {
106 		if (req->crypto.wpa_versions & NL80211_WPA_VERSION_1)
107 			crypto_auth_type = WLAN_CRYPTO_AUTH_WPA;
108 		else
109 			crypto_auth_type = WLAN_CRYPTO_AUTH_RSNA;
110 	} else if (!req->crypto.n_ciphers_pairwise) {
111 		crypto_auth_type = WLAN_CRYPTO_AUTH_OPEN;
112 	}
113 
114 	QDF_SET_PARAM(connect_req->crypto.auth_type, crypto_auth_type);
115 }
116 
117 static
118 QDF_STATUS osif_cm_set_crypto_params(struct wlan_cm_connect_req *connect_req,
119 				     const struct cfg80211_connect_params *req)
120 {
121 	uint32_t i = 0;
122 	QDF_STATUS status;
123 	wlan_crypto_cipher_type cipher = WLAN_CRYPTO_CIPHER_NONE;
124 	wlan_crypto_key_mgmt akm;
125 
126 	connect_req->crypto.wpa_versions = req->crypto.wpa_versions;
127 
128 	osif_cm_set_auth_type(connect_req, req);
129 
130 	if (req->crypto.cipher_group)
131 		cipher = osif_nl_to_crypto_cipher_type(cipher);
132 
133 	QDF_SET_PARAM(connect_req->crypto.group_cipher, cipher);
134 
135 	/* Fill Pairwise ciphers */
136 	if (req->crypto.n_ciphers_pairwise) {
137 		for (i = 0; i < req->crypto.n_ciphers_pairwise &&
138 		     i < NL80211_MAX_NR_CIPHER_SUITES; i++) {
139 			cipher = osif_nl_to_crypto_cipher_type(
140 					req->crypto.ciphers_pairwise[i]);
141 			QDF_SET_PARAM(connect_req->crypto.ciphers_pairwise,
142 				      cipher);
143 		}
144 	} else {
145 		QDF_SET_PARAM(connect_req->crypto.ciphers_pairwise,
146 			      WLAN_CRYPTO_CIPHER_NONE);
147 	}
148 
149 	/* Fill AKM suites */
150 	if (req->crypto.n_akm_suites) {
151 		for (i = 0; i < req->crypto.n_akm_suites &&
152 		     i < NL80211_MAX_NR_AKM_SUITES; i++) {
153 			akm = osif_nl_to_crypto_akm_type(
154 					req->crypto.akm_suites[i]);
155 			QDF_SET_PARAM(connect_req->crypto.akm_suites, akm);
156 		}
157 	} else {
158 		QDF_SET_PARAM(connect_req->crypto.akm_suites,
159 			      WLAN_CRYPTO_KEY_MGMT_NONE);
160 	}
161 
162 	/* Fill WEP Key information */
163 	status = osif_cm_set_wep_key_params(connect_req, req);
164 	if (QDF_IS_STATUS_ERROR(status))
165 		osif_err("set wep key params failed");
166 
167 	return status;
168 }
169 
170 #ifdef WLAN_FEATURE_FILS_SK
171 static bool osif_cm_is_fils_auth_type(enum nl80211_auth_type auth_type)
172 {
173 	switch (auth_type) {
174 	case NL80211_AUTHTYPE_FILS_SK:
175 	case NL80211_AUTHTYPE_FILS_SK_PFS:
176 	case NL80211_AUTHTYPE_FILS_PK:
177 		return true;
178 	default:
179 		return false;
180 	}
181 }
182 
183 static bool osif_cm_is_akm_suite_fils(uint32_t key_mgmt)
184 {
185 	switch (key_mgmt) {
186 	case WLAN_AKM_SUITE_FILS_SHA256:
187 	case WLAN_AKM_SUITE_FILS_SHA384:
188 	case WLAN_AKM_SUITE_FT_FILS_SHA256:
189 	case WLAN_AKM_SUITE_FT_FILS_SHA384:
190 		return true;
191 	default:
192 		return false;
193 	}
194 }
195 
196 static bool osif_cm_is_conn_type_fils(const struct cfg80211_connect_params *req)
197 {
198 	int num_akm_suites = req->crypto.n_akm_suites;
199 	uint32_t key_mgmt = req->crypto.akm_suites[0];
200 	bool is_fils_auth_type =
201 		osif_cm_is_fils_auth_type(req->auth_type);
202 
203 	if (num_akm_suites <= 0)
204 		return false;
205 
206 	/*
207 	 * Auth type will be either be OPEN or FILS type for a FILS connection
208 	 */
209 	if (!is_fils_auth_type &&
210 	    req->auth_type != NL80211_AUTHTYPE_OPEN_SYSTEM)
211 		return false;
212 
213 	if (!osif_cm_is_akm_suite_fils(key_mgmt))
214 		return false;
215 
216 	osif_debug("Fils Auth %d AKM %d", req->auth_type, key_mgmt);
217 
218 	return true;
219 }
220 
221 static QDF_STATUS
222 osif_cm_set_fils_info(struct wlan_cm_connect_req *connect_req,
223 		      const struct cfg80211_connect_params *req)
224 {
225 	connect_req->fils_info.is_fils_connection =
226 					osif_cm_is_conn_type_fils(req);
227 	connect_req->fils_info.username_len = req->fils_erp_username_len;
228 
229 	if (connect_req->fils_info.username_len >
230 					WLAN_CM_FILS_MAX_KEYNAME_NAI_LENGTH) {
231 		osif_err("Invalid fils username len");
232 		return QDF_STATUS_E_INVAL;
233 	}
234 	qdf_mem_zero(connect_req->fils_info.username,
235 		     WLAN_CM_FILS_MAX_KEYNAME_NAI_LENGTH);
236 	qdf_mem_copy(connect_req->fils_info.username, req->fils_erp_username,
237 		     connect_req->fils_info.username_len);
238 
239 	connect_req->fils_info.realm_len = req->fils_erp_username_len;
240 
241 	if (connect_req->fils_info.realm_len > WLAN_CM_FILS_MAX_REALM_LEN) {
242 		osif_err("Invalid fils realm len");
243 		return QDF_STATUS_E_INVAL;
244 	}
245 	qdf_mem_zero(connect_req->fils_info.realm, WLAN_CM_FILS_MAX_REALM_LEN);
246 	qdf_mem_copy(connect_req->fils_info.realm, req->fils_erp_realm,
247 		     connect_req->fils_info.realm_len);
248 
249 	connect_req->fils_info.next_seq_num = req->fils_erp_next_seq_num;
250 
251 	connect_req->fils_info.rrk_len = req->fils_erp_rrk_len;
252 
253 	if (connect_req->fils_info.rrk_len > WLAN_CM_FILS_MAX_RRK_LENGTH) {
254 		osif_err("Invalid fils rrk len");
255 		return QDF_STATUS_E_INVAL;
256 	}
257 	qdf_mem_zero(connect_req->fils_info.rrk, WLAN_CM_FILS_MAX_RRK_LENGTH);
258 	qdf_mem_copy(connect_req->fils_info.rrk, req->fils_erp_rrk,
259 		     connect_req->fils_info.rrk_len);
260 
261 	return QDF_STATUS_SUCCESS;
262 }
263 #else
264 static inline
265 QDF_STATUS osif_cm_set_fils_info(struct wlan_cm_connect_req *connect_req,
266 				 const struct cfg80211_connect_params *req)
267 {
268 	return QDF_STATUS_SUCCESS;
269 }
270 #endif
271 
272 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
273 static inline void
274 osif_cm_set_prev_bssid(struct wlan_cm_connect_req *connect_req,
275 		       const struct cfg80211_connect_params *req)
276 {
277 	qdf_mem_copy(connect_req->prev_bssid.bytes, req->prev_bssid,
278 		     QDF_MAC_ADDR_SIZE);
279 }
280 
281 static inline
282 void osif_cm_dump_prev_bssid(const struct cfg80211_connect_params *req)
283 {
284 	if (req->prev_bssid)
285 		osif_nofl_debug("prev BSSID "QDF_MAC_ADDR_FMT,
286 				QDF_MAC_ADDR_REF(req->prev_bssid));
287 }
288 
289 #else
290 static inline void
291 osif_cm_set_prev_bssid(struct wlan_cm_connect_req *connect_req,
292 		       const struct cfg80211_connect_params *req)
293 {
294 }
295 
296 static inline
297 void osif_cm_dump_prev_bssid(const struct cfg80211_connect_params *req)
298 {
299 }
300 
301 #endif
302 
303 static inline void
304 osif_cm_dump_connect_req(struct net_device *dev, uint8_t vdev_id,
305 			 const struct cfg80211_connect_params *req)
306 {
307 	uint32_t i;
308 
309 	osif_nofl_debug("connect req for %s(vdevid-%d) freq %d SSID %.*s auth type %d WPA ver %d n_akm %d n_cipher %d grp_cipher %x mfp %d freq hint %d",
310 			dev->name, vdev_id,
311 			req->channel ? req->channel->center_freq : 0,
312 			(int)req->ssid_len, req->ssid, req->auth_type,
313 			req->crypto.wpa_versions,
314 			req->crypto.n_akm_suites,
315 			req->crypto.n_ciphers_pairwise,
316 			req->crypto.cipher_group, req->mfp,
317 			req->channel_hint ? req->channel_hint->center_freq : 0);
318 	if (req->bssid)
319 		osif_nofl_debug("BSSID "QDF_MAC_ADDR_FMT,
320 				QDF_MAC_ADDR_REF(req->bssid));
321 	if (req->bssid_hint)
322 		osif_nofl_debug("BSSID hint "QDF_MAC_ADDR_FMT,
323 				QDF_MAC_ADDR_REF(req->bssid_hint));
324 	osif_cm_dump_prev_bssid(req);
325 
326 	for (i = 0; i < req->crypto.n_akm_suites; i++)
327 		osif_nofl_debug("akm[%d] = %x", i, req->crypto.akm_suites[i]);
328 
329 	for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
330 		osif_nofl_debug("cipher_pairwise[%d] = %x", i,
331 				req->crypto.ciphers_pairwise[i]);
332 }
333 
334 static void
335 osif_cm_fill_connect_params(struct wlan_cm_connect_req *req,
336 			    const struct osif_connect_params *params)
337 {
338 	if (!params)
339 		return;
340 
341 	if (params->scan_ie.len) {
342 		req->scan_ie.ptr = qdf_mem_malloc(params->scan_ie.len);
343 		if (req->scan_ie.ptr) {
344 			qdf_mem_copy(req->scan_ie.ptr, params->scan_ie.ptr,
345 				     params->scan_ie.len);
346 			req->scan_ie.len = params->scan_ie.len;
347 		}
348 	}
349 	req->dot11mode_filter = params->dot11mode_filter;
350 	req->force_rsne_override = params->force_rsne_override;
351 	req->sae_pwe = params->sae_pwe;
352 }
353 
354 static void osif_cm_free_connect_req(struct wlan_cm_connect_req *connect_req)
355 {
356 	if (connect_req->scan_ie.ptr) {
357 		qdf_mem_free(connect_req->scan_ie.ptr);
358 		connect_req->assoc_ie.ptr = NULL;
359 	}
360 
361 	if (connect_req->assoc_ie.ptr) {
362 		qdf_mem_free(connect_req->assoc_ie.ptr);
363 		connect_req->assoc_ie.ptr = NULL;
364 	}
365 
366 	osif_cm_free_wep_key_params(connect_req);
367 	qdf_mem_free(connect_req);
368 }
369 
370 int osif_cm_connect(struct net_device *dev, struct wlan_objmgr_vdev *vdev,
371 		    const struct cfg80211_connect_params *req,
372 		    const struct osif_connect_params *params)
373 {
374 	struct wlan_cm_connect_req *connect_req;
375 	const u8 *bssid_hint = req->bssid_hint;
376 	uint8_t vdev_id = vdev->vdev_objmgr.vdev_id;
377 	QDF_STATUS status;
378 	struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT;
379 	struct wlan_objmgr_vdev *temp_vdev;
380 
381 	if (req->bssid)
382 		qdf_mem_copy(bssid.bytes, req->bssid,
383 			     QDF_MAC_ADDR_SIZE);
384 	else if (bssid_hint)
385 		qdf_mem_copy(bssid.bytes, req->bssid_hint,
386 			     QDF_MAC_ADDR_SIZE);
387 
388 	temp_vdev = wlan_objmgr_get_vdev_by_macaddr_from_pdev(
389 						wlan_vdev_get_pdev(vdev),
390 						bssid.bytes,
391 						WLAN_OSIF_CM_ID);
392 
393 	if (temp_vdev) {
394 		osif_err("vdev %d already exist with same mac address"
395 			 QDF_MAC_ADDR_FMT, wlan_vdev_get_id(temp_vdev),
396 			 QDF_MAC_ADDR_REF(bssid.bytes));
397 		wlan_objmgr_vdev_release_ref(temp_vdev, WLAN_OSIF_CM_ID);
398 		return -EINVAL;
399 	}
400 	osif_cm_dump_connect_req(dev, vdev_id, req);
401 
402 	status = osif_cm_reset_id_and_src(vdev);
403 	if (QDF_IS_STATUS_ERROR(status))
404 		return qdf_status_to_os_return(status);
405 
406 	connect_req = qdf_mem_malloc(sizeof(*connect_req));
407 	if (!connect_req)
408 		return -ENOMEM;
409 
410 	connect_req->vdev_id = vdev_id;
411 	connect_req->source = CM_OSIF_CONNECT;
412 	if (req->bssid)
413 		qdf_mem_copy(connect_req->bssid.bytes, req->bssid,
414 			     QDF_MAC_ADDR_SIZE);
415 	else if (bssid_hint)
416 		qdf_mem_copy(connect_req->bssid_hint.bytes, req->bssid_hint,
417 			     QDF_MAC_ADDR_SIZE);
418 
419 	osif_cm_set_prev_bssid(connect_req, req);
420 
421 	connect_req->ssid.length = req->ssid_len;
422 	if (connect_req->ssid.length > WLAN_SSID_MAX_LEN) {
423 		osif_err("Invalid ssid len %zu", req->ssid_len);
424 		osif_cm_free_connect_req(connect_req);
425 		return -EINVAL;
426 	}
427 
428 	qdf_mem_copy(connect_req->ssid.ssid, req->ssid,
429 		     connect_req->ssid.length);
430 
431 	if (req->channel)
432 		connect_req->chan_freq = req->channel->center_freq;
433 	else
434 		connect_req->chan_freq = 0;
435 
436 	status = osif_cm_set_crypto_params(connect_req, req);
437 	if (QDF_IS_STATUS_ERROR(status))
438 		goto connect_start_fail;
439 
440 	connect_req->ht_caps = req->ht_capa.cap_info;
441 	connect_req->ht_caps_mask = req->ht_capa_mask.cap_info;
442 	connect_req->vht_caps = req->vht_capa.vht_cap_info;
443 	connect_req->vht_caps_mask = req->vht_capa_mask.vht_cap_info;
444 
445 	/* Copy complete ie */
446 	connect_req->assoc_ie.len = req->ie_len;
447 	connect_req->assoc_ie.ptr = qdf_mem_malloc(req->ie_len);
448 	if (!connect_req->assoc_ie.ptr) {
449 		connect_req->assoc_ie.len = 0;
450 		status = QDF_STATUS_E_NOMEM;
451 		goto connect_start_fail;
452 	}
453 	qdf_mem_copy(connect_req->assoc_ie.ptr, req->ie,
454 		     connect_req->assoc_ie.len);
455 
456 	status = osif_cm_set_fils_info(connect_req, req);
457 	if (QDF_IS_STATUS_ERROR(status))
458 		goto connect_start_fail;
459 
460 	osif_cm_fill_connect_params(connect_req, params);
461 
462 	status = ucfg_cm_start_connect(vdev, connect_req);
463 	if (QDF_IS_STATUS_ERROR(status))
464 		osif_err("Connect failed with status %d", status);
465 
466 connect_start_fail:
467 	osif_cm_free_connect_req(connect_req);
468 
469 	return qdf_status_to_os_return(status);
470 }
471 
472 static QDF_STATUS osif_cm_send_disconnect(struct wlan_objmgr_vdev *vdev,
473 					  uint16_t reason)
474 {
475 	QDF_STATUS status;
476 	struct wlan_cm_disconnect_req *req;
477 
478 	status = osif_cm_reset_id_and_src(vdev);
479 	if (QDF_IS_STATUS_ERROR(status))
480 		return qdf_status_to_os_return(status);
481 
482 	req = qdf_mem_malloc(sizeof(*req));
483 	if (!req)
484 		return QDF_STATUS_E_NOMEM;
485 
486 	req->vdev_id = wlan_vdev_get_id(vdev);
487 	req->source = CM_OSIF_DISCONNECT;
488 	req->reason_code = reason;
489 	status = ucfg_cm_start_disconnect(vdev, req);
490 	qdf_mem_free(req);
491 
492 	return status;
493 }
494 
495 int osif_cm_disconnect(struct net_device *dev, struct wlan_objmgr_vdev *vdev,
496 		       uint16_t reason)
497 {
498 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
499 	QDF_STATUS status;
500 
501 	osif_info("%s(vdevid-%d): Received Disconnect reason:%d %s",
502 		  dev->name, vdev_id, reason,
503 		  ucfg_cm_reason_code_to_str(reason));
504 
505 	status = osif_cm_send_disconnect(vdev, reason);
506 	if (QDF_IS_STATUS_ERROR(status))
507 		osif_err("Disconnect failed with status %d", status);
508 
509 	return qdf_status_to_os_return(status);
510 }
511 
512 int osif_cm_disconnect_sync(struct wlan_objmgr_vdev *vdev, uint16_t reason)
513 {
514 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
515 	QDF_STATUS status;
516 
517 	osif_info("vdevid-%d: Received Disconnect reason:%d %s",
518 		  vdev_id, reason, ucfg_cm_reason_code_to_str(reason));
519 
520 	status = ucfg_cm_disconnect_sync(vdev, CM_OSIF_DISCONNECT, reason);
521 
522 	return qdf_status_to_os_return(status);
523 }
524