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