wifi: cfg80211: report per-link errors during association
authorBenjamin Berg <benjamin.berg@intel.com>
Wed, 20 Sep 2023 18:25:27 +0000 (21:25 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 25 Sep 2023 07:12:34 +0000 (09:12 +0200)
When one of the links (other than the assoc_link) is misconfigured
and cannot work the association will fail. However, userspace was not
able to tell that the operation only failed because of a problem with
one of the links. Fix this, by allowing the driver to set a per-link
error code and reporting the (first) offending link by setting the
bad_attr accordingly.

This only allows us to report the first error, but that is sufficient
for userspace to e.g. remove the offending link and retry.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230920211508.ebe63c0bd513.I40799998f02bf987acee1501a2522dc98bb6eb5a@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
net/wireless/nl80211.c

index 899e9ffa604871f958d8cd876b2b66ae70a0fe16..34c50f7273d1c8b8b815bf5c16ab6810ee1f3def 100644 (file)
@@ -2980,12 +2980,15 @@ struct cfg80211_auth_request {
  * @elems_len: length of the elements
  * @disabled: If set this link should be included during association etc. but it
  *     should not be used until enabled by the AP MLD.
+ * @error: per-link error code, must be <= 0. If there is an error, then the
+ *     operation as a whole must fail.
  */
 struct cfg80211_assoc_link {
        struct cfg80211_bss *bss;
        const u8 *elems;
        size_t elems_len;
        bool disabled;
+       int error;
 };
 
 /**
index cbdf635e60257c151e4fa0a5273cb3e1ca94f080..87b21c0c0f2551832bfd4a893be3329a5221bb50 100644 (file)
@@ -10941,8 +10941,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 
                if (cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
                                           req.ie, req.ie_len)) {
-                       GENL_SET_ERR_MSG(info,
-                                        "non-inheritance makes no sense");
+                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                           info->attrs[NL80211_ATTR_IE],
+                                           "non-inheritance makes no sense");
                        return -EINVAL;
                }
        }
@@ -11067,6 +11068,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 
                        if (!attrs[NL80211_ATTR_MLO_LINK_ID]) {
                                err = -EINVAL;
+                               NL_SET_BAD_ATTR(info->extack, link);
                                goto free;
                        }
 
@@ -11074,6 +11076,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                        /* cannot use the same link ID again */
                        if (req.links[link_id].bss) {
                                err = -EINVAL;
+                               NL_SET_BAD_ATTR(info->extack, link);
                                goto free;
                        }
                        req.links[link_id].bss =
@@ -11081,6 +11084,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                        if (IS_ERR(req.links[link_id].bss)) {
                                err = PTR_ERR(req.links[link_id].bss);
                                req.links[link_id].bss = NULL;
+                               NL_SET_ERR_MSG_ATTR(info->extack,
+                                                   link, "Error fetching BSS for link");
                                goto free;
                        }
 
@@ -11093,8 +11098,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                                if (cfg80211_find_elem(WLAN_EID_FRAGMENT,
                                                       req.links[link_id].elems,
                                                       req.links[link_id].elems_len)) {
-                                       GENL_SET_ERR_MSG(info,
-                                                        "cannot deal with fragmentation");
+                                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                                           attrs[NL80211_ATTR_IE],
+                                                           "cannot deal with fragmentation");
                                        err = -EINVAL;
                                        goto free;
                                }
@@ -11102,8 +11108,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                                if (cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
                                                           req.links[link_id].elems,
                                                           req.links[link_id].elems_len)) {
-                                       GENL_SET_ERR_MSG(info,
-                                                        "cannot deal with non-inheritance");
+                                       NL_SET_ERR_MSG_ATTR(info->extack,
+                                                           attrs[NL80211_ATTR_IE],
+                                                           "cannot deal with non-inheritance");
                                        err = -EINVAL;
                                        goto free;
                                }
@@ -11146,6 +11153,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 
        err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
        if (!err) {
+               struct nlattr *link;
+               int rem = 0;
+
                err = cfg80211_mlme_assoc(rdev, dev, &req);
 
                if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
@@ -11154,6 +11164,34 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                        memcpy(dev->ieee80211_ptr->disconnect_bssid,
                               ap_addr, ETH_ALEN);
                }
+
+               /* Report error from first problematic link */
+               if (info->attrs[NL80211_ATTR_MLO_LINKS]) {
+                       nla_for_each_nested(link,
+                                           info->attrs[NL80211_ATTR_MLO_LINKS],
+                                           rem) {
+                               struct nlattr *link_id_attr =
+                                       nla_find_nested(link, NL80211_ATTR_MLO_LINK_ID);
+
+                               if (!link_id_attr)
+                                       continue;
+
+                               link_id = nla_get_u8(link_id_attr);
+
+                               if (link_id == req.link_id)
+                                       continue;
+
+                               if (!req.links[link_id].error ||
+                                   WARN_ON(req.links[link_id].error > 0))
+                                       continue;
+
+                               WARN_ON(err >= 0);
+
+                               NL_SET_BAD_ATTR(info->extack, link);
+                               err = req.links[link_id].error;
+                               break;
+                       }
+               }
        }
 
 free: