]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/wireless/nl80211.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland...
[karo-tx-linux.git] / net / wireless / nl80211.c
index b3a476fe82725f738f5215b32cdf2296970451d2..b3d3cf8931cb9a3c055df2e497943ec2500cfc51 100644 (file)
@@ -47,22 +47,21 @@ static struct genl_family nl80211_fam = {
 };
 
 /* internal helper: get rdev and dev */
-static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
-                                      struct cfg80211_registered_device **rdev,
-                                      struct net_device **dev)
+static int get_rdev_dev_by_ifindex(struct net *netns, struct nlattr **attrs,
+                                  struct cfg80211_registered_device **rdev,
+                                  struct net_device **dev)
 {
-       struct nlattr **attrs = info->attrs;
        int ifindex;
 
        if (!attrs[NL80211_ATTR_IFINDEX])
                return -EINVAL;
 
        ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
-       *dev = dev_get_by_index(genl_info_net(info), ifindex);
+       *dev = dev_get_by_index(netns, ifindex);
        if (!*dev)
                return -ENODEV;
 
-       *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
+       *rdev = cfg80211_get_dev_from_ifindex(netns, ifindex);
        if (IS_ERR(*rdev)) {
                dev_put(*dev);
                return PTR_ERR(*rdev);
@@ -89,8 +88,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
        [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
 
-       [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
-       [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
+       [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
+       [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN },
 
        [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
        [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
@@ -98,7 +97,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
        [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
        [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
-       [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
+       [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
        [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
 
        [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
@@ -196,6 +195,15 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
        [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
        [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
+       [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
+       [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
+                                     .len = IEEE80211_MAX_DATA_LEN },
+       [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
+       [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_HT_CAPABILITY_MASK] = {
+               .len = NL80211_HT_CAPABILITY_LEN
+       },
+       [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
 };
 
 /* policy for the key attributes */
@@ -203,7 +211,7 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
        [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
        [NL80211_KEY_IDX] = { .type = NLA_U8 },
        [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
-       [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
+       [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
        [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
        [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
        [NL80211_KEY_TYPE] = { .type = NLA_U32 },
@@ -758,6 +766,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
                    dev->wiphy.available_antennas_rx);
 
+       if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD)
+               NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
+                           dev->wiphy.probe_resp_offload);
+
        if ((dev->wiphy.available_antennas_tx ||
             dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
                u32 tx_ant = 0, rx_ant = 0;
@@ -874,7 +886,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        CMD(set_pmksa, SET_PMKSA);
        CMD(del_pmksa, DEL_PMKSA);
        CMD(flush_pmksa, FLUSH_PMKSA);
-       CMD(remain_on_channel, REMAIN_ON_CHANNEL);
+       if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
+               CMD(remain_on_channel, REMAIN_ON_CHANNEL);
        CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
        CMD(mgmt_tx, FRAME);
        CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
@@ -890,6 +903,16 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        }
        if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
                CMD(sched_scan_start, START_SCHED_SCAN);
+       CMD(probe_client, PROBE_CLIENT);
+       CMD(set_noack_map, SET_NOACK_MAP);
+       if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
+               i++;
+               NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS);
+       }
+
+#ifdef CONFIG_NL80211_TESTMODE
+       CMD(testmode_cmd, TESTMODE);
+#endif
 
 #undef CMD
 
@@ -905,11 +928,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 
        nla_nest_end(msg, nl_cmds);
 
-       if (dev->ops->remain_on_channel)
+       if (dev->ops->remain_on_channel &&
+           dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
                NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
                            dev->wiphy.max_remain_on_channel_duration);
 
-       if (dev->ops->mgmt_tx_cancel_wait)
+       if (dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)
                NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
 
        if (mgmt_stypes) {
@@ -1007,6 +1031,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        if (nl80211_put_iface_combinations(&dev->wiphy, msg))
                goto nla_put_failure;
 
+       if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME)
+               NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME,
+                           dev->wiphy.ap_sme_capa);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features);
+
+       if (dev->wiphy.ht_capa_mod_mask)
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
+                       sizeof(*dev->wiphy.ht_capa_mod_mask),
+                       dev->wiphy.ht_capa_mod_mask);
+
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -1725,6 +1760,23 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
 }
 
+static int nl80211_set_noack_map(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];
+       u16 noack_map;
+
+       if (!info->attrs[NL80211_ATTR_NOACK_MAP])
+               return -EINVAL;
+
+       if (!rdev->ops->set_noack_map)
+               return -EOPNOTSUPP;
+
+       noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
+
+       return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
+}
+
 struct get_key_cookie {
        struct sk_buff *msg;
        int error;
@@ -2155,6 +2207,13 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
                        nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
        }
 
+       if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
+               params.probe_resp =
+                       nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
+               params.probe_resp_len =
+                       nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
+       }
+
        err = call(&rdev->wiphy, dev, &params);
        if (!err && params.interval)
                wdev->beacon_interval = params.interval;
@@ -2187,6 +2246,7 @@ static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
        [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
        [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
        [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
+       [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
 };
 
 static int parse_station_flags(struct genl_info *info,
@@ -2330,6 +2390,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
        if (sinfo->filled & STATION_INFO_TX_FAILED)
                NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
                            sinfo->tx_failed);
+       if (sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT)
+               NLA_PUT_U32(msg, NL80211_STA_INFO_BEACON_LOSS,
+                           sinfo->beacon_loss_count);
        if (sinfo->filled & STATION_INFO_BSS_PARAM) {
                bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
                if (!bss_param)
@@ -2453,26 +2516,34 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 /*
  * Get vlan interface making sure it is running and on the right wiphy.
  */
-static int get_vlan(struct genl_info *info,
-                   struct cfg80211_registered_device *rdev,
-                   struct net_device **vlan)
+static struct net_device *get_vlan(struct genl_info *info,
+                                  struct cfg80211_registered_device *rdev)
 {
        struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
-       *vlan = NULL;
-
-       if (vlanattr) {
-               *vlan = dev_get_by_index(genl_info_net(info),
-                                        nla_get_u32(vlanattr));
-               if (!*vlan)
-                       return -ENODEV;
-               if (!(*vlan)->ieee80211_ptr)
-                       return -EINVAL;
-               if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
-                       return -EINVAL;
-               if (!netif_running(*vlan))
-                       return -ENETDOWN;
+       struct net_device *v;
+       int ret;
+
+       if (!vlanattr)
+               return NULL;
+
+       v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
+       if (!v)
+               return ERR_PTR(-ENODEV);
+
+       if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
+               ret = -EINVAL;
+               goto error;
        }
-       return 0;
+
+       if (!netif_running(v)) {
+               ret = -ENETDOWN;
+               goto error;
+       }
+
+       return v;
+ error:
+       dev_put(v);
+       return ERR_PTR(ret);
 }
 
 static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
@@ -2511,6 +2582,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                params.ht_capa =
                        nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
 
+       if (!rdev->ops->change_station)
+               return -EOPNOTSUPP;
+
        if (parse_station_flags(info, &params))
                return -EINVAL;
 
@@ -2522,73 +2596,84 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                params.plink_state =
                    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
 
-       err = get_vlan(info, rdev, &params.vlan);
-       if (err)
-               goto out;
-
-       /* validate settings */
-       err = 0;
-
        switch (dev->ieee80211_ptr->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_P2P_GO:
                /* disallow mesh-specific things */
                if (params.plink_action)
-                       err = -EINVAL;
+                       return -EINVAL;
+
+               /* TDLS can't be set, ... */
+               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+                       return -EINVAL;
+               /*
+                * ... but don't bother the driver with it. This works around
+                * a hostapd/wpa_supplicant issue -- it always includes the
+                * TLDS_PEER flag in the mask even for AP mode.
+                */
+               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+               /* accept only the listed bits */
+               if (params.sta_flags_mask &
+                               ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                                 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+                                 BIT(NL80211_STA_FLAG_WME) |
+                                 BIT(NL80211_STA_FLAG_MFP)))
+                       return -EINVAL;
+
+               /* must be last in here for error handling */
+               params.vlan = get_vlan(info, rdev);
+               if (IS_ERR(params.vlan))
+                       return PTR_ERR(params.vlan);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
                /* disallow things sta doesn't support */
                if (params.plink_action)
-                       err = -EINVAL;
-               if (params.vlan)
-                       err = -EINVAL;
-               if (params.supported_rates &&
-                   !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.ht_capa)
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.listen_interval >= 0)
-                       err = -EINVAL;
-               if (params.sta_flags_mask &
-                               ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
-                                 BIT(NL80211_STA_FLAG_TDLS_PEER)))
-                       err = -EINVAL;
-               /* can't change the TDLS bit */
-               if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
-                   (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)))
-                       err = -EINVAL;
+                       return -EINVAL;
+               /*
+                * Don't allow userspace to change the TDLS_PEER flag,
+                * but silently ignore attempts to change it since we
+                * don't have state here to verify that it doesn't try
+                * to change the flag.
+                */
+               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+               /* reject any changes other than AUTHORIZED */
+               if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
+                       return -EINVAL;
                break;
        case NL80211_IFTYPE_MESH_POINT:
                /* disallow things mesh doesn't support */
                if (params.vlan)
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.ht_capa)
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.listen_interval >= 0)
-                       err = -EINVAL;
+                       return -EINVAL;
+               /*
+                * No special handling for TDLS here -- the userspace
+                * mesh code doesn't have this bug.
+                */
                if (params.sta_flags_mask &
                                ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
                                  BIT(NL80211_STA_FLAG_MFP) |
                                  BIT(NL80211_STA_FLAG_AUTHORIZED)))
-                       err = -EINVAL;
+                       return -EINVAL;
                break;
        default:
-               err = -EINVAL;
+               return -EOPNOTSUPP;
        }
 
-       if (err)
-               goto out;
-
-       if (!rdev->ops->change_station) {
-               err = -EOPNOTSUPP;
-               goto out;
-       }
+       /* be aware of params.vlan when changing code here */
 
        err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
 
- out:
        if (params.vlan)
                dev_put(params.vlan);
 
@@ -2643,70 +2728,81 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                params.plink_action =
                    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
 
+       if (!rdev->ops->add_station)
+               return -EOPNOTSUPP;
+
        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;
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_GO:
+               /* 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;
 
-               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_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 (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 (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
+                       params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
+               }
+               /* TDLS peers cannot be added */
+               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
                        return -EINVAL;
+               /* but don't bother the driver with it */
+               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
 
-               params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
+               /* must be last in here for error handling */
+               params.vlan = get_vlan(info, rdev);
+               if (IS_ERR(params.vlan))
+                       return PTR_ERR(params.vlan);
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               /* TDLS peers cannot be added */
+               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+                       return -EINVAL;
+               break;
+       case NL80211_IFTYPE_STATION:
+               /* Only TDLS peers can be added */
+               if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+                       return -EINVAL;
+               /* Can only add if TDLS ... */
+               if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
+                       return -EOPNOTSUPP;
+               /* ... with external setup is supported */
+               if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
+                       return -EOPNOTSUPP;
+               break;
+       default:
+               return -EOPNOTSUPP;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
-               return -EINVAL;
-
-       /*
-        * Only managed stations can add TDLS peers, and only when the
-        * wiphy supports external TDLS setup.
-        */
-       if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION &&
-           !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
-             (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
-             (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)))
-               return -EINVAL;
-
-       err = get_vlan(info, rdev, &params.vlan);
-       if (err)
-               goto out;
-
-       /* validate settings */
-       err = 0;
-
-       if (!rdev->ops->add_station) {
-               err = -EOPNOTSUPP;
-               goto out;
-       }
+       /* be aware of params.vlan when changing code here */
 
        err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
 
- out:
        if (params.vlan)
                dev_put(params.vlan);
        return err;
@@ -3127,6 +3223,8 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
                        cur_params.dot11MeshHWMPactivePathTimeout);
        NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
                        cur_params.dot11MeshHWMPpreqMinInterval);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+                       cur_params.dot11MeshHWMPperrMinInterval);
        NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
                        cur_params.dot11MeshHWMPnetDiameterTraversalTime);
        NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
@@ -3161,6 +3259,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
        [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
        [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
        [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_HWMP_PERR_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 },
@@ -3235,6 +3334,9 @@ do {\
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
                        mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
                        nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
+                       mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
+                       nla_get_u16);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
                        dot11MeshHWMPnetDiameterTraversalTime,
                        mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
@@ -3357,6 +3459,9 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
 
        NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
                cfg80211_regdomain->alpha2);
+       if (cfg80211_regdomain->dfs_region)
+               NLA_PUT_U8(msg, NL80211_ATTR_DFS_REGION,
+                          cfg80211_regdomain->dfs_region);
 
        nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
        if (!nl_reg_rules)
@@ -3415,6 +3520,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        char *alpha2 = NULL;
        int rem_reg_rules = 0, r = 0;
        u32 num_rules = 0, rule_idx = 0, size_of_regd;
+       u8 dfs_region = 0;
        struct ieee80211_regdomain *rd = NULL;
 
        if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -3425,6 +3531,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 
        alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
 
+       if (info->attrs[NL80211_ATTR_DFS_REGION])
+               dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
+
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
                        rem_reg_rules) {
                num_rules++;
@@ -3452,6 +3561,13 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        rd->alpha2[0] = alpha2[0];
        rd->alpha2[1] = alpha2[1];
 
+       /*
+        * Disable DFS master mode if the DFS region was
+        * not supported or known on this kernel.
+        */
+       if (reg_supported_dfs_region(dfs_region))
+               rd->dfs_region = dfs_region;
+
        nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
                        rem_reg_rules) {
                nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
@@ -4359,6 +4475,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
        int err, ssid_len, ie_len = 0;
        bool use_mfp = false;
+       u32 flags = 0;
+       struct ieee80211_ht_cap *ht_capa = NULL;
+       struct ieee80211_ht_cap *ht_capa_mask = NULL;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -4402,11 +4521,25 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_PREV_BSSID])
                prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
 
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
+               flags |= ASSOC_REQ_DISABLE_HT;
+
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+               ht_capa_mask =
+                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
+
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+               if (!ht_capa_mask)
+                       return -EINVAL;
+               ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+       }
+
        err = nl80211_crypto_settings(rdev, info, &crypto, 1);
        if (!err)
                err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
                                          ssid, ssid_len, ie, ie_len, use_mfp,
-                                         &crypto);
+                                         &crypto, flags, ht_capa,
+                                         ht_capa_mask);
 
        return err;
 }
