Merge branch 'master' of git://git.infradead.org/users/linville/wireless
[linux-2.6-block.git] / net / wireless / nl80211.c
index 88a565f130a5a2e2e0f72fca3c204505a810a652..b85075761e2422eaa2f8f2170501359b3e400bab 100644 (file)
 #include "nl80211.h"
 #include "reg.h"
 
+static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type);
+static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
+                                  struct genl_info *info,
+                                  struct cfg80211_crypto_settings *settings,
+                                  int cipher_limit);
+
 static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                            struct genl_info *info);
 static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
@@ -176,6 +182,16 @@ 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 },
+       [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
+       [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
+                                        .len = IEEE80211_MAX_DATA_LEN },
+       [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
+                                        .len = IEEE80211_MAX_DATA_LEN },
+       [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
+       [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -204,6 +220,24 @@ 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 },
+};
+
+static const struct nla_policy
+nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
+       [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY,
+                                                .len = IEEE80211_MAX_SSID_LEN },
 };
 
 /* ifidx get helper */
@@ -683,13 +717,24 @@ 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);
+       NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS,
+                  dev->wiphy.max_match_sets);
 
        if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
                NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
        if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
                NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
+       if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD);
+
+       if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT);
 
        NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
                sizeof(u32) * dev->wiphy.n_cipher_suites,
@@ -853,8 +898,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
                            dev->wiphy.max_remain_on_channel_duration);
 
-       /* for now at least assume all drivers have it */
-       if (dev->ops->mgmt_tx)
+       if (dev->ops->mgmt_tx_cancel_wait)
                NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
 
        if (mgmt_stypes) {
@@ -920,6 +964,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,
@@ -1182,6 +1236,11 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        goto bad_res;
                }
 
+               if (!netdev) {
+                       result = -EINVAL;
+                       goto bad_res;
+               }
+
                nla_for_each_nested(nl_txq_params,
                                    info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
                                    rem_txq_params) {
@@ -1194,6 +1253,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                                goto bad_res;
 
                        result = rdev->ops->set_txq_params(&rdev->wiphy,
+                                                          netdev,
                                                           &txq_params);
                        if (result)
                                goto bad_res;
@@ -1957,7 +2017,10 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        struct beacon_parameters params;
        int haveinfo = 0, err;
 
-       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
+           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
+           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
+           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
                return -EINVAL;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
@@ -1983,6 +2046,49 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
                if (err)
                        return err;
 
+               /*
+                * In theory, some of these attributes could be required for
+                * NEW_BEACON, but since they were not used when the command was
+                * originally added, keep them optional for old user space
+                * programs to work with drivers that do not need the additional
+                * information.
+                */
+               if (info->attrs[NL80211_ATTR_SSID]) {
+                       params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+                       params.ssid_len =
+                               nla_len(info->attrs[NL80211_ATTR_SSID]);
+                       if (params.ssid_len == 0 ||
+                           params.ssid_len > IEEE80211_MAX_SSID_LEN)
+                               return -EINVAL;
+               }
+
+               if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
+                       params.hidden_ssid = nla_get_u32(
+                               info->attrs[NL80211_ATTR_HIDDEN_SSID]);
+                       if (params.hidden_ssid !=
+                           NL80211_HIDDEN_SSID_NOT_IN_USE &&
+                           params.hidden_ssid !=
+                           NL80211_HIDDEN_SSID_ZERO_LEN &&
+                           params.hidden_ssid !=
+                           NL80211_HIDDEN_SSID_ZERO_CONTENTS)
+                               return -EINVAL;
+               }
+
+               params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
+
+               if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+                       params.auth_type = nla_get_u32(
+                               info->attrs[NL80211_ATTR_AUTH_TYPE]);
+                       if (!nl80211_valid_auth_type(params.auth_type))
+                               return -EINVAL;
+               } else
+                       params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+
+               err = nl80211_crypto_settings(rdev, info, &params.crypto,
+                                             NL80211_MAX_NR_CIPHER_SUITES);
+               if (err)
+                       return err;
+
                call = rdev->ops->add_beacon;
                break;
        case NL80211_CMD_SET_BEACON:
@@ -2013,6 +2119,25 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        if (!haveinfo)
                return -EINVAL;
 
+       if (info->attrs[NL80211_ATTR_IE]) {
+               params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
+               params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
+               params.proberesp_ies =
+                       nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
+               params.proberesp_ies_len =
+                       nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
+       }
+
+       if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
+               params.assocresp_ies =
+                       nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+               params.assocresp_ies_len =
+                       nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
+       }
+
        err = call(&rdev->wiphy, dev, &params);
        if (!err && params.interval)
                wdev->beacon_interval = params.interval;
@@ -2209,6 +2334,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
        }
        nla_nest_end(msg, sinfoattr);
 
+       if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES)
+               NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
+                       sinfo->assoc_req_ies);
+
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -2236,6 +2365,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
        }
 
        while (1) {
+               memset(&sinfo, 0, sizeof(sinfo));
                err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
                                             mac_addr, &sinfo);
                if (err == -ENOENT)
