Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cfg80211: Indicate MLO connection info in connect and roam callbacks

The MLO links used for connection with an MLD AP are decided by the
driver in case of SME offloaded to driver.

Add support for the drivers to indicate the information of links used
for MLO connection in connect and roam callbacks, update the connected
links information in wdev from connect/roam result sent by driver.
Also, send the connected links information to userspace.

Add a netlink flag attribute to indicate that userspace supports
handling of MLO connection. Drivers must not do MLO connection when this
flag is not set. This is to maintain backwards compatibility with older
supplicant versions which doesn't have support for MLO connection.

Signed-off-by: Veerendranath Jakkam <quic_vjakkam@quicinc.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Veerendranath Jakkam and committed by
Johannes Berg
efbabc11 245e5ebc

+481 -149
+1 -1
drivers/net/wireless/ath/ath6kl/cfg80211.c
··· 807 807 cfg80211_put_bss(ar->wiphy, bss); 808 808 } else if (vif->sme_state == SME_CONNECTED) { 809 809 struct cfg80211_roam_info roam_info = { 810 - .bss = bss, 810 + .links[0].bss = bss, 811 811 .req_ie = assoc_req_ie, 812 812 .req_ie_len = assoc_req_len, 813 813 .resp_ie = assoc_resp_ie,
+2 -2
drivers/net/wireless/ath/wil6210/wmi.c
··· 1822 1822 freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ); 1823 1823 1824 1824 memset(&info, 0, sizeof(info)); 1825 - info.channel = ieee80211_get_channel(wiphy, freq); 1826 - info.bss = vif->bss; 1825 + info.links[0].channel = ieee80211_get_channel(wiphy, freq); 1826 + info.links[0].bss = vif->bss; 1827 1827 info.req_ie = assoc_req_ie; 1828 1828 info.req_ie_len = assoc_req_ie_len; 1829 1829 info.resp_ie = assoc_resp_ie;
+3 -3
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
··· 6017 6017 done: 6018 6018 kfree(buf); 6019 6019 6020 - roam_info.channel = notify_channel; 6021 - roam_info.bssid = profile->bssid; 6020 + roam_info.links[0].channel = notify_channel; 6021 + roam_info.links[0].bssid = profile->bssid; 6022 6022 roam_info.req_ie = conn_info->req_ie; 6023 6023 roam_info.req_ie_len = conn_info->req_ie_len; 6024 6024 roam_info.resp_ie = conn_info->resp_ie; ··· 6061 6061 } else { 6062 6062 conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; 6063 6063 } 6064 - conn_params.bssid = profile->bssid; 6064 + conn_params.links[0].bssid = profile->bssid; 6065 6065 conn_params.req_ie = conn_info->req_ie; 6066 6066 conn_params.req_ie_len = conn_info->req_ie_len; 6067 6067 conn_params.resp_ie = conn_info->resp_ie;
+3 -2
drivers/net/wireless/rndis_wlan.c
··· 2813 2813 resp_ie_len, 0, GFP_KERNEL); 2814 2814 } else { 2815 2815 struct cfg80211_roam_info roam_info = { 2816 - .channel = get_current_channel(usbdev, NULL), 2817 - .bssid = bssid, 2816 + .links[0].channel = 2817 + get_current_channel(usbdev, NULL), 2818 + .links[0].bssid = bssid, 2818 2819 .req_ie = req_ie, 2819 2820 .req_ie_len = req_ie_len, 2820 2821 .resp_ie = resp_ie,
+2 -2
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
··· 450 450 451 451 notify_channel = ieee80211_get_channel(wiphy, freq); 452 452 453 - roam_info.channel = notify_channel; 454 - roam_info.bssid = cur_network->network.mac_address; 453 + roam_info.links[0].channel = notify_channel; 454 + roam_info.links[0].bssid = cur_network->network.mac_address; 455 455 roam_info.req_ie = 456 456 pmlmepriv->assoc_req+sizeof(struct ieee80211_hdr_3addr)+2; 457 457 roam_info.req_ie_len =
+1 -1
drivers/staging/wlan-ng/cfg80211.c
··· 645 645 void prism2_roamed(struct wlandevice *wlandev) 646 646 { 647 647 struct cfg80211_roam_info roam_info = { 648 - .bssid = wlandev->bssid, 648 + .links[0].bssid = wlandev->bssid, 649 649 }; 650 650 651 651 cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL);
+61 -23
include/net/cfg80211.h
··· 2763 2763 * request (connect callback). 2764 2764 * @ASSOC_REQ_DISABLE_HE: Disable HE 2765 2765 * @ASSOC_REQ_DISABLE_EHT: Disable EHT 2766 + * @CONNECT_REQ_MLO_SUPPORT: Userspace indicates support for handling MLD links. 2767 + * Drivers shall disable MLO features for the current association if this 2768 + * flag is not set. 2766 2769 */ 2767 2770 enum cfg80211_assoc_req_flags { 2768 2771 ASSOC_REQ_DISABLE_HT = BIT(0), ··· 2774 2771 CONNECT_REQ_EXTERNAL_AUTH_SUPPORT = BIT(3), 2775 2772 ASSOC_REQ_DISABLE_HE = BIT(4), 2776 2773 ASSOC_REQ_DISABLE_EHT = BIT(5), 2774 + CONNECT_REQ_MLO_SUPPORT = BIT(6), 2777 2775 }; 2778 2776 2779 2777 /** ··· 5784 5780 !(wdev->valid_links & BIT(link_id))); 5785 5781 } 5786 5782 5787 - #define for_each_valid_link(wdev, link_id) \ 5788 - for (link_id = 0; \ 5789 - link_id < ((wdev)->valid_links ? ARRAY_SIZE((wdev)->links) : 1); \ 5790 - link_id++) \ 5791 - if (!(wdev)->valid_links || \ 5792 - ((wdev)->valid_links & BIT(link_id))) 5783 + #define for_each_valid_link(link_info, link_id) \ 5784 + for (link_id = 0; \ 5785 + link_id < ((link_info)->valid_links ? \ 5786 + ARRAY_SIZE((link_info)->links) : 1); \ 5787 + link_id++) \ 5788 + if (!(link_info)->valid_links || \ 5789 + ((link_info)->valid_links & BIT(link_id))) 5793 5790 5794 5791 /** 5795 5792 * DOC: Utility functions ··· 7301 7296 * indicate that this is a failure, but without a status code. 7302 7297 * @timeout_reason is used to report the reason for the timeout in that 7303 7298 * case. 7304 - * @bssid: The BSSID of the AP (may be %NULL) 7305 - * @bss: Entry of bss to which STA got connected to, can be obtained through 7306 - * cfg80211_get_bss() (may be %NULL). But it is recommended to store the 7307 - * bss from the connect_request and hold a reference to it and return 7308 - * through this param to avoid a warning if the bss is expired during the 7309 - * connection, esp. for those drivers implementing connect op. 7310 - * Only one parameter among @bssid and @bss needs to be specified. 7311 7299 * @req_ie: Association request IEs (may be %NULL) 7312 7300 * @req_ie_len: Association request IEs length 7313 7301 * @resp_ie: Association response IEs (may be %NULL) ··· 7312 7314 * not known. This value is used only if @status < 0 to indicate that the 7313 7315 * failure is due to a timeout and not due to explicit rejection by the AP. 7314 7316 * This value is ignored in other cases (@status >= 0). 7317 + * @valid_links: For MLO connection, BIT mask of the valid link ids. Otherwise 7318 + * zero. 7319 + * @ap_mld_addr: For MLO connection, MLD address of the AP. Otherwise %NULL. 7320 + * @links : For MLO connection, contains link info for the valid links indicated 7321 + * using @valid_links. For non-MLO connection, links[0] contains the 7322 + * connected AP info. 7323 + * @links.addr: For MLO connection, MAC address of the STA link. Otherwise 7324 + * %NULL. 7325 + * @links.bssid: For MLO connection, MAC address of the AP link. For non-MLO 7326 + * connection, links[0].bssid points to the BSSID of the AP (may be %NULL). 7327 + * @links.bss: For MLO connection, entry of bss to which STA link is connected. 7328 + * For non-MLO connection, links[0].bss points to entry of bss to which STA 7329 + * is connected. It can be obtained through cfg80211_get_bss() (may be 7330 + * %NULL). It is recommended to store the bss from the connect_request and 7331 + * hold a reference to it and return through this param to avoid a warning 7332 + * if the bss is expired during the connection, esp. for those drivers 7333 + * implementing connect op. Only one parameter among @bssid and @bss needs 7334 + * to be specified. 7315 7335 */ 7316 7336 struct cfg80211_connect_resp_params { 7317 7337 int status; 7318 - const u8 *bssid; 7319 - struct cfg80211_bss *bss; 7320 7338 const u8 *req_ie; 7321 7339 size_t req_ie_len; 7322 7340 const u8 *resp_ie; 7323 7341 size_t resp_ie_len; 7324 7342 struct cfg80211_fils_resp_params fils; 7325 7343 enum nl80211_timeout_reason timeout_reason; 7344 + 7345 + const u8 *ap_mld_addr; 7346 + u16 valid_links; 7347 + struct { 7348 + const u8 *addr; 7349 + const u8 *bssid; 7350 + struct cfg80211_bss *bss; 7351 + } links[IEEE80211_MLD_MAX_NUM_LINKS]; 7326 7352 }; 7327 7353 7328 7354 /** ··· 7416 7394 7417 7395 memset(&params, 0, sizeof(params)); 7418 7396 params.status = status; 7419 - params.bssid = bssid; 7420 - params.bss = bss; 7397 + params.links[0].bssid = bssid; 7398 + params.links[0].bss = bss; 7421 7399 params.req_ie = req_ie; 7422 7400 params.req_ie_len = req_ie_len; 7423 7401 params.resp_ie = resp_ie; ··· 7488 7466 /** 7489 7467 * struct cfg80211_roam_info - driver initiated roaming information 7490 7468 * 7491 - * @channel: the channel of the new AP 7492 - * @bss: entry of bss to which STA got roamed (may be %NULL if %bssid is set) 7493 - * @bssid: the BSSID of the new AP (may be %NULL if %bss is set) 7494 7469 * @req_ie: association request IEs (maybe be %NULL) 7495 7470 * @req_ie_len: association request IEs length 7496 7471 * @resp_ie: association response IEs (may be %NULL) 7497 7472 * @resp_ie_len: assoc response IEs length 7498 7473 * @fils: FILS related roaming information. 7474 + * @valid_links: For MLO roaming, BIT mask of the new valid links is set. 7475 + * Otherwise zero. 7476 + * @ap_mld_addr: For MLO roaming, MLD address of the new AP. Otherwise %NULL. 7477 + * @links : For MLO roaming, contains new link info for the valid links set in 7478 + * @valid_links. For non-MLO roaming, links[0] contains the new AP info. 7479 + * @links.addr: For MLO roaming, MAC address of the STA link. Otherwise %NULL. 7480 + * @links.bssid: For MLO roaming, MAC address of the new AP link. For non-MLO 7481 + * roaming, links[0].bssid points to the BSSID of the new AP. May be 7482 + * %NULL if %links.bss is set. 7483 + * @links.channel: the channel of the new AP. 7484 + * @links.bss: For MLO roaming, entry of new bss to which STA link got 7485 + * roamed. For non-MLO roaming, links[0].bss points to entry of bss to 7486 + * which STA got roamed (may be %NULL if %links.bssid is set) 7499 7487 */ 7500 7488 struct cfg80211_roam_info { 7501 - struct ieee80211_channel *channel; 7502 - struct cfg80211_bss *bss; 7503 - const u8 *bssid; 7504 7489 const u8 *req_ie; 7505 7490 size_t req_ie_len; 7506 7491 const u8 *resp_ie; 7507 7492 size_t resp_ie_len; 7508 7493 struct cfg80211_fils_resp_params fils; 7494 + 7495 + const u8 *ap_mld_addr; 7496 + u16 valid_links; 7497 + struct { 7498 + const u8 *addr; 7499 + const u8 *bssid; 7500 + struct ieee80211_channel *channel; 7501 + struct cfg80211_bss *bss; 7502 + } links[IEEE80211_MLD_MAX_NUM_LINKS]; 7509 7503 }; 7510 7504 7511 7505 /**
+6
include/uapi/linux/nl80211.h
··· 2690 2690 * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as 2691 2691 * authenticate/associate. 2692 2692 * 2693 + * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO 2694 + * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not 2695 + * included in NL80211_CMD_CONNECT drivers must not perform MLO connection. 2696 + * 2693 2697 * @NUM_NL80211_ATTR: total number of nl80211_attrs available 2694 2698 * @NL80211_ATTR_MAX: highest attribute number currently defined 2695 2699 * @__NL80211_ATTR_AFTER_LAST: internal use ··· 3211 3207 NL80211_ATTR_MLO_LINKS, 3212 3208 NL80211_ATTR_MLO_LINK_ID, 3213 3209 NL80211_ATTR_MLD_ADDR, 3210 + 3211 + NL80211_ATTR_MLO_SUPPORT, 3214 3212 3215 3213 /* add attributes here, update the policy in nl80211.c */ 3216 3214
+2 -2
net/wireless/mlme.c
··· 42 42 43 43 memset(&cr, 0, sizeof(cr)); 44 44 cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); 45 - cr.bssid = mgmt->bssid; 46 - cr.bss = bss; 45 + cr.links[0].bssid = mgmt->bssid; 46 + cr.links[0].bss = bss; 47 47 cr.req_ie = req_ies; 48 48 cr.req_ie_len = req_ies_len; 49 49 cr.resp_ie = resp_ie;
+114 -6
net/wireless/nl80211.c
··· 797 797 [NL80211_ATTR_MLO_LINK_ID] = 798 798 NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS), 799 799 [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN), 800 + [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, 800 801 }; 801 802 802 803 /* policy for the key attributes */ ··· 11530 11529 connect.flags |= CONNECT_REQ_EXTERNAL_AUTH_SUPPORT; 11531 11530 } 11532 11531 11532 + if (nla_get_flag(info->attrs[NL80211_ATTR_MLO_SUPPORT])) 11533 + connect.flags |= CONNECT_REQ_MLO_SUPPORT; 11534 + 11533 11535 wdev_lock(dev->ieee80211_ptr); 11534 11536 11535 11537 err = cfg80211_connect(rdev, dev, &connect, connkeys, ··· 17308 17304 { 17309 17305 struct sk_buff *msg; 17310 17306 void *hdr; 17307 + unsigned int link; 17308 + size_t link_info_size = 0; 17309 + const u8 *connected_addr = cr->valid_links ? 17310 + cr->ap_mld_addr : cr->links[0].bssid; 17311 + 17312 + if (cr->valid_links) { 17313 + for_each_valid_link(cr, link) { 17314 + /* Nested attribute header */ 17315 + link_info_size += NLA_HDRLEN; 17316 + /* Link ID */ 17317 + link_info_size += nla_total_size(sizeof(u8)); 17318 + link_info_size += cr->links[link].addr ? 17319 + nla_total_size(ETH_ALEN) : 0; 17320 + link_info_size += (cr->links[link].bssid || 17321 + cr->links[link].bss) ? 17322 + nla_total_size(ETH_ALEN) : 0; 17323 + } 17324 + } 17311 17325 17312 17326 msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len + 17313 17327 cr->fils.kek_len + cr->fils.pmk_len + 17314 - (cr->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); 17328 + (cr->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, 17329 + gfp); 17315 17330 if (!msg) 17316 17331 return; 17317 17332 ··· 17342 17319 17343 17320 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || 17344 17321 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || 17345 - (cr->bssid && 17346 - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) || 17322 + (connected_addr && 17323 + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr)) || 17347 17324 nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, 17348 17325 cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : 17349 17326 cr->status) || ··· 17369 17346 nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid))))) 17370 17347 goto nla_put_failure; 17371 17348 17349 + if (cr->valid_links) { 17350 + int i = 1; 17351 + struct nlattr *nested; 17352 + 17353 + nested = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); 17354 + if (!nested) 17355 + goto nla_put_failure; 17356 + 17357 + for_each_valid_link(cr, link) { 17358 + struct nlattr *nested_mlo_links; 17359 + const u8 *bssid = cr->links[link].bss ? 17360 + cr->links[link].bss->bssid : 17361 + cr->links[link].bssid; 17362 + 17363 + nested_mlo_links = nla_nest_start(msg, i); 17364 + if (!nested_mlo_links) 17365 + goto nla_put_failure; 17366 + 17367 + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link) || 17368 + (bssid && 17369 + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid)) || 17370 + (cr->links[link].addr && 17371 + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, 17372 + cr->links[link].addr))) 17373 + goto nla_put_failure; 17374 + 17375 + nla_nest_end(msg, nested_mlo_links); 17376 + i++; 17377 + } 17378 + nla_nest_end(msg, nested); 17379 + } 17380 + 17372 17381 genlmsg_end(msg, hdr); 17373 17382 17374 17383 genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, ··· 17417 17362 { 17418 17363 struct sk_buff *msg; 17419 17364 void *hdr; 17420 - const u8 *bssid = info->bss ? info->bss->bssid : info->bssid; 17365 + size_t link_info_size = 0; 17366 + unsigned int link; 17367 + const u8 *connected_addr = info->ap_mld_addr ? 17368 + info->ap_mld_addr : 17369 + (info->links[0].bss ? 17370 + info->links[0].bss->bssid : 17371 + info->links[0].bssid); 17372 + 17373 + if (info->valid_links) { 17374 + for_each_valid_link(info, link) { 17375 + /* Nested attribute header */ 17376 + link_info_size += NLA_HDRLEN; 17377 + /* Link ID */ 17378 + link_info_size += nla_total_size(sizeof(u8)); 17379 + link_info_size += info->links[link].addr ? 17380 + nla_total_size(ETH_ALEN) : 0; 17381 + link_info_size += (info->links[link].bssid || 17382 + info->links[link].bss) ? 17383 + nla_total_size(ETH_ALEN) : 0; 17384 + } 17385 + } 17421 17386 17422 17387 msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len + 17423 17388 info->fils.kek_len + info->fils.pmk_len + 17424 - (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); 17389 + (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + 17390 + link_info_size, gfp); 17425 17391 if (!msg) 17426 17392 return; 17427 17393 ··· 17454 17378 17455 17379 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || 17456 17380 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || 17457 - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) || 17381 + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) || 17458 17382 (info->req_ie && 17459 17383 nla_put(msg, NL80211_ATTR_REQ_IE, info->req_ie_len, 17460 17384 info->req_ie)) || ··· 17472 17396 (info->fils.pmkid && 17473 17397 nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid))) 17474 17398 goto nla_put_failure; 17399 + 17400 + if (info->valid_links) { 17401 + int i = 1; 17402 + struct nlattr *nested; 17403 + 17404 + nested = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); 17405 + if (!nested) 17406 + goto nla_put_failure; 17407 + 17408 + for_each_valid_link(info, link) { 17409 + struct nlattr *nested_mlo_links; 17410 + const u8 *bssid = info->links[link].bss ? 17411 + info->links[link].bss->bssid : 17412 + info->links[link].bssid; 17413 + 17414 + nested_mlo_links = nla_nest_start(msg, i); 17415 + if (!nested_mlo_links) 17416 + goto nla_put_failure; 17417 + 17418 + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link) || 17419 + (bssid && 17420 + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid)) || 17421 + (info->links[link].addr && 17422 + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, 17423 + info->links[link].addr))) 17424 + goto nla_put_failure; 17425 + 17426 + nla_nest_end(msg, nested_mlo_links); 17427 + i++; 17428 + } 17429 + nla_nest_end(msg, nested); 17430 + } 17475 17431 17476 17432 genlmsg_end(msg, hdr); 17477 17433
+286 -107
net/wireless/sme.c
··· 275 275 276 276 memset(&cr, 0, sizeof(cr)); 277 277 cr.status = -1; 278 - cr.bssid = bssid; 278 + cr.links[0].bssid = bssid; 279 279 cr.timeout_reason = treason; 280 280 __cfg80211_connect_result(wdev->netdev, &cr, false); 281 281 } ··· 384 384 385 385 memset(&cr, 0, sizeof(cr)); 386 386 cr.status = status_code; 387 - cr.bssid = mgmt->bssid; 387 + cr.links[0].bssid = mgmt->bssid; 388 388 cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; 389 389 __cfg80211_connect_result(wdev->netdev, &cr, false); 390 390 } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { ··· 698 698 699 699 DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); 700 700 701 + static void 702 + cfg80211_connect_result_release_bsses(struct wireless_dev *wdev, 703 + struct cfg80211_connect_resp_params *cr) 704 + { 705 + unsigned int link; 706 + 707 + for_each_valid_link(cr, link) { 708 + if (!cr->links[link].bss) 709 + continue; 710 + cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss)); 711 + cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); 712 + } 713 + } 714 + 701 715 /* 702 716 * API calls for drivers implementing connect/disconnect and 703 717 * SME event handling ··· 729 715 #ifdef CONFIG_CFG80211_WEXT 730 716 union iwreq_data wrqu; 731 717 #endif 718 + unsigned int link; 719 + const u8 *connected_addr; 720 + bool bss_not_found = false; 732 721 733 722 ASSERT_WDEV_LOCK(wdev); 734 723 735 724 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 736 - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { 737 - cfg80211_put_bss(wdev->wiphy, cr->bss); 738 - return; 725 + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 726 + goto out; 727 + 728 + if (cr->valid_links) { 729 + if (WARN_ON(!cr->ap_mld_addr)) 730 + goto out; 731 + 732 + for_each_valid_link(cr, link) { 733 + if (WARN_ON(!cr->links[link].addr)) 734 + goto out; 735 + } 739 736 } 740 737 741 738 wdev->unprot_beacon_reported = 0; 742 739 nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, 743 740 GFP_KERNEL); 741 + connected_addr = cr->valid_links ? cr->ap_mld_addr : cr->links[0].bssid; 744 742 745 743 #ifdef CONFIG_CFG80211_WEXT 746 - if (wextev) { 744 + if (wextev && !cr->valid_links) { 747 745 if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { 748 746 memset(&wrqu, 0, sizeof(wrqu)); 749 747 wrqu.data.length = cr->req_ie_len; ··· 772 746 773 747 memset(&wrqu, 0, sizeof(wrqu)); 774 748 wrqu.ap_addr.sa_family = ARPHRD_ETHER; 775 - if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) { 776 - memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN); 777 - memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN); 749 + if (connected_addr && cr->status == WLAN_STATUS_SUCCESS) { 750 + memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); 751 + memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); 778 752 wdev->wext.prev_bssid_valid = true; 779 753 } 780 754 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 781 755 } 782 756 #endif 783 757 784 - if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) { 785 - WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); 786 - cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid, 787 - wdev->u.client.ssid, wdev->u.client.ssid_len, 788 - wdev->conn_bss_type, 789 - IEEE80211_PRIVACY_ANY); 790 - if (cr->bss) 791 - cfg80211_hold_bss(bss_from_pub(cr->bss)); 758 + if (cr->status == WLAN_STATUS_SUCCESS) { 759 + for_each_valid_link(cr, link) { 760 + if (WARN_ON_ONCE(!cr->links[link].bss)) 761 + break; 762 + } 763 + 764 + for_each_valid_link(cr, link) { 765 + if (cr->links[link].bss) 766 + continue; 767 + 768 + cr->links[link].bss = 769 + cfg80211_get_bss(wdev->wiphy, NULL, 770 + cr->links[link].bssid, 771 + wdev->u.client.ssid, 772 + wdev->u.client.ssid_len, 773 + wdev->conn_bss_type, 774 + IEEE80211_PRIVACY_ANY); 775 + if (!cr->links[link].bss) { 776 + bss_not_found = true; 777 + break; 778 + } 779 + cfg80211_hold_bss(bss_from_pub(cr->links[link].bss)); 780 + } 792 781 } 793 782 794 783 cfg80211_wdev_release_bsses(wdev); ··· 813 772 wdev->connect_keys = NULL; 814 773 wdev->u.client.ssid_len = 0; 815 774 wdev->conn_owner_nlportid = 0; 816 - if (cr->bss) { 817 - cfg80211_unhold_bss(bss_from_pub(cr->bss)); 818 - cfg80211_put_bss(wdev->wiphy, cr->bss); 819 - } 775 + cfg80211_connect_result_release_bsses(wdev, cr); 820 776 cfg80211_sme_free(wdev); 821 777 return; 822 778 } 823 779 824 - if (WARN_ON(!cr->bss)) 780 + if (WARN_ON(bss_not_found)) { 781 + cfg80211_connect_result_release_bsses(wdev, cr); 825 782 return; 783 + } 826 784 827 - wdev->links[0].client.current_bss = bss_from_pub(cr->bss); 785 + memset(wdev->links, 0, sizeof(wdev->links)); 786 + wdev->valid_links = cr->valid_links; 787 + for_each_valid_link(cr, link) 788 + wdev->links[link].client.current_bss = 789 + bss_from_pub(cr->links[link].bss); 828 790 wdev->connected = true; 829 - ether_addr_copy(wdev->u.client.connected_addr, cr->bss->bssid); 791 + ether_addr_copy(wdev->u.client.connected_addr, connected_addr); 792 + if (cr->valid_links) { 793 + for_each_valid_link(cr, link) 794 + memcpy(wdev->links[link].addr, cr->links[link].addr, 795 + ETH_ALEN); 796 + } 830 797 831 798 if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP)) 832 799 cfg80211_upload_connect_keys(wdev); 833 800 834 801 rcu_read_lock(); 835 - country_elem = ieee80211_bss_get_elem(cr->bss, WLAN_EID_COUNTRY); 802 + for_each_valid_link(cr, link) { 803 + country_elem = 804 + ieee80211_bss_get_elem(cr->links[link].bss, 805 + WLAN_EID_COUNTRY); 806 + if (country_elem) 807 + break; 808 + } 836 809 if (!country_elem) { 837 810 rcu_read_unlock(); 838 811 return; ··· 859 804 if (!country_data) 860 805 return; 861 806 862 - regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band, 807 + regulatory_hint_country_ie(wdev->wiphy, 808 + cr->links[link].bss->channel->band, 863 809 country_data, country_datalen); 864 810 kfree(country_data); 811 + 812 + return; 813 + out: 814 + for_each_valid_link(cr, link) 815 + cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); 865 816 } 866 817 867 - /* Consumes bss object one way or another */ 818 + static void cfg80211_update_link_bss(struct wireless_dev *wdev, 819 + struct cfg80211_bss **bss) 820 + { 821 + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 822 + struct cfg80211_internal_bss *ibss; 823 + 824 + if (!*bss) 825 + return; 826 + 827 + ibss = bss_from_pub(*bss); 828 + if (list_empty(&ibss->list)) { 829 + struct cfg80211_bss *found = NULL, *tmp = *bss; 830 + 831 + found = cfg80211_get_bss(wdev->wiphy, NULL, 832 + (*bss)->bssid, 833 + wdev->u.client.ssid, 834 + wdev->u.client.ssid_len, 835 + wdev->conn_bss_type, 836 + IEEE80211_PRIVACY_ANY); 837 + if (found) { 838 + /* The same BSS is already updated so use it 839 + * instead, as it has latest info. 840 + */ 841 + *bss = found; 842 + } else { 843 + /* Update with BSS provided by driver, it will 844 + * be freshly added and ref cnted, we can free 845 + * the old one. 846 + * 847 + * signal_valid can be false, as we are not 848 + * expecting the BSS to be found. 849 + * 850 + * keep the old timestamp to avoid confusion 851 + */ 852 + cfg80211_bss_update(rdev, ibss, false, 853 + ibss->ts); 854 + } 855 + 856 + cfg80211_put_bss(wdev->wiphy, tmp); 857 + } 858 + } 859 + 860 + /* Consumes bss object(s) one way or another */ 868 861 void cfg80211_connect_done(struct net_device *dev, 869 862 struct cfg80211_connect_resp_params *params, 870 863 gfp_t gfp) ··· 922 819 struct cfg80211_event *ev; 923 820 unsigned long flags; 924 821 u8 *next; 822 + size_t link_info_size = 0; 823 + unsigned int link; 925 824 926 - if (params->bss) { 927 - struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss); 928 - 929 - if (list_empty(&ibss->list)) { 930 - struct cfg80211_bss *found = NULL, *tmp = params->bss; 931 - 932 - found = cfg80211_get_bss(wdev->wiphy, NULL, 933 - params->bss->bssid, 934 - wdev->u.client.ssid, wdev->u.client.ssid_len, 935 - wdev->conn_bss_type, 936 - IEEE80211_PRIVACY_ANY); 937 - if (found) { 938 - /* The same BSS is already updated so use it 939 - * instead, as it has latest info. 940 - */ 941 - params->bss = found; 942 - } else { 943 - /* Update with BSS provided by driver, it will 944 - * be freshly added and ref cnted, we can free 945 - * the old one. 946 - * 947 - * signal_valid can be false, as we are not 948 - * expecting the BSS to be found. 949 - * 950 - * keep the old timestamp to avoid confusion 951 - */ 952 - cfg80211_bss_update(rdev, ibss, false, 953 - ibss->ts); 954 - } 955 - 956 - cfg80211_put_bss(wdev->wiphy, tmp); 957 - } 825 + for_each_valid_link(params, link) { 826 + cfg80211_update_link_bss(wdev, &params->links[link].bss); 827 + link_info_size += params->links[link].bssid ? ETH_ALEN : 0; 828 + link_info_size += params->links[link].addr ? ETH_ALEN : 0; 958 829 } 959 830 960 - ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + 831 + ev = kzalloc(sizeof(*ev) + (params->ap_mld_addr ? ETH_ALEN : 0) + 961 832 params->req_ie_len + params->resp_ie_len + 962 833 params->fils.kek_len + params->fils.pmk_len + 963 - (params->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); 834 + (params->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, 835 + gfp); 836 + 964 837 if (!ev) { 965 - cfg80211_put_bss(wdev->wiphy, params->bss); 838 + for_each_valid_link(params, link) 839 + cfg80211_put_bss(wdev->wiphy, 840 + params->links[link].bss); 966 841 return; 967 842 } 968 843 969 844 ev->type = EVENT_CONNECT_RESULT; 970 845 next = ((u8 *)ev) + sizeof(*ev); 971 - if (params->bssid) { 972 - ev->cr.bssid = next; 973 - memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN); 846 + if (params->ap_mld_addr) { 847 + ev->cr.ap_mld_addr = next; 848 + memcpy((void *)ev->cr.ap_mld_addr, params->ap_mld_addr, 849 + ETH_ALEN); 974 850 next += ETH_ALEN; 975 851 } 976 852 if (params->req_ie_len) { ··· 989 907 ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num; 990 908 if (params->fils.update_erp_next_seq_num) 991 909 ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num; 992 - if (params->bss) 993 - cfg80211_hold_bss(bss_from_pub(params->bss)); 994 - ev->cr.bss = params->bss; 910 + ev->cr.valid_links = params->valid_links; 911 + for_each_valid_link(params, link) { 912 + if (params->links[link].bss) 913 + cfg80211_hold_bss( 914 + bss_from_pub(params->links[link].bss)); 915 + ev->cr.links[link].bss = params->links[link].bss; 916 + 917 + if (params->links[link].addr) { 918 + ev->cr.links[link].addr = next; 919 + memcpy((void *)ev->cr.links[link].addr, 920 + params->links[link].addr, 921 + ETH_ALEN); 922 + next += ETH_ALEN; 923 + } 924 + if (params->links[link].bssid) { 925 + ev->cr.links[link].bssid = next; 926 + memcpy((void *)ev->cr.links[link].bssid, 927 + params->links[link].bssid, 928 + ETH_ALEN); 929 + next += ETH_ALEN; 930 + } 931 + } 995 932 ev->cr.status = params->status; 996 933 ev->cr.timeout_reason = params->timeout_reason; 997 934 ··· 1028 927 #ifdef CONFIG_CFG80211_WEXT 1029 928 union iwreq_data wrqu; 1030 929 #endif 930 + unsigned int link; 931 + const u8 *connected_addr; 932 + 1031 933 ASSERT_WDEV_LOCK(wdev); 1032 934 1033 935 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && ··· 1040 936 if (WARN_ON(!wdev->connected)) 1041 937 goto out; 1042 938 939 + if (info->valid_links) { 940 + if (WARN_ON(!info->ap_mld_addr)) 941 + goto out; 942 + 943 + for_each_valid_link(info, link) { 944 + if (WARN_ON(!info->links[link].addr)) 945 + goto out; 946 + } 947 + } 948 + 1043 949 cfg80211_wdev_release_bsses(wdev); 1044 950 1045 - if (WARN_ON(!info->bss)) 1046 - return; 951 + for_each_valid_link(info, link) { 952 + if (WARN_ON(!info->links[link].bss)) 953 + goto out; 954 + } 1047 955 1048 - cfg80211_hold_bss(bss_from_pub(info->bss)); 1049 - wdev->links[0].client.current_bss = bss_from_pub(info->bss); 1050 - ether_addr_copy(wdev->u.client.connected_addr, info->bss->bssid); 956 + memset(wdev->links, 0, sizeof(wdev->links)); 957 + wdev->valid_links = info->valid_links; 958 + for_each_valid_link(info, link) { 959 + cfg80211_hold_bss(bss_from_pub(info->links[link].bss)); 960 + wdev->links[link].client.current_bss = 961 + bss_from_pub(info->links[link].bss); 962 + } 1051 963 964 + connected_addr = info->valid_links ? 965 + info->ap_mld_addr : 966 + info->links[0].bss->bssid; 967 + ether_addr_copy(wdev->u.client.connected_addr, connected_addr); 968 + if (info->valid_links) { 969 + for_each_valid_link(info, link) 970 + memcpy(wdev->links[link].addr, info->links[link].addr, 971 + ETH_ALEN); 972 + } 1052 973 wdev->unprot_beacon_reported = 0; 1053 974 nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy), 1054 975 wdev->netdev, info, GFP_KERNEL); 1055 976 1056 977 #ifdef CONFIG_CFG80211_WEXT 1057 - if (info->req_ie) { 1058 - memset(&wrqu, 0, sizeof(wrqu)); 1059 - wrqu.data.length = info->req_ie_len; 1060 - wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 1061 - &wrqu, info->req_ie); 1062 - } 978 + if (!info->valid_links) { 979 + if (info->req_ie) { 980 + memset(&wrqu, 0, sizeof(wrqu)); 981 + wrqu.data.length = info->req_ie_len; 982 + wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 983 + &wrqu, info->req_ie); 984 + } 1063 985 1064 - if (info->resp_ie) { 1065 - memset(&wrqu, 0, sizeof(wrqu)); 1066 - wrqu.data.length = info->resp_ie_len; 1067 - wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 1068 - &wrqu, info->resp_ie); 1069 - } 986 + if (info->resp_ie) { 987 + memset(&wrqu, 0, sizeof(wrqu)); 988 + wrqu.data.length = info->resp_ie_len; 989 + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 990 + &wrqu, info->resp_ie); 991 + } 1070 992 1071 - memset(&wrqu, 0, sizeof(wrqu)); 1072 - wrqu.ap_addr.sa_family = ARPHRD_ETHER; 1073 - memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN); 1074 - memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN); 1075 - wdev->wext.prev_bssid_valid = true; 1076 - wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 993 + memset(&wrqu, 0, sizeof(wrqu)); 994 + wrqu.ap_addr.sa_family = ARPHRD_ETHER; 995 + memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); 996 + memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); 997 + wdev->wext.prev_bssid_valid = true; 998 + wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 999 + } 1077 1000 #endif 1078 1001 1079 1002 return; 1080 1003 out: 1081 - cfg80211_put_bss(wdev->wiphy, info->bss); 1004 + for_each_valid_link(info, link) 1005 + cfg80211_put_bss(wdev->wiphy, info->links[link].bss); 1082 1006 } 1083 1007 1084 - /* Consumes info->bss object one way or another */ 1008 + /* Consumes info->links.bss object(s) one way or another */ 1085 1009 void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, 1086 1010 gfp_t gfp) 1087 1011 { ··· 1118 986 struct cfg80211_event *ev; 1119 987 unsigned long flags; 1120 988 u8 *next; 989 + unsigned int link; 990 + size_t link_info_size = 0; 991 + bool bss_not_found = false; 1121 992 1122 - if (!info->bss) { 1123 - info->bss = cfg80211_get_bss(wdev->wiphy, info->channel, 1124 - info->bssid, wdev->u.client.ssid, 1125 - wdev->u.client.ssid_len, 1126 - wdev->conn_bss_type, 1127 - IEEE80211_PRIVACY_ANY); 993 + for_each_valid_link(info, link) { 994 + link_info_size += info->links[link].addr ? ETH_ALEN : 0; 995 + link_info_size += info->links[link].bssid ? ETH_ALEN : 0; 996 + 997 + if (info->links[link].bss) 998 + continue; 999 + 1000 + info->links[link].bss = 1001 + cfg80211_get_bss(wdev->wiphy, 1002 + info->links[link].channel, 1003 + info->links[link].bssid, 1004 + wdev->u.client.ssid, 1005 + wdev->u.client.ssid_len, 1006 + wdev->conn_bss_type, 1007 + IEEE80211_PRIVACY_ANY); 1008 + 1009 + if (!info->links[link].bss) { 1010 + bss_not_found = true; 1011 + break; 1012 + } 1128 1013 } 1129 1014 1130 - if (WARN_ON(!info->bss)) 1131 - return; 1015 + if (WARN_ON(bss_not_found)) 1016 + goto out; 1132 1017 1133 1018 ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len + 1134 1019 info->fils.kek_len + info->fils.pmk_len + 1135 - (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); 1136 - if (!ev) { 1137 - cfg80211_put_bss(wdev->wiphy, info->bss); 1138 - return; 1139 - } 1020 + (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + 1021 + (info->ap_mld_addr ? ETH_ALEN : 0) + link_info_size, gfp); 1022 + if (!ev) 1023 + goto out; 1140 1024 1141 1025 ev->type = EVENT_ROAMED; 1142 1026 next = ((u8 *)ev) + sizeof(*ev); ··· 1192 1044 ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num; 1193 1045 if (info->fils.update_erp_next_seq_num) 1194 1046 ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; 1195 - ev->rm.bss = info->bss; 1047 + if (info->ap_mld_addr) { 1048 + ev->rm.ap_mld_addr = next; 1049 + memcpy((void *)ev->rm.ap_mld_addr, info->ap_mld_addr, 1050 + ETH_ALEN); 1051 + next += ETH_ALEN; 1052 + } 1053 + ev->rm.valid_links = info->valid_links; 1054 + for_each_valid_link(info, link) { 1055 + ev->rm.links[link].bss = info->links[link].bss; 1056 + 1057 + if (info->links[link].addr) { 1058 + ev->rm.links[link].addr = next; 1059 + memcpy((void *)ev->rm.links[link].addr, 1060 + info->links[link].addr, 1061 + ETH_ALEN); 1062 + next += ETH_ALEN; 1063 + } 1064 + 1065 + if (info->links[link].bssid) { 1066 + ev->rm.links[link].bssid = next; 1067 + memcpy((void *)ev->rm.links[link].bssid, 1068 + info->links[link].bssid, 1069 + ETH_ALEN); 1070 + next += ETH_ALEN; 1071 + } 1072 + } 1196 1073 1197 1074 spin_lock_irqsave(&wdev->event_lock, flags); 1198 1075 list_add_tail(&ev->list, &wdev->event_list); 1199 1076 spin_unlock_irqrestore(&wdev->event_lock, flags); 1200 1077 queue_work(cfg80211_wq, &rdev->event_work); 1078 + 1079 + return; 1080 + out: 1081 + for_each_valid_link(info, link) 1082 + cfg80211_put_bss(wdev->wiphy, info->links[link].bss); 1083 + 1201 1084 } 1202 1085 EXPORT_SYMBOL(cfg80211_roamed); 1203 1086