@@ -4577,13 +4710,41 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
-       ibss.channel = ieee80211_get_channel(wiphy,
-               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+               enum nl80211_channel_type channel_type;
+
+               channel_type = nla_get_u32(
+                               info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+               if (channel_type != NL80211_CHAN_NO_HT &&
+                   channel_type != NL80211_CHAN_HT20 &&
+                   channel_type != NL80211_CHAN_HT40MINUS &&
+                   channel_type != NL80211_CHAN_HT40PLUS)
+                       return -EINVAL;
+
+               if (channel_type != NL80211_CHAN_NO_HT &&
+                   !(wiphy->features & NL80211_FEATURE_HT_IBSS))
+                       return -EINVAL;
+
+               ibss.channel_type = channel_type;
+       } else {
+               ibss.channel_type = NL80211_CHAN_NO_HT;
+       }
+
+       ibss.channel = rdev_freq_to_chan(rdev,
+               nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
+               ibss.channel_type);
        if (!ibss.channel ||
            ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
            ibss.channel->flags & IEEE80211_CHAN_DISABLED)
                return -EINVAL;
 
+       /* Both channels should be able to initiate communication */
+       if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
+            ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
+           !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
+                                         ibss.channel_type))
+               return -EINVAL;
+
        ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
        ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
 
@@ -4662,7 +4823,7 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_testmode_dump(struct sk_buff *skb,
                                 struct netlink_callback *cb)
 {
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        int err;
        long phy_idx;
        void *data = NULL;
@@ -4680,9 +4841,21 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                                  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_WIPHY]) {
+                       phy_idx = nla_get_u32(
+                               nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
+               } else {
+                       struct net_device *netdev;
+
+                       err = get_rdev_dev_by_ifindex(sock_net(skb->sk),
+                                                     nl80211_fam.attrbuf,
+                                                     &rdev, &netdev);
+                       if (err)
+                               return err;
+                       dev_put(netdev);
+                       phy_idx = rdev->wiphy_idx;
+                       cfg80211_unlock_rdev(rdev);
+               }
                if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
                        cb->args[1] =
                                (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
@@ -4694,15 +4867,15 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        }
 
        mutex_lock(&cfg80211_mutex);
-       dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
-       if (!dev) {
+       rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
+       if (!rdev) {
                mutex_unlock(&cfg80211_mutex);
                return -ENOENT;
        }
-       cfg80211_lock_rdev(dev);
+       cfg80211_lock_rdev(rdev);
        mutex_unlock(&cfg80211_mutex);
 
-       if (!dev->ops->testmode_dump) {
+       if (!rdev->ops->testmode_dump) {
                err = -EOPNOTSUPP;
                goto out_err;
        }
@@ -4713,7 +4886,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                                           NL80211_CMD_TESTMODE);
                struct nlattr *tmdata;
 
-               if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
+               if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx) < 0) {
                        genlmsg_cancel(skb, hdr);
                        break;
                }
