]> Pileus Git - ~andy/linux/blobdiff - net/wireless/nl80211.c
Merge remote-tracking branch 'wireless-next/master' into mac80211-next
[~andy/linux] / net / wireless / nl80211.c
index 93e3356091ff9cfac5937ce633e1a2efffbfbd83..2d0c19c6133b3586d2f512fcb487396a349bf091 100644 (file)
@@ -30,9 +30,9 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
                                   struct cfg80211_crypto_settings *settings,
                                   int cipher_limit);
 
-static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
+static int nl80211_pre_doit(const 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,
+static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info);
 
 /* the netlink family */
@@ -47,6 +47,25 @@ static struct genl_family nl80211_fam = {
        .post_doit = nl80211_post_doit,
 };
 
+/* multicast groups */
+enum nl80211_multicast_groups {
+       NL80211_MCGRP_CONFIG,
+       NL80211_MCGRP_SCAN,
+       NL80211_MCGRP_REGULATORY,
+       NL80211_MCGRP_MLME,
+       NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
+};
+
+static const struct genl_multicast_group nl80211_mcgrps[] = {
+       [NL80211_MCGRP_CONFIG] = { .name = "config", },
+       [NL80211_MCGRP_SCAN] = { .name = "scan", },
+       [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
+       [NL80211_MCGRP_MLME] = { .name = "mlme", },
+#ifdef CONFIG_NL80211_TESTMODE
+       [NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
+#endif
+};
+
 /* returns ERR_PTR values */
 static struct wireless_dev *
 __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
@@ -358,6 +377,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
        [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
        [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 },
+       [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
+       [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
+       [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
 };
 
 /* policy for the key attributes */
@@ -1166,6 +1188,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
        struct nlattr *nl_bands, *nl_band;
        struct nlattr *nl_freqs, *nl_freq;
        struct nlattr *nl_cmds;
+       struct nlattr *nl_vendor_cmds;
        enum ieee80211_band band;
        struct ieee80211_channel *chan;
        int i;
@@ -1561,6 +1584,19 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
                    (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
                     nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
                        goto nla_put_failure;
+               state->split_start++;
+               break;
+       case 11:
+               nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+               if (!nl_vendor_cmds)
+                       goto nla_put_failure;
+
+               for (i = 0; i < dev->wiphy.n_vendor_commands; i++)
+                       if (nla_put(msg, i + 1,
+                                   sizeof(struct nl80211_vendor_cmd_info),
+                                   &dev->wiphy.vendor_commands[i].info))
+                               goto nla_put_failure;
+               nla_nest_end(msg, nl_vendor_cmds);
 
                /* done */
                state->split_start = 0;
@@ -2670,7 +2706,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
        hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                             NL80211_CMD_NEW_KEY);
        if (!hdr)
-               return -ENOBUFS;
+               goto nla_put_failure;
 
        cookie.msg = msg;
        cookie.idx = key_idx;
@@ -5353,6 +5389,10 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                                err = -EINVAL;
                                goto out_free;
                        }
+
+                       if (!wiphy->bands[band])
+                               continue;
+
                        err = ieee80211_get_ratemask(wiphy->bands[band],
                                                     nla_data(attr),
                                                     nla_len(attr),
@@ -5651,8 +5691,13 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_chan_def chandef;
+       enum nl80211_dfs_regions dfs_region;
        int err;
 
+       dfs_region = reg_get_dfs_region(wdev->wiphy);
+       if (dfs_region == NL80211_DFS_UNSET)
+               return -EINVAL;
+
        err = nl80211_parse_chandef(rdev, info, &chandef);
        if (err)
                return err;
@@ -6677,12 +6722,42 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static struct sk_buff *
+__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
+                           int approxlen, u32 portid, u32 seq,
+                           enum nl80211_commands cmd,
+                           enum nl80211_attrs attr, gfp_t gfp)
+{
+       struct sk_buff *skb;
+       void *hdr;
+       struct nlattr *data;
 
-#ifdef CONFIG_NL80211_TESTMODE
-static struct genl_multicast_group nl80211_testmode_mcgrp = {
-       .name = "testmode",
-};
+       skb = nlmsg_new(approxlen + 100, gfp);
+       if (!skb)
+               return NULL;
+
+       hdr = nl80211hdr_put(skb, portid, seq, 0, cmd);
+       if (!hdr) {
+               kfree_skb(skb);
+               return NULL;
+       }
+
+       if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+               goto nla_put_failure;
+       data = nla_nest_start(skb, attr);
+
+       ((void **)skb->cb)[0] = rdev;
+       ((void **)skb->cb)[1] = hdr;
+       ((void **)skb->cb)[2] = data;
 
+       return skb;
+
+ nla_put_failure:
+       kfree_skb(skb);
+       return NULL;
+}
+
+#ifdef CONFIG_NL80211_TESTMODE
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -6705,11 +6780,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NL80211_ATTR_TESTDATA])
                return -EINVAL;
 
-       rdev->testmode_info = info;
+       rdev->cur_cmd_info = info;
        err = rdev_testmode_cmd(rdev, wdev,
                                nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
                                nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
-       rdev->testmode_info = NULL;
+       rdev->cur_cmd_info = NULL;
 
        return err;
 }
@@ -6809,77 +6884,14 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        return err;
 }
 
-static struct sk_buff *
-__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
-                             int approxlen, u32 portid, u32 seq, gfp_t gfp)
-{
-       struct sk_buff *skb;
-       void *hdr;
-       struct nlattr *data;
-
-       skb = nlmsg_new(approxlen + 100, gfp);
-       if (!skb)
-               return NULL;
-
-       hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE);
-       if (!hdr) {
-               kfree_skb(skb);
-               return NULL;
-       }
-
-       if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
-               goto nla_put_failure;
-       data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
-
-       ((void **)skb->cb)[0] = rdev;
-       ((void **)skb->cb)[1] = hdr;
-       ((void **)skb->cb)[2] = data;
-
-       return skb;
-
- nla_put_failure:
-       kfree_skb(skb);
-       return NULL;
-}
-
-struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
-                                                 int approxlen)
-{
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-
-       if (WARN_ON(!rdev->testmode_info))
-               return NULL;
-
-       return __cfg80211_testmode_alloc_skb(rdev, approxlen,
-                               rdev->testmode_info->snd_portid,
-                               rdev->testmode_info->snd_seq,
-                               GFP_KERNEL);
-}
-EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
-
-int cfg80211_testmode_reply(struct sk_buff *skb)
-{
-       struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
-       void *hdr = ((void **)skb->cb)[1];
-       struct nlattr *data = ((void **)skb->cb)[2];
-
-       if (WARN_ON(!rdev->testmode_info)) {
-               kfree_skb(skb);
-               return -EINVAL;
-       }
-
-       nla_nest_end(skb, data);
-       genlmsg_end(skb, hdr);
-       return genlmsg_reply(skb, rdev->testmode_info);
-}
-EXPORT_SYMBOL(cfg80211_testmode_reply);
-
 struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
                                                  int approxlen, gfp_t gfp)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
 