@@ -2437,6 +2567,12 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static struct nla_policy
+nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
+       [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
+       [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
+};
+
 static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -2482,6 +2618,33 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
        if (parse_station_flags(info, &params))
                return -EINVAL;
 
+       /* parse WME attributes if sta is WME capable */
+       if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
+           (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
+           info->attrs[NL80211_ATTR_STA_WME]) {
+               struct nlattr *tb[NL80211_STA_WME_MAX + 1];
+               struct nlattr *nla;
+
+               nla = info->attrs[NL80211_ATTR_STA_WME];
+               err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
+                                      nl80211_sta_wme_policy);
+               if (err)
+                       return err;
+
+               if (tb[NL80211_STA_WME_UAPSD_QUEUES])
+                       params.uapsd_queues =
+                            nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
+               if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+                       return -EINVAL;
+
+               if (tb[NL80211_STA_WME_MAX_SP])
+                       params.max_sp =
+                            nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
+
+               if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
+                       return -EINVAL;
+       }
+
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
@@ -2927,6 +3090,10 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
                        cur_params.dot11MeshHWMPnetDiameterTraversalTime);
        NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
                        cur_params.dot11MeshHWMPRootMode);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+                       cur_params.dot11MeshHWMPRannInterval);
+       NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+                       cur_params.dot11MeshGateAnnouncementProtocol);
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
        return genlmsg_reply(msg, info);
@@ -2954,6 +3121,9 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
        [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
        [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
+       [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
 };
 
 static const struct nla_policy
@@ -3032,6 +3202,14 @@ do {\
                        dot11MeshHWMPRootMode, mask,
                        NL80211_MESHCONF_HWMP_ROOTMODE,
                        nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+                       dot11MeshHWMPRannInterval, mask,
+                       NL80211_MESHCONF_HWMP_RANN_INTERVAL,
+                       nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+                       dot11MeshGateAnnouncementProtocol, mask,
+                       NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
+                       nla_get_u8);
        if (mask_out)
                *mask_out = mask;
 
@@ -3297,7 +3475,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 +3494,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 +3555,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;
@@ -3406,11 +3586,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        i = 0;
        if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
-                       request->ssids[i].ssid_len = nla_len(attr);
-                       if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
+                       if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
                                err = -EINVAL;
                                goto out_free;
                        }
+                       request->ssids[i].ssid_len = nla_len(attr);
                        memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
                        i++;
                }
@@ -3423,6 +3603,33 @@ 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->no_cck =
+               nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+
        request->dev = dev;
        request->wiphy = &rdev->wiphy;
 
@@ -3449,10 +3656,11 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        struct net_device *dev = info->user_ptr[1];
        struct nlattr *attr;
        struct wiphy *wiphy;
-       int err, tmp, n_ssids = 0, n_channels, i;
+       int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
        u32 interval;
        enum ieee80211_band band;
        size_t ie_len;
+       struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
            !rdev->ops->sched_scan_start)
@@ -3461,9 +3669,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 +3696,16 @@ 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_SCHED_SCAN_MATCH])
+               nla_for_each_nested(attr,
+                                   info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+                                   tmp)
+                       n_match_sets++;
+
+       if (n_match_sets > wiphy->max_match_sets)
                return -EINVAL;
 
        if (info->attrs[NL80211_ATTR_IE])
@@ -3499,15 +3713,25 @@ 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->match_sets) * n_match_sets
                        + 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];
@@ -3519,6 +3743,18 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                        request->ie = (void *)(request->channels + n_channels);
        }
 
+       if (n_match_sets) {
+               if (request->ie)
+                       request->match_sets = (void *)(request->ie + ie_len);
+               else if (request->ssids)
+                       request->match_sets =
+                               (void *)(request->ssids + n_ssids);
+               else
+                       request->match_sets =
+                               (void *)(request->channels + n_channels);
+       }
+       request->n_match_sets = n_match_sets;
+
        i = 0;
        if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                /* user specified, bail out if channel not found */
@@ -3572,18 +3808,42 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
        if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
                                    tmp) {
-                       request->ssids[i].ssid_len = nla_len(attr);
-                       if (request->ssids[i].ssid_len >
-                           IEEE80211_MAX_SSID_LEN) {
+                       if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
                                err = -EINVAL;
                                goto out_free;
                        }
+                       request->ssids[i].ssid_len = nla_len(attr);
                        memcpy(request->ssids[i].ssid, nla_data(attr),
                               nla_len(attr));
                        i++;
                }
        }
 
+       i = 0;
+       if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+               nla_for_each_nested(attr,
+                                   info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+                                   tmp) {
+                       struct nlattr *ssid;
+
+                       nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
+                                 nla_data(attr), nla_len(attr),
+                                 nl80211_match_policy);
+                       ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID];
+                       if (ssid) {
+                               if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
+                                       err = -EINVAL;
+                                       goto out_free;
+                               }
+                               memcpy(request->match_sets[i].ssid.ssid,
+                                      nla_data(ssid), nla_len(ssid));
+                               request->match_sets[i].ssid.ssid_len =
+                                       nla_len(ssid);
+                       }
+                       i++;
+               }
+       }
+
        if (info->attrs[NL80211_ATTR_IE]) {
                request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
                memcpy((void *)request->ie,
@@ -3606,6 +3866,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 out_free:
        kfree(request);
 out:
+       mutex_unlock(&rdev->sched_scan_mtx);
        return err;
 }
 
@@ -3613,15 +3874,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)
@@ -3633,11 +3900,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);
 
@@ -3726,11 +3995,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--;
@@ -3754,10 +4024,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)
@@ -3820,6 +4086,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)
@@ -3827,6 +4095,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,
@@ -3855,22 +4136,6 @@ static bool nl80211_valid_wpa_versions(u32 wpa_versions)
                                  NL80211_WPA_VERSION_2));
 }
 
-static bool nl80211_valid_akm_suite(u32 akm)
-{
-       return akm == WLAN_AKM_SUITE_8021X ||
-               akm == WLAN_AKM_SUITE_PSK;
-}
-
-static bool nl80211_valid_cipher_suite(u32 cipher)
-{
-       return cipher == WLAN_CIPHER_SUITE_WEP40 ||
-               cipher == WLAN_CIPHER_SUITE_WEP104 ||
-               cipher == WLAN_CIPHER_SUITE_TKIP ||
-               cipher == WLAN_CIPHER_SUITE_CCMP ||
-               cipher == WLAN_CIPHER_SUITE_AES_CMAC;
-}
-
-
 static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -4003,7 +4268,8 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
                memcpy(settings->ciphers_pairwise, data, len);
 
                for (i = 0; i < settings->n_ciphers_pairwise; i++)
-                       if (!nl80211_valid_cipher_suite(
+                       if (!cfg80211_supported_cipher_suite(
+                                       &rdev->wiphy,
                                        settings->ciphers_pairwise[i]))
                                return -EINVAL;
        }
@@ -4011,7 +4277,8 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
        if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
                settings->cipher_group =
                        nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
-               if (!nl80211_valid_cipher_suite(settings->cipher_group))
+               if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
+                                                    settings->cipher_group))
                        return -EINVAL;
        }
 
@@ -4024,7 +4291,7 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
 
        if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
                void *data;
-               int len, i;
+               int len;
 
                data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
                len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
@@ -4033,11 +4300,10 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
                if (len % sizeof(u32))
                        return -EINVAL;
 
-               memcpy(settings->akm_suites, data, len);
+               if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
+                       return -EINVAL;
 
-               for (i = 0; i < settings->n_ciphers_pairwise; i++)
-                       if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
-                               return -EINVAL;
+               memcpy(settings->akm_suites, data, len);
        }
 
        return 0;
@@ -4256,8 +4522,12 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 
        wiphy = &rdev->wiphy;
 
-       if (info->attrs[NL80211_ATTR_MAC])
+       if (info->attrs[NL80211_ATTR_MAC]) {
                ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+               if (!is_valid_ether_addr(ibss.bssid))
+                       return -EINVAL;
+       }
        ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
        ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
@@ -4283,25 +4553,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;
-
-               if (n_rates == 0)
-                       return -EINVAL;
-
-               for (i = 0; i < n_rates; i++) {
-                       int rate = (rates[i] & 0x7f) * 5;
-                       bool found = false;
+               int err;
 
-                       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] &&
@@ -4361,6 +4618,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)
@@ -4840,6 +5184,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *msg;
        unsigned int wait = 0;
        bool offchan;
+       bool no_cck;
 
        if (!info->attrs[NL80211_ATTR_FRAME] ||
            !info->attrs[NL80211_ATTR_WIPHY_FREQ])
@@ -4876,6 +5221,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
 
+       no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+
        freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
        chan = rdev_freq_to_chan(rdev, freq, channel_type);
        if (chan == NULL)
@@ -4896,7 +5243,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                                    channel_type_valid, wait,
                                    nla_data(info->attrs[NL80211_ATTR_FRAME]),
                                    nla_len(info->attrs[NL80211_ATTR_FRAME]),
-                                   &cookie);
+                                   no_cck, &cookie);
        if (err)
                goto free_msg;
 
@@ -5150,6 +5497,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;
@@ -5226,6 +5581,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;
@@ -5307,6 +5689,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
@@ -5658,6 +6091,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 |
@@ -5837,6 +6271,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 = {
@@ -6464,7 +6906,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
        if (addr)
                NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
        NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
-       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
+       if (key_id != -1)
+               NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
        if (tsc)
                NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
 
@@ -6780,6 +7223,97 @@ 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_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
+                                   struct net_device *netdev, int index,
+                                   const u8 *bssid, bool preauth, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       struct nlattr *attr;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
+       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);
+
+       attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
+       if (!attr)
+               goto nla_put_failure;
+
+       NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index);
+       NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid);
+       if (preauth)
+               NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH);
+
+       nla_nest_end(msg, 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,