@@ -4723,8 +4896,8 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                        genlmsg_cancel(skb, hdr);
                        break;
                }
-               err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
-                                             data, data_len);
+               err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb,
+                                              data, data_len);
                nla_nest_end(skb, tmdata);
 
                if (err == -ENOBUFS || err == -ENOENT) {
@@ -4742,7 +4915,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        /* see above */
        cb->args[0] = phy_idx + 1;
  out_err:
-       cfg80211_unlock_rdev(dev);
+       cfg80211_unlock_rdev(rdev);
        return err;
 }
 
@@ -4896,6 +5069,22 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                        return PTR_ERR(connkeys);
        }
 
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
+               connect.flags |= ASSOC_REQ_DISABLE_HT;
+
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+               memcpy(&connect.ht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+                      sizeof(connect.ht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+                       return -EINVAL;
+               memcpy(&connect.ht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+                      sizeof(connect.ht_capa));
+       }
+
        err = cfg80211_connect(rdev, dev, &connect, connkeys);
        if (err)
                kfree(connkeys);
@@ -5083,7 +5272,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
            duration > rdev->wiphy.max_remain_on_channel_duration)
                return -EINVAL;
 
-       if (!rdev->ops->remain_on_channel)
+       if (!rdev->ops->remain_on_channel ||
+           !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
                return -EOPNOTSUPP;
 
        if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
@@ -5271,12 +5461,13 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        bool channel_type_valid = false;
        u32 freq;
        int err;
-       void *hdr;
+       void *hdr = NULL;
        u64 cookie;
-       struct sk_buff *msg;
+       struct sk_buff *msg = NULL;
        unsigned int wait = 0;
-       bool offchan;
-       bool no_cck;
+       bool offchan, no_cck, dont_wait_for_ack;
+
+       dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
 
        if (!info->attrs[NL80211_ATTR_FRAME] ||
            !info->attrs[NL80211_ATTR_WIPHY_FREQ])
@@ -5295,7 +5486,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                return -EOPNOTSUPP;
 
        if (info->attrs[NL80211_ATTR_DURATION]) {
-               if (!rdev->ops->mgmt_tx_cancel_wait)
+               if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
                        return -EINVAL;
                wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
        }
@@ -5313,6 +5504,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
        offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
 
+       if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
+               return -EINVAL;
+
        no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
 
        freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
@@ -5320,29 +5514,36 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        if (chan == NULL)
                return -EINVAL;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
-               return -ENOMEM;
+       if (!dont_wait_for_ack) {
+               msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+               if (!msg)
+                       return -ENOMEM;
 
-       hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
-                            NL80211_CMD_FRAME);
+               hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+                                    NL80211_CMD_FRAME);
 
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
-               goto free_msg;
+               if (IS_ERR(hdr)) {
+                       err = PTR_ERR(hdr);
+                       goto free_msg;
+               }
        }