-       return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
+       return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
+                                          NL80211_CMD_TESTMODE,
+                                          NL80211_ATTR_TESTDATA, gfp);
 }
 EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
 
@@ -6891,8 +6903,8 @@ void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
 
        nla_nest_end(skb, data);
        genlmsg_end(skb, hdr);
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), skb, 0,
-                               nl80211_testmode_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
+                               NL80211_MCGRP_TESTMODE, gfp);
 }
 EXPORT_SYMBOL(cfg80211_testmode_event);
 #endif
@@ -7319,8 +7331,8 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
 static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
        [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
                                    .len = NL80211_MAX_SUPP_RATES },
-       [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
-                                .len = NL80211_MAX_SUPP_HT_RATES },
+       [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
+                               .len = NL80211_MAX_SUPP_HT_RATES },
 };
 
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
@@ -7334,9 +7346,6 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
        struct nlattr *tx_rates;
        struct ieee80211_supported_band *sband;
 
-       if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
-               return -EINVAL;
-
        if (!rdev->ops->set_bitrate_mask)
                return -EOPNOTSUPP;
 
@@ -7344,17 +7353,20 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
        /* Default to all rates enabled */
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                sband = rdev->wiphy.bands[i];
-               mask.control[i].legacy =
-                       sband ? (1 << sband->n_bitrates) - 1 : 0;
-               if (sband)
-                       memcpy(mask.control[i].mcs,
-                              sband->ht_cap.mcs.rx_mask,
-                              sizeof(mask.control[i].mcs));
-               else
-                       memset(mask.control[i].mcs, 0,
-                              sizeof(mask.control[i].mcs));
+
+               if (!sband)
+                       continue;
+
+               mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
+               memcpy(mask.control[i].ht_mcs,
+                      sband->ht_cap.mcs.rx_mask,
+                      sizeof(mask.control[i].ht_mcs));
        }
 
