summaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c365
1 files changed, 330 insertions, 35 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f07602d7bf68..28d2aa109bee 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -176,6 +176,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
[NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
+ [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
};
/* policy for the key attributes */
@@ -204,6 +206,18 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
[NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
+ [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
+};
+
+/* policy for GTK rekey offload attributes */
+static const struct nla_policy
+nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
+ [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
+ [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
+ [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
};
/* ifidx get helper */
@@ -683,8 +697,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
dev->wiphy.coverage_class);
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
dev->wiphy.max_scan_ssids);
+ NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+ dev->wiphy.max_sched_scan_ssids);
NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
dev->wiphy.max_scan_ie_len);
+ NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+ dev->wiphy.max_sched_scan_ie_len);
if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
@@ -920,6 +938,16 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
if (dev->wiphy.wowlan.n_patterns) {
struct nl80211_wowlan_pattern_support pat = {
.max_patterns = dev->wiphy.wowlan.n_patterns,
@@ -3297,7 +3325,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
struct nlattr *attr;
struct wiphy *wiphy;
int err, tmp, n_ssids = 0, n_channels, i;
- enum ieee80211_band band;
size_t ie_len;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
@@ -3317,6 +3344,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (!n_channels)
return -EINVAL;
} else {
+ enum ieee80211_band band;
n_channels = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++)
@@ -3377,6 +3405,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
i++;
}
} else {
+ enum ieee80211_band band;
+
/* all channels */
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
int j;
@@ -3423,6 +3453,30 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
request->ie_len);
}
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ if (wiphy->bands[i])
+ request->rates[i] =
+ (1 << wiphy->bands[i]->n_bitrates) - 1;
+
+ if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
+ nla_for_each_nested(attr,
+ info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
+ tmp) {
+ enum ieee80211_band band = nla_type(attr);
+
+ if (band < 0 || band > IEEE80211_NUM_BANDS) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ err = ieee80211_get_ratemask(wiphy->bands[band],
+ nla_data(attr),
+ nla_len(attr),
+ &request->rates[band]);
+ if (err)
+ goto out_free;
+ }
+ }
+
request->dev = dev;
request->wiphy = &rdev->wiphy;
@@ -3461,9 +3515,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL;
- if (rdev->sched_scan_req)
- return -EINPROGRESS;
-
if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
return -EINVAL;
@@ -3491,7 +3542,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
tmp)
n_ssids++;
- if (n_ssids > wiphy->max_scan_ssids)
+ if (n_ssids > wiphy->max_sched_scan_ssids)
return -EINVAL;
if (info->attrs[NL80211_ATTR_IE])
@@ -3499,15 +3550,24 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
else
ie_len = 0;
- if (ie_len > wiphy->max_scan_ie_len)
+ if (ie_len > wiphy->max_sched_scan_ie_len)
return -EINVAL;
+ mutex_lock(&rdev->sched_scan_mtx);
+
+ if (rdev->sched_scan_req) {
+ err = -EINPROGRESS;
+ goto out;
+ }
+
request = kzalloc(sizeof(*request)
+ sizeof(*request->ssids) * n_ssids
+ sizeof(*request->channels) * n_channels
+ ie_len, GFP_KERNEL);
- if (!request)
- return -ENOMEM;
+ if (!request) {
+ err = -ENOMEM;
+ goto out;
+ }
if (n_ssids)
request->ssids = (void *)&request->channels[n_channels];
@@ -3605,6 +3665,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
out_free:
kfree(request);
out:
+ mutex_unlock(&rdev->sched_scan_mtx);
return err;
}
@@ -3612,15 +3673,21 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
!rdev->ops->sched_scan_stop)
return -EOPNOTSUPP;
- return __cfg80211_stop_sched_scan(rdev, false);
+ mutex_lock(&rdev->sched_scan_mtx);
+ err = __cfg80211_stop_sched_scan(rdev, false);
+ mutex_unlock(&rdev->sched_scan_mtx);
+
+ return err;
}
-static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
+ u32 seq, int flags,
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_internal_bss *intbss)
@@ -3632,11 +3699,13 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
ASSERT_WDEV_LOCK(wdev);
- hdr = nl80211hdr_put(msg, pid, seq, flags,
+ hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
NL80211_CMD_NEW_SCAN_RESULTS);
if (!hdr)
return -1;
+ genl_dump_check_consistent(cb, hdr, &nl80211_fam);
+
NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
@@ -3725,11 +3794,12 @@ static int nl80211_dump_scan(struct sk_buff *skb,
spin_lock_bh(&rdev->bss_lock);
cfg80211_bss_expire(rdev);
+ cb->seq = rdev->bss_generation;
+
list_for_each_entry(scan, &rdev->bss_list, list) {
if (++idx <= start)
continue;
- if (nl80211_send_bss(skb,
- NETLINK_CB(cb->skb).pid,
+ if (nl80211_send_bss(skb, cb,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
rdev, wdev, scan) < 0) {
idx--;
@@ -3753,10 +3823,6 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
void *hdr;
struct nlattr *infoattr;
- /* Survey without a channel doesn't make sense */
- if (!survey->channel)
- return -EINVAL;
-
hdr = nl80211hdr_put(msg, pid, seq, flags,
NL80211_CMD_NEW_SURVEY_RESULTS);
if (!hdr)
@@ -3819,6 +3885,8 @@ static int nl80211_dump_survey(struct sk_buff *skb,
}
while (1) {
+ struct ieee80211_channel *chan;
+
res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
&survey);
if (res == -ENOENT)
@@ -3826,6 +3894,19 @@ static int nl80211_dump_survey(struct sk_buff *skb,
if (res)
goto out_err;
+ /* Survey without a channel doesn't make sense */
+ if (!survey.channel) {
+ res = -EINVAL;
+ goto out;
+ }
+
+ chan = ieee80211_get_channel(&dev->wiphy,
+ survey.channel->center_freq);
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+ survey_idx++;
+ continue;
+ }
+
if (nl80211_send_survey(skb,
NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -4282,25 +4363,12 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
struct ieee80211_supported_band *sband =
wiphy->bands[ibss.channel->band];
- int i, j;
+ int err;
- if (n_rates == 0)
- return -EINVAL;
-
- for (i = 0; i < n_rates; i++) {
- int rate = (rates[i] & 0x7f) * 5;
- bool found = false;
-
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate) {
- found = true;
- ibss.basic_rates |= BIT(j);
- break;
- }
- }
- if (!found)
- return -EINVAL;
- }
+ err = ieee80211_get_ratemask(sband, rates, n_rates,
+ &ibss.basic_rates);
+ if (err)
+ return err;
}
if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
@@ -4360,6 +4428,93 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_testmode_dump(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct cfg80211_registered_device *dev;
+ int err;
+ long phy_idx;
+ void *data = NULL;
+ int data_len = 0;
+
+ if (cb->args[0]) {
+ /*
+ * 0 is a valid index, but not valid for args[0],
+ * so we need to offset by 1.
+ */
+ phy_idx = cb->args[0] - 1;
+ } else {
+ err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+ nl80211_fam.attrbuf, nl80211_fam.maxattr,
+ nl80211_policy);
+ if (err)
+ return err;
+ if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
+ return -EINVAL;
+ phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
+ if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
+ cb->args[1] =
+ (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
+ }
+
+ if (cb->args[1]) {
+ data = nla_data((void *)cb->args[1]);
+ data_len = nla_len((void *)cb->args[1]);
+ }
+
+ mutex_lock(&cfg80211_mutex);
+ dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
+ if (!dev) {
+ mutex_unlock(&cfg80211_mutex);
+ return -ENOENT;
+ }
+ cfg80211_lock_rdev(dev);
+ mutex_unlock(&cfg80211_mutex);
+
+ if (!dev->ops->testmode_dump) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ NL80211_CMD_TESTMODE);
+ struct nlattr *tmdata;
+
+ if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+
+ tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
+ if (!tmdata) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+ err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
+ data, data_len);
+ nla_nest_end(skb, tmdata);
+
+ if (err == -ENOBUFS || err == -ENOENT) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ } else if (err) {
+ genlmsg_cancel(skb, hdr);
+ goto out_err;
+ }
+
+ genlmsg_end(skb, hdr);
+ }
+
+ err = skb->len;
+ /* see above */
+ cb->args[0] = phy_idx + 1;
+ out_err:
+ cfg80211_unlock_rdev(dev);
+ return err;
+}
+
static struct sk_buff *
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 pid, u32 seq, gfp_t gfp)
@@ -5149,6 +5304,14 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
if (rdev->wowlan->magic_pkt)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+ if (rdev->wowlan->gtk_rekey_failure)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
+ if (rdev->wowlan->eap_identity_req)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
+ if (rdev->wowlan->four_way_handshake)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
+ if (rdev->wowlan->rfkill_release)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
if (rdev->wowlan->n_patterns) {
struct nlattr *nl_pats, *nl_pat;
int i, pat_len;
@@ -5225,6 +5388,33 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
new_triggers.magic_pkt = true;
}
+ if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
+ return -EINVAL;
+
+ if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
+ return -EINVAL;
+ new_triggers.gtk_rekey_failure = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
+ return -EINVAL;
+ new_triggers.eap_identity_req = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
+ return -EINVAL;
+ new_triggers.four_way_handshake = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
+ return -EINVAL;
+ new_triggers.rfkill_release = true;
+ }
+
if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
struct nlattr *pat;
int n_patterns = 0;
@@ -5306,6 +5496,57 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_set_rekey_data(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 wireless_dev *wdev = dev->ieee80211_ptr;
+ struct nlattr *tb[NUM_NL80211_REKEY_DATA];
+ struct cfg80211_gtk_rekey_data rekey_data;
+ int err;
+
+ if (!info->attrs[NL80211_ATTR_REKEY_DATA])
+ return -EINVAL;
+
+ err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
+ nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
+ nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
+ nl80211_rekey_policy);
+ if (err)
+ return err;
+
+ if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
+ return -ERANGE;
+ if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
+ return -ERANGE;
+ if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
+ return -ERANGE;
+
+ memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
+ NL80211_KEK_LEN);
+ memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
+ NL80211_KCK_LEN);
+ memcpy(rekey_data.replay_ctr,
+ nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
+ NL80211_REPLAY_CTR_LEN);
+
+ wdev_lock(wdev);
+ if (!wdev->current_bss) {
+ err = -ENOTCONN;
+ goto out;
+ }
+
+ if (!rdev->ops->set_rekey_data) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -5657,6 +5898,7 @@ static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_TESTMODE,
.doit = nl80211_testmode_do,
+ .dumpit = nl80211_testmode_dump,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
@@ -5836,6 +6078,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
+ .doit = nl80211_set_rekey_data,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -6780,6 +7030,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *bssid,
+ const u8 *replay_ctr, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ struct nlattr *rekey_attr;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ 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);
+
+ rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
+ if (!rekey_attr)
+ goto nla_put_failure;
+
+ NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
+ NL80211_REPLAY_CTR_LEN, replay_ctr);
+
+ nla_nest_end(msg, rekey_attr);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,