+
        err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
                                    channel_type_valid, wait,
                                    nla_data(info->attrs[NL80211_ATTR_FRAME]),
                                    nla_len(info->attrs[NL80211_ATTR_FRAME]),
-                                   no_cck, &cookie);
+                                   no_cck, dont_wait_for_ack, &cookie);
        if (err)
                goto free_msg;
 
-       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+       if (msg) {
+               NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
 
-       genlmsg_end(msg, hdr);
-       return genlmsg_reply(msg, info);
+               genlmsg_end(msg, hdr);
+               return genlmsg_reply(msg, info);
+       }
+
+       return 0;
 
  nla_put_failure:
        err = -ENOBUFS;
@@ -5540,6 +5741,11 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
        setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
        setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
 
+       if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
+           !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
+                           nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
+                       return -EINVAL;
+
        if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
                /* parse additional setup parameters if given */
                err = nl80211_parse_mesh_setup(info, &setup);
@@ -5832,6 +6038,91 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static int nl80211_register_unexpected_frame(struct sk_buff *skb,
+                                            struct genl_info *info)
+{
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+       if (wdev->iftype != NL80211_IFTYPE_AP &&
+           wdev->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EINVAL;
+
+       if (wdev->ap_unexpected_nlpid)
+               return -EBUSY;
+
+       wdev->ap_unexpected_nlpid = info->snd_pid;
+       return 0;
+}
+
+static int nl80211_probe_client(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 sk_buff *msg;
+       void *hdr;
+       const u8 *addr;
+       u64 cookie;
+       int err;
+
+       if (wdev->iftype != NL80211_IFTYPE_AP &&
+           wdev->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!rdev->ops->probe_client)
+               return -EOPNOTSUPP;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+                            NL80211_CMD_PROBE_CLIENT);
+
+       if (IS_ERR(hdr)) {
+               err = PTR_ERR(hdr);
+               goto free_msg;
+       }
+
+       addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
+       if (err)
+               goto free_msg;
+
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+
+       genlmsg_end(msg, hdr);
+
+       return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+       err = -ENOBUFS;
+ free_msg:
+       nlmsg_free(msg);
+       return err;
+}
+
+static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+
+       if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
+               return -EOPNOTSUPP;
+
+       if (rdev->ap_beacons_nlpid)
+               return -EBUSY;
+
+       rdev->ap_beacons_nlpid = info->snd_pid;
+
+       return 0;
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -5859,7 +6150,8 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
                }
                info->user_ptr[0] = rdev;
        } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