+       /* if no rates are given set it back to the defaults */
+       if (!info->attrs[NL80211_ATTR_TX_RATES])
+               goto out;
+
        /*
         * The nested attribute uses enum nl80211_band as the index. This maps
         * directly to the enum ieee80211_band values used in cfg80211.
@@ -7379,12 +7391,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                            nla_len(tb[NL80211_TXRATE_LEGACY]))
                                return -EINVAL;
                }
-               if (tb[NL80211_TXRATE_MCS]) {
+               if (tb[NL80211_TXRATE_HT]) {
                        if (!ht_rateset_to_mask(
                                        sband,
-                                       nla_data(tb[NL80211_TXRATE_MCS]),
-                                       nla_len(tb[NL80211_TXRATE_MCS]),
-                                       mask.control[band].mcs))
+                                       nla_data(tb[NL80211_TXRATE_HT]),
+                                       nla_len(tb[NL80211_TXRATE_HT]),
+                                       mask.control[band].ht_mcs))
                                return -EINVAL;
                }
 
@@ -7395,7 +7407,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                                return -EINVAL;
 
                        for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
-                               if (mask.control[band].mcs[i])
+                               if (mask.control[band].ht_mcs[i])
                                        break;
 
                        /* legacy and mcs rates may not be both empty */
@@ -7404,6 +7416,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                }
        }
 
+out:
        return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
 }
 
@@ -8863,6 +8876,111 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
        return 0;
 }
 
