From 7b0a0e3c3a88260b6fcb017e49f198463aa62ed1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Apr 2022 16:50:57 +0200 Subject: wifi: cfg80211: do some rework towards MLO link APIs In order to support multi-link operation with multiple links, start adding some APIs. The notable addition here is to have the link ID in a new nl80211 attribute, that will be used to differentiate the links in many nl80211 operations. So far, this patch adds the netlink NL80211_ATTR_MLO_LINK_ID attribute (as well as the NL80211_ATTR_MLO_LINKS attribute) and plugs it through the system in some places, checking the validity etc. along with other infrastructure needed for it. For now, I've decided to include only the over-the-air link ID in the API. I know we discussed that we eventually need to have to have other ways of identifying a link, but for local AP mode and auth/assoc commands as well as set_key etc. we'll use the OTA ID. Also included in this patch is some refactoring of the data structures in struct wireless_dev, splitting for the first time the data into type dependent pieces, to make reasoning about these things easier. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 98f905f16411..a9a2c9fef295 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -323,6 +323,17 @@ * Once the association is done, the driver cleans the FILS AAD data. */ +/** + * DOC: Multi-Link Operation + * + * In Multi-Link Operation, a connection between to MLDs utilizes multiple + * links. To use this in nl80211, various commands and responses now need + * to or will include the new %NL80211_ATTR_MLO_LINKS attribute. + * Additionally, various commands that need to operate on a specific link + * now need to be given the %NL80211_ATTR_MLO_LINK_ID attribute, e.g. to + * use %NL80211_CMD_START_AP or similar functions. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -1237,6 +1248,12 @@ * to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to * specify the timeout value. * + * @NL80211_CMD_ADD_LINK: Add a new link to an interface. The + * %NL80211_ATTR_MLO_LINK_ID attribute is used for the new link. + * @NL80211_CMD_REMOVE_LINK: Remove a link from an interface. This may come + * without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links + * in preparation for e.g. roaming to a regular (non-MLO) AP. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1481,6 +1498,9 @@ enum nl80211_commands { NL80211_CMD_ASSOC_COMEBACK, + NL80211_CMD_ADD_LINK, + NL80211_CMD_REMOVE_LINK, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2663,6 +2683,11 @@ enum nl80211_commands { * association request when used with NL80211_CMD_NEW_STATION). Can be set * only if %NL80211_STA_FLAG_WME is set. * + * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with + * various commands that need a link ID to operate. + * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some + * per-link information and a link ID. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3177,6 +3202,9 @@ enum nl80211_attrs { NL80211_ATTR_DISABLE_EHT, + NL80211_ATTR_MLO_LINKS, + NL80211_ATTR_MLO_LINK_ID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, -- cgit v1.2.3-59-g8ed1b From d648c23024bd01333acd2fd5e34bcde0ffb66b16 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 31 May 2022 19:48:33 +0200 Subject: wifi: nl80211: support MLO in auth/assoc For authentication, we need the BSS, the link_id and the AP MLD address to create the link and station, (for now) the driver assigns a link address and sends the frame, the MLD address needs to be the address of the interface. For association, pass the list of BSSes that were selected for the MLO connection, along with extra per-STA profile elements, the AP MLD address and the link ID on which the association request should be sent. Note that for now we don't have a proper way to pass the link address(es) and so the driver/mac80211 will select one, but depending on how that selection works it means that assoc w/o auth data still being around (mac80211 implementation detail) the association won't necessarily work - so this will need to be extended in the future to sort out the link addressing. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 31 +++++++++++++++ include/uapi/linux/nl80211.h | 3 ++ net/wireless/mlme.c | 30 +++++++++++++-- net/wireless/nl80211.c | 91 ++++++++++++++++++++++++++++++++++++++++++-- net/wireless/sme.c | 2 + 5 files changed, 151 insertions(+), 6 deletions(-) (limited to 'include/uapi/linux') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 772e099fc932..a4f9e6094118 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2718,6 +2718,12 @@ static inline const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 id) * Authentication algorithm number, i.e., starting at the Authentication * transaction sequence number field. * @auth_data_len: Length of auth_data buffer in octets + * @link_id: if >= 0, indicates authentication should be done as an MLD, + * the interface address is included as the MLD address and the + * necessary link (with the given link_id) will be created (and + * given an MLD address) by the driver + * @ap_mld_addr: AP MLD address in case of authentication request with + * an AP MLD, valid iff @link_id >= 0 */ struct cfg80211_auth_request { struct cfg80211_bss *bss; @@ -2728,6 +2734,21 @@ struct cfg80211_auth_request { u8 key_len, key_idx; const u8 *auth_data; size_t auth_data_len; + s8 link_id; + const u8 *ap_mld_addr; +}; + +/** + * struct cfg80211_assoc_link - per-link information for MLO association + * @bss: the BSS pointer, see also &struct cfg80211_assoc_request::bss; + * if this is %NULL for a link, that link is not requested + * @elems: extra elements for the per-STA profile for this link + * @elems_len: length of the elements + */ +struct cfg80211_assoc_link { + struct cfg80211_bss *bss; + const u8 *elems; + size_t elems_len; }; /** @@ -2761,6 +2782,8 @@ enum cfg80211_assoc_req_flags { * given a reference that it must give back to cfg80211_send_rx_assoc() * or to cfg80211_assoc_timeout(). To ensure proper refcounting, new * association requests while already associating must be rejected. + * This also applies to the @links.bss parameter, which is used instead + * of this one (it is %NULL) for MLO associations. * @ie: Extra IEs to add to (Re)Association Request frame or %NULL * @ie_len: Length of ie buffer in octets * @use_mfp: Use management frame protection (IEEE 802.11w) in this association @@ -2785,6 +2808,11 @@ enum cfg80211_assoc_req_flags { * with 16 octets of STA Nonce followed by 16 octets of AP Nonce. * @s1g_capa: S1G capability override * @s1g_capa_mask: S1G capability override mask + * @links: per-link information for MLO connections + * @link_id: >= 0 for MLO connections, where links are given, and indicates + * the link on which the association request should be sent + * @ap_mld_addr: AP MLD address in case of MLO association request, + * valid iff @link_id >= 0 */ struct cfg80211_assoc_request { struct cfg80211_bss *bss; @@ -2800,6 +2828,9 @@ struct cfg80211_assoc_request { size_t fils_kek_len; const u8 *fils_nonces; struct ieee80211_s1g_cap s1g_capa, s1g_capa_mask; + struct cfg80211_assoc_link links[IEEE80211_MLD_MAX_NUM_LINKS]; + const u8 *ap_mld_addr; + s8 link_id; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index a9a2c9fef295..60ad9a9f153d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2687,6 +2687,8 @@ enum nl80211_commands { * various commands that need a link ID to operate. * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some * per-link information and a link ID. + * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as + * authenticate/associate. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -3204,6 +3206,7 @@ enum nl80211_attrs { NL80211_ATTR_MLO_LINKS, NL80211_ATTR_MLO_LINK_ID, + NL80211_ATTR_MLD_ADDR, /* add attributes here, update the policy in nl80211.c */ diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 532113937469..d92eed0e52cd 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -241,6 +241,10 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, if (!req->bss) return -ENOENT; + if (req->link_id >= 0 && + !(wdev->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)) + return -EINVAL; + if (req->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { if (!req->key || !req->key_len || req->key_idx < 0 || req->key_idx > 3) @@ -294,10 +298,19 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct cfg80211_assoc_request *req) { struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; + int err, i, j; ASSERT_WDEV_LOCK(wdev); + for (i = 1; i < ARRAY_SIZE(req->links); i++) { + if (!req->links[i].bss) + continue; + for (j = 0; j < i; j++) { + if (req->links[i].bss == req->links[j].bss) + return -EINVAL; + } + } + if (wdev->connected && (!req->prev_bssid || !ether_addr_equal(wdev->u.client.connected_addr, req->prev_bssid))) @@ -310,8 +323,19 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, err = rdev_assoc(rdev, dev, req); if (!err) { - cfg80211_ref_bss(&rdev->wiphy, req->bss); - cfg80211_hold_bss(bss_from_pub(req->bss)); + int link_id; + + if (req->bss) { + cfg80211_ref_bss(&rdev->wiphy, req->bss); + cfg80211_hold_bss(bss_from_pub(req->bss)); + } + + for (link_id = 0; link_id < ARRAY_SIZE(req->links); link_id++) { + if (!req->links[link_id].bss) + continue; + cfg80211_ref_bss(&rdev->wiphy, req->links[link_id].bss); + cfg80211_hold_bss(bss_from_pub(req->links[link_id].bss)); + } } return err; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5a4d3ddcdf80..9bc66a21ac3a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -796,6 +796,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NLA_POLICY_NESTED_ARRAY(nl80211_policy), [NL80211_ATTR_MLO_LINK_ID] = NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS), + [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN), }; /* policy for the key attributes */ @@ -10282,6 +10283,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) req.key = key.p.key; req.key_len = key.p.key_len; req.key_idx = key.idx; + req.link_id = nl80211_link_id_or_invalid(info->attrs); + if (req.link_id >= 0) { + if (!info->attrs[NL80211_ATTR_MLD_ADDR]) + return -EINVAL; + req.ap_mld_addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]); + } req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, IEEE80211_BSS_TYPE_ESS, @@ -10475,7 +10482,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct cfg80211_assoc_request req = {}; + struct nlattr **attrs = NULL; const u8 *bssid, *ssid; + unsigned int link_id; int err, ssid_len; if (dev->ieee80211_ptr->conn_owner_nlportid && @@ -10585,9 +10594,81 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) sizeof(req.s1g_capa)); } - req.bss = nl80211_assoc_bss(rdev, ssid, ssid_len, info->attrs, &bssid); - if (IS_ERR(req.bss)) - return PTR_ERR(req.bss); + req.link_id = nl80211_link_id_or_invalid(info->attrs); + + if (info->attrs[NL80211_ATTR_MLO_LINKS]) { + unsigned int attrsize = NUM_NL80211_ATTR * sizeof(*attrs); + struct nlattr *link; + int rem = 0; + + if (req.link_id < 0) + return -EINVAL; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO)) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_MAC] || + info->attrs[NL80211_ATTR_WIPHY_FREQ] || + !info->attrs[NL80211_ATTR_MLD_ADDR]) + return -EINVAL; + + req.ap_mld_addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]); + + attrs = kzalloc(attrsize, GFP_KERNEL); + if (!attrs) + return -ENOMEM; + + nla_for_each_nested(link, + info->attrs[NL80211_ATTR_MLO_LINKS], + rem) { + memset(attrs, 0, attrsize); + + nla_parse_nested(attrs, NL80211_ATTR_MAX, + link, NULL, NULL); + + if (!attrs[NL80211_ATTR_MLO_LINK_ID]) { + err = -EINVAL; + goto free; + } + + link_id = nla_get_u8(attrs[NL80211_ATTR_MLO_LINK_ID]); + /* cannot use the same link ID again */ + if (req.links[link_id].bss) { + err = -EINVAL; + goto free; + } + req.links[link_id].bss = + nl80211_assoc_bss(rdev, ssid, ssid_len, attrs, + &bssid); + if (IS_ERR(req.links[link_id].bss)) { + err = PTR_ERR(req.links[link_id].bss); + goto free; + } + + if (attrs[NL80211_ATTR_IE]) { + req.links[link_id].elems = + nla_data(attrs[NL80211_ATTR_IE]); + req.links[link_id].elems_len = + nla_len(attrs[NL80211_ATTR_IE]); + } + } + + if (!req.links[req.link_id].bss) { + err = -EINVAL; + goto free; + } + + kfree(attrs); + attrs = NULL; + } else { + if (req.link_id >= 0) + return -EINVAL; + + req.bss = nl80211_assoc_bss(rdev, ssid, ssid_len, info->attrs, + &bssid); + if (IS_ERR(req.bss)) + return PTR_ERR(req.bss); + } err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); if (!err) { @@ -10605,7 +10686,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) wdev_unlock(dev->ieee80211_ptr); } +free: + for (link_id = 0; link_id < ARRAY_SIZE(req.links); link_id++) + cfg80211_put_bss(&rdev->wiphy, req.links[link_id].bss); cfg80211_put_bss(&rdev->wiphy, req.bss); + kfree(attrs); return err; } diff --git a/net/wireless/sme.c b/net/wireless/sme.c index c8a99b90723b..b3c6ce4f85ee 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -177,6 +177,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev, params->ssid, params->ssid_len, IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); + auth_req.link_id = -1; err = cfg80211_mlme_auth(rdev, wdev->netdev, &auth_req); cfg80211_put_bss(&rdev->wiphy, auth_req.bss); return err; @@ -198,6 +199,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev, req.ht_capa_mask = params->ht_capa_mask; req.vht_capa = params->vht_capa; req.vht_capa_mask = params->vht_capa_mask; + req.link_id = -1; req.bss = cfg80211_get_bss(&rdev->wiphy, params->channel, params->bssid, -- cgit v1.2.3-59-g8ed1b From efbabc11650040c64884ff3019b88c7bcc0ceb1d Mon Sep 17 00:00:00 2001 From: Veerendranath Jakkam Date: Wed, 8 Jun 2022 14:46:37 +0530 Subject: 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 Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/ath/wil6210/wmi.c | 4 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 6 +- drivers/net/wireless/rndis_wlan.c | 5 +- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 4 +- drivers/staging/wlan-ng/cfg80211.c | 2 +- include/net/cfg80211.h | 84 +++-- include/uapi/linux/nl80211.h | 6 + net/wireless/mlme.c | 4 +- net/wireless/nl80211.c | 120 ++++++- net/wireless/sme.c | 391 +++++++++++++++------ 11 files changed, 480 insertions(+), 148 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 33ed54738d47..e11c7e9accc0 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -807,7 +807,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, cfg80211_put_bss(ar->wiphy, bss); } else if (vif->sme_state == SME_CONNECTED) { struct cfg80211_roam_info roam_info = { - .bss = bss, + .links[0].bss = bss, .req_ie = assoc_req_ie, .req_ie_len = assoc_req_len, .resp_ie = assoc_resp_ie, diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 98b4c189eecc..ea7bd403e706 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1822,8 +1822,8 @@ wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len) freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ); memset(&info, 0, sizeof(info)); - info.channel = ieee80211_get_channel(wiphy, freq); - info.bss = vif->bss; + info.links[0].channel = ieee80211_get_channel(wiphy, freq); + info.links[0].bss = vif->bss; info.req_ie = assoc_req_ie; info.req_ie_len = assoc_req_ie_len; info.resp_ie = assoc_resp_ie; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 11e1f07f83e0..3ae6779fe153 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6017,8 +6017,8 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, done: kfree(buf); - roam_info.channel = notify_channel; - roam_info.bssid = profile->bssid; + roam_info.links[0].channel = notify_channel; + roam_info.links[0].bssid = profile->bssid; roam_info.req_ie = conn_info->req_ie; roam_info.req_ie_len = conn_info->req_ie_len; roam_info.resp_ie = conn_info->resp_ie; @@ -6061,7 +6061,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, } else { conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; } - conn_params.bssid = profile->bssid; + conn_params.links[0].bssid = profile->bssid; conn_params.req_ie = conn_info->req_ie; conn_params.req_ie_len = conn_info->req_ie_len; conn_params.resp_ie = conn_info->resp_ie; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index ff2448394a1e..05524291d60c 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2813,8 +2813,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) resp_ie_len, 0, GFP_KERNEL); } else { struct cfg80211_roam_info roam_info = { - .channel = get_current_channel(usbdev, NULL), - .bssid = bssid, + .links[0].channel = + get_current_channel(usbdev, NULL), + .links[0].bssid = bssid, .req_ie = req_ie, .req_ie_len = req_ie_len, .resp_ie = resp_ie, diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index 349aa3c4b668..cf35125b7891 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -450,8 +450,8 @@ check_bss: notify_channel = ieee80211_get_channel(wiphy, freq); - roam_info.channel = notify_channel; - roam_info.bssid = cur_network->network.mac_address; + roam_info.links[0].channel = notify_channel; + roam_info.links[0].bssid = cur_network->network.mac_address; roam_info.req_ie = pmlmepriv->assoc_req+sizeof(struct ieee80211_hdr_3addr)+2; roam_info.req_ie_len = diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 87379edce9a8..b7b56d8406d1 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -645,7 +645,7 @@ void prism2_disconnected(struct wlandevice *wlandev) void prism2_roamed(struct wlandevice *wlandev) { struct cfg80211_roam_info roam_info = { - .bssid = wlandev->bssid, + .links[0].bssid = wlandev->bssid, }; cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5706f96b819a..996782c44838 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2763,6 +2763,9 @@ struct cfg80211_assoc_link { * request (connect callback). * @ASSOC_REQ_DISABLE_HE: Disable HE * @ASSOC_REQ_DISABLE_EHT: Disable EHT + * @CONNECT_REQ_MLO_SUPPORT: Userspace indicates support for handling MLD links. + * Drivers shall disable MLO features for the current association if this + * flag is not set. */ enum cfg80211_assoc_req_flags { ASSOC_REQ_DISABLE_HT = BIT(0), @@ -2771,6 +2774,7 @@ enum cfg80211_assoc_req_flags { CONNECT_REQ_EXTERNAL_AUTH_SUPPORT = BIT(3), ASSOC_REQ_DISABLE_HE = BIT(4), ASSOC_REQ_DISABLE_EHT = BIT(5), + CONNECT_REQ_MLO_SUPPORT = BIT(6), }; /** @@ -5780,12 +5784,13 @@ static inline void WARN_INVALID_LINK_ID(struct wireless_dev *wdev, !(wdev->valid_links & BIT(link_id))); } -#define for_each_valid_link(wdev, link_id) \ - for (link_id = 0; \ - link_id < ((wdev)->valid_links ? ARRAY_SIZE((wdev)->links) : 1); \ - link_id++) \ - if (!(wdev)->valid_links || \ - ((wdev)->valid_links & BIT(link_id))) +#define for_each_valid_link(link_info, link_id) \ + for (link_id = 0; \ + link_id < ((link_info)->valid_links ? \ + ARRAY_SIZE((link_info)->links) : 1); \ + link_id++) \ + if (!(link_info)->valid_links || \ + ((link_info)->valid_links & BIT(link_id))) /** * DOC: Utility functions @@ -7296,13 +7301,6 @@ struct cfg80211_fils_resp_params { * indicate that this is a failure, but without a status code. * @timeout_reason is used to report the reason for the timeout in that * case. - * @bssid: The BSSID of the AP (may be %NULL) - * @bss: Entry of bss to which STA got connected to, can be obtained through - * cfg80211_get_bss() (may be %NULL). But it is recommended to store the - * bss from the connect_request and hold a reference to it and return - * through this param to avoid a warning if the bss is expired during the - * connection, esp. for those drivers implementing connect op. - * Only one parameter among @bssid and @bss needs to be specified. * @req_ie: Association request IEs (may be %NULL) * @req_ie_len: Association request IEs length * @resp_ie: Association response IEs (may be %NULL) @@ -7314,17 +7312,41 @@ struct cfg80211_fils_resp_params { * not known. This value is used only if @status < 0 to indicate that the * failure is due to a timeout and not due to explicit rejection by the AP. * This value is ignored in other cases (@status >= 0). + * @valid_links: For MLO connection, BIT mask of the valid link ids. Otherwise + * zero. + * @ap_mld_addr: For MLO connection, MLD address of the AP. Otherwise %NULL. + * @links : For MLO connection, contains link info for the valid links indicated + * using @valid_links. For non-MLO connection, links[0] contains the + * connected AP info. + * @links.addr: For MLO connection, MAC address of the STA link. Otherwise + * %NULL. + * @links.bssid: For MLO connection, MAC address of the AP link. For non-MLO + * connection, links[0].bssid points to the BSSID of the AP (may be %NULL). + * @links.bss: For MLO connection, entry of bss to which STA link is connected. + * For non-MLO connection, links[0].bss points to entry of bss to which STA + * is connected. It can be obtained through cfg80211_get_bss() (may be + * %NULL). It is recommended to store the bss from the connect_request and + * hold a reference to it and return through this param to avoid a warning + * if the bss is expired during the connection, esp. for those drivers + * implementing connect op. Only one parameter among @bssid and @bss needs + * to be specified. */ struct cfg80211_connect_resp_params { int status; - const u8 *bssid; - struct cfg80211_bss *bss; const u8 *req_ie; size_t req_ie_len; const u8 *resp_ie; size_t resp_ie_len; struct cfg80211_fils_resp_params fils; enum nl80211_timeout_reason timeout_reason; + + const u8 *ap_mld_addr; + u16 valid_links; + struct { + const u8 *addr; + const u8 *bssid; + struct cfg80211_bss *bss; + } links[IEEE80211_MLD_MAX_NUM_LINKS]; }; /** @@ -7394,8 +7416,8 @@ cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, memset(¶ms, 0, sizeof(params)); params.status = status; - params.bssid = bssid; - params.bss = bss; + params.links[0].bssid = bssid; + params.links[0].bss = bss; params.req_ie = req_ie; params.req_ie_len = req_ie_len; params.resp_ie = resp_ie; @@ -7466,24 +7488,40 @@ cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, /** * struct cfg80211_roam_info - driver initiated roaming information * - * @channel: the channel of the new AP - * @bss: entry of bss to which STA got roamed (may be %NULL if %bssid is set) - * @bssid: the BSSID of the new AP (may be %NULL if %bss is set) * @req_ie: association request IEs (maybe be %NULL) * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length * @fils: FILS related roaming information. + * @valid_links: For MLO roaming, BIT mask of the new valid links is set. + * Otherwise zero. + * @ap_mld_addr: For MLO roaming, MLD address of the new AP. Otherwise %NULL. + * @links : For MLO roaming, contains new link info for the valid links set in + * @valid_links. For non-MLO roaming, links[0] contains the new AP info. + * @links.addr: For MLO roaming, MAC address of the STA link. Otherwise %NULL. + * @links.bssid: For MLO roaming, MAC address of the new AP link. For non-MLO + * roaming, links[0].bssid points to the BSSID of the new AP. May be + * %NULL if %links.bss is set. + * @links.channel: the channel of the new AP. + * @links.bss: For MLO roaming, entry of new bss to which STA link got + * roamed. For non-MLO roaming, links[0].bss points to entry of bss to + * which STA got roamed (may be %NULL if %links.bssid is set) */ struct cfg80211_roam_info { - struct ieee80211_channel *channel; - struct cfg80211_bss *bss; - const u8 *bssid; const u8 *req_ie; size_t req_ie_len; const u8 *resp_ie; size_t resp_ie_len; struct cfg80211_fils_resp_params fils; + + const u8 *ap_mld_addr; + u16 valid_links; + struct { + const u8 *addr; + const u8 *bssid; + struct ieee80211_channel *channel; + struct cfg80211_bss *bss; + } links[IEEE80211_MLD_MAX_NUM_LINKS]; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 60ad9a9f153d..89f64f46b98d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2690,6 +2690,10 @@ enum nl80211_commands { * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as * authenticate/associate. * + * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO + * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not + * included in NL80211_CMD_CONNECT drivers must not perform MLO connection. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3208,6 +3212,8 @@ enum nl80211_attrs { NL80211_ATTR_MLO_LINK_ID, NL80211_ATTR_MLD_ADDR, + NL80211_ATTR_MLO_SUPPORT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index d92eed0e52cd..8a84cf77667c 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -42,8 +42,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, memset(&cr, 0, sizeof(cr)); cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); - cr.bssid = mgmt->bssid; - cr.bss = bss; + cr.links[0].bssid = mgmt->bssid; + cr.links[0].bss = bss; cr.req_ie = req_ies; cr.req_ie_len = req_ies_len; cr.resp_ie = resp_ie; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index aca799b9971e..6a45801c783c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -797,6 +797,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_MLO_LINK_ID] = NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS), [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN), + [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -11529,6 +11530,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) connect.flags |= CONNECT_REQ_EXTERNAL_AUTH_SUPPORT; } + if (nla_get_flag(info->attrs[NL80211_ATTR_MLO_SUPPORT])) + connect.flags |= CONNECT_REQ_MLO_SUPPORT; + wdev_lock(dev->ieee80211_ptr); err = cfg80211_connect(rdev, dev, &connect, connkeys, @@ -17304,10 +17308,29 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, { struct sk_buff *msg; void *hdr; + unsigned int link; + size_t link_info_size = 0; + const u8 *connected_addr = cr->valid_links ? + cr->ap_mld_addr : cr->links[0].bssid; + + if (cr->valid_links) { + for_each_valid_link(cr, link) { + /* Nested attribute header */ + link_info_size += NLA_HDRLEN; + /* Link ID */ + link_info_size += nla_total_size(sizeof(u8)); + link_info_size += cr->links[link].addr ? + nla_total_size(ETH_ALEN) : 0; + link_info_size += (cr->links[link].bssid || + cr->links[link].bss) ? + nla_total_size(ETH_ALEN) : 0; + } + } msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len + cr->fils.kek_len + cr->fils.pmk_len + - (cr->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); + (cr->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, + gfp); if (!msg) return; @@ -17319,8 +17342,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - (cr->bssid && - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) || + (connected_addr && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr)) || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : cr->status) || @@ -17346,6 +17369,38 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid))))) goto nla_put_failure; + if (cr->valid_links) { + int i = 1; + struct nlattr *nested; + + nested = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); + if (!nested) + goto nla_put_failure; + + for_each_valid_link(cr, link) { + struct nlattr *nested_mlo_links; + const u8 *bssid = cr->links[link].bss ? + cr->links[link].bss->bssid : + cr->links[link].bssid; + + nested_mlo_links = nla_nest_start(msg, i); + if (!nested_mlo_links) + goto nla_put_failure; + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link) || + (bssid && + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid)) || + (cr->links[link].addr && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + cr->links[link].addr))) + goto nla_put_failure; + + nla_nest_end(msg, nested_mlo_links); + i++; + } + nla_nest_end(msg, nested); + } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, @@ -17362,11 +17417,32 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, { struct sk_buff *msg; void *hdr; - const u8 *bssid = info->bss ? info->bss->bssid : info->bssid; + size_t link_info_size = 0; + unsigned int link; + const u8 *connected_addr = info->ap_mld_addr ? + info->ap_mld_addr : + (info->links[0].bss ? + info->links[0].bss->bssid : + info->links[0].bssid); + + if (info->valid_links) { + for_each_valid_link(info, link) { + /* Nested attribute header */ + link_info_size += NLA_HDRLEN; + /* Link ID */ + link_info_size += nla_total_size(sizeof(u8)); + link_info_size += info->links[link].addr ? + nla_total_size(ETH_ALEN) : 0; + link_info_size += (info->links[link].bssid || + info->links[link].bss) ? + nla_total_size(ETH_ALEN) : 0; + } + } msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len + info->fils.kek_len + info->fils.pmk_len + - (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); + (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + + link_info_size, gfp); if (!msg) return; @@ -17378,7 +17454,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) || (info->req_ie && nla_put(msg, NL80211_ATTR_REQ_IE, info->req_ie_len, info->req_ie)) || @@ -17397,6 +17473,38 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, info->fils.pmkid))) goto nla_put_failure; + if (info->valid_links) { + int i = 1; + struct nlattr *nested; + + nested = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS); + if (!nested) + goto nla_put_failure; + + for_each_valid_link(info, link) { + struct nlattr *nested_mlo_links; + const u8 *bssid = info->links[link].bss ? + info->links[link].bss->bssid : + info->links[link].bssid; + + nested_mlo_links = nla_nest_start(msg, i); + if (!nested_mlo_links) + goto nla_put_failure; + + if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link) || + (bssid && + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid)) || + (info->links[link].addr && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + info->links[link].addr))) + goto nla_put_failure; + + nla_nest_end(msg, nested_mlo_links); + i++; + } + nla_nest_end(msg, nested); + } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index b3c6ce4f85ee..00be498aab2e 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -275,7 +275,7 @@ void cfg80211_conn_work(struct work_struct *work) memset(&cr, 0, sizeof(cr)); cr.status = -1; - cr.bssid = bssid; + cr.links[0].bssid = bssid; cr.timeout_reason = treason; __cfg80211_connect_result(wdev->netdev, &cr, false); } @@ -384,7 +384,7 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) memset(&cr, 0, sizeof(cr)); cr.status = status_code; - cr.bssid = mgmt->bssid; + cr.links[0].bssid = mgmt->bssid; cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; __cfg80211_connect_result(wdev->netdev, &cr, false); } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { @@ -698,6 +698,20 @@ static void disconnect_work(struct work_struct *work) DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); +static void +cfg80211_connect_result_release_bsses(struct wireless_dev *wdev, + struct cfg80211_connect_resp_params *cr) +{ + unsigned int link; + + for_each_valid_link(cr, link) { + if (!cr->links[link].bss) + continue; + cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss)); + cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); + } +} + /* * API calls for drivers implementing connect/disconnect and * SME event handling @@ -715,21 +729,33 @@ void __cfg80211_connect_result(struct net_device *dev, #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif + unsigned int link; + const u8 *connected_addr; + bool bss_not_found = false; ASSERT_WDEV_LOCK(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { - cfg80211_put_bss(wdev->wiphy, cr->bss); - return; + wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) + goto out; + + if (cr->valid_links) { + if (WARN_ON(!cr->ap_mld_addr)) + goto out; + + for_each_valid_link(cr, link) { + if (WARN_ON(!cr->links[link].addr)) + goto out; + } } wdev->unprot_beacon_reported = 0; nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, GFP_KERNEL); + connected_addr = cr->valid_links ? cr->ap_mld_addr : cr->links[0].bssid; #ifdef CONFIG_CFG80211_WEXT - if (wextev) { + if (wextev && !cr->valid_links) { if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = cr->req_ie_len; @@ -746,23 +772,38 @@ void __cfg80211_connect_result(struct net_device *dev, memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) { - memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN); + if (connected_addr && cr->status == WLAN_STATUS_SUCCESS) { + memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); wdev->wext.prev_bssid_valid = true; } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif - if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) { - WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); - cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid, - wdev->u.client.ssid, wdev->u.client.ssid_len, - wdev->conn_bss_type, - IEEE80211_PRIVACY_ANY); - if (cr->bss) - cfg80211_hold_bss(bss_from_pub(cr->bss)); + if (cr->status == WLAN_STATUS_SUCCESS) { + for_each_valid_link(cr, link) { + if (WARN_ON_ONCE(!cr->links[link].bss)) + break; + } + + for_each_valid_link(cr, link) { + if (cr->links[link].bss) + continue; + + cr->links[link].bss = + cfg80211_get_bss(wdev->wiphy, NULL, + cr->links[link].bssid, + wdev->u.client.ssid, + wdev->u.client.ssid_len, + wdev->conn_bss_type, + IEEE80211_PRIVACY_ANY); + if (!cr->links[link].bss) { + bss_not_found = true; + break; + } + cfg80211_hold_bss(bss_from_pub(cr->links[link].bss)); + } } cfg80211_wdev_release_bsses(wdev); @@ -772,26 +813,40 @@ void __cfg80211_connect_result(struct net_device *dev, wdev->connect_keys = NULL; wdev->u.client.ssid_len = 0; wdev->conn_owner_nlportid = 0; - if (cr->bss) { - cfg80211_unhold_bss(bss_from_pub(cr->bss)); - cfg80211_put_bss(wdev->wiphy, cr->bss); - } + cfg80211_connect_result_release_bsses(wdev, cr); cfg80211_sme_free(wdev); return; } - if (WARN_ON(!cr->bss)) + if (WARN_ON(bss_not_found)) { + cfg80211_connect_result_release_bsses(wdev, cr); return; + } - wdev->links[0].client.current_bss = bss_from_pub(cr->bss); + memset(wdev->links, 0, sizeof(wdev->links)); + wdev->valid_links = cr->valid_links; + for_each_valid_link(cr, link) + wdev->links[link].client.current_bss = + bss_from_pub(cr->links[link].bss); wdev->connected = true; - ether_addr_copy(wdev->u.client.connected_addr, cr->bss->bssid); + ether_addr_copy(wdev->u.client.connected_addr, connected_addr); + if (cr->valid_links) { + for_each_valid_link(cr, link) + memcpy(wdev->links[link].addr, cr->links[link].addr, + ETH_ALEN); + } if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP)) cfg80211_upload_connect_keys(wdev); rcu_read_lock(); - country_elem = ieee80211_bss_get_elem(cr->bss, WLAN_EID_COUNTRY); + for_each_valid_link(cr, link) { + country_elem = + ieee80211_bss_get_elem(cr->links[link].bss, + WLAN_EID_COUNTRY); + if (country_elem) + break; + } if (!country_elem) { rcu_read_unlock(); return; @@ -804,12 +859,60 @@ void __cfg80211_connect_result(struct net_device *dev, if (!country_data) return; - regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band, + regulatory_hint_country_ie(wdev->wiphy, + cr->links[link].bss->channel->band, country_data, country_datalen); kfree(country_data); + + return; +out: + for_each_valid_link(cr, link) + cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); } -/* Consumes bss object one way or another */ +static void cfg80211_update_link_bss(struct wireless_dev *wdev, + struct cfg80211_bss **bss) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct cfg80211_internal_bss *ibss; + + if (!*bss) + return; + + ibss = bss_from_pub(*bss); + if (list_empty(&ibss->list)) { + struct cfg80211_bss *found = NULL, *tmp = *bss; + + found = cfg80211_get_bss(wdev->wiphy, NULL, + (*bss)->bssid, + wdev->u.client.ssid, + wdev->u.client.ssid_len, + wdev->conn_bss_type, + IEEE80211_PRIVACY_ANY); + if (found) { + /* The same BSS is already updated so use it + * instead, as it has latest info. + */ + *bss = found; + } else { + /* Update with BSS provided by driver, it will + * be freshly added and ref cnted, we can free + * the old one. + * + * signal_valid can be false, as we are not + * expecting the BSS to be found. + * + * keep the old timestamp to avoid confusion + */ + cfg80211_bss_update(rdev, ibss, false, + ibss->ts); + } + + cfg80211_put_bss(wdev->wiphy, tmp); + } +} + +/* Consumes bss object(s) one way or another */ void cfg80211_connect_done(struct net_device *dev, struct cfg80211_connect_resp_params *params, gfp_t gfp) @@ -819,55 +922,34 @@ void cfg80211_connect_done(struct net_device *dev, struct cfg80211_event *ev; unsigned long flags; u8 *next; + size_t link_info_size = 0; + unsigned int link; - if (params->bss) { - struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss); - - if (list_empty(&ibss->list)) { - struct cfg80211_bss *found = NULL, *tmp = params->bss; - - found = cfg80211_get_bss(wdev->wiphy, NULL, - params->bss->bssid, - wdev->u.client.ssid, wdev->u.client.ssid_len, - wdev->conn_bss_type, - IEEE80211_PRIVACY_ANY); - if (found) { - /* The same BSS is already updated so use it - * instead, as it has latest info. - */ - params->bss = found; - } else { - /* Update with BSS provided by driver, it will - * be freshly added and ref cnted, we can free - * the old one. - * - * signal_valid can be false, as we are not - * expecting the BSS to be found. - * - * keep the old timestamp to avoid confusion - */ - cfg80211_bss_update(rdev, ibss, false, - ibss->ts); - } - - cfg80211_put_bss(wdev->wiphy, tmp); - } + for_each_valid_link(params, link) { + cfg80211_update_link_bss(wdev, ¶ms->links[link].bss); + link_info_size += params->links[link].bssid ? ETH_ALEN : 0; + link_info_size += params->links[link].addr ? ETH_ALEN : 0; } - ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + + ev = kzalloc(sizeof(*ev) + (params->ap_mld_addr ? ETH_ALEN : 0) + params->req_ie_len + params->resp_ie_len + params->fils.kek_len + params->fils.pmk_len + - (params->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); + (params->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, + gfp); + if (!ev) { - cfg80211_put_bss(wdev->wiphy, params->bss); + for_each_valid_link(params, link) + cfg80211_put_bss(wdev->wiphy, + params->links[link].bss); return; } ev->type = EVENT_CONNECT_RESULT; next = ((u8 *)ev) + sizeof(*ev); - if (params->bssid) { - ev->cr.bssid = next; - memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN); + if (params->ap_mld_addr) { + ev->cr.ap_mld_addr = next; + memcpy((void *)ev->cr.ap_mld_addr, params->ap_mld_addr, + ETH_ALEN); next += ETH_ALEN; } if (params->req_ie_len) { @@ -907,9 +989,28 @@ void cfg80211_connect_done(struct net_device *dev, ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num; if (params->fils.update_erp_next_seq_num) ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num; - if (params->bss) - cfg80211_hold_bss(bss_from_pub(params->bss)); - ev->cr.bss = params->bss; + ev->cr.valid_links = params->valid_links; + for_each_valid_link(params, link) { + if (params->links[link].bss) + cfg80211_hold_bss( + bss_from_pub(params->links[link].bss)); + ev->cr.links[link].bss = params->links[link].bss; + + if (params->links[link].addr) { + ev->cr.links[link].addr = next; + memcpy((void *)ev->cr.links[link].addr, + params->links[link].addr, + ETH_ALEN); + next += ETH_ALEN; + } + if (params->links[link].bssid) { + ev->cr.links[link].bssid = next; + memcpy((void *)ev->cr.links[link].bssid, + params->links[link].bssid, + ETH_ALEN); + next += ETH_ALEN; + } + } ev->cr.status = params->status; ev->cr.timeout_reason = params->timeout_reason; @@ -927,6 +1028,9 @@ void __cfg80211_roamed(struct wireless_dev *wdev, #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif + unsigned int link; + const u8 *connected_addr; + ASSERT_WDEV_LOCK(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && @@ -936,48 +1040,76 @@ void __cfg80211_roamed(struct wireless_dev *wdev, if (WARN_ON(!wdev->connected)) goto out; + if (info->valid_links) { + if (WARN_ON(!info->ap_mld_addr)) + goto out; + + for_each_valid_link(info, link) { + if (WARN_ON(!info->links[link].addr)) + goto out; + } + } + cfg80211_wdev_release_bsses(wdev); - if (WARN_ON(!info->bss)) - return; + for_each_valid_link(info, link) { + if (WARN_ON(!info->links[link].bss)) + goto out; + } - cfg80211_hold_bss(bss_from_pub(info->bss)); - wdev->links[0].client.current_bss = bss_from_pub(info->bss); - ether_addr_copy(wdev->u.client.connected_addr, info->bss->bssid); + memset(wdev->links, 0, sizeof(wdev->links)); + wdev->valid_links = info->valid_links; + for_each_valid_link(info, link) { + cfg80211_hold_bss(bss_from_pub(info->links[link].bss)); + wdev->links[link].client.current_bss = + bss_from_pub(info->links[link].bss); + } + connected_addr = info->valid_links ? + info->ap_mld_addr : + info->links[0].bss->bssid; + ether_addr_copy(wdev->u.client.connected_addr, connected_addr); + if (info->valid_links) { + for_each_valid_link(info, link) + memcpy(wdev->links[link].addr, info->links[link].addr, + ETH_ALEN); + } wdev->unprot_beacon_reported = 0; nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy), wdev->netdev, info, GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT - if (info->req_ie) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = info->req_ie_len; - wireless_send_event(wdev->netdev, IWEVASSOCREQIE, - &wrqu, info->req_ie); - } + if (!info->valid_links) { + if (info->req_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = info->req_ie_len; + wireless_send_event(wdev->netdev, IWEVASSOCREQIE, + &wrqu, info->req_ie); + } + + if (info->resp_ie) { + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = info->resp_ie_len; + wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, + &wrqu, info->resp_ie); + } - if (info->resp_ie) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = info->resp_ie_len; - wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, - &wrqu, info->resp_ie); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); + wdev->wext.prev_bssid_valid = true; + wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN); - wdev->wext.prev_bssid_valid = true; - wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif return; out: - cfg80211_put_bss(wdev->wiphy, info->bss); + for_each_valid_link(info, link) + cfg80211_put_bss(wdev->wiphy, info->links[link].bss); } -/* Consumes info->bss object one way or another */ +/* Consumes info->links.bss object(s) one way or another */ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, gfp_t gfp) { @@ -986,25 +1118,41 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, struct cfg80211_event *ev; unsigned long flags; u8 *next; + unsigned int link; + size_t link_info_size = 0; + bool bss_not_found = false; + + for_each_valid_link(info, link) { + link_info_size += info->links[link].addr ? ETH_ALEN : 0; + link_info_size += info->links[link].bssid ? ETH_ALEN : 0; - if (!info->bss) { - info->bss = cfg80211_get_bss(wdev->wiphy, info->channel, - info->bssid, wdev->u.client.ssid, - wdev->u.client.ssid_len, - wdev->conn_bss_type, - IEEE80211_PRIVACY_ANY); + if (info->links[link].bss) + continue; + + info->links[link].bss = + cfg80211_get_bss(wdev->wiphy, + info->links[link].channel, + info->links[link].bssid, + wdev->u.client.ssid, + wdev->u.client.ssid_len, + wdev->conn_bss_type, + IEEE80211_PRIVACY_ANY); + + if (!info->links[link].bss) { + bss_not_found = true; + break; + } } - if (WARN_ON(!info->bss)) - return; + if (WARN_ON(bss_not_found)) + goto out; ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len + info->fils.kek_len + info->fils.pmk_len + - (info->fils.pmkid ? WLAN_PMKID_LEN : 0), gfp); - if (!ev) { - cfg80211_put_bss(wdev->wiphy, info->bss); - return; - } + (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + + (info->ap_mld_addr ? ETH_ALEN : 0) + link_info_size, gfp); + if (!ev) + goto out; ev->type = EVENT_ROAMED; next = ((u8 *)ev) + sizeof(*ev); @@ -1044,12 +1192,43 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num; if (info->fils.update_erp_next_seq_num) ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; - ev->rm.bss = info->bss; + if (info->ap_mld_addr) { + ev->rm.ap_mld_addr = next; + memcpy((void *)ev->rm.ap_mld_addr, info->ap_mld_addr, + ETH_ALEN); + next += ETH_ALEN; + } + ev->rm.valid_links = info->valid_links; + for_each_valid_link(info, link) { + ev->rm.links[link].bss = info->links[link].bss; + + if (info->links[link].addr) { + ev->rm.links[link].addr = next; + memcpy((void *)ev->rm.links[link].addr, + info->links[link].addr, + ETH_ALEN); + next += ETH_ALEN; + } + + if (info->links[link].bssid) { + ev->rm.links[link].bssid = next; + memcpy((void *)ev->rm.links[link].bssid, + info->links[link].bssid, + ETH_ALEN); + next += ETH_ALEN; + } + } spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); + + return; +out: + for_each_valid_link(info, link) + cfg80211_put_bss(wdev->wiphy, info->links[link].bss); + } EXPORT_SYMBOL(cfg80211_roamed); -- cgit v1.2.3-59-g8ed1b From ecad3b0b99bff7247a11f8c7cb19ac9b0cb28b09 Mon Sep 17 00:00:00 2001 From: Veerendranath Jakkam Date: Mon, 23 May 2022 18:55:58 +0530 Subject: wifi: cfg80211: Increase akm_suites array size in cfg80211_crypto_settings Increase akm_suites array size in struct cfg80211_crypto_settings to 10 and advertise the capability to userspace. This allows userspace to send more than two AKMs to driver in netlink commands such as NL80211_CMD_CONNECT. This capability is needed for implementing WPA3-Personal transition mode correctly with any driver that handles roaming internally. Currently, the possible AKMs for multi-AKM connect can include PSK, PSK-SHA-256, SAE, FT-PSK and FT-SAE. Since the count is already 5, increasing the akm_suites array size to 10 should be reasonable for future usecases. Signed-off-by: Veerendranath Jakkam Link: https://lore.kernel.org/r/1653312358-12321-1-git-send-email-quic_vjakkam@quicinc.com Signed-off-by: Johannes Berg --- drivers/net/wireless/quantenna/qtnfmac/commands.c | 12 ++++++++---- include/net/cfg80211.h | 11 ++++++++++- include/uapi/linux/nl80211.h | 14 ++++++++++++++ net/wireless/core.c | 6 ++++++ net/wireless/nl80211.c | 7 ++++++- 5 files changed, 44 insertions(+), 6 deletions(-) (limited to 'include/uapi/linux') diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index 3d734a7a5ba8..0fad53693292 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -241,6 +241,7 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, struct qlink_auth_encr *aen; int ret; int i; + int n; if (!qtnf_cmd_start_ap_can_fit(vif, s)) return -E2BIG; @@ -280,8 +281,9 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) aen->ciphers_pairwise[i] = cpu_to_le32(s->crypto.ciphers_pairwise[i]); - aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites); - for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) + n = min(QLINK_MAX_NR_AKM_SUITES, s->crypto.n_akm_suites); + aen->n_akm_suites = cpu_to_le32(n); + for (i = 0; i < n; i++) aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]); aen->control_port = s->crypto.control_port; aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt; @@ -2076,6 +2078,7 @@ int qtnf_cmd_send_connect(struct qtnf_vif *vif, struct qlink_auth_encr *aen; int ret; int i; + int n; u32 connect_flags = 0; cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, @@ -2132,9 +2135,10 @@ int qtnf_cmd_send_connect(struct qtnf_vif *vif, aen->ciphers_pairwise[i] = cpu_to_le32(sme->crypto.ciphers_pairwise[i]); - aen->n_akm_suites = cpu_to_le32(sme->crypto.n_akm_suites); + n = min(QLINK_MAX_NR_AKM_SUITES, sme->crypto.n_akm_suites); + aen->n_akm_suites = cpu_to_le32(n); - for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) + for (i = 0; i < n; i++) aen->akm_suites[i] = cpu_to_le32(sme->crypto.akm_suites[i]); aen->control_port = sme->crypto.control_port; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 87ebed6a48bd..6bc161d653f3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1063,6 +1063,7 @@ struct survey_info { }; #define CFG80211_MAX_WEP_KEYS 4 +#define CFG80211_MAX_NUM_AKM_SUITES 10 /** * struct cfg80211_crypto_settings - Crypto settings @@ -1114,7 +1115,7 @@ struct cfg80211_crypto_settings { int n_ciphers_pairwise; u32 ciphers_pairwise[NL80211_MAX_NR_CIPHER_SUITES]; int n_akm_suites; - u32 akm_suites[NL80211_MAX_NR_AKM_SUITES]; + u32 akm_suites[CFG80211_MAX_NUM_AKM_SUITES]; bool control_port; __be16 control_port_ethertype; bool control_port_no_encrypt; @@ -5200,6 +5201,13 @@ struct wiphy_iftype_akm_suites { * @ema_max_profile_periodicity: maximum profile periodicity supported by * the driver. Setting this field to a non-zero value indicates that the * driver supports enhanced multi-BSSID advertisements (EMA AP). + * @max_num_akm_suites: maximum number of AKM suites allowed for + * configuration through %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and + * %NL80211_CMD_START_AP. Set to NL80211_MAX_NR_AKM_SUITES if not set by + * driver. If set by driver minimum allowed value is + * NL80211_MAX_NR_AKM_SUITES in order to avoid compatibility issues with + * legacy userspace and maximum allowed value is + * CFG80211_MAX_NUM_AKM_SUITES. */ struct wiphy { struct mutex mtx; @@ -5346,6 +5354,7 @@ struct wiphy { u8 mbssid_max_interfaces; u8 ema_max_profile_periodicity; + u16 max_num_akm_suites; char priv[] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 89f64f46b98d..279f9715919e 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2694,6 +2694,13 @@ enum nl80211_commands { * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not * included in NL80211_CMD_CONNECT drivers must not perform MLO connection. * + * @NL80211_ATTR_MAX_NUM_AKM_SUITES: U16 attribute. Indicates maximum number of + * AKM suites allowed for %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and + * %NL80211_CMD_START_AP in %NL80211_CMD_GET_WIPHY response. If this + * attribute is not present userspace shall consider maximum number of AKM + * suites allowed as %NL80211_MAX_NR_AKM_SUITES which is the legacy maximum + * number prior to the introduction of this attribute. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3214,6 +3221,8 @@ enum nl80211_attrs { NL80211_ATTR_MLO_SUPPORT, + NL80211_ATTR_MAX_NUM_AKM_SUITES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3268,6 +3277,11 @@ enum nl80211_attrs { #define NL80211_HE_MIN_CAPABILITY_LEN 16 #define NL80211_HE_MAX_CAPABILITY_LEN 54 #define NL80211_MAX_NR_CIPHER_SUITES 5 + +/* + * NL80211_MAX_NR_AKM_SUITES is obsolete when %NL80211_ATTR_MAX_NUM_AKM_SUITES + * present in %NL80211_CMD_GET_WIPHY response. + */ #define NL80211_MAX_NR_AKM_SUITES 2 #define NL80211_EHT_MIN_CAPABILITY_LEN 13 #define NL80211_EHT_MAX_CAPABILITY_LEN 51 diff --git a/net/wireless/core.c b/net/wireless/core.c index 3e5d12040726..6b5321bb1176 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -913,6 +913,12 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; #endif + if (!wiphy->max_num_akm_suites) + wiphy->max_num_akm_suites = NL80211_MAX_NR_AKM_SUITES; + else if (wiphy->max_num_akm_suites < NL80211_MAX_NR_AKM_SUITES || + wiphy->max_num_akm_suites > CFG80211_MAX_NUM_AKM_SUITES) + return -EINVAL; + /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b583a76ef492..e2b6740268a6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -798,6 +798,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS), [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN), [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT }, }; /* policy for the key attributes */ @@ -2932,6 +2933,10 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (nl80211_put_mbssid_support(&rdev->wiphy, msg)) goto nla_put_failure; + if (nla_put_u16(msg, NL80211_ATTR_MAX_NUM_AKM_SUITES, + rdev->wiphy.max_num_akm_suites)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; @@ -10431,7 +10436,7 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, if (len % sizeof(u32)) return -EINVAL; - if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES) + if (settings->n_akm_suites > rdev->wiphy.max_num_akm_suites) return -EINVAL; memcpy(settings->akm_suites, data, len); -- cgit v1.2.3-59-g8ed1b From 3c512307de4097aaaab3f4741c7a98fe88afa469 Mon Sep 17 00:00:00 2001 From: Veerendranath Jakkam Date: Fri, 8 Jul 2022 17:56:07 +0530 Subject: wifi: nl80211: fix sending link ID info of associated BSS commit dd374f84baec ("wifi: nl80211: expose link ID for associated BSSes") used a top-level attribute to send link ID of the associated BSS in the nested attribute NL80211_ATTR_BSS. But since NL80211_ATTR_BSS is a nested attribute of the attributes defined in enum nl80211_bss, define a new attribute in enum nl80211_bss and use it for sending the link ID of the BSS. Fixes: dd374f84baec ("wifi: nl80211: expose link ID for associated BSSes") Signed-off-by: Veerendranath Jakkam Reviewed-by: Jeff Johnson Link: https://lore.kernel.org/r/20220708122607.1836958-1-quic_vjakkam@quicinc.com Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 2 ++ net/wireless/nl80211.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include/uapi/linux') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 279f9715919e..7bb1ae59f3a5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4904,6 +4904,7 @@ enum nl80211_bss_scan_width { * Contains a nested array of signal strength attributes (u8, dBm), * using the nesting index as the antenna number. * @NL80211_BSS_FREQUENCY_OFFSET: frequency offset in KHz + * @NL80211_BSS_MLO_LINK_ID: MLO link ID of the BSS (u8). * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -4929,6 +4930,7 @@ enum nl80211_bss { NL80211_BSS_PARENT_BSSID, NL80211_BSS_CHAIN_SIGNAL, NL80211_BSS_FREQUENCY_OFFSET, + NL80211_BSS_MLO_LINK_ID, /* keep last */ __NL80211_BSS_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e20d0fc9678a..22c4cf6fbb57 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9991,7 +9991,8 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, (nla_put_u32(msg, NL80211_BSS_STATUS, NL80211_BSS_STATUS_ASSOCIATED) || (wdev->valid_links && - nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)))) + nla_put_u8(msg, NL80211_BSS_MLO_LINK_ID, + link_id)))) goto nla_put_failure; } break; -- cgit v1.2.3-59-g8ed1b