-               err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+               err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs,
+                                             &rdev, &dev);
                if (err) {
                        if (rtnl)
                                rtnl_unlock();
@@ -6387,6 +6679,39 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_UNEXPECTED_FRAME,
+               .doit = nl80211_register_unexpected_frame,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_PROBE_CLIENT,
+               .doit = nl80211_probe_client,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_REGISTER_BEACONS,
+               .doit = nl80211_register_beacons,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_SET_NOACK_MAP,
+               .doit = nl80211_set_noack_map,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -6639,10 +6964,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
        if (wiphy_idx_valid(request->wiphy_idx))
                NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        rcu_read_lock();
        genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
@@ -6678,10 +7000,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
        NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -6762,10 +7081,7 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
        NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -6821,10 +7137,7 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
        if (resp_ie)
                NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -6862,10 +7175,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
        if (resp_ie)
                NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -6903,10 +7213,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
        if (ie)
                NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, GFP_KERNEL);
@@ -6939,10 +7246,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -6977,10 +7281,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
        if (ie_len && ie)
                NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -7019,10 +7320,7 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
        if (tsc)
                NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -7073,10 +7371,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        rcu_read_lock();
        genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
@@ -7119,10 +7414,7 @@ static void nl80211_send_remain_on_chan_event(
        if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
                NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -7193,10 +7485,7 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -7207,13 +7496,68 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
+static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
+                                      const u8 *addr, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       int err;
+       u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
+
+       if (!nlpid)
+               return false;
+
+       msg = nlmsg_new(100, gfp);
+       if (!msg)
+               return true;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return true;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+       err = genlmsg_end(msg, hdr);
+       if (err < 0) {
+               nlmsg_free(msg);
+               return true;
+       }
+
+       genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
+       return true;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+       return true;
+}
+
+bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
+{
+       return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
+                                         addr, gfp);
+}
+
+bool nl80211_unexpected_4addr_frame(struct net_device *dev,
+                                   const u8 *addr, gfp_t gfp)
+{
+       return __nl80211_unexpected_frame(dev,
+                                         NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+                                         addr, gfp);
+}
+
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
                      struct net_device *netdev, u32 nlpid,
                      int freq, const u8 *buf, size_t len, gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
-       int err;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
@@ -7230,16 +7574,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
        NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
 
-       err = genlmsg_end(msg, hdr);
-       if (err < 0) {
-               nlmsg_free(msg);
-               return err;
-       }
+       genlmsg_end(msg, hdr);
 
-       err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
-       if (err < 0)
-               return err;
-       return 0;
+       return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
 
  nla_put_failure:
        genlmsg_cancel(msg, hdr);
@@ -7272,10 +7609,7 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
        if (ack)
                NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
        return;
@@ -7317,10 +7651,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
 
        nla_nest_end(msg, pinfoattr);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -7362,10 +7693,7 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
 
        nla_nest_end(msg, rekey_attr);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -7408,10 +7736,7 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
 
        nla_nest_end(msg, attr);
 
-       if (genlmsg_end(msg, hdr) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
+       genlmsg_end(msg, hdr);
 
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
@@ -7453,7 +7778,45 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
 
        nla_nest_end(msg, pinfoattr);
 
-       if (genlmsg_end(msg, hdr) < 0) {
+       genlmsg_end(msg, hdr);
+
+       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 cfg80211_probe_status(struct net_device *dev, const u8 *addr,
+                          u64 cookie, bool acked, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       int err;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+       if (acked)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
+
+       err = genlmsg_end(msg, hdr);
+       if (err < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -7466,6 +7829,45 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_probe_status);
+
+void cfg80211_report_obss_beacon(struct wiphy *wiphy,
+                                const u8 *frame, size_t len,
+                                int freq, gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
+
+       if (!nlpid)
+               return;
+
+       msg = nlmsg_new(len + 100, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       if (freq)
+               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame);
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_report_obss_beacon);
 
 static int nl80211_netlink_notify(struct notifier_block * nb,
                                  unsigned long state,
@@ -7480,9 +7882,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
 
        rcu_read_lock();
 
-       list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
+       list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
                list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
                        cfg80211_mlme_unregister_socket(wdev, notify->pid);
+               if (rdev->ap_beacons_nlpid == notify->pid)
+                       rdev->ap_beacons_nlpid = 0;
+       }
 
        rcu_read_unlock();