+static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev =
+               __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+       int i, err;
+       u32 vid, subcmd;
+
+       if (!rdev->wiphy.vendor_commands)
+               return -EOPNOTSUPP;
+
+       if (IS_ERR(wdev)) {
+               err = PTR_ERR(wdev);
+               if (err != -EINVAL)
+                       return err;
+               wdev = NULL;
+       } else if (wdev->wiphy != &rdev->wiphy) {
+               return -EINVAL;
+       }
+
+       if (!info->attrs[NL80211_ATTR_VENDOR_ID] ||
+           !info->attrs[NL80211_ATTR_VENDOR_SUBCMD])
+               return -EINVAL;
+
+       vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]);
+       subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]);
+       for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
+               const struct wiphy_vendor_command *vcmd;
+               void *data = NULL;
+               int len = 0;
+
+               vcmd = &rdev->wiphy.vendor_commands[i];
+
+               if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
+                       continue;
+
+               if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
+                                  WIPHY_VENDOR_CMD_NEED_NETDEV)) {
+                       if (!wdev)
+                               return -EINVAL;
+                       if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
+                           !wdev->netdev)
+                               return -EINVAL;
+
+                       if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
+                               if (wdev->netdev &&
+                                   !netif_running(wdev->netdev))
+                                       return -ENETDOWN;
+                               if (!wdev->netdev && !wdev->p2p_started)
+                                       return -ENETDOWN;
+                       }
+               } else {
+                       wdev = NULL;
+               }
+
+               if (info->attrs[NL80211_ATTR_VENDOR_DATA]) {
+                       data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+                       len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]);
+               }
+
+               rdev->cur_cmd_info = info;
+               err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev,
+                                                         data, len);
+               rdev->cur_cmd_info = NULL;
+               return err;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
+                                          enum nl80211_commands cmd,
+                                          enum nl80211_attrs attr,
+                                          int approxlen)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       if (WARN_ON(!rdev->cur_cmd_info))
+               return NULL;
+
+       return __cfg80211_alloc_vendor_skb(rdev, approxlen,
+                                          rdev->cur_cmd_info->snd_portid,
+                                          rdev->cur_cmd_info->snd_seq,
+                                          cmd, attr, GFP_KERNEL);
+}
+EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);
+
+int cfg80211_vendor_cmd_reply(struct sk_buff *skb)
+{
+       struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
+       void *hdr = ((void **)skb->cb)[1];
+       struct nlattr *data = ((void **)skb->cb)[2];
+
+       if (WARN_ON(!rdev->cur_cmd_info)) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       nla_nest_end(skb, data);
+       genlmsg_end(skb, hdr);
+       return genlmsg_reply(skb, rdev->cur_cmd_info);
+}
+EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply);
+
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -8874,7 +8992,7 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
 #define NL80211_FLAG_NEED_WDEV_UP      (NL80211_FLAG_NEED_WDEV |\
                                         NL80211_FLAG_CHECK_NETDEV_UP)
 
-static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
+static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                            struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev;
@@ -8943,7 +9061,7 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
        return 0;
 }
 
-static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
+static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info)
 {
        if (info->user_ptr[1]) {
@@ -8960,7 +9078,7 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
                rtnl_unlock();
 }
 
-static struct genl_ops nl80211_ops[] = {
+static const struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_GET_WIPHY,
                .doit = nl80211_get_wiphy,
@@ -9587,21 +9705,14 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
-};
-
-static struct genl_multicast_group nl80211_mlme_mcgrp = {
-       .name = "mlme",
-};
-
-/* multicast groups */
-static struct genl_multicast_group nl80211_config_mcgrp = {
-       .name = "config",
-};
-static struct genl_multicast_group nl80211_scan_mcgrp = {
-       .name = "scan",
-};
-static struct genl_multicast_group nl80211_regulatory_mcgrp = {
-       .name = "regulatory",
+       {
+               .cmd = NL80211_CMD_VENDOR,
+               .doit = nl80211_vendor_cmd,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 /* notification functions */
@@ -9620,8 +9731,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_config_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_CONFIG, GFP_KERNEL);
 }
 
 static int nl80211_add_scan_req(struct sk_buff *msg,
@@ -9656,8 +9767,9 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
            nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
                goto nla_put_failure;
 
-       if (req->flags)
-               nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags);
+       if (req->flags &&
+           nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags))
+               goto nla_put_failure;
 
        return 0;
  nla_put_failure:
@@ -9730,8 +9842,8 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
@@ -9749,8 +9861,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
@@ -9768,8 +9880,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
@@ -9787,8 +9899,8 @@ void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
@@ -9805,8 +9917,8 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
 /*
@@ -9860,8 +9972,8 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
        genlmsg_end(msg, hdr);
 
        rcu_read_lock();
-       genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
-                               GFP_ATOMIC);
+       genlmsg_multicast_allns(&nl80211_fam, msg, 0,
+                               NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
        rcu_read_unlock();
 
        return;
@@ -9896,8 +10008,8 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -9984,8 +10096,8 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10040,8 +10152,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10079,8 +10191,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10117,8 +10229,8 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, GFP_KERNEL);
        return;
 
  nla_put_failure:
@@ -10151,8 +10263,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10192,8 +10304,8 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10231,8 +10343,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10284,8 +10396,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        genlmsg_end(msg, hdr);
 
        rcu_read_lock();
-       genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
-                               GFP_ATOMIC);
+       genlmsg_multicast_allns(&nl80211_fam, msg, 0,
+                               NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
        rcu_read_unlock();
 
        return;
@@ -10330,8 +10442,8 @@ static void nl80211_send_remain_on_chan_event(
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10385,8 +10497,8 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
                return;
        }
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
 }
 EXPORT_SYMBOL(cfg80211_new_sta);
 
@@ -10415,8 +10527,8 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10451,8 +10563,8 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10613,8 +10725,8 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10662,8 +10774,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10707,8 +10819,8 @@ static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10765,8 +10877,8 @@ nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10812,8 +10924,8 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10886,8 +10998,8 @@ void cfg80211_cqm_txe_notify(struct net_device *dev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10935,8 +11047,8 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -10982,8 +11094,8 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -11022,8 +11134,8 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -11113,6 +11225,8 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                struct nlattr *reasons;
 
                reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+               if (!reasons)
+                       goto free_msg;
 
                if (wakeup->disconnect &&
                    nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT))
@@ -11138,16 +11252,18 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
                                wakeup->pattern_idx))
                        goto free_msg;
 
-               if (wakeup->tcp_match)
-                       nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH);
+               if (wakeup->tcp_match &&
+                   nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH))
+                       goto free_msg;
 
-               if (wakeup->tcp_connlost)
-                       nla_put_flag(msg,
-                                    NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST);
+               if (wakeup->tcp_connlost &&
+                   nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST))
+                       goto free_msg;
 
-               if (wakeup->tcp_nomoretokens)
-                       nla_put_flag(msg,
-                               NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS);
+               if (wakeup->tcp_nomoretokens &&
+                   nla_put_flag(msg,
+                                NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS))
+                       goto free_msg;
 
                if (wakeup->packet) {
                        u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211;
@@ -11174,8 +11290,8 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  free_msg:
@@ -11216,8 +11332,8 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
        return;
 
  nla_put_failure:
@@ -11283,24 +11399,29 @@ void cfg80211_ft_event(struct net_device *netdev,
                return;
 
        hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
-       if (!hdr) {
-               nlmsg_free(msg);
-               return;
-       }
+       if (!hdr)
+               goto out;
 
-       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, ft_event->target_ap);
-       if (ft_event->ies)
-               nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
-       if (ft_event->ric_ies)
-               nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
-                       ft_event->ric_ies);
+       if (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, ft_event->target_ap))
+               goto out;
+
+       if (ft_event->ies &&
+           nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies))
+               goto out;
+       if (ft_event->ric_ies &&
+           nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
+                   ft_event->ric_ies))
+               goto out;
 
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
-                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, GFP_KERNEL);
+       return;
+ out:
+       nlmsg_free(msg);
 }
 EXPORT_SYMBOL(cfg80211_ft_event);
 
@@ -11349,33 +11470,11 @@ int nl80211_init(void)
 {
        int err;
 
-       err = genl_register_family_with_ops(&nl80211_fam,
-               nl80211_ops, ARRAY_SIZE(nl80211_ops));
+       err = genl_register_family_with_ops_groups(&nl80211_fam, nl80211_ops,
+                                                  nl80211_mcgrps);
        if (err)
                return err;
 
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
-       if (err)
-               goto err_out;
-
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
-       if (err)
-               goto err_out;
-
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
-       if (err)
-               goto err_out;
-
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
-       if (err)
-               goto err_out;
-
-#ifdef CONFIG_NL80211_TESTMODE
-       err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
-       if (err)
-               goto err_out;
-#endif
-
        err = netlink_register_notifier(&nl80211_netlink_notifier);
        if (err)
                goto err_out;