Commit d648c230 authored by Johannes Berg's avatar Johannes Berg

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: default avatarJohannes Berg <johannes.berg@intel.com>
parent a503a953
...@@ -2718,6 +2718,12 @@ static inline const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 id) ...@@ -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 * Authentication algorithm number, i.e., starting at the Authentication
* transaction sequence number field. * transaction sequence number field.
* @auth_data_len: Length of auth_data buffer in octets * @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_auth_request {
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
...@@ -2728,6 +2734,21 @@ struct cfg80211_auth_request { ...@@ -2728,6 +2734,21 @@ struct cfg80211_auth_request {
u8 key_len, key_idx; u8 key_len, key_idx;
const u8 *auth_data; const u8 *auth_data;
size_t auth_data_len; 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 { ...@@ -2761,6 +2782,8 @@ enum cfg80211_assoc_req_flags {
* given a reference that it must give back to cfg80211_send_rx_assoc() * given a reference that it must give back to cfg80211_send_rx_assoc()
* or to cfg80211_assoc_timeout(). To ensure proper refcounting, new * or to cfg80211_assoc_timeout(). To ensure proper refcounting, new
* association requests while already associating must be rejected. * 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: Extra IEs to add to (Re)Association Request frame or %NULL
* @ie_len: Length of ie buffer in octets * @ie_len: Length of ie buffer in octets
* @use_mfp: Use management frame protection (IEEE 802.11w) in this association * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
...@@ -2785,6 +2808,11 @@ enum cfg80211_assoc_req_flags { ...@@ -2785,6 +2808,11 @@ enum cfg80211_assoc_req_flags {
* with 16 octets of STA Nonce followed by 16 octets of AP Nonce. * with 16 octets of STA Nonce followed by 16 octets of AP Nonce.
* @s1g_capa: S1G capability override * @s1g_capa: S1G capability override
* @s1g_capa_mask: S1G capability override mask * @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_assoc_request {
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
...@@ -2800,6 +2828,9 @@ struct cfg80211_assoc_request { ...@@ -2800,6 +2828,9 @@ struct cfg80211_assoc_request {
size_t fils_kek_len; size_t fils_kek_len;
const u8 *fils_nonces; const u8 *fils_nonces;
struct ieee80211_s1g_cap s1g_capa, s1g_capa_mask; 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;
}; };
/** /**
......
...@@ -2687,6 +2687,8 @@ enum nl80211_commands { ...@@ -2687,6 +2687,8 @@ enum nl80211_commands {
* various commands that need a link ID to operate. * various commands that need a link ID to operate.
* @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
* per-link information and a link ID. * 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 * @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
...@@ -3204,6 +3206,7 @@ enum nl80211_attrs { ...@@ -3204,6 +3206,7 @@ enum nl80211_attrs {
NL80211_ATTR_MLO_LINKS, NL80211_ATTR_MLO_LINKS,
NL80211_ATTR_MLO_LINK_ID, NL80211_ATTR_MLO_LINK_ID,
NL80211_ATTR_MLD_ADDR,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
......
...@@ -241,6 +241,10 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, ...@@ -241,6 +241,10 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
if (!req->bss) if (!req->bss)
return -ENOENT; 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->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
if (!req->key || !req->key_len || if (!req->key || !req->key_len ||
req->key_idx < 0 || req->key_idx > 3) req->key_idx < 0 || req->key_idx > 3)
...@@ -294,10 +298,19 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, ...@@ -294,10 +298,19 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct cfg80211_assoc_request *req) struct cfg80211_assoc_request *req)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
int err; int err, i, j;
ASSERT_WDEV_LOCK(wdev); 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 && if (wdev->connected &&
(!req->prev_bssid || (!req->prev_bssid ||
!ether_addr_equal(wdev->u.client.connected_addr, 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, ...@@ -310,8 +323,19 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
err = rdev_assoc(rdev, dev, req); err = rdev_assoc(rdev, dev, req);
if (!err) { if (!err) {
cfg80211_ref_bss(&rdev->wiphy, req->bss); int link_id;
cfg80211_hold_bss(bss_from_pub(req->bss));
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; return err;
} }
......
...@@ -796,6 +796,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { ...@@ -796,6 +796,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
NLA_POLICY_NESTED_ARRAY(nl80211_policy), NLA_POLICY_NESTED_ARRAY(nl80211_policy),
[NL80211_ATTR_MLO_LINK_ID] = [NL80211_ATTR_MLO_LINK_ID] =
NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS), 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 */ /* policy for the key attributes */
...@@ -10282,6 +10283,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) ...@@ -10282,6 +10283,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
req.key = key.p.key; req.key = key.p.key;
req.key_len = key.p.key_len; req.key_len = key.p.key_len;
req.key_idx = key.idx; 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, req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
IEEE80211_BSS_TYPE_ESS, IEEE80211_BSS_TYPE_ESS,
...@@ -10475,7 +10482,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) ...@@ -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 cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
struct cfg80211_assoc_request req = {}; struct cfg80211_assoc_request req = {};
struct nlattr **attrs = NULL;
const u8 *bssid, *ssid; const u8 *bssid, *ssid;
unsigned int link_id;
int err, ssid_len; int err, ssid_len;
if (dev->ieee80211_ptr->conn_owner_nlportid && if (dev->ieee80211_ptr->conn_owner_nlportid &&
...@@ -10585,9 +10594,81 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) ...@@ -10585,9 +10594,81 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
sizeof(req.s1g_capa)); sizeof(req.s1g_capa));
} }
req.bss = nl80211_assoc_bss(rdev, ssid, ssid_len, info->attrs, &bssid); req.link_id = nl80211_link_id_or_invalid(info->attrs);
if (IS_ERR(req.bss))
return PTR_ERR(req.bss); 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); err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
if (!err) { if (!err) {
...@@ -10605,7 +10686,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) ...@@ -10605,7 +10686,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
wdev_unlock(dev->ieee80211_ptr); 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); cfg80211_put_bss(&rdev->wiphy, req.bss);
kfree(attrs);
return err; return err;
} }
......
...@@ -177,6 +177,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev, ...@@ -177,6 +177,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev,
params->ssid, params->ssid_len, params->ssid, params->ssid_len,
IEEE80211_BSS_TYPE_ESS, IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY_ANY); IEEE80211_PRIVACY_ANY);
auth_req.link_id = -1;
err = cfg80211_mlme_auth(rdev, wdev->netdev, &auth_req); err = cfg80211_mlme_auth(rdev, wdev->netdev, &auth_req);
cfg80211_put_bss(&rdev->wiphy, auth_req.bss); cfg80211_put_bss(&rdev->wiphy, auth_req.bss);
return err; return err;
...@@ -198,6 +199,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev, ...@@ -198,6 +199,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev,
req.ht_capa_mask = params->ht_capa_mask; req.ht_capa_mask = params->ht_capa_mask;
req.vht_capa = params->vht_capa; req.vht_capa = params->vht_capa;
req.vht_capa_mask = params->vht_capa_mask; req.vht_capa_mask = params->vht_capa_mask;
req.link_id = -1;
req.bss = cfg80211_get_bss(&rdev->wiphy, params->channel, req.bss = cfg80211_get_bss(&rdev->wiphy, params->channel,
params->bssid, params->bssid,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment