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