diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 912009fb9dda..ccc4341a0aea 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8464,16 +8464,19 @@ static void cfg80211_sta_set_mld_sinfo(struct station_info *sinfo) sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); } +struct nl80211_dump_station_ctx { + int sta_idx; + u8 mac_addr[ETH_ALEN]; + struct station_info sinfo; +}; + static int nl80211_dump_station(struct sk_buff *skb, struct netlink_callback *cb) { - struct station_info sinfo; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; - u8 mac_addr[ETH_ALEN]; - int sta_idx = cb->args[2]; - bool sinfo_alloc = false; - int err, i; + struct nl80211_dump_station_ctx *ctx = (void *)cb->args[2]; + int err; err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL); if (err) @@ -8481,6 +8484,15 @@ static int nl80211_dump_station(struct sk_buff *skb, /* nl80211_prepare_wdev_dump acquired it in the successful case */ __acquire(&rdev->wiphy.mtx); + if (!ctx) { + ctx = kzalloc_obj(*ctx); + if (!ctx) { + err = -ENOMEM; + goto out_err; + } + cb->args[2] = (long)ctx; + } + if (!wdev->netdev && wdev->iftype != NL80211_IFTYPE_NAN) { err = -EINVAL; goto out_err; @@ -8491,55 +8503,83 @@ static int nl80211_dump_station(struct sk_buff *skb, goto out_err; } - while (1) { - memset(&sinfo, 0, sizeof(sinfo)); + while (true) { + void *hdr; - for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { - sinfo.links[i] = - kzalloc_obj(*sinfo.links[0]); - if (!sinfo.links[i]) { + memset(&ctx->sinfo, 0, sizeof(ctx->sinfo)); + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + ctx->sinfo.links[i] = + kzalloc_obj(*ctx->sinfo.links[0]); + if (!ctx->sinfo.links[i]) { err = -ENOMEM; - goto out_err; + goto out_err_release; } - sinfo_alloc = true; } - err = rdev_dump_station(rdev, wdev, sta_idx, - mac_addr, &sinfo); - if (err == -ENOENT) - break; + err = rdev_dump_station(rdev, wdev, ctx->sta_idx, + ctx->mac_addr, &ctx->sinfo); + if (err == -ENOENT) { + err = skb->len; + goto out_err_release; + } if (err) - goto out_err; + goto out_err_release; - if (sinfo.valid_links) - cfg80211_sta_set_mld_sinfo(&sinfo); + if (ctx->sinfo.valid_links) + cfg80211_sta_set_mld_sinfo(&ctx->sinfo); - /* reset the sinfo_alloc flag as nl80211_send_station() - * always releases sinfo - */ - sinfo_alloc = false; + hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + NL80211_CMD_NEW_STATION); + if (!hdr) { + err = skb->len; + goto out_err_release; + } - if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION, - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev, mac_addr, - &sinfo) < 0) - goto out; + if ((wdev->netdev && + nla_put_u32(skb, NL80211_ATTR_IFINDEX, + wdev->netdev->ifindex)) || + nla_put_u64_64bit(skb, NL80211_ATTR_WDEV, + wdev_id(wdev), NL80211_ATTR_PAD) || + nla_put(skb, NL80211_ATTR_MAC, ETH_ALEN, ctx->mac_addr) || + nla_put_u32(skb, NL80211_ATTR_GENERATION, + ctx->sinfo.generation)) { + genlmsg_cancel(skb, hdr); + err = skb->len; + goto out_err_release; + } - sta_idx++; + if (nl80211_put_sta_info_common(skb, rdev, &ctx->sinfo)) { + genlmsg_cancel(skb, hdr); + err = skb->len; + goto out_err_release; + } + + genlmsg_end(skb, hdr); + cfg80211_sinfo_release_content(&ctx->sinfo); + ctx->sta_idx++; } - out: - cb->args[2] = sta_idx; - err = skb->len; - out_err: - if (sinfo_alloc) - cfg80211_sinfo_release_content(&sinfo); +out_err_release: + cfg80211_sinfo_release_content(&ctx->sinfo); + memset(&ctx->sinfo, 0, sizeof(ctx->sinfo)); +out_err: wiphy_unlock(&rdev->wiphy); return err; } +static int nl80211_dump_station_done(struct netlink_callback *cb) +{ + struct nl80211_dump_station_ctx *ctx = (void *)cb->args[2]; + + if (ctx) { + cfg80211_sinfo_release_content(&ctx->sinfo); + kfree(ctx); + } + return 0; +} + static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -19517,6 +19557,14 @@ static const struct genl_ops nl80211_ops[] = { /* can be retrieved by unprivileged users */ .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), }, + { + .cmd = NL80211_CMD_GET_STATION, + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .doit = nl80211_get_station, + .dumpit = nl80211_dump_station, + .done = nl80211_dump_station_done, + .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV), + }, }; static const struct genl_small_ops nl80211_small_ops[] = { @@ -19616,13 +19664,6 @@ static const struct genl_small_ops nl80211_small_ops[] = { .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_MLO_VALID_LINK_ID), }, - { - .cmd = NL80211_CMD_GET_STATION, - .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, - .doit = nl80211_get_station, - .dumpit = nl80211_dump_station, - .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV), - }, { .cmd = NL80211_CMD_SET_STATION, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,