diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 88 |
1 files changed, 60 insertions, 28 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9b0a3b8fd20a..ba44f98c56d9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6934,16 +6934,35 @@ static int nl80211_probe_client(struct sk_buff *skb, static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_beacon_registration *reg, *nreg; + int rv; if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) return -EOPNOTSUPP; - if (rdev->ap_beacons_nlportid) - return -EBUSY; + nreg = kzalloc(sizeof(*nreg), GFP_KERNEL); + if (!nreg) + return -ENOMEM; + + /* First, check if already registered. */ + spin_lock_bh(&rdev->beacon_registrations_lock); + list_for_each_entry(reg, &rdev->beacon_registrations, list) { + if (reg->nlportid == info->snd_portid) { + rv = -EALREADY; + goto out_err; + } + } + /* Add it to the list */ + nreg->nlportid = info->snd_portid; + list_add(&nreg->list, &rdev->beacon_registrations); - rdev->ap_beacons_nlportid = info->snd_portid; + spin_unlock_bh(&rdev->beacon_registrations_lock); return 0; +out_err: + spin_unlock_bh(&rdev->beacon_registrations_lock); + kfree(nreg); + return rv; } static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) @@ -8957,43 +8976,46 @@ EXPORT_SYMBOL(cfg80211_probe_status); void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, - int freq, int sig_dbm, gfp_t gfp) + int freq, int sig_dbm) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct sk_buff *msg; void *hdr; - u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid); + struct cfg80211_beacon_registration *reg; trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm); - if (!nlportid) - return; - - msg = nlmsg_new(len + 100, gfp); - if (!msg) - return; + spin_lock_bh(&rdev->beacon_registrations_lock); + list_for_each_entry(reg, &rdev->beacon_registrations, list) { + msg = nlmsg_new(len + 100, GFP_ATOMIC); + if (!msg) { + spin_unlock_bh(&rdev->beacon_registrations_lock); + return; + } - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); - if (!hdr) { - nlmsg_free(msg); - return; - } + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); + if (!hdr) + goto nla_put_failure; - if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - (freq && - nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || - (sig_dbm && - nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || - nla_put(msg, NL80211_ATTR_FRAME, len, frame)) - goto nla_put_failure; + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + (freq && + nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || + (sig_dbm && + nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || + nla_put(msg, NL80211_ATTR_FRAME, len, frame)) + goto nla_put_failure; - genlmsg_end(msg, hdr); + genlmsg_end(msg, hdr); - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid); + } + spin_unlock_bh(&rdev->beacon_registrations_lock); return; nla_put_failure: - genlmsg_cancel(msg, hdr); + spin_unlock_bh(&rdev->beacon_registrations_lock); + if (hdr) + genlmsg_cancel(msg, hdr); nlmsg_free(msg); } EXPORT_SYMBOL(cfg80211_report_obss_beacon); @@ -9005,6 +9027,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb, struct netlink_notify *notify = _notify; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; + struct cfg80211_beacon_registration *reg, *tmp; if (state != NETLINK_URELEASE) return NOTIFY_DONE; @@ -9014,8 +9037,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb, list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) cfg80211_mlme_unregister_socket(wdev, notify->portid); - if (rdev->ap_beacons_nlportid == notify->portid) - rdev->ap_beacons_nlportid = 0; + + spin_lock_bh(&rdev->beacon_registrations_lock); + list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, + list) { + if (reg->nlportid == notify->portid) { + list_del(®->list); + kfree(reg); + break; + } + } + spin_unlock_bh(&rdev->beacon_registrations_lock); } rcu_read_unlock(); |